about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.bundler-audit.yml3
-rw-r--r--.codeclimate.yml39
-rw-r--r--.deepsource.toml23
-rw-r--r--.devcontainer/Dockerfile12
-rw-r--r--.devcontainer/devcontainer.json33
-rw-r--r--.devcontainer/docker-compose.yml24
-rwxr-xr-x.devcontainer/post-create.sh13
-rw-r--r--.devcontainer/welcome-message.txt8
-rw-r--r--.editorconfig1
-rw-r--r--.eslintrc.js166
-rw-r--r--.haml-lint.yml103
-rw-r--r--.haml-lint_todo.yml106
-rwxr-xr-x.husky/pre-commit4
-rw-r--r--.nvmrc2
-rw-r--r--.prettierignore36
-rw-r--r--.rubocop.yml556
-rw-r--r--.rubocop_todo.yml2492
-rw-r--r--.ruby-version2
-rw-r--r--.yarnclean3
-rw-r--r--CHANGELOG.md336
-rw-r--r--CODE_OF_CONDUCT.md20
-rw-r--r--CONTRIBUTING.md28
-rw-r--r--Capfile1
-rw-r--r--Dockerfile11
-rw-r--r--Gemfile59
-rw-r--r--Gemfile.lock467
-rw-r--r--README.md3
-rw-r--r--SECURITY.md5
-rw-r--r--app/controllers/admin/account_actions_controller.rb2
-rw-r--r--app/controllers/admin/dashboard_controller.rb12
-rw-r--r--app/controllers/admin/domain_blocks_controller.rb6
-rw-r--r--app/controllers/admin/email_domain_blocks_controller.rb6
-rw-r--r--app/controllers/admin/export_domain_allows_controller.rb4
-rw-r--r--app/controllers/admin/export_domain_blocks_controller.rb24
-rw-r--r--app/controllers/admin/reports/actions_controller.rb17
-rw-r--r--app/controllers/api/v1/accounts/credentials_controller.rb12
-rw-r--r--app/controllers/api/v1/accounts/follower_accounts_controller.rb8
-rw-r--r--app/controllers/api/v1/accounts/following_accounts_controller.rb8
-rw-r--r--app/controllers/api/v1/accounts/statuses_controller.rb8
-rw-r--r--app/controllers/api/v1/accounts_controller.rb2
-rw-r--r--app/controllers/api/v1/admin/accounts_controller.rb4
-rw-r--r--app/controllers/api/v1/admin/trends/tags_controller.rb8
-rw-r--r--app/controllers/api/v1/announcements_controller.rb4
-rw-r--r--app/controllers/api/v1/blocks_controller.rb8
-rw-r--r--app/controllers/api/v1/conversations_controller.rb8
-rw-r--r--app/controllers/api/v1/domain_blocks_controller.rb8
-rw-r--r--app/controllers/api/v1/emails/confirmations_controller.rb4
-rw-r--r--app/controllers/api/v1/endorsements_controller.rb8
-rw-r--r--app/controllers/api/v1/favourites_controller.rb8
-rw-r--r--app/controllers/api/v1/follow_requests_controller.rb8
-rw-r--r--app/controllers/api/v1/instances/translation_languages_controller.rb23
-rw-r--r--app/controllers/api/v1/lists/accounts_controller.rb8
-rw-r--r--app/controllers/api/v1/mutes_controller.rb8
-rw-r--r--app/controllers/api/v1/notifications_controller.rb12
-rw-r--r--app/controllers/api/v1/scheduled_statuses_controller.rb8
-rw-r--r--app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb8
-rw-r--r--app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb8
-rw-r--r--app/controllers/api/v1/statuses_controller.rb15
-rw-r--r--app/controllers/api/v1/streaming_controller.rb6
-rw-r--r--app/controllers/api/v1/tags_controller.rb1
-rw-r--r--app/controllers/api/v1/timelines/public_controller.rb2
-rw-r--r--app/controllers/api/v1/trends/links_controller.rb12
-rw-r--r--app/controllers/api/v1/trends/statuses_controller.rb12
-rw-r--r--app/controllers/api/v1/trends/tags_controller.rb12
-rw-r--r--app/controllers/api/v2/admin/accounts_controller.rb4
-rw-r--r--app/controllers/application_controller.rb16
-rw-r--r--app/controllers/auth/confirmations_controller.rb13
-rw-r--r--app/controllers/auth/omniauth_callbacks_controller.rb2
-rw-r--r--app/controllers/auth/registrations_controller.rb6
-rw-r--r--app/controllers/auth/sessions_controller.rb8
-rw-r--r--app/controllers/backups_controller.rb31
-rw-r--r--app/controllers/concerns/account_controller_concern.rb3
-rw-r--r--app/controllers/concerns/admin_export_controller_concern.rb10
-rw-r--r--app/controllers/concerns/cache_concern.rb163
-rw-r--r--app/controllers/concerns/rate_limit_headers.rb14
-rw-r--r--app/controllers/concerns/session_tracking_concern.rb1
-rw-r--r--app/controllers/concerns/signature_verification.rb21
-rw-r--r--app/controllers/concerns/two_factor_authentication_concern.rb16
-rw-r--r--app/controllers/filters/statuses_controller.rb4
-rw-r--r--app/controllers/media_controller.rb2
-rw-r--r--app/controllers/media_proxy_controller.rb2
-rw-r--r--app/controllers/relationships_controller.rb6
-rw-r--r--app/controllers/settings/applications_controller.rb8
-rw-r--r--app/controllers/settings/flavours_controller.rb18
-rw-r--r--app/controllers/settings/preferences_controller.rb43
-rw-r--r--app/controllers/settings/two_factor_authentication/otp_authentication_controller.rb9
-rw-r--r--app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb4
-rw-r--r--app/controllers/statuses_controller.rb5
-rw-r--r--app/controllers/tags_controller.rb13
-rw-r--r--app/helpers/accounts_helper.rb2
-rw-r--r--app/helpers/admin/action_logs_helper.rb2
-rw-r--r--app/helpers/admin/announcements_helper.rb11
-rw-r--r--app/helpers/admin/dashboard_helper.rb24
-rw-r--r--app/helpers/admin/trends/statuses_helper.rb12
-rw-r--r--app/helpers/application_helper.rb13
-rw-r--r--app/helpers/branding_helper.rb14
-rw-r--r--app/helpers/domain_control_helper.rb12
-rw-r--r--app/helpers/email_helper.rb2
-rw-r--r--app/helpers/formatting_helper.rb44
-rw-r--r--app/helpers/home_helper.rb14
-rw-r--r--app/helpers/instance_helper.rb12
-rw-r--r--app/helpers/jsonld_helper.rb20
-rw-r--r--app/helpers/languages_helper.rb9
-rw-r--r--app/javascript/core/embed.js2
-rw-r--r--app/javascript/core/settings.js4
-rw-r--r--app/javascript/flavours/glitch/actions/account_notes.js14
-rw-r--r--app/javascript/flavours/glitch/actions/accounts.js166
-rw-r--r--app/javascript/flavours/glitch/actions/alerts.js6
-rw-r--r--app/javascript/flavours/glitch/actions/blocks.js16
-rw-r--r--app/javascript/flavours/glitch/actions/bookmarks.js16
-rw-r--r--app/javascript/flavours/glitch/actions/boosts.js2
-rw-r--r--app/javascript/flavours/glitch/actions/columns.js6
-rw-r--r--app/javascript/flavours/glitch/actions/compose.js146
-rw-r--r--app/javascript/flavours/glitch/actions/custom_emojis.js8
-rw-r--r--app/javascript/flavours/glitch/actions/domain_blocks.js32
-rw-r--r--app/javascript/flavours/glitch/actions/emojis.js2
-rw-r--r--app/javascript/flavours/glitch/actions/favourites.js16
-rw-r--r--app/javascript/flavours/glitch/actions/height_cache.js4
-rw-r--r--app/javascript/flavours/glitch/actions/interactions.js80
-rw-r--r--app/javascript/flavours/glitch/actions/local_settings.js10
-rw-r--r--app/javascript/flavours/glitch/actions/markers.js12
-rw-r--r--app/javascript/flavours/glitch/actions/modal.js4
-rw-r--r--app/javascript/flavours/glitch/actions/mutes.js16
-rw-r--r--app/javascript/flavours/glitch/actions/notifications.js40
-rw-r--r--app/javascript/flavours/glitch/actions/onboarding.js2
-rw-r--r--app/javascript/flavours/glitch/actions/pin_statuses.js8
-rw-r--r--app/javascript/flavours/glitch/actions/push_notifications/registerer.js2
-rw-r--r--app/javascript/flavours/glitch/actions/search.js12
-rw-r--r--app/javascript/flavours/glitch/actions/server.js27
-rw-r--r--app/javascript/flavours/glitch/actions/settings.js4
-rw-r--r--app/javascript/flavours/glitch/actions/statuses.js48
-rw-r--r--app/javascript/flavours/glitch/actions/store.js4
-rw-r--r--app/javascript/flavours/glitch/actions/suggestions.js8
-rw-r--r--app/javascript/flavours/glitch/actions/tags.js82
-rw-r--r--app/javascript/flavours/glitch/actions/timelines.js20
-rw-r--r--app/javascript/flavours/glitch/base_polyfills.js10
-rw-r--r--app/javascript/flavours/glitch/compare_id.js2
-rw-r--r--app/javascript/flavours/glitch/components/account.jsx (renamed from app/javascript/flavours/glitch/components/account.js)15
-rw-r--r--app/javascript/flavours/glitch/components/admin/Counter.jsx (renamed from app/javascript/flavours/glitch/components/admin/Counter.js)0
-rw-r--r--app/javascript/flavours/glitch/components/admin/Dimension.jsx (renamed from app/javascript/flavours/glitch/components/admin/Dimension.js)0
-rw-r--r--app/javascript/flavours/glitch/components/admin/ReportReasonSelector.jsx (renamed from app/javascript/flavours/glitch/components/admin/ReportReasonSelector.js)7
-rw-r--r--app/javascript/flavours/glitch/components/admin/Retention.jsx (renamed from app/javascript/flavours/glitch/components/admin/Retention.js)2
-rw-r--r--app/javascript/flavours/glitch/components/admin/Trends.jsx (renamed from app/javascript/flavours/glitch/components/admin/Trends.js)2
-rw-r--r--app/javascript/flavours/glitch/components/animated_number.jsx (renamed from app/javascript/flavours/glitch/components/animated_number.js)4
-rw-r--r--app/javascript/flavours/glitch/components/attachment_list.jsx (renamed from app/javascript/flavours/glitch/components/attachment_list.js)0
-rw-r--r--app/javascript/flavours/glitch/components/autosuggest_emoji.jsx (renamed from app/javascript/flavours/glitch/components/autosuggest_emoji.js)0
-rw-r--r--app/javascript/flavours/glitch/components/autosuggest_hashtag.jsx (renamed from app/javascript/flavours/glitch/components/autosuggest_hashtag.js)0
-rw-r--r--app/javascript/flavours/glitch/components/autosuggest_input.jsx (renamed from app/javascript/flavours/glitch/components/autosuggest_input.js)22
-rw-r--r--app/javascript/flavours/glitch/components/autosuggest_textarea.jsx (renamed from app/javascript/flavours/glitch/components/autosuggest_textarea.js)22
-rw-r--r--app/javascript/flavours/glitch/components/avatar.jsx (renamed from app/javascript/flavours/glitch/components/avatar.js)4
-rw-r--r--app/javascript/flavours/glitch/components/avatar_composite.jsx (renamed from app/javascript/flavours/glitch/components/avatar_composite.js)0
-rw-r--r--app/javascript/flavours/glitch/components/avatar_overlay.jsx (renamed from app/javascript/flavours/glitch/components/avatar_overlay.js)0
-rw-r--r--app/javascript/flavours/glitch/components/blurhash.jsx (renamed from app/javascript/flavours/glitch/components/blurhash.js)0
-rw-r--r--app/javascript/flavours/glitch/components/button.jsx (renamed from app/javascript/flavours/glitch/components/button.js)4
-rw-r--r--app/javascript/flavours/glitch/components/check.jsx (renamed from app/javascript/flavours/glitch/components/check.js)0
-rw-r--r--app/javascript/flavours/glitch/components/column.jsx (renamed from app/javascript/flavours/glitch/components/column.js)4
-rw-r--r--app/javascript/flavours/glitch/components/column_back_button.jsx (renamed from app/javascript/flavours/glitch/components/column_back_button.js)2
-rw-r--r--app/javascript/flavours/glitch/components/column_back_button_slim.jsx (renamed from app/javascript/flavours/glitch/components/column_back_button_slim.js)4
-rw-r--r--app/javascript/flavours/glitch/components/column_header.jsx (renamed from app/javascript/flavours/glitch/components/column_header.js)19
-rw-r--r--app/javascript/flavours/glitch/components/common_counter.jsx (renamed from app/javascript/flavours/glitch/components/common_counter.js)0
-rw-r--r--app/javascript/flavours/glitch/components/dismissable_banner.jsx (renamed from app/javascript/flavours/glitch/components/dismissable_banner.js)5
-rw-r--r--app/javascript/flavours/glitch/components/display_name.jsx (renamed from app/javascript/flavours/glitch/components/display_name.js)6
-rw-r--r--app/javascript/flavours/glitch/components/domain.jsx (renamed from app/javascript/mastodon/components/domain.js)5
-rw-r--r--app/javascript/flavours/glitch/components/dropdown_menu.jsx (renamed from app/javascript/flavours/glitch/components/dropdown_menu.js)36
-rw-r--r--app/javascript/flavours/glitch/components/edited_timestamp/index.jsx (renamed from app/javascript/flavours/glitch/components/edited_timestamp/index.js)8
-rw-r--r--app/javascript/flavours/glitch/components/error_boundary.jsx (renamed from app/javascript/flavours/glitch/components/error_boundary.js)4
-rw-r--r--app/javascript/flavours/glitch/components/gifv.jsx (renamed from app/javascript/flavours/glitch/components/gifv.js)13
-rw-r--r--app/javascript/flavours/glitch/components/hashtag.jsx (renamed from app/javascript/flavours/glitch/components/hashtag.js)0
-rw-r--r--app/javascript/flavours/glitch/components/icon.jsx (renamed from app/javascript/flavours/glitch/components/icon.js)0
-rw-r--r--app/javascript/flavours/glitch/components/icon_button.jsx (renamed from app/javascript/flavours/glitch/components/icon_button.js)10
-rw-r--r--app/javascript/flavours/glitch/components/icon_with_badge.jsx (renamed from app/javascript/flavours/glitch/components/icon_with_badge.js)0
-rw-r--r--app/javascript/flavours/glitch/components/image.jsx (renamed from app/javascript/flavours/glitch/components/image.js)0
-rw-r--r--app/javascript/flavours/glitch/components/inline_account.jsx (renamed from app/javascript/flavours/glitch/components/inline_account.js)3
-rw-r--r--app/javascript/flavours/glitch/components/intersection_observer_article.jsx (renamed from app/javascript/flavours/glitch/components/intersection_observer_article.js)19
-rw-r--r--app/javascript/flavours/glitch/components/link.jsx (renamed from app/javascript/flavours/glitch/components/link.js)0
-rw-r--r--app/javascript/flavours/glitch/components/load_gap.jsx (renamed from app/javascript/flavours/glitch/components/load_gap.js)5
-rw-r--r--app/javascript/flavours/glitch/components/load_more.jsx (renamed from app/javascript/flavours/glitch/components/load_more.js)4
-rw-r--r--app/javascript/flavours/glitch/components/load_pending.jsx (renamed from app/javascript/flavours/glitch/components/load_pending.js)2
-rw-r--r--app/javascript/flavours/glitch/components/loading_indicator.jsx (renamed from app/javascript/flavours/glitch/components/loading_indicator.js)0
-rw-r--r--app/javascript/flavours/glitch/components/logo.jsx (renamed from app/javascript/flavours/glitch/components/logo.js)0
-rw-r--r--app/javascript/flavours/glitch/components/media_attachments.jsx (renamed from app/javascript/flavours/glitch/components/media_attachments.js)12
-rw-r--r--app/javascript/flavours/glitch/components/media_gallery.jsx (renamed from app/javascript/flavours/glitch/components/media_gallery.js)35
-rw-r--r--app/javascript/flavours/glitch/components/missing_indicator.jsx (renamed from app/javascript/flavours/glitch/components/missing_indicator.js)0
-rw-r--r--app/javascript/flavours/glitch/components/modal_root.jsx (renamed from app/javascript/flavours/glitch/components/modal_root.js)9
-rw-r--r--app/javascript/flavours/glitch/components/navigation_portal.jsx (renamed from app/javascript/flavours/glitch/components/navigation_portal.js)3
-rw-r--r--app/javascript/flavours/glitch/components/not_signed_in_indicator.jsx (renamed from app/javascript/flavours/glitch/components/not_signed_in_indicator.js)0
-rw-r--r--app/javascript/flavours/glitch/components/notification_purge_buttons.jsx (renamed from app/javascript/flavours/glitch/components/notification_purge_buttons.js)3
-rw-r--r--app/javascript/flavours/glitch/components/permalink.jsx (renamed from app/javascript/flavours/glitch/components/permalink.js)4
-rw-r--r--app/javascript/flavours/glitch/components/picture_in_picture_placeholder.jsx (renamed from app/javascript/flavours/glitch/components/picture_in_picture_placeholder.js)9
-rw-r--r--app/javascript/flavours/glitch/components/poll.jsx (renamed from app/javascript/flavours/glitch/components/poll.js)14
-rw-r--r--app/javascript/flavours/glitch/components/radio_button.jsx (renamed from app/javascript/flavours/glitch/components/radio_button.js)0
-rw-r--r--app/javascript/flavours/glitch/components/regeneration_indicator.jsx (renamed from app/javascript/flavours/glitch/components/regeneration_indicator.js)0
-rw-r--r--app/javascript/flavours/glitch/components/relative_timestamp.jsx (renamed from app/javascript/mastodon/components/relative_timestamp.js)3
-rw-r--r--app/javascript/flavours/glitch/components/scrollable_list.jsx (renamed from app/javascript/flavours/glitch/components/scrollable_list.js)25
-rw-r--r--app/javascript/flavours/glitch/components/server_banner.jsx (renamed from app/javascript/flavours/glitch/components/server_banner.js)4
-rw-r--r--app/javascript/flavours/glitch/components/setting_text.jsx (renamed from app/javascript/flavours/glitch/components/setting_text.js)2
-rw-r--r--app/javascript/flavours/glitch/components/short_number.jsx (renamed from app/javascript/flavours/glitch/components/short_number.js)0
-rw-r--r--app/javascript/flavours/glitch/components/skeleton.jsx (renamed from app/javascript/flavours/glitch/components/skeleton.js)0
-rw-r--r--app/javascript/flavours/glitch/components/spoilers.jsx (renamed from app/javascript/flavours/glitch/components/spoilers.js)32
-rw-r--r--app/javascript/flavours/glitch/components/status.jsx (renamed from app/javascript/flavours/glitch/components/status.js)87
-rw-r--r--app/javascript/flavours/glitch/components/status_action_bar.jsx (renamed from app/javascript/flavours/glitch/components/status_action_bar.js)47
-rw-r--r--app/javascript/flavours/glitch/components/status_content.jsx (renamed from app/javascript/flavours/glitch/components/status_content.js)51
-rw-r--r--app/javascript/flavours/glitch/components/status_header.jsx (renamed from app/javascript/flavours/glitch/components/status_header.js)4
-rw-r--r--app/javascript/flavours/glitch/components/status_icons.jsx (renamed from app/javascript/flavours/glitch/components/status_icons.js)25
-rw-r--r--app/javascript/flavours/glitch/components/status_list.jsx (renamed from app/javascript/flavours/glitch/components/status_list.js)12
-rw-r--r--app/javascript/flavours/glitch/components/status_prepend.jsx (renamed from app/javascript/flavours/glitch/components/status_prepend.js)6
-rw-r--r--app/javascript/flavours/glitch/components/status_visibility_icon.jsx (renamed from app/javascript/flavours/glitch/components/status_visibility_icon.js)3
-rw-r--r--app/javascript/flavours/glitch/components/timeline_hint.jsx (renamed from app/javascript/flavours/glitch/components/timeline_hint.js)0
-rw-r--r--app/javascript/flavours/glitch/containers/account_container.jsx (renamed from app/javascript/flavours/glitch/containers/account_container.js)0
-rw-r--r--app/javascript/flavours/glitch/containers/admin_component.jsx (renamed from app/javascript/flavours/glitch/containers/admin_component.js)0
-rw-r--r--app/javascript/flavours/glitch/containers/compose_container.jsx (renamed from app/javascript/flavours/glitch/containers/compose_container.js)0
-rw-r--r--app/javascript/flavours/glitch/containers/domain_container.jsx (renamed from app/javascript/flavours/glitch/containers/domain_container.js)0
-rw-r--r--app/javascript/flavours/glitch/containers/mastodon.jsx (renamed from app/javascript/flavours/glitch/containers/mastodon.js)0
-rw-r--r--app/javascript/flavours/glitch/containers/media_container.jsx (renamed from app/javascript/flavours/glitch/containers/media_container.js)10
-rw-r--r--app/javascript/flavours/glitch/containers/status_container.js15
-rw-r--r--app/javascript/flavours/glitch/extra_polyfills.js1
-rw-r--r--app/javascript/flavours/glitch/features/about/index.jsx (renamed from app/javascript/flavours/glitch/features/about/index.js)10
-rw-r--r--app/javascript/flavours/glitch/features/account/components/account_note.jsx (renamed from app/javascript/flavours/glitch/features/account/components/account_note.js)11
-rw-r--r--app/javascript/flavours/glitch/features/account/components/action_bar.jsx (renamed from app/javascript/flavours/glitch/features/account/components/action_bar.js)7
-rw-r--r--app/javascript/flavours/glitch/features/account/components/featured_tags.jsx (renamed from app/javascript/flavours/glitch/features/account/components/featured_tags.js)3
-rw-r--r--app/javascript/flavours/glitch/features/account/components/follow_request_note.jsx (renamed from app/javascript/flavours/glitch/features/account/components/follow_request_note.js)0
-rw-r--r--app/javascript/flavours/glitch/features/account/components/header.jsx (renamed from app/javascript/flavours/glitch/features/account/components/header.js)28
-rw-r--r--app/javascript/flavours/glitch/features/account/components/profile_column_header.jsx (renamed from app/javascript/flavours/glitch/features/account/components/profile_column_header.js)3
-rw-r--r--app/javascript/flavours/glitch/features/account/navigation.jsx (renamed from app/javascript/flavours/glitch/features/account/navigation.js)3
-rw-r--r--app/javascript/flavours/glitch/features/account_gallery/components/media_item.jsx (renamed from app/javascript/flavours/glitch/features/account_gallery/components/media_item.js)11
-rw-r--r--app/javascript/flavours/glitch/features/account_gallery/index.jsx (renamed from app/javascript/flavours/glitch/features/account_gallery/index.js)19
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/components/header.jsx (renamed from app/javascript/flavours/glitch/features/account_timeline/components/header.js)32
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/components/limited_account_hint.jsx (renamed from app/javascript/flavours/glitch/features/account_timeline/components/limited_account_hint.js)5
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/components/moved_note.jsx (renamed from app/javascript/flavours/glitch/features/account_timeline/components/moved_note.js)4
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/containers/header_container.jsx (renamed from app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js)2
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/index.jsx (renamed from app/javascript/flavours/glitch/features/account_timeline/index.js)9
-rw-r--r--app/javascript/flavours/glitch/features/audio/index.jsx (renamed from app/javascript/flavours/glitch/features/audio/index.js)101
-rw-r--r--app/javascript/flavours/glitch/features/blocks/index.jsx (renamed from app/javascript/flavours/glitch/features/blocks/index.js)4
-rw-r--r--app/javascript/flavours/glitch/features/bookmarked_statuses/index.jsx (renamed from app/javascript/flavours/glitch/features/bookmarked_statuses/index.js)14
-rw-r--r--app/javascript/flavours/glitch/features/closed_registrations_modal/index.jsx (renamed from app/javascript/flavours/glitch/features/closed_registrations_modal/index.js)5
-rw-r--r--app/javascript/flavours/glitch/features/community_timeline/components/column_settings.jsx (renamed from app/javascript/flavours/glitch/features/community_timeline/components/column_settings.js)3
-rw-r--r--app/javascript/flavours/glitch/features/community_timeline/containers/column_settings_container.js2
-rw-r--r--app/javascript/flavours/glitch/features/community_timeline/index.jsx (renamed from app/javascript/flavours/glitch/features/community_timeline/index.js)14
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/action_bar.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/action_bar.js)7
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/autosuggest_account.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/autosuggest_account.js)0
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/character_counter.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/character_counter.js)0
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/compose_form.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/compose_form.js)51
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/dropdown.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/dropdown.js)22
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/dropdown_menu.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js)16
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.js)46
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/header.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/header.js)15
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/language_dropdown.js)37
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/navigation_bar.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/navigation_bar.js)0
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/options.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/options.js)18
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/poll_form.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/poll_form.js)25
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/privacy_dropdown.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/privacy_dropdown.js)3
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/publisher.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/publisher.js)6
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/reply_indicator.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/reply_indicator.js)5
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/search.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/search.js)19
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/search_results.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/search_results.js)8
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/text_icon_button.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/text_icon_button.js)0
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/textarea_icons.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/textarea_icons.js)6
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/upload.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/upload.js)21
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/upload_form.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/upload_form.js)1
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/upload_progress.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/upload_progress.js)0
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/warning.jsx (renamed from app/javascript/flavours/glitch/features/compose/components/warning.js)0
-rw-r--r--app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js35
-rw-r--r--app/javascript/flavours/glitch/features/compose/containers/options_container.js2
-rw-r--r--app/javascript/flavours/glitch/features/compose/containers/poll_form_container.js6
-rw-r--r--app/javascript/flavours/glitch/features/compose/containers/sensitive_button_container.jsx (renamed from app/javascript/flavours/glitch/features/compose/containers/sensitive_button_container.js)0
-rw-r--r--app/javascript/flavours/glitch/features/compose/containers/upload_container.js3
-rw-r--r--app/javascript/flavours/glitch/features/compose/containers/warning_container.jsx (renamed from app/javascript/flavours/glitch/features/compose/containers/warning_container.js)6
-rw-r--r--app/javascript/flavours/glitch/features/compose/index.jsx (renamed from app/javascript/flavours/glitch/features/compose/index.js)8
-rw-r--r--app/javascript/flavours/glitch/features/compose/util/counter.js4
-rw-r--r--app/javascript/flavours/glitch/features/direct_timeline/components/column_settings.jsx (renamed from app/javascript/flavours/glitch/features/direct_timeline/components/column_settings.js)3
-rw-r--r--app/javascript/flavours/glitch/features/direct_timeline/components/conversation.jsx (renamed from app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js)29
-rw-r--r--app/javascript/flavours/glitch/features/direct_timeline/components/conversations_list.jsx (renamed from app/javascript/flavours/glitch/features/direct_timeline/components/conversations_list.js)14
-rw-r--r--app/javascript/flavours/glitch/features/direct_timeline/index.jsx (renamed from app/javascript/flavours/glitch/features/direct_timeline/index.js)20
-rw-r--r--app/javascript/flavours/glitch/features/directory/components/account_card.jsx (renamed from app/javascript/flavours/glitch/features/directory/components/account_card.js)15
-rw-r--r--app/javascript/flavours/glitch/features/directory/index.jsx (renamed from app/javascript/flavours/glitch/features/directory/index.js)18
-rw-r--r--app/javascript/flavours/glitch/features/domain_blocks/index.jsx (renamed from app/javascript/flavours/glitch/features/domain_blocks/index.js)4
-rw-r--r--app/javascript/flavours/glitch/features/emoji/emoji.js4
-rw-r--r--app/javascript/flavours/glitch/features/emoji/emoji_utils.js12
-rw-r--r--app/javascript/flavours/glitch/features/explore/components/story.jsx (renamed from app/javascript/flavours/glitch/features/explore/components/story.js)0
-rw-r--r--app/javascript/flavours/glitch/features/explore/index.jsx (renamed from app/javascript/flavours/glitch/features/explore/index.js)12
-rw-r--r--app/javascript/flavours/glitch/features/explore/links.jsx (renamed from app/javascript/flavours/glitch/features/explore/links.js)3
-rw-r--r--app/javascript/flavours/glitch/features/explore/results.jsx (renamed from app/javascript/flavours/glitch/features/explore/results.js)4
-rw-r--r--app/javascript/flavours/glitch/features/explore/statuses.jsx (renamed from app/javascript/flavours/glitch/features/explore/statuses.js)5
-rw-r--r--app/javascript/flavours/glitch/features/explore/suggestions.jsx (renamed from app/javascript/flavours/glitch/features/explore/suggestions.js)5
-rw-r--r--app/javascript/flavours/glitch/features/explore/tags.jsx (renamed from app/javascript/flavours/glitch/features/explore/tags.js)3
-rw-r--r--app/javascript/flavours/glitch/features/favourited_statuses/index.jsx (renamed from app/javascript/flavours/glitch/features/favourited_statuses/index.js)14
-rw-r--r--app/javascript/flavours/glitch/features/favourites/index.jsx (renamed from app/javascript/flavours/glitch/features/favourites/index.js)10
-rw-r--r--app/javascript/flavours/glitch/features/filters/added_to_filter.jsx (renamed from app/javascript/flavours/glitch/features/filters/added_to_filter.js)3
-rw-r--r--app/javascript/flavours/glitch/features/filters/select_filter.jsx (renamed from app/javascript/flavours/glitch/features/filters/select_filter.js)22
-rw-r--r--app/javascript/flavours/glitch/features/follow_recommendations/components/account.jsx (renamed from app/javascript/flavours/glitch/features/follow_recommendations/components/account.js)8
-rw-r--r--app/javascript/flavours/glitch/features/follow_recommendations/index.jsx (renamed from app/javascript/flavours/glitch/features/follow_recommendations/index.js)5
-rw-r--r--app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.jsx (renamed from app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.js)3
-rw-r--r--app/javascript/flavours/glitch/features/follow_requests/index.jsx (renamed from app/javascript/flavours/glitch/features/follow_requests/index.js)16
-rw-r--r--app/javascript/flavours/glitch/features/followed_tags/index.jsx89
-rw-r--r--app/javascript/flavours/glitch/features/followers/index.jsx (renamed from app/javascript/flavours/glitch/features/followers/index.js)7
-rw-r--r--app/javascript/flavours/glitch/features/following/index.jsx (renamed from app/javascript/flavours/glitch/features/following/index.js)7
-rw-r--r--app/javascript/flavours/glitch/features/generic_not_found/index.jsx (renamed from app/javascript/flavours/glitch/features/generic_not_found/index.js)0
-rw-r--r--app/javascript/flavours/glitch/features/getting_started/components/announcements.jsx (renamed from app/javascript/flavours/glitch/features/getting_started/components/announcements.js)34
-rw-r--r--app/javascript/flavours/glitch/features/getting_started/components/trends.jsx (renamed from app/javascript/flavours/glitch/features/getting_started/components/trends.js)0
-rw-r--r--app/javascript/flavours/glitch/features/getting_started/index.jsx (renamed from app/javascript/flavours/glitch/features/getting_started/index.js)8
-rw-r--r--app/javascript/flavours/glitch/features/getting_started_misc/index.jsx (renamed from app/javascript/flavours/glitch/features/getting_started_misc/index.js)37
-rw-r--r--app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.jsx (renamed from app/javascript/mastodon/features/hashtag_timeline/components/column_settings.js)3
-rw-r--r--app/javascript/flavours/glitch/features/hashtag_timeline/index.jsx (renamed from app/javascript/flavours/glitch/features/hashtag_timeline/index.js)26
-rw-r--r--app/javascript/flavours/glitch/features/home_timeline/components/column_settings.jsx (renamed from app/javascript/flavours/glitch/features/home_timeline/components/column_settings.js)3
-rw-r--r--app/javascript/flavours/glitch/features/home_timeline/index.jsx (renamed from app/javascript/flavours/glitch/features/home_timeline/index.js)16
-rw-r--r--app/javascript/flavours/glitch/features/interaction_modal/index.jsx (renamed from app/javascript/flavours/glitch/features/interaction_modal/index.js)11
-rw-r--r--app/javascript/flavours/glitch/features/keyboard_shortcuts/index.jsx (renamed from app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js)4
-rw-r--r--app/javascript/flavours/glitch/features/list_adder/components/account.jsx (renamed from app/javascript/flavours/glitch/features/list_adder/components/account.js)4
-rw-r--r--app/javascript/flavours/glitch/features/list_adder/components/list.jsx (renamed from app/javascript/flavours/glitch/features/list_adder/components/list.js)6
-rw-r--r--app/javascript/flavours/glitch/features/list_adder/index.jsx (renamed from app/javascript/flavours/glitch/features/list_adder/index.js)4
-rw-r--r--app/javascript/flavours/glitch/features/list_editor/components/account.jsx (renamed from app/javascript/flavours/glitch/features/list_editor/components/account.js)0
-rw-r--r--app/javascript/flavours/glitch/features/list_editor/components/edit_list_form.jsx (renamed from app/javascript/flavours/glitch/features/list_editor/components/edit_list_form.js)10
-rw-r--r--app/javascript/flavours/glitch/features/list_editor/components/search.jsx (renamed from app/javascript/flavours/glitch/features/list_editor/components/search.js)8
-rw-r--r--app/javascript/flavours/glitch/features/list_editor/index.jsx (renamed from app/javascript/flavours/glitch/features/list_editor/index.js)6
-rw-r--r--app/javascript/flavours/glitch/features/list_timeline/index.jsx (renamed from app/javascript/flavours/glitch/features/list_timeline/index.js)26
-rw-r--r--app/javascript/flavours/glitch/features/lists/components/new_list_form.jsx (renamed from app/javascript/flavours/glitch/features/lists/components/new_list_form.js)10
-rw-r--r--app/javascript/flavours/glitch/features/lists/index.jsx (renamed from app/javascript/flavours/glitch/features/lists/index.js)4
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/index.jsx (renamed from app/javascript/flavours/glitch/features/local_settings/index.js)0
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/navigation/index.jsx (renamed from app/javascript/flavours/glitch/features/local_settings/navigation/index.js)5
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/navigation/item/index.jsx (renamed from app/javascript/flavours/glitch/features/local_settings/navigation/item/index.js)4
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/page/deprecated_item/index.jsx (renamed from app/javascript/flavours/glitch/features/local_settings/page/deprecated_item/index.js)0
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/page/index.jsx (renamed from app/javascript/flavours/glitch/features/local_settings/page/index.js)27
-rw-r--r--app/javascript/flavours/glitch/features/local_settings/page/item/index.jsx (renamed from app/javascript/flavours/glitch/features/local_settings/page/item/index.js)14
-rw-r--r--app/javascript/flavours/glitch/features/mutes/index.jsx (renamed from app/javascript/flavours/glitch/features/mutes/index.js)4
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/admin_report.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/admin_report.js)12
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/admin_signup.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/admin_signup.js)12
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/clear_column_button.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/clear_column_button.js)2
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/column_settings.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/column_settings.js)2
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/filter_bar.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/filter_bar.js)3
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/follow.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/follow.js)12
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/follow_request.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/follow_request.js)15
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/grant_permission_button.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/grant_permission_button.js)2
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/notification.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/notification.js)5
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/notifications_permission_banner.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/notifications_permission_banner.js)8
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/overlay.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/overlay.js)5
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/pill_bar_button.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/pill_bar_button.js)6
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/report.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/report.js)3
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/setting_toggle.jsx (renamed from app/javascript/flavours/glitch/features/notifications/components/setting_toggle.js)4
-rw-r--r--app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js3
-rw-r--r--app/javascript/flavours/glitch/features/notifications/index.jsx (renamed from app/javascript/flavours/glitch/features/notifications/index.js)22
-rw-r--r--app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx (renamed from app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js)8
-rw-r--r--app/javascript/flavours/glitch/features/picture_in_picture/components/header.jsx (renamed from app/javascript/flavours/glitch/features/picture_in_picture/components/header.js)4
-rw-r--r--app/javascript/flavours/glitch/features/picture_in_picture/index.jsx (renamed from app/javascript/flavours/glitch/features/picture_in_picture/index.js)5
-rw-r--r--app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js2
-rw-r--r--app/javascript/flavours/glitch/features/pinned_accounts_editor/index.jsx (renamed from app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js)6
-rw-r--r--app/javascript/flavours/glitch/features/pinned_statuses/index.jsx (renamed from app/javascript/flavours/glitch/features/pinned_statuses/index.js)8
-rw-r--r--app/javascript/flavours/glitch/features/privacy_policy/index.jsx (renamed from app/javascript/flavours/glitch/features/privacy_policy/index.js)3
-rw-r--r--app/javascript/flavours/glitch/features/public_timeline/components/column_settings.jsx (renamed from app/javascript/flavours/glitch/features/public_timeline/components/column_settings.js)3
-rw-r--r--app/javascript/flavours/glitch/features/public_timeline/containers/column_settings_container.js2
-rw-r--r--app/javascript/flavours/glitch/features/public_timeline/index.jsx (renamed from app/javascript/flavours/glitch/features/public_timeline/index.js)14
-rw-r--r--app/javascript/flavours/glitch/features/reblogs/index.jsx (renamed from app/javascript/flavours/glitch/features/reblogs/index.js)10
-rw-r--r--app/javascript/flavours/glitch/features/report/category.jsx (renamed from app/javascript/flavours/glitch/features/report/category.js)4
-rw-r--r--app/javascript/flavours/glitch/features/report/comment.jsx (renamed from app/javascript/flavours/glitch/features/report/comment.js)3
-rw-r--r--app/javascript/flavours/glitch/features/report/components/option.jsx (renamed from app/javascript/flavours/glitch/features/report/components/option.js)6
-rw-r--r--app/javascript/flavours/glitch/features/report/components/status_check_box.jsx (renamed from app/javascript/flavours/glitch/features/report/components/status_check_box.js)0
-rw-r--r--app/javascript/flavours/glitch/features/report/rules.jsx (renamed from app/javascript/flavours/glitch/features/report/rules.js)3
-rw-r--r--app/javascript/flavours/glitch/features/report/statuses.jsx (renamed from app/javascript/flavours/glitch/features/report/statuses.js)3
-rw-r--r--app/javascript/flavours/glitch/features/report/thanks.jsx (renamed from app/javascript/flavours/glitch/features/report/thanks.js)3
-rw-r--r--app/javascript/flavours/glitch/features/standalone/compose/index.jsx (renamed from app/javascript/flavours/glitch/features/standalone/compose/index.js)2
-rw-r--r--app/javascript/flavours/glitch/features/status/components/action_bar.jsx (renamed from app/javascript/flavours/glitch/features/status/components/action_bar.js)37
-rw-r--r--app/javascript/flavours/glitch/features/status/components/card.jsx (renamed from app/javascript/flavours/glitch/features/status/components/card.js)11
-rw-r--r--app/javascript/flavours/glitch/features/status/components/detailed_status.jsx (renamed from app/javascript/flavours/glitch/features/status/components/detailed_status.js)26
-rw-r--r--app/javascript/flavours/glitch/features/status/index.jsx (renamed from app/javascript/flavours/glitch/features/status/index.js)108
-rw-r--r--app/javascript/flavours/glitch/features/subscribed_languages_modal/index.jsx (renamed from app/javascript/flavours/glitch/features/subscribed_languages_modal/index.js)6
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/actions_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/actions_modal.js)4
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/audio_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/audio_modal.js)8
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/block_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/block_modal.js)12
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/boost_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/boost_modal.js)16
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/bundle.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/bundle.js)10
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/bundle_column_error.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/bundle_column_error.js)11
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/bundle_modal_error.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/bundle_modal_error.js)4
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/column.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/column.js)6
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/column_header.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/column_header.js)2
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/column_link.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/column_link.js)4
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/column_loading.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/column_loading.js)0
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/column_subheading.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/column_subheading.js)0
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/columns_area.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/columns_area.js)12
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/compare_history_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/compare_history_modal.js)14
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/compose_panel.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/compose_panel.js)5
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/confirmation_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js)13
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.js)7
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/disabled_account_banner.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/disabled_account_banner.js)8
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/doodle_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/doodle_modal.js)9
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/drawer_loading.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/drawer_loading.js)0
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/embed_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/embed_modal.js)9
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/favourite_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/favourite_modal.js)11
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/filter_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/filter_modal.js)4
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/focal_point_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js)47
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/follow_requests_column_link.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/follow_requests_column_link.js)4
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/header.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/header.js)4
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/image_loader.jsx (renamed from app/javascript/mastodon/features/ui/components/image_loader.js)13
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/image_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/image_modal.js)3
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/link_footer.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/link_footer.js)24
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/list_panel.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/list_panel.js)4
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/media_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/media_modal.js)29
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/modal_loading.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/modal_loading.js)0
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/modal_root.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/modal_root.js)16
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/mute_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/mute_modal.js)14
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/navigation_panel.js)5
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/onboarding_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/onboarding_modal.js)24
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/report_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/report_modal.js)6
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/sign_in_banner.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/sign_in_banner.js)2
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/upload_area.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/upload_area.js)2
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/video_modal.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/video_modal.js)13
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/zoomable_image.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/zoomable_image.js)36
-rw-r--r--app/javascript/flavours/glitch/features/ui/index.jsx (renamed from app/javascript/flavours/glitch/features/ui/index.js)84
-rw-r--r--app/javascript/flavours/glitch/features/ui/util/async-components.js4
-rw-r--r--app/javascript/flavours/glitch/features/ui/util/react_router_helpers.jsx (renamed from app/javascript/flavours/glitch/features/ui/util/react_router_helpers.js)10
-rw-r--r--app/javascript/flavours/glitch/features/ui/util/reduced_motion.jsx (renamed from app/javascript/flavours/glitch/features/ui/util/reduced_motion.js)2
-rw-r--r--app/javascript/flavours/glitch/features/video/index.jsx (renamed from app/javascript/flavours/glitch/features/video/index.js)67
-rw-r--r--app/javascript/flavours/glitch/initial_state.js6
-rw-r--r--app/javascript/flavours/glitch/load_polyfills.js2
-rw-r--r--app/javascript/flavours/glitch/locales/af.json207
-rw-r--r--app/javascript/flavours/glitch/locales/an.json207
-rw-r--r--app/javascript/flavours/glitch/locales/ar.json202
-rw-r--r--app/javascript/flavours/glitch/locales/ast.json202
-rw-r--r--app/javascript/flavours/glitch/locales/be.json207
-rw-r--r--app/javascript/flavours/glitch/locales/bg.json202
-rw-r--r--app/javascript/flavours/glitch/locales/bn.json202
-rw-r--r--app/javascript/flavours/glitch/locales/br.json202
-rw-r--r--app/javascript/flavours/glitch/locales/bs.json207
-rw-r--r--app/javascript/flavours/glitch/locales/ca.json202
-rw-r--r--app/javascript/flavours/glitch/locales/ckb.json202
-rw-r--r--app/javascript/flavours/glitch/locales/co.json202
-rw-r--r--app/javascript/flavours/glitch/locales/cs.json57
-rw-r--r--app/javascript/flavours/glitch/locales/cy.json202
-rw-r--r--app/javascript/flavours/glitch/locales/da.json202
-rw-r--r--app/javascript/flavours/glitch/locales/de.json8
-rw-r--r--app/javascript/flavours/glitch/locales/defaultMessages.json37
-rw-r--r--app/javascript/flavours/glitch/locales/el.json202
-rw-r--r--app/javascript/flavours/glitch/locales/en-GB.json207
-rw-r--r--app/javascript/flavours/glitch/locales/en.json8
-rw-r--r--app/javascript/flavours/glitch/locales/eo.json158
-rw-r--r--app/javascript/flavours/glitch/locales/es-AR.json113
-rw-r--r--app/javascript/flavours/glitch/locales/es-MX.json113
-rw-r--r--app/javascript/flavours/glitch/locales/es.json185
-rw-r--r--app/javascript/flavours/glitch/locales/et.json202
-rw-r--r--app/javascript/flavours/glitch/locales/eu.json202
-rw-r--r--app/javascript/flavours/glitch/locales/fa.json202
-rw-r--r--app/javascript/flavours/glitch/locales/fi.json202
-rw-r--r--app/javascript/flavours/glitch/locales/fo.json207
-rw-r--r--app/javascript/flavours/glitch/locales/fr-QC.json8
-rw-r--r--app/javascript/flavours/glitch/locales/fr.json8
-rw-r--r--app/javascript/flavours/glitch/locales/fy.json207
-rw-r--r--app/javascript/flavours/glitch/locales/ga.json202
-rw-r--r--app/javascript/flavours/glitch/locales/gd.json202
-rw-r--r--app/javascript/flavours/glitch/locales/gl.json202
-rw-r--r--app/javascript/flavours/glitch/locales/he.json202
-rw-r--r--app/javascript/flavours/glitch/locales/hi.json190
-rw-r--r--app/javascript/flavours/glitch/locales/hr.json202
-rw-r--r--app/javascript/flavours/glitch/locales/hu.json202
-rw-r--r--app/javascript/flavours/glitch/locales/hy.json202
-rw-r--r--app/javascript/flavours/glitch/locales/id.json202
-rw-r--r--app/javascript/flavours/glitch/locales/ig.json207
-rw-r--r--app/javascript/flavours/glitch/locales/io.json202
-rw-r--r--app/javascript/flavours/glitch/locales/is.json202
-rw-r--r--app/javascript/flavours/glitch/locales/it.json202
-rw-r--r--app/javascript/flavours/glitch/locales/ja.json84
-rw-r--r--app/javascript/flavours/glitch/locales/ka.json202
-rw-r--r--app/javascript/flavours/glitch/locales/kab.json202
-rw-r--r--app/javascript/flavours/glitch/locales/kk.json202
-rw-r--r--app/javascript/flavours/glitch/locales/kn.json202
-rw-r--r--app/javascript/flavours/glitch/locales/ko.json8
-rw-r--r--app/javascript/flavours/glitch/locales/ku.json202
-rw-r--r--app/javascript/flavours/glitch/locales/kw.json202
-rw-r--r--app/javascript/flavours/glitch/locales/la.json207
-rw-r--r--app/javascript/flavours/glitch/locales/lt.json202
-rw-r--r--app/javascript/flavours/glitch/locales/lv.json202
-rw-r--r--app/javascript/flavours/glitch/locales/mk.json202
-rw-r--r--app/javascript/flavours/glitch/locales/ml.json202
-rw-r--r--app/javascript/flavours/glitch/locales/mr.json202
-rw-r--r--app/javascript/flavours/glitch/locales/ms.json202
-rw-r--r--app/javascript/flavours/glitch/locales/my.json207
-rw-r--r--app/javascript/flavours/glitch/locales/nl.json202
-rw-r--r--app/javascript/flavours/glitch/locales/nn.json202
-rw-r--r--app/javascript/flavours/glitch/locales/no.json202
-rw-r--r--app/javascript/flavours/glitch/locales/oc.json202
-rw-r--r--app/javascript/flavours/glitch/locales/pa.json202
-rw-r--r--app/javascript/flavours/glitch/locales/pl.json153
-rw-r--r--app/javascript/flavours/glitch/locales/pt-BR.json14
-rw-r--r--app/javascript/flavours/glitch/locales/pt-PT.json202
-rw-r--r--app/javascript/flavours/glitch/locales/ro.json202
-rw-r--r--app/javascript/flavours/glitch/locales/ru.json202
-rw-r--r--app/javascript/flavours/glitch/locales/sa.json202
-rw-r--r--app/javascript/flavours/glitch/locales/sc.json202
-rw-r--r--app/javascript/flavours/glitch/locales/sco.json207
-rw-r--r--app/javascript/flavours/glitch/locales/si.json202
-rw-r--r--app/javascript/flavours/glitch/locales/sk.json202
-rw-r--r--app/javascript/flavours/glitch/locales/sl.json202
-rw-r--r--app/javascript/flavours/glitch/locales/sq.json202
-rw-r--r--app/javascript/flavours/glitch/locales/sr-Latn.json202
-rw-r--r--app/javascript/flavours/glitch/locales/sr.json202
-rw-r--r--app/javascript/flavours/glitch/locales/sv.json202
-rw-r--r--app/javascript/flavours/glitch/locales/szl.json9
-rw-r--r--app/javascript/flavours/glitch/locales/ta.json202
-rw-r--r--app/javascript/flavours/glitch/locales/tai.json9
-rw-r--r--app/javascript/flavours/glitch/locales/te.json202
-rw-r--r--app/javascript/flavours/glitch/locales/th.json202
-rw-r--r--app/javascript/flavours/glitch/locales/tr.json202
-rw-r--r--app/javascript/flavours/glitch/locales/tt.json202
-rw-r--r--app/javascript/flavours/glitch/locales/ug.json202
-rw-r--r--app/javascript/flavours/glitch/locales/uk.json160
-rw-r--r--app/javascript/flavours/glitch/locales/ur.json202
-rw-r--r--app/javascript/flavours/glitch/locales/vi.json202
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_an.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_be.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_bs.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_en-GB.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_fo.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_fr-QC.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_fy.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_ig.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_la.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_my.json2
-rw-r--r--app/javascript/flavours/glitch/locales/whitelist_sco.json2
-rw-r--r--app/javascript/flavours/glitch/locales/zgh.json9
-rw-r--r--app/javascript/flavours/glitch/locales/zh-CN.json30
-rw-r--r--app/javascript/flavours/glitch/locales/zh-HK.json202
-rw-r--r--app/javascript/flavours/glitch/locales/zh-TW.json202
-rw-r--r--app/javascript/flavours/glitch/main.jsx (renamed from app/javascript/flavours/glitch/main.js)0
-rw-r--r--app/javascript/flavours/glitch/middleware/errors.js2
-rw-r--r--app/javascript/flavours/glitch/middleware/loading_bar.js2
-rw-r--r--app/javascript/flavours/glitch/middleware/sounds.js2
-rw-r--r--app/javascript/flavours/glitch/packs/admin.jsx (renamed from app/javascript/flavours/glitch/packs/admin.js)0
-rw-r--r--app/javascript/flavours/glitch/packs/public.jsx (renamed from app/javascript/flavours/glitch/packs/public.js)9
-rw-r--r--app/javascript/flavours/glitch/packs/settings.js5
-rw-r--r--app/javascript/flavours/glitch/packs/share.jsx (renamed from app/javascript/flavours/glitch/packs/share.js)0
-rw-r--r--app/javascript/flavours/glitch/performance.js1
-rw-r--r--app/javascript/flavours/glitch/reducers/accounts.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/accounts_counters.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/accounts_map.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/alerts.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/announcements.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/compose.js49
-rw-r--r--app/javascript/flavours/glitch/reducers/contexts.js4
-rw-r--r--app/javascript/flavours/glitch/reducers/conversations.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/custom_emojis.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/domain_lists.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/filters.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/followed_tags.js42
-rw-r--r--app/javascript/flavours/glitch/reducers/height_cache.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/index.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/list_adder.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/list_editor.js10
-rw-r--r--app/javascript/flavours/glitch/reducers/lists.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/local_settings.js3
-rw-r--r--app/javascript/flavours/glitch/reducers/markers.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/media_attachments.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/meta.js10
-rw-r--r--app/javascript/flavours/glitch/reducers/modal.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/notifications.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/picture_in_picture.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/push_notifications.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/relationships.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/search.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/server.js9
-rw-r--r--app/javascript/flavours/glitch/reducers/settings.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/status_lists.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/statuses.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/suggestions.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/tags.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/timelines.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/trends.js2
-rw-r--r--app/javascript/flavours/glitch/reducers/user_lists.js2
-rw-r--r--app/javascript/flavours/glitch/store/configureStore.js2
-rw-r--r--app/javascript/flavours/glitch/styles/_mixins.scss7
-rw-r--r--app/javascript/flavours/glitch/styles/accessibility.scss7
-rw-r--r--app/javascript/flavours/glitch/styles/accounts.scss10
-rw-r--r--app/javascript/flavours/glitch/styles/admin.scss38
-rw-r--r--app/javascript/flavours/glitch/styles/basics.scss98
-rw-r--r--app/javascript/flavours/glitch/styles/components/accounts.scss65
-rw-r--r--app/javascript/flavours/glitch/styles/components/announcements.scss6
-rw-r--r--app/javascript/flavours/glitch/styles/components/columns.scss27
-rw-r--r--app/javascript/flavours/glitch/styles/components/compose_form.scss166
-rw-r--r--app/javascript/flavours/glitch/styles/components/directory.scss11
-rw-r--r--app/javascript/flavours/glitch/styles/components/doodle.scss22
-rw-r--r--app/javascript/flavours/glitch/styles/components/drawer.scss55
-rw-r--r--app/javascript/flavours/glitch/styles/components/emoji.scss2
-rw-r--r--app/javascript/flavours/glitch/styles/components/emoji_picker.scss8
-rw-r--r--app/javascript/flavours/glitch/styles/components/index.scss1805
-rw-r--r--app/javascript/flavours/glitch/styles/components/local_settings.scss17
-rw-r--r--app/javascript/flavours/glitch/styles/components/media.scss19
-rw-r--r--app/javascript/flavours/glitch/styles/components/metadata.scss0
-rw-r--r--app/javascript/flavours/glitch/styles/components/misc.scss1814
-rw-r--r--app/javascript/flavours/glitch/styles/components/modal.scss20
-rw-r--r--app/javascript/flavours/glitch/styles/components/privacy_policy.scss6
-rw-r--r--app/javascript/flavours/glitch/styles/components/search.scss4
-rw-r--r--app/javascript/flavours/glitch/styles/components/sensitive.scss8
-rw-r--r--app/javascript/flavours/glitch/styles/components/single_column.scss2
-rw-r--r--app/javascript/flavours/glitch/styles/components/status.scss135
-rw-r--r--app/javascript/flavours/glitch/styles/containers.scss3
-rw-r--r--app/javascript/flavours/glitch/styles/contrast/variables.scss2
-rw-r--r--app/javascript/flavours/glitch/styles/dashboard.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/forms.scss57
-rw-r--r--app/javascript/flavours/glitch/styles/index.scss1
-rw-r--r--app/javascript/flavours/glitch/styles/mastodon-light/diff.scss76
-rw-r--r--app/javascript/flavours/glitch/styles/modal.scss4
-rw-r--r--app/javascript/flavours/glitch/styles/polls.scss22
-rw-r--r--app/javascript/flavours/glitch/styles/rich_text.scss101
-rw-r--r--app/javascript/flavours/glitch/styles/rtl.scss18
-rw-r--r--app/javascript/flavours/glitch/styles/statuses.scss2
-rw-r--r--app/javascript/flavours/glitch/styles/variables.scss33
-rw-r--r--app/javascript/flavours/glitch/styles/widgets.scss2
-rw-r--r--app/javascript/flavours/glitch/theme.yml12
-rw-r--r--app/javascript/flavours/glitch/utils/hashtag.js8
-rw-r--r--app/javascript/flavours/glitch/utils/icons.jsx (renamed from app/javascript/flavours/glitch/utils/icons.js)0
-rw-r--r--app/javascript/flavours/glitch/utils/notifications.js2
-rw-r--r--app/javascript/flavours/glitch/utils/privacy_preference.js2
-rw-r--r--app/javascript/flavours/glitch/utils/react_helpers.js2
-rw-r--r--app/javascript/flavours/glitch/uuid.js2
-rw-r--r--app/javascript/flavours/vanilla/theme.yml14
-rw-r--r--app/javascript/hooks/useHovering.ts17
-rw-r--r--app/javascript/mastodon/actions/compose.js127
-rw-r--r--app/javascript/mastodon/actions/markers.js2
-rw-r--r--app/javascript/mastodon/actions/picture_in_picture.js1
-rw-r--r--app/javascript/mastodon/actions/push_notifications/registerer.js2
-rw-r--r--app/javascript/mastodon/actions/search.js45
-rw-r--r--app/javascript/mastodon/actions/server.js27
-rw-r--r--app/javascript/mastodon/actions/streaming.js20
-rw-r--r--app/javascript/mastodon/actions/tags.js82
-rw-r--r--app/javascript/mastodon/api.js4
-rw-r--r--app/javascript/mastodon/base_polyfills.js10
-rw-r--r--app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.jsx.snap (renamed from app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.js.snap)0
-rw-r--r--app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.jsx.snap (renamed from app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.js.snap)0
-rw-r--r--app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.jsx.snap (renamed from app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.js.snap)0
-rw-r--r--app/javascript/mastodon/components/__tests__/__snapshots__/button-test.jsx.snap (renamed from app/javascript/mastodon/components/__tests__/__snapshots__/button-test.js.snap)0
-rw-r--r--app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.jsx.snap (renamed from app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.js.snap)0
-rw-r--r--app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.jsx (renamed from app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js)0
-rw-r--r--app/javascript/mastodon/components/__tests__/avatar-test.jsx (renamed from app/javascript/mastodon/components/__tests__/avatar-test.js)0
-rw-r--r--app/javascript/mastodon/components/__tests__/avatar_overlay-test.jsx (renamed from app/javascript/mastodon/components/__tests__/avatar_overlay-test.js)0
-rw-r--r--app/javascript/mastodon/components/__tests__/button-test.jsx (renamed from app/javascript/mastodon/components/__tests__/button-test.js)0
-rw-r--r--app/javascript/mastodon/components/__tests__/display_name-test.jsx (renamed from app/javascript/mastodon/components/__tests__/display_name-test.js)0
-rw-r--r--app/javascript/mastodon/components/account.jsx (renamed from app/javascript/mastodon/components/account.js)96
-rw-r--r--app/javascript/mastodon/components/admin/Counter.jsx (renamed from app/javascript/mastodon/components/admin/Counter.js)0
-rw-r--r--app/javascript/mastodon/components/admin/Dimension.jsx (renamed from app/javascript/mastodon/components/admin/Dimension.js)0
-rw-r--r--app/javascript/mastodon/components/admin/ReportReasonSelector.jsx (renamed from app/javascript/mastodon/components/admin/ReportReasonSelector.js)7
-rw-r--r--app/javascript/mastodon/components/admin/Retention.jsx (renamed from app/javascript/mastodon/components/admin/Retention.js)0
-rw-r--r--app/javascript/mastodon/components/admin/Trends.jsx (renamed from app/javascript/mastodon/components/admin/Trends.js)2
-rw-r--r--app/javascript/mastodon/components/animated_number.jsx (renamed from app/javascript/mastodon/components/animated_number.js)4
-rw-r--r--app/javascript/mastodon/components/attachment_list.jsx (renamed from app/javascript/mastodon/components/attachment_list.js)0
-rw-r--r--app/javascript/mastodon/components/autosuggest_emoji.jsx (renamed from app/javascript/mastodon/components/autosuggest_emoji.js)0
-rw-r--r--app/javascript/mastodon/components/autosuggest_hashtag.jsx (renamed from app/javascript/mastodon/components/autosuggest_hashtag.js)0
-rw-r--r--app/javascript/mastodon/components/autosuggest_input.jsx (renamed from app/javascript/mastodon/components/autosuggest_input.js)22
-rw-r--r--app/javascript/mastodon/components/autosuggest_textarea.jsx (renamed from app/javascript/mastodon/components/autosuggest_textarea.js)22
-rw-r--r--app/javascript/mastodon/components/avatar.js62
-rw-r--r--app/javascript/mastodon/components/avatar.tsx49
-rw-r--r--app/javascript/mastodon/components/avatar_composite.jsx (renamed from app/javascript/mastodon/components/avatar_composite.js)0
-rw-r--r--app/javascript/mastodon/components/avatar_overlay.jsx (renamed from app/javascript/mastodon/components/avatar_overlay.js)4
-rw-r--r--app/javascript/mastodon/components/blurhash.jsx (renamed from app/javascript/mastodon/components/blurhash.js)1
-rw-r--r--app/javascript/mastodon/components/button.jsx (renamed from app/javascript/mastodon/components/button.js)4
-rw-r--r--app/javascript/mastodon/components/check.jsx (renamed from app/javascript/mastodon/components/check.js)0
-rw-r--r--app/javascript/mastodon/components/column.jsx (renamed from app/javascript/mastodon/components/column.js)4
-rw-r--r--app/javascript/mastodon/components/column_back_button.jsx (renamed from app/javascript/mastodon/components/column_back_button.js)8
-rw-r--r--app/javascript/mastodon/components/column_back_button_slim.jsx (renamed from app/javascript/mastodon/components/column_back_button_slim.js)2
-rw-r--r--app/javascript/mastodon/components/column_header.jsx (renamed from app/javascript/mastodon/components/column_header.js)31
-rw-r--r--app/javascript/mastodon/components/common_counter.jsx (renamed from app/javascript/mastodon/components/common_counter.js)0
-rw-r--r--app/javascript/mastodon/components/dismissable_banner.jsx (renamed from app/javascript/mastodon/components/dismissable_banner.js)5
-rw-r--r--app/javascript/mastodon/components/display_name.jsx (renamed from app/javascript/mastodon/components/display_name.js)4
-rw-r--r--app/javascript/mastodon/components/domain.jsx (renamed from app/javascript/flavours/glitch/components/domain.js)5
-rw-r--r--app/javascript/mastodon/components/dropdown_menu.jsx (renamed from app/javascript/mastodon/components/dropdown_menu.js)36
-rw-r--r--app/javascript/mastodon/components/edited_timestamp/index.jsx (renamed from app/javascript/mastodon/components/edited_timestamp/index.js)8
-rw-r--r--app/javascript/mastodon/components/error_boundary.jsx (renamed from app/javascript/mastodon/components/error_boundary.js)2
-rw-r--r--app/javascript/mastodon/components/gifv.jsx (renamed from app/javascript/mastodon/components/gifv.js)13
-rw-r--r--app/javascript/mastodon/components/hashtag.jsx (renamed from app/javascript/mastodon/components/hashtag.js)9
-rw-r--r--app/javascript/mastodon/components/icon.jsx (renamed from app/javascript/mastodon/components/icon.js)0
-rw-r--r--app/javascript/mastodon/components/icon_button.jsx (renamed from app/javascript/mastodon/components/icon_button.js)14
-rw-r--r--app/javascript/mastodon/components/icon_with_badge.jsx (renamed from app/javascript/mastodon/components/icon_with_badge.js)0
-rw-r--r--app/javascript/mastodon/components/image.jsx (renamed from app/javascript/mastodon/components/image.js)0
-rw-r--r--app/javascript/mastodon/components/inline_account.jsx (renamed from app/javascript/mastodon/components/inline_account.js)3
-rw-r--r--app/javascript/mastodon/components/intersection_observer_article.jsx (renamed from app/javascript/mastodon/components/intersection_observer_article.js)16
-rw-r--r--app/javascript/mastodon/components/load_gap.jsx (renamed from app/javascript/mastodon/components/load_gap.js)5
-rw-r--r--app/javascript/mastodon/components/load_more.jsx (renamed from app/javascript/mastodon/components/load_more.js)4
-rw-r--r--app/javascript/mastodon/components/load_pending.jsx (renamed from app/javascript/mastodon/components/load_pending.js)2
-rw-r--r--app/javascript/mastodon/components/loading_indicator.jsx (renamed from app/javascript/mastodon/components/loading_indicator.js)0
-rw-r--r--app/javascript/mastodon/components/logo.jsx (renamed from app/javascript/mastodon/components/logo.js)0
-rw-r--r--app/javascript/mastodon/components/media_attachments.jsx (renamed from app/javascript/mastodon/components/media_attachments.js)12
-rw-r--r--app/javascript/mastodon/components/media_gallery.jsx (renamed from app/javascript/mastodon/components/media_gallery.js)32
-rw-r--r--app/javascript/mastodon/components/missing_indicator.jsx (renamed from app/javascript/mastodon/components/missing_indicator.js)0
-rw-r--r--app/javascript/mastodon/components/modal_root.jsx (renamed from app/javascript/mastodon/components/modal_root.js)8
-rw-r--r--app/javascript/mastodon/components/navigation_portal.jsx (renamed from app/javascript/mastodon/components/navigation_portal.js)2
-rw-r--r--app/javascript/mastodon/components/not_signed_in_indicator.jsx (renamed from app/javascript/mastodon/components/not_signed_in_indicator.js)0
-rw-r--r--app/javascript/mastodon/components/picture_in_picture_placeholder.jsx (renamed from app/javascript/mastodon/components/picture_in_picture_placeholder.js)9
-rw-r--r--app/javascript/mastodon/components/poll.jsx (renamed from app/javascript/mastodon/components/poll.js)14
-rw-r--r--app/javascript/mastodon/components/radio_button.jsx (renamed from app/javascript/mastodon/components/radio_button.js)0
-rw-r--r--app/javascript/mastodon/components/regeneration_indicator.jsx (renamed from app/javascript/mastodon/components/regeneration_indicator.js)0
-rw-r--r--app/javascript/mastodon/components/relative_timestamp.jsx (renamed from app/javascript/flavours/glitch/components/relative_timestamp.js)3
-rw-r--r--app/javascript/mastodon/components/scrollable_list.jsx (renamed from app/javascript/mastodon/components/scrollable_list.js)27
-rw-r--r--app/javascript/mastodon/components/server_banner.jsx (renamed from app/javascript/mastodon/components/server_banner.js)6
-rw-r--r--app/javascript/mastodon/components/short_number.jsx (renamed from app/javascript/mastodon/components/short_number.js)0
-rw-r--r--app/javascript/mastodon/components/skeleton.jsx (renamed from app/javascript/mastodon/components/skeleton.js)0
-rw-r--r--app/javascript/mastodon/components/status.jsx (renamed from app/javascript/mastodon/components/status.js)72
-rw-r--r--app/javascript/mastodon/components/status_action_bar.jsx (renamed from app/javascript/mastodon/components/status_action_bar.js)52
-rw-r--r--app/javascript/mastodon/components/status_content.jsx (renamed from app/javascript/mastodon/components/status_content.js)41
-rw-r--r--app/javascript/mastodon/components/status_list.jsx (renamed from app/javascript/mastodon/components/status_list.js)12
-rw-r--r--app/javascript/mastodon/components/timeline_hint.jsx (renamed from app/javascript/mastodon/components/timeline_hint.js)0
-rw-r--r--app/javascript/mastodon/containers/account_container.jsx (renamed from app/javascript/mastodon/containers/account_container.js)0
-rw-r--r--app/javascript/mastodon/containers/admin_component.jsx (renamed from app/javascript/mastodon/containers/admin_component.js)0
-rw-r--r--app/javascript/mastodon/containers/compose_container.jsx (renamed from app/javascript/mastodon/containers/compose_container.js)0
-rw-r--r--app/javascript/mastodon/containers/domain_container.jsx (renamed from app/javascript/mastodon/containers/domain_container.js)0
-rw-r--r--app/javascript/mastodon/containers/mastodon.jsx (renamed from app/javascript/mastodon/containers/mastodon.js)0
-rw-r--r--app/javascript/mastodon/containers/media_container.jsx (renamed from app/javascript/mastodon/containers/media_container.js)10
-rw-r--r--app/javascript/mastodon/containers/status_container.jsx (renamed from app/javascript/mastodon/containers/status_container.js)15
-rw-r--r--app/javascript/mastodon/extra_polyfills.js1
-rw-r--r--app/javascript/mastodon/features/about/index.jsx (renamed from app/javascript/mastodon/features/about/index.js)12
-rw-r--r--app/javascript/mastodon/features/account/components/account_note.jsx (renamed from app/javascript/mastodon/features/account/components/account_note.js)9
-rw-r--r--app/javascript/mastodon/features/account/components/featured_tags.jsx (renamed from app/javascript/mastodon/features/account/components/featured_tags.js)3
-rw-r--r--app/javascript/mastodon/features/account/components/follow_request_note.jsx (renamed from app/javascript/mastodon/features/account/components/follow_request_note.js)0
-rw-r--r--app/javascript/mastodon/features/account/components/header.jsx (renamed from app/javascript/mastodon/features/account/components/header.js)27
-rw-r--r--app/javascript/mastodon/features/account/navigation.jsx (renamed from app/javascript/mastodon/features/account/navigation.js)3
-rw-r--r--app/javascript/mastodon/features/account_gallery/components/media_item.jsx (renamed from app/javascript/mastodon/features/account_gallery/components/media_item.js)15
-rw-r--r--app/javascript/mastodon/features/account_gallery/index.jsx (renamed from app/javascript/mastodon/features/account_gallery/index.js)15
-rw-r--r--app/javascript/mastodon/features/account_timeline/components/header.jsx (renamed from app/javascript/mastodon/features/account_timeline/components/header.js)32
-rw-r--r--app/javascript/mastodon/features/account_timeline/components/limited_account_hint.jsx (renamed from app/javascript/mastodon/features/account_timeline/components/limited_account_hint.js)5
-rw-r--r--app/javascript/mastodon/features/account_timeline/components/moved_note.jsx (renamed from app/javascript/mastodon/features/account_timeline/components/moved_note.js)0
-rw-r--r--app/javascript/mastodon/features/account_timeline/containers/header_container.jsx (renamed from app/javascript/mastodon/features/account_timeline/containers/header_container.js)0
-rw-r--r--app/javascript/mastodon/features/account_timeline/index.jsx (renamed from app/javascript/mastodon/features/account_timeline/index.js)5
-rw-r--r--app/javascript/mastodon/features/audio/index.jsx (renamed from app/javascript/mastodon/features/audio/index.js)99
-rw-r--r--app/javascript/mastodon/features/blocks/index.jsx (renamed from app/javascript/mastodon/features/blocks/index.js)4
-rw-r--r--app/javascript/mastodon/features/bookmarked_statuses/index.jsx (renamed from app/javascript/mastodon/features/bookmarked_statuses/index.js)14
-rw-r--r--app/javascript/mastodon/features/closed_registrations_modal/index.jsx (renamed from app/javascript/mastodon/features/closed_registrations_modal/index.js)3
-rw-r--r--app/javascript/mastodon/features/community_timeline/components/column_settings.jsx (renamed from app/javascript/mastodon/features/community_timeline/components/column_settings.js)3
-rw-r--r--app/javascript/mastodon/features/community_timeline/index.jsx (renamed from app/javascript/mastodon/features/community_timeline/index.js)14
-rw-r--r--app/javascript/mastodon/features/compose/components/action_bar.jsx (renamed from app/javascript/mastodon/features/compose/components/action_bar.js)7
-rw-r--r--app/javascript/mastodon/features/compose/components/autosuggest_account.jsx (renamed from app/javascript/mastodon/features/compose/components/autosuggest_account.js)0
-rw-r--r--app/javascript/mastodon/features/compose/components/character_counter.jsx (renamed from app/javascript/mastodon/features/compose/components/character_counter.js)0
-rw-r--r--app/javascript/mastodon/features/compose/components/compose_form.jsx (renamed from app/javascript/mastodon/features/compose/components/compose_form.js)37
-rw-r--r--app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx (renamed from app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js)46
-rw-r--r--app/javascript/mastodon/features/compose/components/language_dropdown.jsx (renamed from app/javascript/mastodon/features/compose/components/language_dropdown.js)37
-rw-r--r--app/javascript/mastodon/features/compose/components/navigation_bar.jsx (renamed from app/javascript/mastodon/features/compose/components/navigation_bar.js)0
-rw-r--r--app/javascript/mastodon/features/compose/components/poll_button.jsx (renamed from app/javascript/mastodon/features/compose/components/poll_button.js)6
-rw-r--r--app/javascript/mastodon/features/compose/components/poll_form.jsx (renamed from app/javascript/mastodon/features/compose/components/poll_form.js)29
-rw-r--r--app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx (renamed from app/javascript/mastodon/features/compose/components/privacy_dropdown.js)35
-rw-r--r--app/javascript/mastodon/features/compose/components/reply_indicator.jsx (renamed from app/javascript/mastodon/features/compose/components/reply_indicator.js)7
-rw-r--r--app/javascript/mastodon/features/compose/components/search.js147
-rw-r--r--app/javascript/mastodon/features/compose/components/search.jsx335
-rw-r--r--app/javascript/mastodon/features/compose/components/search_results.jsx (renamed from app/javascript/mastodon/features/compose/components/search_results.js)5
-rw-r--r--app/javascript/mastodon/features/compose/components/text_icon_button.jsx (renamed from app/javascript/mastodon/features/compose/components/text_icon_button.js)0
-rw-r--r--app/javascript/mastodon/features/compose/components/upload.jsx (renamed from app/javascript/mastodon/features/compose/components/upload.js)15
-rw-r--r--app/javascript/mastodon/features/compose/components/upload_button.jsx (renamed from app/javascript/mastodon/features/compose/components/upload_button.js)10
-rw-r--r--app/javascript/mastodon/features/compose/components/upload_form.jsx (renamed from app/javascript/mastodon/features/compose/components/upload_form.js)0
-rw-r--r--app/javascript/mastodon/features/compose/components/upload_progress.jsx (renamed from app/javascript/mastodon/features/compose/components/upload_progress.js)0
-rw-r--r--app/javascript/mastodon/features/compose/components/warning.jsx (renamed from app/javascript/mastodon/features/compose/components/warning.js)0
-rw-r--r--app/javascript/mastodon/features/compose/containers/compose_form_container.js1
-rw-r--r--app/javascript/mastodon/features/compose/containers/poll_form_container.js6
-rw-r--r--app/javascript/mastodon/features/compose/containers/search_container.js22
-rw-r--r--app/javascript/mastodon/features/compose/containers/sensitive_button_container.jsx (renamed from app/javascript/mastodon/features/compose/containers/sensitive_button_container.js)2
-rw-r--r--app/javascript/mastodon/features/compose/containers/upload_container.js3
-rw-r--r--app/javascript/mastodon/features/compose/containers/warning_container.jsx (renamed from app/javascript/mastodon/features/compose/containers/warning_container.js)30
-rw-r--r--app/javascript/mastodon/features/compose/index.jsx (renamed from app/javascript/mastodon/features/compose/index.js)10
-rw-r--r--app/javascript/mastodon/features/compose/util/counter.js2
-rw-r--r--app/javascript/mastodon/features/direct_timeline/components/conversation.jsx (renamed from app/javascript/mastodon/features/direct_timeline/components/conversation.js)25
-rw-r--r--app/javascript/mastodon/features/direct_timeline/components/conversations_list.jsx (renamed from app/javascript/mastodon/features/direct_timeline/components/conversations_list.js)14
-rw-r--r--app/javascript/mastodon/features/direct_timeline/index.jsx (renamed from app/javascript/mastodon/features/direct_timeline/index.js)20
-rw-r--r--app/javascript/mastodon/features/directory/components/account_card.jsx (renamed from app/javascript/mastodon/features/directory/components/account_card.js)13
-rw-r--r--app/javascript/mastodon/features/directory/index.jsx (renamed from app/javascript/mastodon/features/directory/index.js)18
-rw-r--r--app/javascript/mastodon/features/domain_blocks/index.jsx (renamed from app/javascript/mastodon/features/domain_blocks/index.js)4
-rw-r--r--app/javascript/mastodon/features/emoji/emoji.js4
-rw-r--r--app/javascript/mastodon/features/emoji/emoji_mart_data_light.js2
-rw-r--r--app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.js6
-rw-r--r--app/javascript/mastodon/features/emoji/emoji_utils.js12
-rw-r--r--app/javascript/mastodon/features/explore/components/story.jsx (renamed from app/javascript/mastodon/features/explore/components/story.js)0
-rw-r--r--app/javascript/mastodon/features/explore/index.jsx (renamed from app/javascript/mastodon/features/explore/index.js)12
-rw-r--r--app/javascript/mastodon/features/explore/links.jsx (renamed from app/javascript/mastodon/features/explore/links.js)3
-rw-r--r--app/javascript/mastodon/features/explore/results.jsx (renamed from app/javascript/mastodon/features/explore/results.js)6
-rw-r--r--app/javascript/mastodon/features/explore/statuses.jsx (renamed from app/javascript/mastodon/features/explore/statuses.js)5
-rw-r--r--app/javascript/mastodon/features/explore/suggestions.jsx (renamed from app/javascript/mastodon/features/explore/suggestions.js)3
-rw-r--r--app/javascript/mastodon/features/explore/tags.jsx (renamed from app/javascript/mastodon/features/explore/tags.js)3
-rw-r--r--app/javascript/mastodon/features/favourited_statuses/index.jsx (renamed from app/javascript/mastodon/features/favourited_statuses/index.js)14
-rw-r--r--app/javascript/mastodon/features/favourites/index.jsx (renamed from app/javascript/mastodon/features/favourites/index.js)6
-rw-r--r--app/javascript/mastodon/features/filters/added_to_filter.jsx (renamed from app/javascript/mastodon/features/filters/added_to_filter.js)3
-rw-r--r--app/javascript/mastodon/features/filters/select_filter.jsx (renamed from app/javascript/mastodon/features/filters/select_filter.js)22
-rw-r--r--app/javascript/mastodon/features/follow_recommendations/components/account.jsx (renamed from app/javascript/mastodon/features/follow_recommendations/components/account.js)8
-rw-r--r--app/javascript/mastodon/features/follow_recommendations/index.jsx (renamed from app/javascript/mastodon/features/follow_recommendations/index.js)5
-rw-r--r--app/javascript/mastodon/features/follow_requests/components/account_authorize.jsx (renamed from app/javascript/mastodon/features/follow_requests/components/account_authorize.js)3
-rw-r--r--app/javascript/mastodon/features/follow_requests/index.jsx (renamed from app/javascript/mastodon/features/follow_requests/index.js)16
-rw-r--r--app/javascript/mastodon/features/followed_tags/index.jsx89
-rw-r--r--app/javascript/mastodon/features/followers/index.jsx (renamed from app/javascript/mastodon/features/followers/index.js)3
-rw-r--r--app/javascript/mastodon/features/following/index.jsx (renamed from app/javascript/mastodon/features/following/index.js)3
-rw-r--r--app/javascript/mastodon/features/generic_not_found/index.jsx (renamed from app/javascript/mastodon/features/generic_not_found/index.js)0
-rw-r--r--app/javascript/mastodon/features/getting_started/components/announcements.jsx (renamed from app/javascript/mastodon/features/getting_started/components/announcements.js)34
-rw-r--r--app/javascript/mastodon/features/getting_started/components/trends.jsx (renamed from app/javascript/mastodon/features/getting_started/components/trends.js)0
-rw-r--r--app/javascript/mastodon/features/getting_started/index.jsx (renamed from app/javascript/mastodon/features/getting_started/index.js)6
-rw-r--r--app/javascript/mastodon/features/hashtag_timeline/components/column_settings.jsx (renamed from app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js)7
-rw-r--r--app/javascript/mastodon/features/hashtag_timeline/index.jsx (renamed from app/javascript/mastodon/features/hashtag_timeline/index.js)26
-rw-r--r--app/javascript/mastodon/features/home_timeline/components/column_settings.jsx (renamed from app/javascript/mastodon/features/home_timeline/components/column_settings.js)3
-rw-r--r--app/javascript/mastodon/features/home_timeline/index.jsx (renamed from app/javascript/mastodon/features/home_timeline/index.js)16
-rw-r--r--app/javascript/mastodon/features/interaction_modal/index.jsx (renamed from app/javascript/mastodon/features/interaction_modal/index.js)11
-rw-r--r--app/javascript/mastodon/features/keyboard_shortcuts/index.jsx (renamed from app/javascript/mastodon/features/keyboard_shortcuts/index.js)3
-rw-r--r--app/javascript/mastodon/features/list_adder/components/account.jsx (renamed from app/javascript/mastodon/features/list_adder/components/account.js)5
-rw-r--r--app/javascript/mastodon/features/list_adder/components/list.jsx (renamed from app/javascript/mastodon/features/list_adder/components/list.js)4
-rw-r--r--app/javascript/mastodon/features/list_adder/index.jsx (renamed from app/javascript/mastodon/features/list_adder/index.js)4
-rw-r--r--app/javascript/mastodon/features/list_editor/components/account.jsx (renamed from app/javascript/mastodon/features/list_editor/components/account.js)4
-rw-r--r--app/javascript/mastodon/features/list_editor/components/edit_list_form.jsx (renamed from app/javascript/mastodon/features/list_editor/components/edit_list_form.js)10
-rw-r--r--app/javascript/mastodon/features/list_editor/components/search.jsx (renamed from app/javascript/mastodon/features/list_editor/components/search.js)12
-rw-r--r--app/javascript/mastodon/features/list_editor/index.jsx (renamed from app/javascript/mastodon/features/list_editor/index.js)6
-rw-r--r--app/javascript/mastodon/features/list_timeline/index.jsx (renamed from app/javascript/mastodon/features/list_timeline/index.js)24
-rw-r--r--app/javascript/mastodon/features/lists/components/new_list_form.jsx (renamed from app/javascript/mastodon/features/lists/components/new_list_form.js)10
-rw-r--r--app/javascript/mastodon/features/lists/index.jsx (renamed from app/javascript/mastodon/features/lists/index.js)4
-rw-r--r--app/javascript/mastodon/features/mutes/index.jsx (renamed from app/javascript/mastodon/features/mutes/index.js)4
-rw-r--r--app/javascript/mastodon/features/notifications/components/clear_column_button.jsx (renamed from app/javascript/mastodon/features/notifications/components/clear_column_button.js)2
-rw-r--r--app/javascript/mastodon/features/notifications/components/column_settings.jsx (renamed from app/javascript/mastodon/features/notifications/components/column_settings.js)2
-rw-r--r--app/javascript/mastodon/features/notifications/components/filter_bar.jsx (renamed from app/javascript/mastodon/features/notifications/components/filter_bar.js)3
-rw-r--r--app/javascript/mastodon/features/notifications/components/follow_request.jsx (renamed from app/javascript/mastodon/features/notifications/components/follow_request.js)9
-rw-r--r--app/javascript/mastodon/features/notifications/components/grant_permission_button.jsx (renamed from app/javascript/mastodon/features/notifications/components/grant_permission_button.js)2
-rw-r--r--app/javascript/mastodon/features/notifications/components/notification.jsx (renamed from app/javascript/mastodon/features/notifications/components/notification.js)58
-rw-r--r--app/javascript/mastodon/features/notifications/components/notifications_permission_banner.jsx (renamed from app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js)8
-rw-r--r--app/javascript/mastodon/features/notifications/components/report.jsx (renamed from app/javascript/mastodon/features/notifications/components/report.js)3
-rw-r--r--app/javascript/mastodon/features/notifications/components/setting_toggle.jsx (renamed from app/javascript/mastodon/features/notifications/components/setting_toggle.js)4
-rw-r--r--app/javascript/mastodon/features/notifications/containers/column_settings_container.js3
-rw-r--r--app/javascript/mastodon/features/notifications/containers/notification_container.js2
-rw-r--r--app/javascript/mastodon/features/notifications/index.jsx (renamed from app/javascript/mastodon/features/notifications/index.js)16
-rw-r--r--app/javascript/mastodon/features/picture_in_picture/components/footer.jsx (renamed from app/javascript/mastodon/features/picture_in_picture/components/footer.js)10
-rw-r--r--app/javascript/mastodon/features/picture_in_picture/components/header.jsx (renamed from app/javascript/mastodon/features/picture_in_picture/components/header.js)4
-rw-r--r--app/javascript/mastodon/features/picture_in_picture/index.jsx (renamed from app/javascript/mastodon/features/picture_in_picture/index.js)5
-rw-r--r--app/javascript/mastodon/features/pinned_statuses/index.jsx (renamed from app/javascript/mastodon/features/pinned_statuses/index.js)8
-rw-r--r--app/javascript/mastodon/features/privacy_policy/index.jsx (renamed from app/javascript/mastodon/features/privacy_policy/index.js)3
-rw-r--r--app/javascript/mastodon/features/public_timeline/components/column_settings.jsx (renamed from app/javascript/mastodon/features/public_timeline/components/column_settings.js)3
-rw-r--r--app/javascript/mastodon/features/public_timeline/index.jsx (renamed from app/javascript/mastodon/features/public_timeline/index.js)14
-rw-r--r--app/javascript/mastodon/features/reblogs/index.jsx (renamed from app/javascript/mastodon/features/reblogs/index.js)6
-rw-r--r--app/javascript/mastodon/features/report/category.jsx (renamed from app/javascript/mastodon/features/report/category.js)4
-rw-r--r--app/javascript/mastodon/features/report/comment.jsx (renamed from app/javascript/mastodon/features/report/comment.js)3
-rw-r--r--app/javascript/mastodon/features/report/components/option.jsx (renamed from app/javascript/mastodon/features/report/components/option.js)6
-rw-r--r--app/javascript/mastodon/features/report/components/status_check_box.jsx (renamed from app/javascript/mastodon/features/report/components/status_check_box.js)3
-rw-r--r--app/javascript/mastodon/features/report/rules.jsx (renamed from app/javascript/mastodon/features/report/rules.js)3
-rw-r--r--app/javascript/mastodon/features/report/statuses.jsx (renamed from app/javascript/mastodon/features/report/statuses.js)3
-rw-r--r--app/javascript/mastodon/features/report/thanks.jsx (renamed from app/javascript/mastodon/features/report/thanks.js)3
-rw-r--r--app/javascript/mastodon/features/standalone/compose/index.jsx (renamed from app/javascript/mastodon/features/standalone/compose/index.js)2
-rw-r--r--app/javascript/mastodon/features/status/components/action_bar.jsx (renamed from app/javascript/mastodon/features/status/components/action_bar.js)44
-rw-r--r--app/javascript/mastodon/features/status/components/card.jsx (renamed from app/javascript/mastodon/features/status/components/card.js)11
-rw-r--r--app/javascript/mastodon/features/status/components/detailed_status.jsx (renamed from app/javascript/mastodon/features/status/components/detailed_status.js)30
-rw-r--r--app/javascript/mastodon/features/status/index.jsx (renamed from app/javascript/mastodon/features/status/index.js)106
-rw-r--r--app/javascript/mastodon/features/subscribed_languages_modal/index.jsx (renamed from app/javascript/mastodon/features/subscribed_languages_modal/index.js)6
-rw-r--r--app/javascript/mastodon/features/ui/components/__tests__/column-test.jsx (renamed from app/javascript/mastodon/features/ui/components/__tests__/column-test.js)0
-rw-r--r--app/javascript/mastodon/features/ui/components/actions_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/actions_modal.js)4
-rw-r--r--app/javascript/mastodon/features/ui/components/audio_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/audio_modal.js)8
-rw-r--r--app/javascript/mastodon/features/ui/components/block_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/block_modal.js)12
-rw-r--r--app/javascript/mastodon/features/ui/components/boost_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/boost_modal.js)12
-rw-r--r--app/javascript/mastodon/features/ui/components/bundle.jsx (renamed from app/javascript/mastodon/features/ui/components/bundle.js)10
-rw-r--r--app/javascript/mastodon/features/ui/components/bundle_column_error.jsx (renamed from app/javascript/mastodon/features/ui/components/bundle_column_error.js)11
-rw-r--r--app/javascript/mastodon/features/ui/components/bundle_modal_error.jsx (renamed from app/javascript/mastodon/features/ui/components/bundle_modal_error.js)4
-rw-r--r--app/javascript/mastodon/features/ui/components/column.jsx (renamed from app/javascript/mastodon/features/ui/components/column.js)6
-rw-r--r--app/javascript/mastodon/features/ui/components/column_header.jsx (renamed from app/javascript/mastodon/features/ui/components/column_header.js)2
-rw-r--r--app/javascript/mastodon/features/ui/components/column_link.jsx (renamed from app/javascript/mastodon/features/ui/components/column_link.js)0
-rw-r--r--app/javascript/mastodon/features/ui/components/column_loading.jsx (renamed from app/javascript/mastodon/features/ui/components/column_loading.js)0
-rw-r--r--app/javascript/mastodon/features/ui/components/column_subheading.jsx (renamed from app/javascript/mastodon/features/ui/components/column_subheading.js)0
-rw-r--r--app/javascript/mastodon/features/ui/components/columns_area.jsx (renamed from app/javascript/mastodon/features/ui/components/columns_area.js)12
-rw-r--r--app/javascript/mastodon/features/ui/components/compare_history_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/compare_history_modal.js)14
-rw-r--r--app/javascript/mastodon/features/ui/components/compose_panel.jsx (renamed from app/javascript/mastodon/features/ui/components/compose_panel.js)7
-rw-r--r--app/javascript/mastodon/features/ui/components/confirmation_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/confirmation_modal.js)11
-rw-r--r--app/javascript/mastodon/features/ui/components/disabled_account_banner.jsx (renamed from app/javascript/mastodon/features/ui/components/disabled_account_banner.js)6
-rw-r--r--app/javascript/mastodon/features/ui/components/drawer_loading.jsx (renamed from app/javascript/mastodon/features/ui/components/drawer_loading.js)0
-rw-r--r--app/javascript/mastodon/features/ui/components/embed_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/embed_modal.js)9
-rw-r--r--app/javascript/mastodon/features/ui/components/filter_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/filter_modal.js)4
-rw-r--r--app/javascript/mastodon/features/ui/components/focal_point_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/focal_point_modal.js)44
-rw-r--r--app/javascript/mastodon/features/ui/components/follow_requests_column_link.jsx (renamed from app/javascript/mastodon/features/ui/components/follow_requests_column_link.js)4
-rw-r--r--app/javascript/mastodon/features/ui/components/header.jsx (renamed from app/javascript/mastodon/features/ui/components/header.js)4
-rw-r--r--app/javascript/mastodon/features/ui/components/image_loader.jsx (renamed from app/javascript/flavours/glitch/features/ui/components/image_loader.js)13
-rw-r--r--app/javascript/mastodon/features/ui/components/image_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/image_modal.js)3
-rw-r--r--app/javascript/mastodon/features/ui/components/link_footer.jsx (renamed from app/javascript/mastodon/features/ui/components/link_footer.js)22
-rw-r--r--app/javascript/mastodon/features/ui/components/list_panel.jsx (renamed from app/javascript/mastodon/features/ui/components/list_panel.js)4
-rw-r--r--app/javascript/mastodon/features/ui/components/media_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/media_modal.js)29
-rw-r--r--app/javascript/mastodon/features/ui/components/modal_loading.jsx (renamed from app/javascript/mastodon/features/ui/components/modal_loading.js)0
-rw-r--r--app/javascript/mastodon/features/ui/components/modal_root.jsx (renamed from app/javascript/mastodon/features/ui/components/modal_root.js)12
-rw-r--r--app/javascript/mastodon/features/ui/components/mute_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/mute_modal.js)14
-rw-r--r--app/javascript/mastodon/features/ui/components/navigation_panel.jsx (renamed from app/javascript/mastodon/features/ui/components/navigation_panel.js)7
-rw-r--r--app/javascript/mastodon/features/ui/components/report_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/report_modal.js)6
-rw-r--r--app/javascript/mastodon/features/ui/components/sign_in_banner.jsx (renamed from app/javascript/mastodon/features/ui/components/sign_in_banner.js)2
-rw-r--r--app/javascript/mastodon/features/ui/components/upload_area.jsx (renamed from app/javascript/mastodon/features/ui/components/upload_area.js)2
-rw-r--r--app/javascript/mastodon/features/ui/components/video_modal.jsx (renamed from app/javascript/mastodon/features/ui/components/video_modal.js)13
-rw-r--r--app/javascript/mastodon/features/ui/components/zoomable_image.jsx (renamed from app/javascript/mastodon/features/ui/components/zoomable_image.js)36
-rw-r--r--app/javascript/mastodon/features/ui/index.jsx (renamed from app/javascript/mastodon/features/ui/index.js)86
-rw-r--r--app/javascript/mastodon/features/ui/util/async-components.js4
-rw-r--r--app/javascript/mastodon/features/ui/util/react_router_helpers.jsx (renamed from app/javascript/mastodon/features/ui/util/react_router_helpers.js)6
-rw-r--r--app/javascript/mastodon/features/ui/util/reduced_motion.jsx (renamed from app/javascript/mastodon/features/ui/util/reduced_motion.js)2
-rw-r--r--app/javascript/mastodon/features/video/index.jsx (renamed from app/javascript/mastodon/features/video/index.js)65
-rw-r--r--app/javascript/mastodon/initial_state.js8
-rw-r--r--app/javascript/mastodon/is_mobile.js4
-rw-r--r--app/javascript/mastodon/load_polyfills.js2
-rw-r--r--app/javascript/mastodon/locales/af.json37
-rw-r--r--app/javascript/mastodon/locales/an.json53
-rw-r--r--app/javascript/mastodon/locales/ar.json59
-rw-r--r--app/javascript/mastodon/locales/ast.json197
-rw-r--r--app/javascript/mastodon/locales/be.json47
-rw-r--r--app/javascript/mastodon/locales/bg.json61
-rw-r--r--app/javascript/mastodon/locales/bn.json35
-rw-r--r--app/javascript/mastodon/locales/br.json37
-rw-r--r--app/javascript/mastodon/locales/bs.json35
-rw-r--r--app/javascript/mastodon/locales/ca.json105
-rw-r--r--app/javascript/mastodon/locales/ckb.json157
-rw-r--r--app/javascript/mastodon/locales/co.json35
-rw-r--r--app/javascript/mastodon/locales/cs.json37
-rw-r--r--app/javascript/mastodon/locales/csb.json664
-rw-r--r--app/javascript/mastodon/locales/cy.json119
-rw-r--r--app/javascript/mastodon/locales/da.json37
-rw-r--r--app/javascript/mastodon/locales/de.json131
-rw-r--r--app/javascript/mastodon/locales/defaultMessages.json93
-rw-r--r--app/javascript/mastodon/locales/el.json103
-rw-r--r--app/javascript/mastodon/locales/en-GB.json47
-rw-r--r--app/javascript/mastodon/locales/en.json42
-rw-r--r--app/javascript/mastodon/locales/eo.json119
-rw-r--r--app/javascript/mastodon/locales/es-AR.json47
-rw-r--r--app/javascript/mastodon/locales/es-MX.json39
-rw-r--r--app/javascript/mastodon/locales/es.json53
-rw-r--r--app/javascript/mastodon/locales/et.json59
-rw-r--r--app/javascript/mastodon/locales/eu.json49
-rw-r--r--app/javascript/mastodon/locales/fa.json73
-rw-r--r--app/javascript/mastodon/locales/fi.json207
-rw-r--r--app/javascript/mastodon/locales/fo.json39
-rw-r--r--app/javascript/mastodon/locales/fr-QC.json41
-rw-r--r--app/javascript/mastodon/locales/fr.json79
-rw-r--r--app/javascript/mastodon/locales/fy.json37
-rw-r--r--app/javascript/mastodon/locales/ga.json77
-rw-r--r--app/javascript/mastodon/locales/gd.json55
-rw-r--r--app/javascript/mastodon/locales/gl.json41
-rw-r--r--app/javascript/mastodon/locales/he.json61
-rw-r--r--app/javascript/mastodon/locales/hi.json49
-rw-r--r--app/javascript/mastodon/locales/hr.json39
-rw-r--r--app/javascript/mastodon/locales/hu.json293
-rw-r--r--app/javascript/mastodon/locales/hy.json167
-rw-r--r--app/javascript/mastodon/locales/id.json55
-rw-r--r--app/javascript/mastodon/locales/ig.json35
-rw-r--r--app/javascript/mastodon/locales/io.json37
-rw-r--r--app/javascript/mastodon/locales/is.json37
-rw-r--r--app/javascript/mastodon/locales/it.json41
-rw-r--r--app/javascript/mastodon/locales/ja.json53
-rw-r--r--app/javascript/mastodon/locales/ka.json35
-rw-r--r--app/javascript/mastodon/locales/kab.json47
-rw-r--r--app/javascript/mastodon/locales/kk.json113
-rw-r--r--app/javascript/mastodon/locales/kn.json35
-rw-r--r--app/javascript/mastodon/locales/ko.json105
-rw-r--r--app/javascript/mastodon/locales/ku.json37
-rw-r--r--app/javascript/mastodon/locales/kw.json37
-rw-r--r--app/javascript/mastodon/locales/la.json41
-rw-r--r--app/javascript/mastodon/locales/locale-data/README.md (renamed from app/javascript/locales/locale-data/README.md)0
-rw-r--r--app/javascript/mastodon/locales/locale-data/oc.js (renamed from app/javascript/locales/locale-data/oc.js)0
-rw-r--r--app/javascript/mastodon/locales/lt.json37
-rw-r--r--app/javascript/mastodon/locales/lv.json53
-rw-r--r--app/javascript/mastodon/locales/mk.json35
-rw-r--r--app/javascript/mastodon/locales/ml.json35
-rw-r--r--app/javascript/mastodon/locales/mr.json39
-rw-r--r--app/javascript/mastodon/locales/ms.json47
-rw-r--r--app/javascript/mastodon/locales/my.json1109
-rw-r--r--app/javascript/mastodon/locales/nl.json49
-rw-r--r--app/javascript/mastodon/locales/nn.json41
-rw-r--r--app/javascript/mastodon/locales/no.json37
-rw-r--r--app/javascript/mastodon/locales/oc.json47
-rw-r--r--app/javascript/mastodon/locales/pa.json35
-rw-r--r--app/javascript/mastodon/locales/pl.json64
-rw-r--r--app/javascript/mastodon/locales/pt-BR.json53
-rw-r--r--app/javascript/mastodon/locales/pt-PT.json43
-rw-r--r--app/javascript/mastodon/locales/ro.json47
-rw-r--r--app/javascript/mastodon/locales/ru.json45
-rw-r--r--app/javascript/mastodon/locales/sa.json759
-rw-r--r--app/javascript/mastodon/locales/sc.json35
-rw-r--r--app/javascript/mastodon/locales/sco.json37
-rw-r--r--app/javascript/mastodon/locales/si.json37
-rw-r--r--app/javascript/mastodon/locales/sk.json79
-rw-r--r--app/javascript/mastodon/locales/sl.json37
-rw-r--r--app/javascript/mastodon/locales/sq.json55
-rw-r--r--app/javascript/mastodon/locales/sr-Latn.json153
-rw-r--r--app/javascript/mastodon/locales/sr.json167
-rw-r--r--app/javascript/mastodon/locales/sv.json59
-rw-r--r--app/javascript/mastodon/locales/szl.json75
-rw-r--r--app/javascript/mastodon/locales/ta.json35
-rw-r--r--app/javascript/mastodon/locales/tai.json35
-rw-r--r--app/javascript/mastodon/locales/te.json35
-rw-r--r--app/javascript/mastodon/locales/th.json61
-rw-r--r--app/javascript/mastodon/locales/tr.json45
-rw-r--r--app/javascript/mastodon/locales/tt.json507
-rw-r--r--app/javascript/mastodon/locales/ug.json35
-rw-r--r--app/javascript/mastodon/locales/uk.json37
-rw-r--r--app/javascript/mastodon/locales/ur.json35
-rw-r--r--app/javascript/mastodon/locales/uz.json664
-rw-r--r--app/javascript/mastodon/locales/vi.json31
-rw-r--r--app/javascript/mastodon/locales/whitelist_csb.json2
-rw-r--r--app/javascript/mastodon/locales/whitelist_uz.json2
-rw-r--r--app/javascript/mastodon/locales/zgh.json35
-rw-r--r--app/javascript/mastodon/locales/zh-CN.json69
-rw-r--r--app/javascript/mastodon/locales/zh-HK.json43
-rw-r--r--app/javascript/mastodon/locales/zh-TW.json153
-rw-r--r--app/javascript/mastodon/main.jsx (renamed from app/javascript/mastodon/main.js)0
-rw-r--r--app/javascript/mastodon/performance.js1
-rw-r--r--app/javascript/mastodon/reducers/compose.js19
-rw-r--r--app/javascript/mastodon/reducers/followed_tags.js42
-rw-r--r--app/javascript/mastodon/reducers/index.js2
-rw-r--r--app/javascript/mastodon/reducers/search.js9
-rw-r--r--app/javascript/mastodon/reducers/server.js9
-rw-r--r--app/javascript/mastodon/selectors/index.js4
-rw-r--r--app/javascript/mastodon/service_worker/web_push_notifications.js10
-rw-r--r--app/javascript/mastodon/stream.js34
-rw-r--r--app/javascript/mastodon/utils/hashtags.js47
-rw-r--r--app/javascript/mastodon/utils/icons.jsx (renamed from app/javascript/mastodon/utils/icons.js)0
-rw-r--r--app/javascript/mastodon/utils/notifications.js2
-rw-r--r--app/javascript/mastodon/utils/resize_image.js189
-rw-r--r--app/javascript/mastodon/uuid.js3
-rw-r--r--app/javascript/mastodon/uuid.ts8
-rw-r--r--app/javascript/packs/admin.jsx (renamed from app/javascript/packs/admin.js)0
-rw-r--r--app/javascript/packs/public-path.js2
-rw-r--r--app/javascript/packs/public.jsx (renamed from app/javascript/packs/public.js)9
-rw-r--r--app/javascript/packs/share.jsx (renamed from app/javascript/packs/share.js)0
-rw-r--r--app/javascript/styles/application.scss1
-rw-r--r--app/javascript/styles/fonts/roboto-mono.scss9
-rw-r--r--app/javascript/styles/fonts/roboto.scss24
-rw-r--r--app/javascript/styles/mastodon-light/diff.scss72
-rw-r--r--app/javascript/styles/mastodon/about.scss4
-rw-r--r--app/javascript/styles/mastodon/accessibility.scss5
-rw-r--r--app/javascript/styles/mastodon/accounts.scss16
-rw-r--r--app/javascript/styles/mastodon/admin.scss113
-rw-r--r--app/javascript/styles/mastodon/basics.scss8
-rw-r--r--app/javascript/styles/mastodon/components.scss649
-rw-r--r--app/javascript/styles/mastodon/containers.scss6
-rw-r--r--app/javascript/styles/mastodon/emoji_picker.scss12
-rw-r--r--app/javascript/styles/mastodon/forms.scss82
-rw-r--r--app/javascript/styles/mastodon/modal.scss6
-rw-r--r--app/javascript/styles/mastodon/polls.scss20
-rw-r--r--app/javascript/styles/mastodon/rich_text.scss64
-rw-r--r--app/javascript/styles/mastodon/rtl.scss365
-rw-r--r--app/javascript/styles/mastodon/statuses.scss10
-rw-r--r--app/javascript/styles/mastodon/tables.scss22
-rw-r--r--app/javascript/styles/mastodon/variables.scss31
-rw-r--r--app/javascript/styles/mastodon/widgets.scss18
-rw-r--r--app/javascript/types/resources.ts13
-rw-r--r--app/lib/activity_tracker.rb14
-rw-r--r--app/lib/activitypub/activity.rb8
-rw-r--r--app/lib/activitypub/activity/create.rb46
-rw-r--r--app/lib/activitypub/case_transform.rb2
-rw-r--r--app/lib/activitypub/forwarder.rb14
-rw-r--r--app/lib/activitypub/linked_data_signature.rb6
-rw-r--r--app/lib/activitypub/parser/media_attachment_parser.rb4
-rw-r--r--app/lib/activitypub/tag_manager.rb2
-rw-r--r--app/lib/admin/metrics/dimension/software_versions_dimension.rb12
-rw-r--r--app/lib/admin/metrics/dimension/space_usage_dimension.rb12
-rw-r--r--app/lib/admin/metrics/retention.rb56
-rw-r--r--app/lib/admin/system_check.rb1
-rw-r--r--app/lib/admin/system_check/elasticsearch_check.rb13
-rw-r--r--app/lib/admin/system_check/media_privacy_check.rb105
-rw-r--r--app/lib/admin/system_check/message.rb11
-rw-r--r--app/lib/advanced_text_formatter.rb1
-rw-r--r--app/lib/delivery_failure_tracker.rb2
-rw-r--r--app/lib/extractor.rb16
-rw-r--r--app/lib/feed_manager.rb24
-rw-r--r--app/lib/html_aware_formatter.rb2
-rw-r--r--app/lib/importer/base_importer.rb2
-rw-r--r--app/lib/importer/statuses_index_importer.rb12
-rw-r--r--app/lib/link_details_extractor.rb38
-rw-r--r--app/lib/ostatus/tag_manager.rb32
-rw-r--r--app/lib/plain_text_formatter.rb6
-rw-r--r--app/lib/rate_limiter.rb2
-rw-r--r--app/lib/request.rb39
-rw-r--r--app/lib/request_pool.rb2
-rw-r--r--app/lib/scope_transformer.rb2
-rw-r--r--app/lib/settings/scoped_settings.rb2
-rw-r--r--app/lib/status_filter.rb1
-rw-r--r--app/lib/status_finder.rb4
-rw-r--r--app/lib/tag_manager.rb1
-rw-r--r--app/lib/themes.rb45
-rw-r--r--app/lib/toc_generator.rb69
-rw-r--r--app/lib/translation_service.rb4
-rw-r--r--app/lib/translation_service/deepl.rb52
-rw-r--r--app/lib/translation_service/libre_translate.rb34
-rw-r--r--app/lib/user_settings_decorator.rb180
-rw-r--r--app/lib/user_settings_serializer.rb19
-rw-r--r--app/lib/vacuum/access_tokens_vacuum.rb4
-rw-r--r--app/lib/validation_error_formatter.rb2
-rw-r--r--app/lib/webfinger.rb5
-rw-r--r--app/mailers/application_mailer.rb14
-rw-r--r--app/models/account.rb38
-rw-r--r--app/models/account/field.rb16
-rw-r--r--app/models/account_conversation.rb2
-rw-r--r--app/models/account_domain_block.rb1
-rw-r--r--app/models/account_filter.rb24
-rw-r--r--app/models/account_moderation_note.rb1
-rw-r--r--app/models/account_note.rb1
-rw-r--r--app/models/account_pin.rb1
-rw-r--r--app/models/account_stat.rb3
-rw-r--r--app/models/account_statuses_cleanup_policy.rb8
-rw-r--r--app/models/account_suggestions/setting_source.rb4
-rw-r--r--app/models/account_summary.rb1
-rw-r--r--app/models/account_warning.rb21
-rw-r--r--app/models/admin/account_action.rb12
-rw-r--r--app/models/admin/action_log.rb2
-rw-r--r--app/models/admin/import.rb44
-rw-r--r--app/models/admin/status_batch_action.rb9
-rw-r--r--app/models/announcement.rb14
-rw-r--r--app/models/backup.rb5
-rw-r--r--app/models/block.rb1
-rw-r--r--app/models/bookmark.rb1
-rw-r--r--app/models/canonical_email_block.rb1
-rw-r--r--app/models/concerns/account_finder_concern.rb6
-rw-r--r--app/models/concerns/account_interactions.rb23
-rw-r--r--app/models/concerns/account_merging.rb16
-rw-r--r--app/models/concerns/attachmentable.rb2
-rw-r--r--app/models/concerns/expireable.rb2
-rw-r--r--app/models/concerns/has_user_settings.rb173
-rw-r--r--app/models/concerns/ldap_authenticable.rb2
-rw-r--r--app/models/concerns/omniauthable.rb9
-rw-r--r--app/models/concerns/paginable.rb4
-rw-r--r--app/models/concerns/pam_authenticable.rb12
-rw-r--r--app/models/concerns/remotable.rb4
-rw-r--r--app/models/concerns/status_threading_concern.rb14
-rw-r--r--app/models/conversation.rb1
-rw-r--r--app/models/conversation_mute.rb1
-rw-r--r--app/models/custom_emoji.rb1
-rw-r--r--app/models/custom_filter.rb11
-rw-r--r--app/models/custom_filter_keyword.rb1
-rw-r--r--app/models/custom_filter_status.rb1
-rw-r--r--app/models/device.rb1
-rw-r--r--app/models/direct_feed.rb9
-rw-r--r--app/models/domain_block.rb3
-rw-r--r--app/models/email_domain_block.rb15
-rw-r--r--app/models/encrypted_message.rb1
-rw-r--r--app/models/favourite.rb2
-rw-r--r--app/models/featured_tag.rb1
-rw-r--r--app/models/follow.rb1
-rw-r--r--app/models/follow_recommendation.rb3
-rw-r--r--app/models/follow_recommendation_suppression.rb5
-rw-r--r--app/models/follow_request.rb1
-rw-r--r--app/models/form/account_batch.rb16
-rw-r--r--app/models/form/admin_settings.rb17
-rw-r--r--app/models/form/custom_emoji_batch.rb12
-rw-r--r--app/models/identity.rb1
-rw-r--r--app/models/import.rb3
-rw-r--r--app/models/instance.rb1
-rw-r--r--app/models/invite.rb1
-rw-r--r--app/models/ip_block.rb1
-rw-r--r--app/models/list.rb3
-rw-r--r--app/models/list_account.rb1
-rw-r--r--app/models/login_activity.rb1
-rw-r--r--app/models/media_attachment.rb17
-rw-r--r--app/models/mention.rb1
-rw-r--r--app/models/mute.rb1
-rw-r--r--app/models/notification.rb23
-rw-r--r--app/models/one_time_key.rb1
-rw-r--r--app/models/poll.rb8
-rw-r--r--app/models/poll_vote.rb2
-rw-r--r--app/models/preview_card.rb9
-rw-r--r--app/models/preview_card_provider.rb1
-rw-r--r--app/models/relay.rb3
-rw-r--r--app/models/remote_follow.rb12
-rw-r--r--app/models/report.rb5
-rw-r--r--app/models/report_note.rb1
-rw-r--r--app/models/session_activation.rb4
-rw-r--r--app/models/setting.rb7
-rw-r--r--app/models/site_upload.rb1
-rw-r--r--app/models/status.rb62
-rw-r--r--app/models/status_edit.rb18
-rw-r--r--app/models/status_pin.rb1
-rw-r--r--app/models/status_stat.rb1
-rw-r--r--app/models/system_key.rb2
-rw-r--r--app/models/tag.rb3
-rw-r--r--app/models/trends/history.rb4
-rw-r--r--app/models/trends/links.rb24
-rw-r--r--app/models/trends/statuses.rb26
-rw-r--r--app/models/trends/tag_filter.rb12
-rw-r--r--app/models/trends/tags.rb12
-rw-r--r--app/models/unavailable_domain.rb1
-rw-r--r--app/models/user.rb105
-rw-r--r--app/models/user_ip.rb3
-rw-r--r--app/models/user_role.rb2
-rw-r--r--app/models/user_settings.rb106
-rw-r--r--app/models/user_settings/dsl.rb37
-rw-r--r--app/models/user_settings/glue.rb23
-rw-r--r--app/models/user_settings/namespace.rb21
-rw-r--r--app/models/user_settings/setting.rb49
-rw-r--r--app/models/web/push_subscription.rb25
-rw-r--r--app/models/web/setting.rb1
-rw-r--r--app/models/webauthn_credential.rb3
-rw-r--r--app/models/webhook.rb3
-rw-r--r--app/presenters/account_relationships_presenter.rb18
-rw-r--r--app/presenters/instance_presenter.rb12
-rw-r--r--app/presenters/tag_relationships_presenter.rb12
-rw-r--r--app/serializers/activitypub/note_serializer.rb1
-rw-r--r--app/serializers/initial_state_serializer.rb9
-rw-r--r--app/serializers/rest/account_serializer.rb42
-rw-r--r--app/serializers/rest/admin/account_serializer.rb3
-rw-r--r--app/serializers/rest/admin/webhook_event_serializer.rb2
-rw-r--r--app/serializers/rest/instance_serializer.rb8
-rw-r--r--app/serializers/rest/mute_serializer.rb4
-rw-r--r--app/serializers/rest/preview_card_serializer.rb2
-rw-r--r--app/serializers/rest/privacy_policy_serializer.rb2
-rw-r--r--app/serializers/rest/status_serializer.rb4
-rw-r--r--app/serializers/rest/v1/instance_serializer.rb1
-rw-r--r--app/serializers/rest/web_push_subscription_serializer.rb6
-rw-r--r--app/services/account_search_service.rb16
-rw-r--r--app/services/activitypub/fetch_featured_collection_service.rb2
-rw-r--r--app/services/activitypub/fetch_featured_tags_collection_service.rb14
-rw-r--r--app/services/activitypub/fetch_remote_account_service.rb2
-rw-r--r--app/services/activitypub/fetch_remote_actor_service.rb8
-rw-r--r--app/services/activitypub/fetch_remote_key_service.rb2
-rw-r--r--app/services/activitypub/fetch_remote_status_service.rb26
-rw-r--r--app/services/activitypub/fetch_replies_service.rb5
-rw-r--r--app/services/activitypub/process_account_service.rb2
-rw-r--r--app/services/activitypub/process_collection_service.rb6
-rw-r--r--app/services/activitypub/process_status_update_service.rb6
-rw-r--r--app/services/backup_service.rb14
-rw-r--r--app/services/batched_remove_status_service.rb18
-rw-r--r--app/services/delete_account_service.rb6
-rw-r--r--app/services/fan_out_on_write_service.rb2
-rw-r--r--app/services/favourite_service.rb1
-rw-r--r--app/services/fetch_link_card_service.rb22
-rw-r--r--app/services/fetch_oembed_service.rb2
-rw-r--r--app/services/fetch_remote_status_service.rb4
-rw-r--r--app/services/fetch_resource_service.rb2
-rw-r--r--app/services/follow_migration_service.rb40
-rw-r--r--app/services/import_service.rb16
-rw-r--r--app/services/keys/claim_service.rb8
-rw-r--r--app/services/keys/query_service.rb8
-rw-r--r--app/services/notify_service.rb15
-rw-r--r--app/services/post_status_service.rb56
-rw-r--r--app/services/process_mentions_service.rb31
-rw-r--r--app/services/reblog_service.rb12
-rw-r--r--app/services/remove_domains_from_followers_service.rb23
-rw-r--r--app/services/remove_from_followers_service.rb4
-rw-r--r--app/services/remove_status_service.rb2
-rw-r--r--app/services/resolve_account_service.rb18
-rw-r--r--app/services/resolve_url_service.rb2
-rw-r--r--app/services/search_service.rb16
-rw-r--r--app/services/suspend_account_service.rb12
-rw-r--r--app/services/translate_status_service.rb16
-rw-r--r--app/services/unsuspend_account_service.rb2
-rw-r--r--app/services/update_account_service.rb2
-rw-r--r--app/services/update_status_service.rb15
-rw-r--r--app/services/verify_link_service.rb6
-rw-r--r--app/services/vote_service.rb2
-rw-r--r--app/validators/domain_validator.rb12
-rw-r--r--app/validators/ed25519_key_validator.rb2
-rw-r--r--app/validators/ed25519_signature_validator.rb2
-rw-r--r--app/validators/email_mx_validator.rb2
-rw-r--r--app/validators/existing_username_validator.rb14
-rw-r--r--app/validators/follow_limit_validator.rb1
-rw-r--r--app/validators/html_validator.rb20
-rw-r--r--app/validators/import_validator.rb12
-rw-r--r--app/validators/status_pin_validator.rb2
-rw-r--r--app/validators/unreserved_username_validator.rb2
-rw-r--r--app/validators/vote_validator.rb26
-rw-r--r--app/views/accounts/show.rss.ruby12
-rw-r--r--app/views/admin/account_actions/new.html.haml2
-rw-r--r--app/views/admin/accounts/show.html.haml12
-rw-r--r--app/views/admin/action_logs/index.html.haml4
-rw-r--r--app/views/admin/announcements/edit.html.haml2
-rw-r--r--app/views/admin/announcements/index.html.haml2
-rw-r--r--app/views/admin/change_emails/show.html.haml2
-rw-r--r--app/views/admin/custom_emojis/index.html.haml4
-rw-r--r--app/views/admin/dashboard/index.html.haml4
-rw-r--r--app/views/admin/disputes/appeals/index.html.haml2
-rw-r--r--app/views/admin/instances/index.html.haml2
-rw-r--r--app/views/admin/report_notes/_report_note.html.haml2
-rw-r--r--app/views/admin/reports/_actions.html.haml2
-rw-r--r--app/views/admin/reports/actions/preview.html.haml78
-rw-r--r--app/views/admin/rules/index.html.haml2
-rw-r--r--app/views/admin/settings/about/show.html.haml3
-rw-r--r--app/views/admin/settings/discovery/show.html.haml3
-rw-r--r--app/views/admin/statuses/show.html.haml2
-rw-r--r--app/views/admin/warning_presets/index.html.haml2
-rw-r--r--app/views/admin/webhooks/index.html.haml2
-rw-r--r--app/views/application/_card.html.haml2
-rw-r--r--app/views/application/_sidebar.html.haml16
-rw-r--r--app/views/auth/confirmations/captcha.html.haml2
-rw-r--r--app/views/auth/confirmations/new.html.haml2
-rw-r--r--app/views/auth/registrations/_sessions.html.haml2
-rw-r--r--app/views/auth/registrations/edit.html.haml4
-rw-r--r--app/views/auth/sessions/new.html.haml2
-rw-r--r--app/views/disputes/strikes/show.html.haml14
-rw-r--r--app/views/filters/_filter_fields.html.haml2
-rw-r--r--app/views/filters/index.html.haml2
-rw-r--r--app/views/kaminari/_gap.html.haml9
-rw-r--r--app/views/kaminari/_next_page.html.haml16
-rw-r--r--app/views/kaminari/_paginator.html.haml15
-rw-r--r--app/views/kaminari/_prev_page.html.haml15
-rw-r--r--app/views/layouts/_theme.html.haml5
-rwxr-xr-xapp/views/layouts/application.html.haml2
-rw-r--r--app/views/layouts/embedded.html.haml2
-rw-r--r--app/views/layouts/mailer.html.haml2
-rw-r--r--app/views/layouts/modal.html.haml2
-rw-r--r--app/views/media/player.html.haml2
-rw-r--r--app/views/notification_mailer/_status.html.haml6
-rw-r--r--app/views/notification_mailer/favourite.html.haml2
-rw-r--r--app/views/notification_mailer/follow_request.html.haml2
-rw-r--r--app/views/oauth/authorizations/show.html.haml2
-rw-r--r--app/views/relationships/show.html.haml2
-rw-r--r--app/views/settings/applications/index.html.haml2
-rw-r--r--app/views/settings/applications/show.html.haml4
-rw-r--r--app/views/settings/exports/show.html.haml2
-rw-r--r--app/views/settings/featured_tags/index.html.haml2
-rw-r--r--app/views/settings/flavours/show.html.haml4
-rw-r--r--app/views/settings/login_activities/_login_activity.html.haml2
-rw-r--r--app/views/settings/login_activities/index.html.haml2
-rw-r--r--app/views/settings/preferences/appearance/show.html.haml73
-rw-r--r--app/views/settings/preferences/notifications/show.html.haml40
-rw-r--r--app/views/settings/preferences/other/show.html.haml39
-rw-r--r--app/views/shared/_og.html.haml2
-rw-r--r--app/views/statuses/_detailed_status.html.haml6
-rw-r--r--app/views/statuses/_og_image.html.haml5
-rw-r--r--app/views/statuses/_poll.html.haml2
-rw-r--r--app/views/statuses/_status.html.haml2
-rw-r--r--app/views/tags/show.rss.ruby12
-rw-r--r--app/views/user_mailer/appeal_rejected.html.haml2
-rw-r--r--app/views/user_mailer/backup_ready.html.haml2
-rw-r--r--app/views/user_mailer/backup_ready.text.erb2
-rw-r--r--app/views/user_mailer/suspicious_sign_in.html.haml2
-rw-r--r--app/views/user_mailer/webauthn_credential_added.html.haml2
-rw-r--r--app/views/user_mailer/webauthn_credential_deleted.html.haml2
-rw-r--r--app/views/well_known/host_meta/show.xml.ruby2
-rw-r--r--app/workers/activitypub/delivery_worker.rb10
-rw-r--r--app/workers/activitypub/distribute_poll_update_worker.rb2
-rw-r--r--app/workers/activitypub/fetch_replies_worker.rb4
-rw-r--r--app/workers/activitypub/migrated_follow_delivery_worker.rb17
-rw-r--r--app/workers/activitypub/move_distribution_worker.rb2
-rw-r--r--app/workers/activitypub/processing_worker.rb2
-rw-r--r--app/workers/activitypub/raw_distribution_worker.rb2
-rw-r--r--app/workers/backup_worker.rb10
-rw-r--r--app/workers/concerns/exponential_backoff.rb2
-rw-r--r--app/workers/fetch_reply_worker.rb4
-rw-r--r--app/workers/import/relationship_worker.rb2
-rw-r--r--app/workers/poll_expiration_notify_worker.rb2
-rw-r--r--app/workers/post_process_media_worker.rb12
-rw-r--r--app/workers/scheduler/accounts_statuses_cleanup_scheduler.rb26
-rw-r--r--app/workers/scheduler/follow_recommendations_scheduler.rb23
-rw-r--r--app/workers/scheduler/indexing_scheduler.rb14
-rw-r--r--app/workers/scheduler/user_cleanup_scheduler.rb2
-rw-r--r--app/workers/thread_resolve_worker.rb4
-rw-r--r--app/workers/unfollow_follow_worker.rb8
-rw-r--r--app/workers/web/push_notification_worker.rb12
-rw-r--r--app/workers/webhooks/delivery_worker.rb2
-rw-r--r--babel.config.js4
-rwxr-xr-xbin/tootctl4
-rw-r--r--config.ru3
-rw-r--r--config/application.rb21
-rw-r--r--config/database.yml1
-rw-r--r--config/environments/development.rb31
-rw-r--r--config/environments/production.rb18
-rw-r--r--config/environments/test.rb13
-rw-r--r--config/i18n-tasks.yml4
-rw-r--r--config/initializers/0_duplicate_migrations.rb24
-rw-r--r--config/initializers/chewy.rb2
-rw-r--r--config/initializers/inflections.rb1
-rw-r--r--config/initializers/paperclip.rb7
-rw-r--r--config/initializers/rack_attack.rb10
-rw-r--r--config/initializers/redis.rb1
-rw-r--r--config/initializers/simple_form.rb1
-rw-r--r--config/locales-glitch/ckb.yml2
-rw-r--r--config/locales-glitch/es-AR.yml22
-rw-r--r--config/locales-glitch/es-MX.yml22
-rw-r--r--config/locales-glitch/es.yml32
-rw-r--r--config/locales-glitch/fr-QC.yml2
-rw-r--r--config/locales-glitch/fr.yml2
-rw-r--r--config/locales-glitch/ku.yml2
-rw-r--r--config/locales-glitch/no.yml3
-rw-r--r--config/locales-glitch/pl.yml36
-rw-r--r--config/locales-glitch/pt-PT.yml41
-rw-r--r--config/locales-glitch/simple_form.ckb.yml2
-rw-r--r--config/locales-glitch/simple_form.es-AR.yml9
-rw-r--r--config/locales-glitch/simple_form.es-MX.yml9
-rw-r--r--config/locales-glitch/simple_form.es.yml9
-rw-r--r--config/locales-glitch/simple_form.ku.yml2
-rw-r--r--config/locales-glitch/simple_form.no.yml3
-rw-r--r--config/locales-glitch/simple_form.pl.yml17
-rw-r--r--config/locales-glitch/simple_form.sr-Latn.yml2
-rw-r--r--config/locales-glitch/simple_form.zh-CN.yml15
-rw-r--r--config/locales-glitch/simple_form.zh-HK.yml2
-rw-r--r--config/locales-glitch/sr-Latn.yml2
-rw-r--r--config/locales-glitch/zh-CN.yml24
-rw-r--r--config/locales-glitch/zh-HK.yml2
-rw-r--r--config/locales/activerecord.bg.yml4
-rw-r--r--config/locales/activerecord.ca.yml2
-rw-r--r--config/locales/activerecord.csb.yml1
-rw-r--r--config/locales/activerecord.de.yml16
-rw-r--r--config/locales/activerecord.el.yml13
-rw-r--r--config/locales/activerecord.eo.yml2
-rw-r--r--config/locales/activerecord.fi.yml4
-rw-r--r--config/locales/activerecord.fr.yml2
-rw-r--r--config/locales/activerecord.fy.yml10
-rw-r--r--config/locales/activerecord.hu.yml2
-rw-r--r--config/locales/activerecord.ka.yml37
-rw-r--r--config/locales/activerecord.kab.yml4
-rw-r--r--config/locales/activerecord.my.yml54
-rw-r--r--config/locales/activerecord.pt-BR.yml2
-rw-r--r--config/locales/activerecord.pt-PT.yml8
-rw-r--r--config/locales/activerecord.tr.yml2
-rw-r--r--config/locales/activerecord.tt.yml4
-rw-r--r--config/locales/activerecord.uz.yml55
-rw-r--r--config/locales/activerecord.zh-TW.yml2
-rw-r--r--config/locales/activerecord.zh_Hant.yml15
-rw-r--r--config/locales/af.yml2
-rw-r--r--config/locales/an.yml30
-rw-r--r--config/locales/ar.yml107
-rw-r--r--config/locales/ast.yml162
-rw-r--r--config/locales/be.yml64
-rw-r--r--config/locales/bg.yml120
-rw-r--r--config/locales/bn.yml2
-rw-r--r--config/locales/br.yml4
-rw-r--r--config/locales/bs.yml2
-rw-r--r--config/locales/ca.yml140
-rw-r--r--config/locales/ckb.yml13
-rw-r--r--config/locales/co.yml12
-rw-r--r--config/locales/cs.yml44
-rw-r--r--config/locales/csb.yml10
-rw-r--r--config/locales/cy.yml96
-rw-r--r--config/locales/da.yml88
-rw-r--r--config/locales/de.yml644
-rw-r--r--config/locales/devise.ar.yml4
-rw-r--r--config/locales/devise.ast.yml4
-rw-r--r--config/locales/devise.bg.yml10
-rw-r--r--config/locales/devise.csb.yml1
-rw-r--r--config/locales/devise.de.yml54
-rw-r--r--config/locales/devise.eo.yml2
-rw-r--r--config/locales/devise.et.yml10
-rw-r--r--config/locales/devise.fi.yml46
-rw-r--r--config/locales/devise.fr.yml22
-rw-r--r--config/locales/devise.fy.yml6
-rw-r--r--config/locales/devise.ga.yml6
-rw-r--r--config/locales/devise.hu.yml98
-rw-r--r--config/locales/devise.ko.yml28
-rw-r--r--config/locales/devise.my.yml113
-rw-r--r--config/locales/devise.pt-PT.yml44
-rw-r--r--config/locales/devise.uz.yml13
-rw-r--r--config/locales/devise.zh-CN.yml6
-rw-r--r--config/locales/devise.zh-TW.yml14
-rw-r--r--config/locales/doorkeeper.af.yml2
-rw-r--r--config/locales/doorkeeper.an.yml14
-rw-r--r--config/locales/doorkeeper.ar.yml4
-rw-r--r--config/locales/doorkeeper.ast.yml24
-rw-r--r--config/locales/doorkeeper.be.yml4
-rw-r--r--config/locales/doorkeeper.bg.yml6
-rw-r--r--config/locales/doorkeeper.ca.yml4
-rw-r--r--config/locales/doorkeeper.ckb.yml2
-rw-r--r--config/locales/doorkeeper.cs.yml4
-rw-r--r--config/locales/doorkeeper.csb.yml1
-rw-r--r--config/locales/doorkeeper.cy.yml20
-rw-r--r--config/locales/doorkeeper.da.yml4
-rw-r--r--config/locales/doorkeeper.de.yml36
-rw-r--r--config/locales/doorkeeper.el.yml21
-rw-r--r--config/locales/doorkeeper.en-GB.yml4
-rw-r--r--config/locales/doorkeeper.eo.yml18
-rw-r--r--config/locales/doorkeeper.es-AR.yml4
-rw-r--r--config/locales/doorkeeper.es-MX.yml4
-rw-r--r--config/locales/doorkeeper.es.yml4
-rw-r--r--config/locales/doorkeeper.et.yml6
-rw-r--r--config/locales/doorkeeper.eu.yml14
-rw-r--r--config/locales/doorkeeper.fa.yml4
-rw-r--r--config/locales/doorkeeper.fi.yml26
-rw-r--r--config/locales/doorkeeper.fo.yml4
-rw-r--r--config/locales/doorkeeper.fr-QC.yml14
-rw-r--r--config/locales/doorkeeper.fr.yml16
-rw-r--r--config/locales/doorkeeper.fy.yml4
-rw-r--r--config/locales/doorkeeper.ga.yml10
-rw-r--r--config/locales/doorkeeper.gd.yml14
-rw-r--r--config/locales/doorkeeper.gl.yml20
-rw-r--r--config/locales/doorkeeper.he.yml4
-rw-r--r--config/locales/doorkeeper.hu.yml72
-rw-r--r--config/locales/doorkeeper.hy.yml3
-rw-r--r--config/locales/doorkeeper.id.yml2
-rw-r--r--config/locales/doorkeeper.io.yml2
-rw-r--r--config/locales/doorkeeper.is.yml4
-rw-r--r--config/locales/doorkeeper.it.yml4
-rw-r--r--config/locales/doorkeeper.ja.yml14
-rw-r--r--config/locales/doorkeeper.ko.yml64
-rw-r--r--config/locales/doorkeeper.ku.yml2
-rw-r--r--config/locales/doorkeeper.lv.yml4
-rw-r--r--config/locales/doorkeeper.ms.yml2
-rw-r--r--config/locales/doorkeeper.my.yml194
-rw-r--r--config/locales/doorkeeper.nl.yml4
-rw-r--r--config/locales/doorkeeper.nn.yml4
-rw-r--r--config/locales/doorkeeper.no.yml4
-rw-r--r--config/locales/doorkeeper.oc.yml9
-rw-r--r--config/locales/doorkeeper.pl.yml6
-rw-r--r--config/locales/doorkeeper.pt-BR.yml14
-rw-r--r--config/locales/doorkeeper.pt-PT.yml28
-rw-r--r--config/locales/doorkeeper.ro.yml2
-rw-r--r--config/locales/doorkeeper.ru.yml12
-rw-r--r--config/locales/doorkeeper.sco.yml2
-rw-r--r--config/locales/doorkeeper.si.yml2
-rw-r--r--config/locales/doorkeeper.sk.yml3
-rw-r--r--config/locales/doorkeeper.sl.yml4
-rw-r--r--config/locales/doorkeeper.sq.yml14
-rw-r--r--config/locales/doorkeeper.sr-Latn.yml19
-rw-r--r--config/locales/doorkeeper.sr.yml19
-rw-r--r--config/locales/doorkeeper.sv.yml4
-rw-r--r--config/locales/doorkeeper.th.yml8
-rw-r--r--config/locales/doorkeeper.tr.yml18
-rw-r--r--config/locales/doorkeeper.uk.yml4
-rw-r--r--config/locales/doorkeeper.uz.yml1
-rw-r--r--config/locales/doorkeeper.vi.yml4
-rw-r--r--config/locales/doorkeeper.zh-CN.yml4
-rw-r--r--config/locales/doorkeeper.zh-HK.yml4
-rw-r--r--config/locales/doorkeeper.zh-TW.yml12
-rw-r--r--config/locales/el.yml843
-rw-r--r--config/locales/en-GB.yml1505
-rw-r--r--config/locales/en.yml60
-rw-r--r--config/locales/en_GB.yml1043
-rw-r--r--config/locales/eo.yml184
-rw-r--r--config/locales/es-AR.yml94
-rw-r--r--config/locales/es-MX.yml66
-rw-r--r--config/locales/es.yml68
-rw-r--r--config/locales/et.yml104
-rw-r--r--config/locales/eu.yml94
-rw-r--r--config/locales/fa.yml30
-rw-r--r--config/locales/fi.yml250
-rw-r--r--config/locales/fo.yml66
-rw-r--r--config/locales/fr-QC.yml82
-rw-r--r--config/locales/fr.yml136
-rw-r--r--config/locales/fy.yml66
-rw-r--r--config/locales/ga.yml205
-rw-r--r--config/locales/gd.yml77
-rw-r--r--config/locales/gl.yml74
-rw-r--r--config/locales/he.yml74
-rw-r--r--config/locales/hi.yml20
-rw-r--r--config/locales/hr.yml6
-rw-r--r--config/locales/hu.yml94
-rw-r--r--config/locales/hy.yml27
-rw-r--r--config/locales/id.yml16
-rw-r--r--config/locales/ig.yml2
-rw-r--r--config/locales/io.yml14
-rw-r--r--config/locales/is.yml66
-rw-r--r--config/locales/it.yml72
-rw-r--r--config/locales/ja.yml81
-rw-r--r--config/locales/ka.yml6
-rw-r--r--config/locales/kab.yml8
-rw-r--r--config/locales/kk.yml11
-rw-r--r--config/locales/kn.yml2
-rw-r--r--config/locales/ko.yml173
-rw-r--r--config/locales/ku.yml14
-rw-r--r--config/locales/kw.yml2
-rw-r--r--config/locales/la.yml2
-rw-r--r--config/locales/lt.yml10
-rw-r--r--config/locales/lv.yml84
-rw-r--r--config/locales/mk.yml2
-rw-r--r--config/locales/ml.yml2
-rw-r--r--config/locales/mr.yml2
-rw-r--r--config/locales/ms.yml3
-rw-r--r--config/locales/my.yml1660
-rw-r--r--config/locales/nl.yml80
-rw-r--r--config/locales/nn.yml125
-rw-r--r--config/locales/no.yml31
-rw-r--r--config/locales/oc.yml20
-rw-r--r--config/locales/pa.yml2
-rw-r--r--config/locales/pl.yml86
-rw-r--r--config/locales/pt-BR.yml76
-rw-r--r--config/locales/pt-PT.yml116
-rw-r--r--config/locales/ro.yml4
-rw-r--r--config/locales/ru.yml62
-rw-r--r--config/locales/sa.yml2
-rw-r--r--config/locales/sc.yml11
-rw-r--r--config/locales/sco.yml14
-rw-r--r--config/locales/si.yml14
-rw-r--r--config/locales/simple_form.an.yml8
-rw-r--r--config/locales/simple_form.ar.yml15
-rw-r--r--config/locales/simple_form.ast.yml48
-rw-r--r--config/locales/simple_form.be.yml12
-rw-r--r--config/locales/simple_form.bg.yml100
-rw-r--r--config/locales/simple_form.ca.yml16
-rw-r--r--config/locales/simple_form.cs.yml12
-rw-r--r--config/locales/simple_form.csb.yml1
-rw-r--r--config/locales/simple_form.cy.yml16
-rw-r--r--config/locales/simple_form.da.yml14
-rw-r--r--config/locales/simple_form.de.yml104
-rw-r--r--config/locales/simple_form.el.yml87
-rw-r--r--config/locales/simple_form.en-GB.yml182
-rw-r--r--config/locales/simple_form.en.yml4
-rw-r--r--config/locales/simple_form.en_GB.yml131
-rw-r--r--config/locales/simple_form.eo.yml26
-rw-r--r--config/locales/simple_form.es-AR.yml12
-rw-r--r--config/locales/simple_form.es-MX.yml12
-rw-r--r--config/locales/simple_form.es.yml12
-rw-r--r--config/locales/simple_form.et.yml20
-rw-r--r--config/locales/simple_form.eu.yml12
-rw-r--r--config/locales/simple_form.fa.yml42
-rw-r--r--config/locales/simple_form.fi.yml24
-rw-r--r--config/locales/simple_form.fo.yml12
-rw-r--r--config/locales/simple_form.fr-QC.yml12
-rw-r--r--config/locales/simple_form.fr.yml64
-rw-r--r--config/locales/simple_form.fy.yml12
-rw-r--r--config/locales/simple_form.ga.yml16
-rw-r--r--config/locales/simple_form.gd.yml14
-rw-r--r--config/locales/simple_form.gl.yml12
-rw-r--r--config/locales/simple_form.he.yml14
-rw-r--r--config/locales/simple_form.hu.yml12
-rw-r--r--config/locales/simple_form.id.yml2
-rw-r--r--config/locales/simple_form.io.yml2
-rw-r--r--config/locales/simple_form.is.yml12
-rw-r--r--config/locales/simple_form.it.yml12
-rw-r--r--config/locales/simple_form.ja.yml46
-rw-r--r--config/locales/simple_form.ko.yml52
-rw-r--r--config/locales/simple_form.ku.yml2
-rw-r--r--config/locales/simple_form.lv.yml12
-rw-r--r--config/locales/simple_form.ms.yml2
-rw-r--r--config/locales/simple_form.my.yml308
-rw-r--r--config/locales/simple_form.nl.yml14
-rw-r--r--config/locales/simple_form.nn.yml2
-rw-r--r--config/locales/simple_form.no.yml2
-rw-r--r--config/locales/simple_form.oc.yml14
-rw-r--r--config/locales/simple_form.pl.yml24
-rw-r--r--config/locales/simple_form.pt-BR.yml13
-rw-r--r--config/locales/simple_form.pt-PT.yml176
-rw-r--r--config/locales/simple_form.ru.yml12
-rw-r--r--config/locales/simple_form.sc.yml2
-rw-r--r--config/locales/simple_form.sco.yml2
-rw-r--r--config/locales/simple_form.si.yml2
-rw-r--r--config/locales/simple_form.sk.yml7
-rw-r--r--config/locales/simple_form.sl.yml12
-rw-r--r--config/locales/simple_form.sq.yml18
-rw-r--r--config/locales/simple_form.sr-Latn.yml144
-rw-r--r--config/locales/simple_form.sr.yml142
-rw-r--r--config/locales/simple_form.sv.yml12
-rw-r--r--config/locales/simple_form.th.yml24
-rw-r--r--config/locales/simple_form.tr.yml16
-rw-r--r--config/locales/simple_form.tt.yml4
-rw-r--r--config/locales/simple_form.uk.yml12
-rw-r--r--config/locales/simple_form.uz.yml1
-rw-r--r--config/locales/simple_form.vi.yml10
-rw-r--r--config/locales/simple_form.zh-CN.yml16
-rw-r--r--config/locales/simple_form.zh-HK.yml12
-rw-r--r--config/locales/simple_form.zh-TW.yml40
-rw-r--r--config/locales/sk.yml169
-rw-r--r--config/locales/sl.yml66
-rw-r--r--config/locales/sq.yml91
-rw-r--r--config/locales/sr-Latn.yml1550
-rw-r--r--config/locales/sr.yml1152
-rw-r--r--config/locales/sv.yml68
-rw-r--r--config/locales/szl.yml2
-rw-r--r--config/locales/ta.yml2
-rw-r--r--config/locales/tai.yml2
-rw-r--r--config/locales/te.yml2
-rw-r--r--config/locales/th.yml102
-rw-r--r--config/locales/tr.yml96
-rw-r--r--config/locales/tt.yml44
-rw-r--r--config/locales/ug.yml2
-rw-r--r--config/locales/uk.yml66
-rw-r--r--config/locales/ur.yml2
-rw-r--r--config/locales/uz.yml49
-rw-r--r--config/locales/vi.yml60
-rw-r--r--config/locales/zgh.yml2
-rw-r--r--config/locales/zh-CN.yml104
-rw-r--r--config/locales/zh-HK.yml70
-rw-r--r--config/locales/zh-TW.yml430
-rw-r--r--config/puma.rb2
-rw-r--r--config/routes.rb18
-rw-r--r--config/settings.yml41
-rw-r--r--config/sidekiq.yml98
-rw-r--r--config/webpack/generateLocalePacks.js8
-rw-r--r--config/webpack/rules/babel.js2
-rw-r--r--config/webpack/shared.js2
-rw-r--r--config/webpacker.yml5
-rw-r--r--db/migrate/20160305115639_add_devise_to_users.rb2
-rw-r--r--db/migrate/20161006213403_rails_settings_migration.rb10
-rw-r--r--db/migrate/20161122163057_remove_unneeded_indexes.rb6
-rw-r--r--db/migrate/20170125145934_add_spoiler_text_to_statuses.rb2
-rw-r--r--db/migrate/20170610000000_add_statuses_index_on_account_id_id.rb2
-rw-r--r--db/migrate/20170716191202_add_hide_notifications_to_mute.rb14
-rw-r--r--db/migrate/20170914032032_default_existing_mutes_to_hiding_notifications.rb11
-rw-r--r--db/migrate/20170918125918_ids_to_bigints.rb26
-rw-r--r--db/migrate/20170920032311_fix_reblogs_in_feeds.rb2
-rw-r--r--db/migrate/20170927215609_add_description_to_media_attachments.rb2
-rw-r--r--db/migrate/20170928082043_create_email_domain_blocks.rb2
-rw-r--r--db/migrate/20171005102658_create_account_moderation_notes.rb2
-rw-r--r--db/migrate/20171005171936_add_disabled_to_custom_emojis.rb2
-rw-r--r--db/migrate/20171006142024_add_uri_to_custom_emojis.rb2
-rw-r--r--db/migrate/20171009222537_create_keyword_mutes.rb2
-rw-r--r--db/migrate/20171010023049_add_foreign_key_to_account_moderation_notes.rb2
-rw-r--r--db/migrate/20171010025614_change_accounts_nonnullable_in_account_moderation_notes.rb2
-rw-r--r--db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb6
-rw-r--r--db/migrate/20171021191900_move_keyword_mutes_into_glitch_namespace.rb2
-rw-r--r--db/migrate/20171028221157_add_reblogs_to_follows.rb2
-rw-r--r--db/migrate/20171107143332_add_memorial_to_accounts.rb2
-rw-r--r--db/migrate/20171107143624_add_disabled_to_users.rb2
-rw-r--r--db/migrate/20171109012327_add_moderator_to_accounts.rb2
-rw-r--r--db/migrate/20171114080328_add_index_domain_to_email_domain_blocks.rb2
-rw-r--r--db/migrate/20171114231651_create_lists.rb2
-rw-r--r--db/migrate/20171116161857_create_list_accounts.rb2
-rw-r--r--db/migrate/20171118012443_add_moved_to_account_id_to_accounts.rb2
-rw-r--r--db/migrate/20171119172437_create_admin_action_logs.rb2
-rw-r--r--db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb2
-rw-r--r--db/migrate/20171125024930_create_invites.rb2
-rw-r--r--db/migrate/20171125031751_add_invite_id_to_users.rb2
-rw-r--r--db/migrate/20171125185353_add_index_reblog_of_id_and_account_to_statuses.rb2
-rw-r--r--db/migrate/20171125190735_remove_old_reblog_index_on_statuses.rb2
-rw-r--r--db/migrate/20171129172043_add_index_on_stream_entries.rb2
-rw-r--r--db/migrate/20171130000000_add_embed_url_to_preview_cards.rb2
-rw-r--r--db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb2
-rw-r--r--db/migrate/20171210213213_add_local_only_flag_to_statuses.rb2
-rw-r--r--db/migrate/20171212195226_remove_duplicate_indexes_in_lists.rb6
-rw-r--r--db/migrate/20171226094803_more_faster_index_on_notifications.rb2
-rw-r--r--db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb2
-rw-r--r--db/migrate/20180109143959_add_remember_token_to_users.rb2
-rw-r--r--db/migrate/20180204034416_create_identities.rb6
-rw-r--r--db/migrate/20180206000000_change_user_id_nonnullable.rb2
-rw-r--r--db/migrate/20180211015820_create_backups.rb2
-rw-r--r--db/migrate/20180304013859_add_featured_collection_url_to_accounts.rb2
-rw-r--r--db/migrate/20180310000000_change_columns_in_notifications_nonnullable.rb2
-rw-r--r--db/migrate/20180402031200_add_assigned_account_id_to_reports.rb2
-rw-r--r--db/migrate/20180402040909_create_report_notes.rb2
-rw-r--r--db/migrate/20180410204633_add_fields_to_accounts.rb2
-rw-r--r--db/migrate/20180410220657_create_bookmarks.rb2
-rw-r--r--db/migrate/20180514130000_improve_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb10
-rw-r--r--db/migrate/20180514140000_revert_index_change_on_statuses_for_api_v1_accounts_account_id_statuses.rb6
-rw-r--r--db/migrate/20180528141303_fix_accounts_unique_index.rb41
-rw-r--r--db/migrate/20180604000556_add_apply_to_mentions_flag_to_keyword_mutes.rb2
-rw-r--r--db/migrate/20180707193142_migrate_filters.rb12
-rw-r--r--db/migrate/20180812173710_copy_status_stats.rb12
-rw-r--r--db/migrate/20180831171112_create_bookmarks.rb2
-rw-r--r--db/migrate/20181024224956_migrate_account_conversations.rb21
-rw-r--r--db/migrate/20181116173541_copy_account_stats.rb12
-rw-r--r--db/migrate/20190306145741_add_lock_version_to_polls.rb1
-rw-r--r--db/migrate/20190314181829_migrate_open_registrations_setting.rb2
-rw-r--r--db/migrate/20190512200918_add_content_type_to_statuses.rb2
-rw-r--r--db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb4
-rw-r--r--db/migrate/20190807135426_add_comments_to_domain_blocks.rb1
-rw-r--r--db/migrate/20191031163205_change_list_account_follow_nullable.rb2
-rw-r--r--db/migrate/20200312162302_add_status_ids_to_announcements.rb1
-rw-r--r--db/migrate/20200407202420_migrate_unavailable_inboxes.rb5
-rw-r--r--db/migrate/20200510110808_reset_web_app_secret.rb3
-rw-r--r--db/migrate/20200510181721_remove_duplicated_indexes_pghero.rb1
-rw-r--r--db/migrate/20200521180606_encrypted_message_ids_to_timestamp_ids.rb2
-rw-r--r--db/migrate/20200620164023_add_fixed_lowercase_index_to_accounts.rb2
-rw-r--r--db/migrate/20200622213645_media_attachment_ids_to_timestamp_ids.rb4
-rw-r--r--db/migrate/20200628133322_create_account_notes.rb1
-rw-r--r--db/migrate/20200917192924_add_notify_to_follows.rb2
-rw-r--r--db/migrate/20210306164523_account_ids_to_timestamp_ids.rb4
-rw-r--r--db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb3
-rw-r--r--db/migrate/20210722120340_create_account_statuses_cleanup_policies.rb1
-rw-r--r--db/migrate/20220209175231_add_content_type_to_status_edits.rb2
-rw-r--r--db/migrate/20220613110834_add_action_to_custom_filters.rb1
-rw-r--r--db/migrate/20230215074327_add_settings_to_users.rb7
-rw-r--r--db/migrate/20230215074423_move_user_settings.rb84
-rw-r--r--db/migrate/20230215074424_move_glitch_user_settings.rb57
-rw-r--r--db/post_migrate/20180813160548_post_migrate_filters.rb6
-rw-r--r--db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb1
-rw-r--r--db/post_migrate/20200917193528_migrate_notifications_type.rb10
-rw-r--r--db/post_migrate/20210308133107_remove_subscription_expires_at_from_accounts.rb4
-rw-r--r--db/post_migrate/20220613110802_remove_whole_word_from_custom_filters.rb1
-rw-r--r--db/post_migrate/20220613110903_remove_irreversible_from_custom_filters.rb1
-rw-r--r--db/post_migrate/20220729171123_fix_custom_filter_keywords_id_seq.rb2
-rw-r--r--db/post_migrate/20221101190723_backfill_admin_action_logs.rb15
-rw-r--r--db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb15
-rw-r--r--db/schema.rb3
-rw-r--r--db/seeds.rb6
-rw-r--r--db/seeds/02_instance_actor.rb2
-rw-r--r--dist/nginx.conf2
-rw-r--r--docker-compose.yml6
-rw-r--r--jest.config.js29
-rw-r--r--lib/active_record/database_tasks_extensions.rb2
-rw-r--r--lib/assets/wordmark.light.css4
-rw-r--r--lib/chewy/strategy/bypass_with_warning.rb12
-rw-r--r--lib/cli.rb4
-rw-r--r--lib/mastodon/accounts_cli.rb66
-rw-r--r--lib/mastodon/cli_helper.rb24
-rw-r--r--lib/mastodon/domains_cli.rb83
-rw-r--r--lib/mastodon/emoji_cli.rb4
-rw-r--r--lib/mastodon/feeds_cli.rb6
-rw-r--r--lib/mastodon/ip_blocks_cli.rb12
-rw-r--r--lib/mastodon/maintenance_cli.rb94
-rw-r--r--lib/mastodon/media_cli.rb18
-rw-r--r--lib/mastodon/migration_helpers.rb4
-rw-r--r--lib/mastodon/migration_warning.rb55
-rw-r--r--lib/mastodon/premailer_webpack_strategy.rb2
-rw-r--r--lib/mastodon/redis_config.rb22
-rw-r--r--lib/mastodon/search_cli.rb12
-rw-r--r--lib/mastodon/sidekiq_middleware.rb4
-rw-r--r--lib/mastodon/snowflake.rb2
-rw-r--r--lib/mastodon/statuses_cli.rb4
-rw-r--r--lib/mastodon/upgrade_cli.rb18
-rw-r--r--lib/mastodon/version.rb2
-rw-r--r--lib/paperclip/attachment_extensions.rb10
-rw-r--r--lib/paperclip/color_extractor.rb36
-rw-r--r--lib/paperclip/gif_transcoder.rb6
-rw-r--r--lib/paperclip/type_corrector.rb2
-rw-r--r--lib/public_file_server_middleware.rb43
-rw-r--r--lib/rails/engine_extensions.rb4
-rw-r--r--lib/sanitize_ext/sanitize_config.rb39
-rw-r--r--lib/tasks/assets.rake14
-rw-r--r--lib/tasks/auto_annotate_models.rake70
-rw-r--r--lib/tasks/branding.rake4
-rw-r--r--lib/tasks/db.rake14
-rw-r--r--lib/tasks/emojis.rake10
-rw-r--r--lib/tasks/glitchsoc.rake8
-rw-r--r--lib/tasks/mastodon.rake74
-rw-r--r--lib/tasks/repo.rake10
-rw-r--r--lib/tasks/statistics.rake4
-rw-r--r--lib/tasks/tests.rake10
-rw-r--r--package.json146
-rw-r--r--postcss.config.js8
-rw-r--r--spec/chewy/accounts_index_spec.rb31
-rw-r--r--spec/chewy/statuses_index_spec.rb31
-rw-r--r--spec/chewy/tags_index_spec.rb31
-rw-r--r--spec/config/initializers/rack_attack_spec.rb50
-rw-r--r--spec/controllers/about_controller_spec.rb2
-rw-r--r--spec/controllers/accounts_controller_spec.rb4
-rw-r--r--spec/controllers/activitypub/claims_controller_spec.rb19
-rw-r--r--spec/controllers/activitypub/collections_controller_spec.rb11
-rw-r--r--spec/controllers/activitypub/followers_synchronizations_controller_spec.rb7
-rw-r--r--spec/controllers/activitypub/inboxes_controller_spec.rb10
-rw-r--r--spec/controllers/activitypub/outboxes_controller_spec.rb9
-rw-r--r--spec/controllers/activitypub/replies_controller_spec.rb3
-rw-r--r--spec/controllers/admin/account_actions_controller_spec.rb23
-rw-r--r--spec/controllers/admin/account_moderation_notes_controller_spec.rb4
-rw-r--r--spec/controllers/admin/accounts_controller_spec.rb50
-rw-r--r--spec/controllers/admin/announcements_controller_spec.rb21
-rw-r--r--spec/controllers/admin/base_controller_spec.rb2
-rw-r--r--spec/controllers/admin/change_emails_controller_spec.rb (renamed from spec/controllers/admin/change_email_controller_spec.rb)12
-rw-r--r--spec/controllers/admin/confirmations_controller_spec.rb4
-rw-r--r--spec/controllers/admin/custom_emojis_controller_spec.rb2
-rw-r--r--spec/controllers/admin/dashboard_controller_spec.rb8
-rw-r--r--spec/controllers/admin/disputes/appeals_controller_spec.rb2
-rw-r--r--spec/controllers/admin/domain_allows_controller_spec.rb2
-rw-r--r--spec/controllers/admin/domain_blocks_controller_spec.rb19
-rw-r--r--spec/controllers/admin/export_domain_allows_controller_spec.rb8
-rw-r--r--spec/controllers/admin/export_domain_blocks_controller_spec.rb38
-rw-r--r--spec/controllers/admin/follow_recommendations_controller_spec.rb21
-rw-r--r--spec/controllers/admin/instances_controller_spec.rb6
-rw-r--r--spec/controllers/admin/invites_controller_spec.rb4
-rw-r--r--spec/controllers/admin/ip_blocks_controller_spec.rb21
-rw-r--r--spec/controllers/admin/relationships_controller_spec.rb23
-rw-r--r--spec/controllers/admin/relays_controller_spec.rb21
-rw-r--r--spec/controllers/admin/report_notes_controller_spec.rb6
-rw-r--r--spec/controllers/admin/reports/actions_controller_spec.rb155
-rw-r--r--spec/controllers/admin/reports_controller_spec.rb11
-rw-r--r--spec/controllers/admin/resets_controller_spec.rb3
-rw-r--r--spec/controllers/admin/roles_controller_spec.rb18
-rw-r--r--spec/controllers/admin/rules_controller_spec.rb21
-rw-r--r--spec/controllers/admin/settings/about_controller_spec.rb21
-rw-r--r--spec/controllers/admin/settings/appearance_controller_spec.rb21
-rw-r--r--spec/controllers/admin/settings/content_retention_controller_spec.rb21
-rw-r--r--spec/controllers/admin/settings/discovery_controller_spec.rb21
-rw-r--r--spec/controllers/admin/settings/registrations_controller_spec.rb21
-rw-r--r--spec/controllers/admin/site_uploads_controller_spec.rb23
-rw-r--r--spec/controllers/admin/statuses_controller_spec.rb2
-rw-r--r--spec/controllers/admin/trends/links/preview_card_providers_controller_spec.rb21
-rw-r--r--spec/controllers/admin/trends/links_controller_spec.rb21
-rw-r--r--spec/controllers/admin/trends/statuses_controller_spec.rb21
-rw-r--r--spec/controllers/admin/trends/tags_controller_spec.rb21
-rw-r--r--spec/controllers/admin/users/roles_controller_spec.rb (renamed from spec/controllers/admin/users/roles_controller.rb)6
-rw-r--r--spec/controllers/admin/users/two_factor_authentications_controller_spec.rb8
-rw-r--r--spec/controllers/admin/warning_presets_controller_spec.rb21
-rw-r--r--spec/controllers/admin/webhooks/secrets_controller_spec.rb23
-rw-r--r--spec/controllers/admin/webhooks_controller_spec.rb21
-rw-r--r--spec/controllers/api/oembed_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/accounts/credentials_controller_spec.rb21
-rw-r--r--spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/accounts/lists_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/accounts/lookup_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/accounts/notes_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/accounts/relationships_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/accounts/search_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/accounts/statuses_controller_spec.rb30
-rw-r--r--spec/controllers/api/v1/accounts_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/admin/account_actions_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/admin/accounts_controller_spec.rb4
-rw-r--r--spec/controllers/api/v1/admin/canonical_email_blocks_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/dimensions_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/domain_allows_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb11
-rw-r--r--spec/controllers/api/v1/admin/email_domain_blocks_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/ip_blocks_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/measures_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/reports_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/admin/retention_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/trends/links_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/admin/trends/tags_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/announcements/reactions_controller_spec.rb4
-rw-r--r--spec/controllers/api/v1/announcements_controller_spec.rb4
-rw-r--r--spec/controllers/api/v1/apps/credentials_controller_spec.rb6
-rw-r--r--spec/controllers/api/v1/apps_controller_spec.rb8
-rw-r--r--spec/controllers/api/v1/blocks_controller_spec.rb6
-rw-r--r--spec/controllers/api/v1/bookmarks_controller_spec.rb14
-rw-r--r--spec/controllers/api/v1/conversations_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/directories_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/domain_blocks_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/emails/confirmations_controller_spec.rb8
-rw-r--r--spec/controllers/api/v1/favourites_controller_spec.rb14
-rw-r--r--spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/featured_tags_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/filters_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/follow_requests_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/followed_tags_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/instances/domain_blocks_controller_spec.rb16
-rw-r--r--spec/controllers/api/v1/instances/extended_descriptions_controller_spec.rb15
-rw-r--r--spec/controllers/api/v1/instances/privacy_policies_controller_spec.rb15
-rw-r--r--spec/controllers/api/v1/instances/rules_controller_spec.rb15
-rw-r--r--spec/controllers/api/v1/instances/translation_languages_controller_spec.rb31
-rw-r--r--spec/controllers/api/v1/lists/accounts_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/lists_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/markers_controller_spec.rb6
-rw-r--r--spec/controllers/api/v1/media_controller_spec.rb8
-rw-r--r--spec/controllers/api/v1/mutes_controller_spec.rb6
-rw-r--r--spec/controllers/api/v1/notifications_controller_spec.rb14
-rw-r--r--spec/controllers/api/v1/polls/votes_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/polls_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/preferences_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/push/subscriptions_controller_spec.rb28
-rw-r--r--spec/controllers/api/v1/reports_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/scheduled_statuses_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb6
-rw-r--r--spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb6
-rw-r--r--spec/controllers/api/v1/statuses/translations_controller_spec.rb34
-rw-r--r--spec/controllers/api/v1/statuses_controller_spec.rb23
-rw-r--r--spec/controllers/api/v1/streaming_controller_spec.rb4
-rw-r--r--spec/controllers/api/v1/suggestions_controller_spec.rb4
-rw-r--r--spec/controllers/api/v1/tags_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/timelines/direct_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/timelines/home_controller_spec.rb2
-rw-r--r--spec/controllers/api/v1/timelines/list_controller_spec.rb4
-rw-r--r--spec/controllers/api/v1/trends/links_controller_spec.rb15
-rw-r--r--spec/controllers/api/v1/trends/statuses_controller_spec.rb15
-rw-r--r--spec/controllers/api/v2/admin/accounts_controller_spec.rb4
-rw-r--r--spec/controllers/api/v2/filters/keywords_controller_spec.rb6
-rw-r--r--spec/controllers/api/v2/filters/statuses_controller_spec.rb6
-rw-r--r--spec/controllers/api/v2/filters_controller_spec.rb2
-rw-r--r--spec/controllers/api/v2/instances_controller_spec.rb22
-rw-r--r--spec/controllers/api/v2/suggestions_controller_spec.rb22
-rw-r--r--spec/controllers/api/web/embeds_controller_spec.rb8
-rw-r--r--spec/controllers/api/web/push_subscriptions_controller_spec.rb6
-rw-r--r--spec/controllers/application_controller_spec.rb19
-rw-r--r--spec/controllers/auth/registrations_controller_spec.rb88
-rw-r--r--spec/controllers/auth/sessions_controller_spec.rb38
-rw-r--r--spec/controllers/auth/setup_controller_spec.rb25
-rw-r--r--spec/controllers/authorize_interactions_controller_spec.rb1
-rw-r--r--spec/controllers/concerns/export_controller_concern_spec.rb2
-rw-r--r--spec/controllers/concerns/signature_verification_spec.rb107
-rw-r--r--spec/controllers/custom_css_controller_spec.rb14
-rw-r--r--spec/controllers/disputes/appeals_controller_spec.rb2
-rw-r--r--spec/controllers/disputes/strikes_controller_spec.rb4
-rw-r--r--spec/controllers/emojis_controller_spec.rb3
-rw-r--r--spec/controllers/filters/statuses_controller_spec.rb41
-rw-r--r--spec/controllers/filters_controller_spec.rb27
-rw-r--r--spec/controllers/follower_accounts_controller_spec.rb3
-rw-r--r--spec/controllers/following_accounts_controller_spec.rb3
-rw-r--r--spec/controllers/health_controller_spec.rb9
-rw-r--r--spec/controllers/home_controller_spec.rb6
-rw-r--r--spec/controllers/instance_actors_controller_spec.rb6
-rw-r--r--spec/controllers/intents_controller_spec.rb3
-rw-r--r--spec/controllers/invites_controller_spec.rb2
-rw-r--r--spec/controllers/manifests_controller_spec.rb2
-rw-r--r--spec/controllers/oauth/authorized_applications_controller_spec.rb2
-rw-r--r--spec/controllers/privacy_controller_spec.rb14
-rw-r--r--spec/controllers/relationships_controller_spec.rb16
-rw-r--r--spec/controllers/settings/aliases_controller_spec.rb21
-rw-r--r--spec/controllers/settings/applications_controller_spec.rb35
-rw-r--r--spec/controllers/settings/deletes_controller_spec.rb2
-rw-r--r--spec/controllers/settings/exports/blocked_accounts_controller_spec.rb2
-rw-r--r--spec/controllers/settings/exports/blocked_domains_controller_spec.rb20
-rw-r--r--spec/controllers/settings/exports/bookmarks_controller_spec.rb2
-rw-r--r--spec/controllers/settings/exports/following_accounts_controller_spec.rb2
-rw-r--r--spec/controllers/settings/exports/lists_controller_spec.rb21
-rw-r--r--spec/controllers/settings/exports/muted_accounts_controller_spec.rb2
-rw-r--r--spec/controllers/settings/featured_tags_controller_spec.rb4
-rw-r--r--spec/controllers/settings/flavours_controller_spec.rb3
-rw-r--r--spec/controllers/settings/imports_controller_spec.rb14
-rw-r--r--spec/controllers/settings/login_activities_controller_spec.rb20
-rw-r--r--spec/controllers/settings/migration/redirects_controller_spec.rb20
-rw-r--r--spec/controllers/settings/migrations_controller_spec.rb22
-rw-r--r--spec/controllers/settings/pictures_controller_spec.rb22
-rw-r--r--spec/controllers/settings/preferences/appearance_controller_spec.rb20
-rw-r--r--spec/controllers/settings/preferences/notifications_controller_spec.rb18
-rw-r--r--spec/controllers/settings/preferences/other_controller_spec.rb20
-rw-r--r--spec/controllers/settings/profiles_controller_spec.rb16
-rw-r--r--spec/controllers/settings/sessions_controller_spec.rb7
-rw-r--r--spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb1
-rw-r--r--spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb10
-rw-r--r--spec/controllers/shares_controller_spec.rb5
-rw-r--r--spec/controllers/statuses_cleanup_controller_spec.rb12
-rw-r--r--spec/controllers/statuses_controller_spec.rb2
-rw-r--r--spec/controllers/tags_controller_spec.rb2
-rw-r--r--spec/controllers/well_known/host_meta_controller_spec.rb14
-rw-r--r--spec/controllers/well_known/nodeinfo_controller_spec.rb7
-rw-r--r--spec/controllers/well_known/webfinger_controller_spec.rb10
-rw-r--r--spec/fabricators/access_grant_fabricator.rb2
-rw-r--r--spec/fabricators/access_token_fabricator.rb2
-rw-r--r--spec/fabricators/accessible_access_token_fabricator.rb2
-rw-r--r--spec/fabricators/account_alias_fabricator.rb5
-rw-r--r--spec/fabricators/account_deletion_request_fabricator.rb3
-rw-r--r--spec/fabricators/account_domain_block_fabricator.rb2
-rw-r--r--spec/fabricators/account_fabricator.rb2
-rw-r--r--spec/fabricators/account_migration_fabricator.rb3
-rw-r--r--spec/fabricators/account_moderation_note_fabricator.rb7
-rw-r--r--spec/fabricators/account_note_fabricator.rb4
-rw-r--r--spec/fabricators/account_pin_fabricator.rb7
-rw-r--r--spec/fabricators/account_stat_fabricator.rb10
-rw-r--r--spec/fabricators/account_statuses_cleanup_policy_fabricator.rb2
-rw-r--r--spec/fabricators/account_tag_stat_fabricator.rb3
-rw-r--r--spec/fabricators/account_warning_fabricator.rb2
-rw-r--r--spec/fabricators/account_warning_preset_fabricator.rb4
-rw-r--r--spec/fabricators/admin_action_log_fabricator.rb6
-rw-r--r--spec/fabricators/announcement_fabricator.rb2
-rw-r--r--spec/fabricators/announcement_mute_fabricator.rb4
-rw-r--r--spec/fabricators/announcement_reaction_fabricator.rb5
-rw-r--r--spec/fabricators/appeal_fabricator.rb2
-rw-r--r--spec/fabricators/application_fabricator.rb2
-rw-r--r--spec/fabricators/backup_fabricator.rb2
-rw-r--r--spec/fabricators/block_fabricator.rb2
-rw-r--r--spec/fabricators/bookmark_fabricator.rb2
-rw-r--r--spec/fabricators/canonical_email_block_fabricator.rb4
-rw-r--r--spec/fabricators/conversation_account_fabricator.rb6
-rw-r--r--spec/fabricators/conversation_fabricator.rb2
-rw-r--r--spec/fabricators/conversation_mute_fabricator.rb2
-rw-r--r--spec/fabricators/custom_emoji_category_fabricator.rb3
-rw-r--r--spec/fabricators/custom_emoji_fabricator.rb4
-rw-r--r--spec/fabricators/custom_filter_fabricator.rb2
-rw-r--r--spec/fabricators/custom_filter_keyword_fabricator.rb4
-rw-r--r--spec/fabricators/custom_filter_status_fabricator.rb2
-rw-r--r--spec/fabricators/device_fabricator.rb2
-rw-r--r--spec/fabricators/domain_allow_fabricator.rb4
-rw-r--r--spec/fabricators/domain_block_fabricator.rb2
-rw-r--r--spec/fabricators/email_domain_block_fabricator.rb2
-rw-r--r--spec/fabricators/encrypted_message_fabricator.rb9
-rw-r--r--spec/fabricators/favourite_fabricator.rb2
-rw-r--r--spec/fabricators/featured_tag_fabricator.rb6
-rw-r--r--spec/fabricators/follow_fabricator.rb2
-rw-r--r--spec/fabricators/follow_recommendation_suppression_fabricator.rb3
-rw-r--r--spec/fabricators/follow_request_fabricator.rb2
-rw-r--r--spec/fabricators/identity_fabricator.rb8
-rw-r--r--spec/fabricators/import_fabricator.rb2
-rw-r--r--spec/fabricators/invite_fabricator.rb2
-rw-r--r--spec/fabricators/ip_block_fabricator.rb6
-rw-r--r--spec/fabricators/list_account_fabricator.rb8
-rw-r--r--spec/fabricators/list_fabricator.rb4
-rw-r--r--spec/fabricators/login_activity_fabricator.rb2
-rw-r--r--spec/fabricators/marker_fabricator.rb2
-rw-r--r--spec/fabricators/media_attachment_fabricator.rb2
-rw-r--r--spec/fabricators/mention_fabricator.rb2
-rw-r--r--spec/fabricators/mute_fabricator.rb2
-rw-r--r--spec/fabricators/notification_fabricator.rb2
-rw-r--r--spec/fabricators/one_time_key_fabricator.rb4
-rw-r--r--spec/fabricators/poll_fabricator.rb2
-rw-r--r--spec/fabricators/poll_vote_fabricator.rb4
-rw-r--r--spec/fabricators/preview_card_fabricator.rb2
-rw-r--r--spec/fabricators/preview_card_provider_fabricator.rb5
-rw-r--r--spec/fabricators/relay_fabricator.rb4
-rw-r--r--spec/fabricators/report_fabricator.rb4
-rw-r--r--spec/fabricators/report_note_fabricator.rb4
-rw-r--r--spec/fabricators/rule_fabricator.rb2
-rw-r--r--spec/fabricators/scheduled_status_fabricator.rb2
-rw-r--r--spec/fabricators/session_activation_fabricator.rb4
-rw-r--r--spec/fabricators/setting_fabricator.rb1
-rw-r--r--spec/fabricators/site_upload_fabricator.rb5
-rw-r--r--spec/fabricators/status_edit_fabricator.rb7
-rw-r--r--spec/fabricators/status_fabricator.rb4
-rw-r--r--spec/fabricators/status_pin_fabricator.rb2
-rw-r--r--spec/fabricators/status_stat_fabricator.rb6
-rw-r--r--spec/fabricators/system_key_fabricator.rb3
-rw-r--r--spec/fabricators/tag_fabricator.rb2
-rw-r--r--spec/fabricators/tag_follow_fabricator.rb2
-rw-r--r--spec/fabricators/unavailable_domain_fabricator.rb4
-rw-r--r--spec/fabricators/user_fabricator.rb7
-rw-r--r--spec/fabricators/user_invite_request_fabricator.rb4
-rw-r--r--spec/fabricators/user_role_fabricator.rb10
-rw-r--r--spec/fabricators/web_push_subscription_fabricator.rb2
-rw-r--r--spec/fabricators/web_setting_fabricator.rb2
-rw-r--r--spec/fabricators/webauthn_credential_fabricator.rb2
-rw-r--r--spec/fabricators/webhook_fabricator.rb2
-rw-r--r--spec/fabricators_spec.rb12
-rw-r--r--spec/features/log_in_spec.rb28
-rw-r--r--spec/features/profile_spec.rb18
-rw-r--r--spec/fixtures/files/domain_blocks.csv6
-rw-r--r--spec/fixtures/files/domain_blocks_list.txt3
-rw-r--r--spec/helpers/accounts_helper_spec.rb10
-rw-r--r--spec/helpers/admin/account_moderation_notes_helper_spec.rb10
-rw-r--r--spec/helpers/admin/action_logs_helper_spec.rb (renamed from spec/helpers/admin/action_log_helper_spec.rb)0
-rw-r--r--spec/helpers/admin/dashboard_helper_spec.rb69
-rw-r--r--spec/helpers/admin/filter_helper_spec.rb2
-rw-r--r--spec/helpers/admin/trends/statuses_helper_spec.rb54
-rw-r--r--spec/helpers/application_helper_spec.rb23
-rw-r--r--spec/helpers/home_helper_spec.rb114
-rw-r--r--spec/helpers/jsonld_helper_spec.rb14
-rw-r--r--spec/helpers/languages_helper_spec.rb48
-rw-r--r--spec/helpers/settings_helper_spec.rb37
-rw-r--r--spec/helpers/statuses_helper_spec.rb92
-rw-r--r--spec/lib/activitypub/activity/accept_spec.rb6
-rw-r--r--spec/lib/activitypub/activity/add_spec.rb14
-rw-r--r--spec/lib/activitypub/activity/announce_spec.rb20
-rw-r--r--spec/lib/activitypub/activity/block_spec.rb2
-rw-r--r--spec/lib/activitypub/activity/create_spec.rb59
-rw-r--r--spec/lib/activitypub/activity/delete_spec.rb4
-rw-r--r--spec/lib/activitypub/activity/flag_spec.rb5
-rw-r--r--spec/lib/activitypub/activity/follow_spec.rb2
-rw-r--r--spec/lib/activitypub/activity/like_spec.rb2
-rw-r--r--spec/lib/activitypub/activity/move_spec.rb2
-rw-r--r--spec/lib/activitypub/activity/reject_spec.rb6
-rw-r--r--spec/lib/activitypub/activity/remove_spec.rb2
-rw-r--r--spec/lib/activitypub/activity/undo_spec.rb6
-rw-r--r--spec/lib/activitypub/activity/update_spec.rb6
-rw-r--r--spec/lib/activitypub/adapter_spec.rb6
-rw-r--r--spec/lib/activitypub/dereferencer_spec.rb6
-rw-r--r--spec/lib/activitypub/linked_data_signature_spec.rb6
-rw-r--r--spec/lib/activitypub/tag_manager_spec.rb2
-rw-r--r--spec/lib/admin/system_check/base_check_spec.rb27
-rw-r--r--spec/lib/admin/system_check/database_schema_check_spec.rb45
-rw-r--r--spec/lib/admin/system_check/elasticsearch_check_spec.rb100
-rw-r--r--spec/lib/admin/system_check/media_privacy_check_spec.rb33
-rw-r--r--spec/lib/admin/system_check/message_spec.rb14
-rw-r--r--spec/lib/admin/system_check/rules_check_spec.rb53
-rw-r--r--spec/lib/admin/system_check/sidekiq_process_check_spec.rb45
-rw-r--r--spec/lib/admin/system_check_spec.rb15
-rw-r--r--spec/lib/advanced_text_formatter_spec.rb82
-rw-r--r--spec/lib/emoji_formatter_spec.rb10
-rw-r--r--spec/lib/entity_cache_spec.rb4
-rw-r--r--spec/lib/extractor_spec.rb8
-rw-r--r--spec/lib/fast_ip_map_spec.rb2
-rw-r--r--spec/lib/feed_manager_spec.rb30
-rw-r--r--spec/lib/html_aware_formatter_spec.rb10
-rw-r--r--spec/lib/importer/accounts_index_importer_spec.rb16
-rw-r--r--spec/lib/importer/base_importer_spec.rb14
-rw-r--r--spec/lib/importer/statuses_index_importer_spec.rb16
-rw-r--r--spec/lib/importer/tags_index_importer_spec.rb16
-rw-r--r--spec/lib/link_details_extractor_spec.rb130
-rw-r--r--spec/lib/mastodon/cli_spec.rb14
-rw-r--r--spec/lib/ostatus/tag_manager_spec.rb12
-rw-r--r--spec/lib/plain_text_formatter_spec.rb65
-rw-r--r--spec/lib/request_pool_spec.rb2
-rw-r--r--spec/lib/request_spec.rb16
-rw-r--r--spec/lib/search_query_transformer_spec.rb18
-rw-r--r--spec/lib/settings/extend_spec.rb16
-rw-r--r--spec/lib/settings/scoped_settings_spec.rb35
-rw-r--r--spec/lib/status_filter_spec.rb7
-rw-r--r--spec/lib/status_reach_finder_spec.rb4
-rw-r--r--spec/lib/suspicious_sign_in_detector_spec.rb6
-rw-r--r--spec/lib/tag_manager_spec.rb22
-rw-r--r--spec/lib/text_formatter_spec.rb88
-rw-r--r--spec/lib/translation_service/deepl_spec.rb82
-rw-r--r--spec/lib/translation_service/libre_translate_spec.rb54
-rw-r--r--spec/lib/user_settings_decorator_spec.rb84
-rw-r--r--spec/lib/vacuum/access_tokens_vacuum_spec.rb2
-rw-r--r--spec/lib/vacuum/backups_vacuum_spec.rb6
-rw-r--r--spec/lib/vacuum/feeds_vacuum_spec.rb2
-rw-r--r--spec/lib/vacuum/media_attachments_vacuum_spec.rb5
-rw-r--r--spec/lib/vacuum/preview_cards_vacuum_spec.rb6
-rw-r--r--spec/lib/vacuum/statuses_vacuum_spec.rb6
-rw-r--r--spec/lib/vacuum/system_keys_vacuum_spec.rb2
-rw-r--r--spec/lib/webfinger_resource_spec.rb30
-rw-r--r--spec/mailers/admin_mailer_spec.rb62
-rw-r--r--spec/mailers/notification_mailer_spec.rb46
-rw-r--r--spec/mailers/previews/admin_mailer_preview.rb2
-rw-r--r--spec/mailers/previews/notification_mailer_preview.rb2
-rw-r--r--spec/mailers/previews/user_mailer_preview.rb2
-rw-r--r--spec/mailers/user_mailer_spec.rb50
-rw-r--r--spec/models/account/field_spec.rb18
-rw-r--r--spec/models/account_alias_spec.rb3
-rw-r--r--spec/models/account_conversation_spec.rb2
-rw-r--r--spec/models/account_deletion_request_spec.rb2
-rw-r--r--spec/models/account_domain_block_spec.rb6
-rw-r--r--spec/models/account_filter_spec.rb28
-rw-r--r--spec/models/account_migration_spec.rb2
-rw-r--r--spec/models/account_moderation_note_spec.rb2
-rw-r--r--spec/models/account_spec.rb113
-rw-r--r--spec/models/account_statuses_cleanup_policy_spec.rb83
-rw-r--r--spec/models/account_statuses_filter_spec.rb4
-rw-r--r--spec/models/account_warning_preset_spec.rb17
-rw-r--r--spec/models/admin/account_action_spec.rb7
-rw-r--r--spec/models/admin/appeal_filter_spec.rb16
-rw-r--r--spec/models/announcement_mute_spec.rb2
-rw-r--r--spec/models/announcement_reaction_spec.rb2
-rw-r--r--spec/models/announcement_spec.rb2
-rw-r--r--spec/models/appeal_spec.rb37
-rw-r--r--spec/models/backup_spec.rb2
-rw-r--r--spec/models/block_spec.rb15
-rw-r--r--spec/models/canonical_email_block_spec.rb2
-rw-r--r--spec/models/concerns/account_counters_spec.rb2
-rw-r--r--spec/models/concerns/account_interactions_spec.rb201
-rw-r--r--spec/models/concerns/remotable_spec.rb8
-rw-r--r--spec/models/conversation_mute_spec.rb2
-rw-r--r--spec/models/conversation_spec.rb2
-rw-r--r--spec/models/custom_emoji_category_spec.rb13
-rw-r--r--spec/models/custom_emoji_filter_spec.rb14
-rw-r--r--spec/models/custom_emoji_spec.rb28
-rw-r--r--spec/models/custom_filter_keyword_spec.rb2
-rw-r--r--spec/models/custom_filter_spec.rb2
-rw-r--r--spec/models/device_spec.rb3
-rw-r--r--spec/models/domain_allow_spec.rb17
-rw-r--r--spec/models/domain_block_spec.rb13
-rw-r--r--spec/models/email_domain_block_spec.rb9
-rw-r--r--spec/models/encrypted_message_spec.rb3
-rw-r--r--spec/models/export_spec.rb4
-rw-r--r--spec/models/extended_description_spec.rb29
-rw-r--r--spec/models/favourite_spec.rb4
-rw-r--r--spec/models/featured_tag_spec.rb2
-rw-r--r--spec/models/follow_recommendation_suppression_spec.rb2
-rw-r--r--spec/models/follow_request_spec.rb2
-rw-r--r--spec/models/follow_spec.rb7
-rw-r--r--spec/models/form/admin_settings_spec.rb36
-rw-r--r--spec/models/form/status_filter_batch_action_spec.rb13
-rw-r--r--spec/models/home_feed_spec.rb6
-rw-r--r--spec/models/identity_spec.rb2
-rw-r--r--spec/models/import_spec.rb13
-rw-r--r--spec/models/invite_spec.rb2
-rw-r--r--spec/models/ip_block_spec.rb14
-rw-r--r--spec/models/list_account_spec.rb2
-rw-r--r--spec/models/list_spec.rb2
-rw-r--r--spec/models/login_activity_spec.rb3
-rw-r--r--spec/models/marker_spec.rb15
-rw-r--r--spec/models/media_attachment_spec.rb44
-rw-r--r--spec/models/mention_spec.rb7
-rw-r--r--spec/models/mute_spec.rb2
-rw-r--r--spec/models/notification_spec.rb4
-rw-r--r--spec/models/one_time_key_spec.rb20
-rw-r--r--spec/models/poll_spec.rb31
-rw-r--r--spec/models/poll_vote_spec.rb49
-rw-r--r--spec/models/preview_card_provider_spec.rb42
-rw-r--r--spec/models/preview_card_spec.rb2
-rw-r--r--spec/models/preview_card_trend_spec.rb2
-rw-r--r--spec/models/privacy_policy_spec.rb28
-rw-r--r--spec/models/public_feed_spec.rb58
-rw-r--r--spec/models/relay_spec.rb2
-rw-r--r--spec/models/remote_follow_spec.rb14
-rw-r--r--spec/models/report_filter_spec.rb2
-rw-r--r--spec/models/report_spec.rb12
-rw-r--r--spec/models/rule_spec.rb18
-rw-r--r--spec/models/scheduled_status_spec.rb2
-rw-r--r--spec/models/session_activation_spec.rb16
-rw-r--r--spec/models/setting_spec.rb16
-rw-r--r--spec/models/status_edit_spec.rb12
-rw-r--r--spec/models/status_pin_spec.rb2
-rw-r--r--spec/models/status_spec.rb60
-rw-r--r--spec/models/status_stat_spec.rb2
-rw-r--r--spec/models/status_trend_spec.rb2
-rw-r--r--spec/models/system_key_spec.rb3
-rw-r--r--spec/models/tag_feed_spec.rb4
-rw-r--r--spec/models/tag_follow_spec.rb2
-rw-r--r--spec/models/tag_spec.rb7
-rw-r--r--spec/models/trends/statuses_spec.rb4
-rw-r--r--spec/models/trends/tags_spec.rb6
-rw-r--r--spec/models/unavailable_domain_spec.rb2
-rw-r--r--spec/models/user_invite_request_spec.rb2
-rw-r--r--spec/models/user_role_spec.rb12
-rw-r--r--spec/models/user_settings/namespace_spec.rb25
-rw-r--r--spec/models/user_settings/setting_spec.rb106
-rw-r--r--spec/models/user_settings_spec.rb110
-rw-r--r--spec/models/user_spec.rb176
-rw-r--r--spec/models/web/push_subscription_spec.rb20
-rw-r--r--spec/models/web/setting_spec.rb2
-rw-r--r--spec/models/webauthn_credentials_spec.rb6
-rw-r--r--spec/models/webhook_spec.rb2
-rw-r--r--spec/policies/account_policy_spec.rb40
-rw-r--r--spec/policies/account_warning_preset_policy_spec.rb24
-rw-r--r--spec/policies/admin/status_policy_spec.rb51
-rw-r--r--spec/policies/announcement_policy_spec.rb24
-rw-r--r--spec/policies/appeal_policy_spec.rb51
-rw-r--r--spec/policies/canonical_email_block_policy_spec.rb24
-rw-r--r--spec/policies/delivery_policy_spec.rb24
-rw-r--r--spec/policies/email_domain_block_policy_spec.rb2
-rw-r--r--spec/policies/follow_recommendation_policy_spec.rb24
-rw-r--r--spec/policies/ip_block_policy_spec.rb24
-rw-r--r--spec/policies/preview_card_policy_spec.rb24
-rw-r--r--spec/policies/preview_card_provider_policy_spec.rb24
-rw-r--r--spec/policies/rule_policy_spec.rb24
-rw-r--r--spec/policies/settings_policy_spec.rb2
-rw-r--r--spec/policies/status_policy_spec.rb12
-rw-r--r--spec/policies/tag_policy_spec.rb2
-rw-r--r--spec/policies/webhook_policy_spec.rb24
-rw-r--r--spec/presenters/familiar_followers_presenter_spec.rb4
-rw-r--r--spec/presenters/instance_presenter_spec.rb58
-rw-r--r--spec/rails_helper.rb14
-rw-r--r--spec/requests/catch_all_route_request_spec.rb26
-rw-r--r--spec/requests/host_meta_request_spec.rb12
-rw-r--r--spec/requests/localization_spec.rb6
-rw-r--r--spec/requests/webfinger_request_spec.rb2
-rw-r--r--spec/routing/accounts_routing_spec.rb2
-rw-r--r--spec/routing/api_routing_spec.rb72
-rw-r--r--spec/routing/well_known_routes_spec.rb22
-rw-r--r--spec/serializers/activitypub/device_serializer_spec.rb20
-rw-r--r--spec/serializers/activitypub/note_serializer_spec.rb (renamed from spec/serializers/activitypub/note_spec.rb)4
-rw-r--r--spec/serializers/activitypub/one_time_key_serializer_spec.rb20
-rw-r--r--spec/serializers/activitypub/undo_like_serializer_spec.rb20
-rw-r--r--spec/serializers/activitypub/update_poll_serializer_spec.rb (renamed from spec/serializers/activitypub/update_poll_spec.rb)4
-rw-r--r--spec/serializers/activitypub/vote_serializer_spec.rb20
-rw-r--r--spec/serializers/rest/account_serializer_spec.rb47
-rw-r--r--spec/serializers/rest/encrypted_message_serializer_spec.rb20
-rw-r--r--spec/serializers/rest/instance_serializer_spec.rb20
-rw-r--r--spec/serializers/rest/keys/claim_result_serializer_spec.rb20
-rw-r--r--spec/serializers/rest/keys/device_serializer_spec.rb20
-rw-r--r--spec/serializers/rest/keys/query_result_serializer_spec.rb20
-rw-r--r--spec/serializers/rest/suggestion_serializer_spec.rb26
-rw-r--r--spec/services/account_search_service_spec.rb6
-rw-r--r--spec/services/account_statuses_cleanup_service_spec.rb6
-rw-r--r--spec/services/activitypub/fetch_featured_collection_service_spec.rb8
-rw-r--r--spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb10
-rw-r--r--spec/services/activitypub/fetch_remote_account_service_spec.rb2
-rw-r--r--spec/services/activitypub/fetch_remote_actor_service_spec.rb2
-rw-r--r--spec/services/activitypub/fetch_remote_key_service_spec.rb2
-rw-r--r--spec/services/activitypub/fetch_remote_status_service_spec.rb138
-rw-r--r--spec/services/activitypub/fetch_replies_service_spec.rb8
-rw-r--r--spec/services/activitypub/process_account_service_spec.rb18
-rw-r--r--spec/services/activitypub/process_collection_service_spec.rb124
-rw-r--r--spec/services/activitypub/process_status_update_service_spec.rb84
-rw-r--r--spec/services/activitypub/synchronize_followers_service_spec.rb8
-rw-r--r--spec/services/after_block_domain_from_account_service_spec.rb6
-rw-r--r--spec/services/after_block_service_spec.rb2
-rw-r--r--spec/services/app_sign_up_service_spec.rb6
-rw-r--r--spec/services/authorize_follow_service_spec.rb6
-rw-r--r--spec/services/batched_remove_status_service_spec.rb2
-rw-r--r--spec/services/block_domain_service_spec.rb12
-rw-r--r--spec/services/block_service_spec.rb6
-rw-r--r--spec/services/bootstrap_timeline_service_spec.rb3
-rw-r--r--spec/services/clear_domain_media_service_spec.rb12
-rw-r--r--spec/services/delete_account_service_spec.rb24
-rw-r--r--spec/services/fan_out_on_write_service_spec.rb9
-rw-r--r--spec/services/favourite_service_spec.rb10
-rw-r--r--spec/services/fetch_link_card_service_spec.rb10
-rw-r--r--spec/services/fetch_oembed_service_spec.rb15
-rw-r--r--spec/services/fetch_remote_status_service_spec.rb7
-rw-r--r--spec/services/fetch_resource_service_spec.rb21
-rw-r--r--spec/services/follow_service_spec.rb8
-rw-r--r--spec/services/import_service_spec.rb29
-rw-r--r--spec/services/mute_service_spec.rb2
-rw-r--r--spec/services/notify_service_spec.rb11
-rw-r--r--spec/services/post_status_service_spec.rb67
-rw-r--r--spec/services/precompute_feed_service_spec.rb3
-rw-r--r--spec/services/process_mentions_service_spec.rb27
-rw-r--r--spec/services/purge_domain_service_spec.rb6
-rw-r--r--spec/services/reblog_service_spec.rb10
-rw-r--r--spec/services/reject_follow_service_spec.rb6
-rw-r--r--spec/services/remove_from_followers_service_spec.rb (renamed from spec/services/remove_from_follwers_service_spec.rb)8
-rw-r--r--spec/services/remove_status_service_spec.rb70
-rw-r--r--spec/services/report_service_spec.rb17
-rw-r--r--spec/services/resolve_account_service_spec.rb34
-rw-r--r--spec/services/resolve_url_service_spec.rb2
-rw-r--r--spec/services/search_service_spec.rb10
-rw-r--r--spec/services/suspend_account_service_spec.rb6
-rw-r--r--spec/services/unallow_domain_service_spec.rb6
-rw-r--r--spec/services/unblock_service_spec.rb6
-rw-r--r--spec/services/unfollow_service_spec.rb6
-rw-r--r--spec/services/unmute_service_spec.rb2
-rw-r--r--spec/services/unsuspend_account_service_spec.rb6
-rw-r--r--spec/services/update_account_service_spec.rb2
-rw-r--r--spec/services/update_status_service_spec.rb35
-rw-r--r--spec/services/verify_link_service_spec.rb24
-rw-r--r--spec/spec_helper.rb12
-rw-r--r--spec/support/examples/lib/admin/checks.rb21
-rw-r--r--spec/support/examples/lib/settings/scoped_settings.rb32
-rw-r--r--spec/support/matchers/json/match_json_schema.rb2
-rw-r--r--spec/support/matchers/model/model_have_error_on_field.rb8
-rw-r--r--spec/support/stories/profile_stories.rb4
-rw-r--r--spec/validators/blacklisted_email_validator_spec.rb8
-rw-r--r--spec/validators/disallowed_hashtags_validator_spec.rb6
-rw-r--r--spec/validators/email_mx_validator_spec.rb43
-rw-r--r--spec/validators/follow_limit_validator_spec.rb4
-rw-r--r--spec/validators/note_length_validator_spec.rb4
-rw-r--r--spec/validators/poll_validator_spec.rb5
-rw-r--r--spec/validators/status_length_validator_spec.rb6
-rw-r--r--spec/validators/unreserved_username_validator_spec.rb10
-rw-r--r--spec/validators/url_validator_spec.rb2
-rw-r--r--spec/workers/activitypub/distribute_poll_update_worker_spec.rb2
-rw-r--r--spec/workers/activitypub/distribution_worker_spec.rb4
-rw-r--r--spec/workers/activitypub/move_distribution_worker_spec.rb10
-rw-r--r--spec/workers/activitypub/processing_worker_spec.rb2
-rw-r--r--spec/workers/activitypub/status_update_distribution_worker_spec.rb2
-rw-r--r--spec/workers/activitypub/update_distribution_worker_spec.rb2
-rw-r--r--spec/workers/admin/account_deletion_worker_spec.rb19
-rw-r--r--spec/workers/cache_buster_worker_spec.rb19
-rw-r--r--spec/workers/domain_block_worker_spec.rb2
-rw-r--r--spec/workers/domain_clear_media_worker_spec.rb2
-rw-r--r--spec/workers/feed_insert_worker_spec.rb10
-rw-r--r--spec/workers/move_worker_spec.rb4
-rw-r--r--spec/workers/poll_expiration_notify_worker_spec.rb72
-rw-r--r--spec/workers/post_process_media_worker_spec.rb13
-rw-r--r--spec/workers/push_conversation_worker_spec.rb13
-rw-r--r--spec/workers/push_encrypted_message_worker_spec.rb13
-rw-r--r--spec/workers/push_update_worker_spec.rb16
-rw-r--r--spec/workers/redownload_avatar_worker_spec.rb13
-rw-r--r--spec/workers/redownload_header_worker_spec.rb13
-rw-r--r--spec/workers/refollow_worker_spec.rb1
-rw-r--r--spec/workers/regeneration_worker_spec.rb2
-rw-r--r--spec/workers/remove_featured_tag_worker_spec.rb15
-rw-r--r--spec/workers/resolve_account_worker_spec.rb13
-rw-r--r--spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb18
-rw-r--r--spec/workers/scheduler/follow_recommendations_scheduler_spec.rb43
-rw-r--r--spec/workers/scheduler/indexing_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/instance_refresh_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/ip_cleanup_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/pghero_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/scheduled_statuses_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/suspended_user_cleanup_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/trends/refresh_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/trends/review_notifications_scheduler_spec.rb13
-rw-r--r--spec/workers/scheduler/user_cleanup_scheduler_spec.rb41
-rw-r--r--spec/workers/scheduler/vacuum_scheduler_spec.rb13
-rw-r--r--spec/workers/unfollow_follow_worker_spec.rb4
-rw-r--r--spec/workers/unpublish_announcement_worker_spec.rb13
-rw-r--r--spec/workers/verify_account_links_worker_spec.rb13
-rw-r--r--spec/workers/web/push_notification_worker_spec.rb2
-rw-r--r--spec/workers/webhooks/delivery_worker_spec.rb13
-rw-r--r--streaming/index.js125
-rw-r--r--stylelint.config.js19
-rw-r--r--tsconfig.json13
-rw-r--r--yarn.lock4522
2389 files changed, 61399 insertions, 22091 deletions
diff --git a/.bundler-audit.yml b/.bundler-audit.yml
new file mode 100644
index 000000000..f84ec8087
--- /dev/null
+++ b/.bundler-audit.yml
@@ -0,0 +1,3 @@
+---
+ignore:
+  - CVE-2015-9284 # Mitigation following https://github.com/omniauth/omniauth/wiki/Resolving-CVE-2015-9284#mitigating-in-rails-applications
diff --git a/.codeclimate.yml b/.codeclimate.yml
deleted file mode 100644
index 59051aae7..000000000
--- a/.codeclimate.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-version: '2'
-checks:
-  argument-count:
-    enabled: false
-  complex-logic:
-    enabled: false
-  file-lines:
-    enabled: false
-  method-complexity:
-    enabled: false
-  method-count:
-    enabled: false
-  method-lines:
-    enabled: false
-  nested-control-flow:
-    enabled: false
-  return-statements:
-    enabled: false
-  similar-code:
-    enabled: false
-  identical-code:
-    enabled: false
-plugins:
-  brakeman:
-    enabled: true
-  bundler-audit:
-    enabled: true
-  eslint:
-    enabled: false
-  rubocop:
-    enabled: false
-  sass-lint:
-    enabled: false
-exclude_patterns:
-  - spec/
-  - vendor/asset/
-
-  - app/javascript/mastodon/locales/**/*.json
-  - config/locales/**/*.yml
diff --git a/.deepsource.toml b/.deepsource.toml
deleted file mode 100644
index bcd310412..000000000
--- a/.deepsource.toml
+++ /dev/null
@@ -1,23 +0,0 @@
-version = 1
-
-test_patterns = ["app/javascript/mastodon/**/__tests__/**"]
-
-exclude_patterns = [
-    "db/migrate/**",
-    "db/post_migrate/**"
-]
-
-[[analyzers]]
-name = "ruby"
-enabled = true
-
-[[analyzers]]
-name = "javascript"
-enabled = true
-
-  [analyzers.meta]
-  environment = [
-    "browser",
-    "jest",
-    "nodejs"
-  ]
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index 425b86a6b..04ac9560c 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -1,16 +1,14 @@
-# [Choice] Ruby version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.1, 3.0, 2, 2.7, 2.6, 3-bullseye, 3.1-bullseye, 3.0-bullseye, 2-bullseye, 2.7-bullseye, 2.6-bullseye, 3-buster, 3.1-buster, 3.0-buster, 2-buster, 2.7-buster, 2.6-buster
-ARG VARIANT=3.1-bullseye
-FROM mcr.microsoft.com/vscode/devcontainers/ruby:${VARIANT}
+# For details, see https://github.com/devcontainers/images/tree/main/src/ruby
+FROM mcr.microsoft.com/devcontainers/ruby:0-3.2-bullseye
 
 # Install Rails
 # RUN gem install rails webdrivers
 
 # Default value to allow debug server to serve content over GitHub Codespace's port forwarding service
 # The value is a comma-separated list of allowed domains
-ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev"
+ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev,.preview.app.github.dev,.app.github.dev"
 
-# [Choice] Node.js version: lts/*, 18, 16, 14
-ARG NODE_VERSION="lts/*"
+ARG NODE_VERSION="16"
 RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"
 
 # [Optional] Uncomment this section to install additional OS packages.
@@ -22,3 +20,5 @@ RUN gem install foreman
 
 # [Optional] Uncomment this line to install global node packages.
 RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g yarn" 2>&1
+
+COPY welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index b98f6a21e..17208a84e 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -1,24 +1,13 @@
+// For more details, see https://aka.ms/devcontainer.json.
 {
   "name": "Mastodon",
   "dockerComposeFile": "docker-compose.yml",
   "service": "app",
-  "workspaceFolder": "/mastodon",
-
-  // Set *default* container specific settings.json values on container create.
-  "settings": {},
-
-  // Add the IDs of extensions you want installed when the container is created.
-  "extensions": [
-    "EditorConfig.EditorConfig",
-    "dbaeumer.vscode-eslint",
-    "rebornix.Ruby",
-    "webben.browserslist"
-  ],
+  "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
 
+  // Features to add to the dev container. More info: https://containers.dev/features.
   "features": {
-    "ghcr.io/devcontainers/features/sshd:1": {
-      "version": "latest"
-    }
+    "ghcr.io/devcontainers/features/sshd:1": {}
   },
 
   // Use 'forwardPorts' to make a list of ports inside the container available locally.
@@ -26,8 +15,18 @@
   "forwardPorts": [3000, 4000],
 
   // Use 'postCreateCommand' to run commands after the container is created.
+  "onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
   "postCreateCommand": ".devcontainer/post-create.sh",
+  "waitFor": "postCreateCommand",
 
-  // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
-  "remoteUser": "vscode"
+  // Configure tool-specific properties.
+  "customizations": {
+    // Configure properties specific to VS Code.
+    "vscode": {
+      // Set *default* container specific settings.json values on container create.
+      "settings": {},
+      // Add the IDs of extensions you want installed when the container is created.
+      "extensions": ["EditorConfig.EditorConfig", "webben.browserslist"]
+    }
+  }
 }
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
index 95f401379..73fe22f3a 100644
--- a/.devcontainer/docker-compose.yml
+++ b/.devcontainer/docker-compose.yml
@@ -5,19 +5,12 @@ services:
     build:
       context: .
       dockerfile: Dockerfile
-      args:
-        # Update 'VARIANT' to pick a version of Ruby: 3, 3.1, 3.0, 2, 2.7, 2.6
-        # Append -bullseye or -buster to pin to an OS version.
-        # Use -bullseye variants on local arm64/Apple Silicon.
-        VARIANT: '3.0-bullseye'
-        # Optional Node.js version to install
-        NODE_VERSION: '16'
     volumes:
-      - ..:/mastodon:cached
+      - ../..:/workspaces:cached
     environment:
       RAILS_ENV: development
       NODE_ENV: development
-
+      BIND: 0.0.0.0
       REDIS_HOST: redis
       REDIS_PORT: '6379'
       DB_HOST: db
@@ -30,10 +23,13 @@ services:
       LIBRE_TRANSLATE_ENDPOINT: http://libretranslate:5000
     # Overrides default command so things don't shut down after the process ends.
     command: sleep infinity
+    ports:
+      - '127.0.0.1:3000:3000'
+      - '127.0.0.1:4000:4000'
+      - '127.0.0.1:80:3000'
     networks:
       - external_network
       - internal_network
-    user: vscode
 
   db:
     image: postgres:14-alpine
@@ -49,7 +45,7 @@ services:
       - internal_network
 
   redis:
-    image: redis:6-alpine
+    image: redis:7-alpine
     restart: unless-stopped
     volumes:
       - redis-data:/data
@@ -74,15 +70,19 @@ services:
         hard: -1
 
   libretranslate:
-    image: libretranslate/libretranslate:v1.2.9
+    image: libretranslate/libretranslate:v1.3.10
     restart: unless-stopped
+    volumes:
+      - lt-data:/home/libretranslate/.local
     networks:
+      - external_network
       - internal_network
 
 volumes:
   postgres-data:
   redis-data:
   es-data:
+  lt-data:
 
 networks:
   external_network:
diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh
index 02f488f12..a075cc7b3 100755
--- a/.devcontainer/post-create.sh
+++ b/.devcontainer/post-create.sh
@@ -3,17 +3,22 @@
 set -e # Fail the whole script on first error
 
 # Fetch Ruby gem dependencies
-bundle install --path vendor/bundle --with='development test'
-
-# Fetch Javascript dependencies
-yarn install
+bundle config path 'vendor/bundle'
+bundle config with 'development test'
+bundle install
 
 # Make Gemfile.lock pristine again
 git checkout -- Gemfile.lock
 
+# Fetch Javascript dependencies
+yarn --frozen-lockfile
+
 # [re]create, migrate, and seed the test database
 RAILS_ENV=test ./bin/rails db:setup
 
+# [re]create, migrate, and seed the development database
+RAILS_ENV=development ./bin/rails db:setup
+
 # Precompile assets for development
 RAILS_ENV=development ./bin/rails assets:precompile
 
diff --git a/.devcontainer/welcome-message.txt b/.devcontainer/welcome-message.txt
new file mode 100644
index 000000000..488cf9285
--- /dev/null
+++ b/.devcontainer/welcome-message.txt
@@ -0,0 +1,8 @@
+👋 Welcome to "Mastodon" in GitHub Codespaces!
+
+🛠️  Your environment is fully setup with all the required software.
+
+🔍 To explore VS Code to its fullest, search using the Command Palette (Cmd/Ctrl + Shift + P or F1).
+
+📝 Edit away, run your app as usual, and we'll automatically make it available for you to access.
+
diff --git a/.editorconfig b/.editorconfig
index 5f8702cf8..b5217da4a 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -10,3 +10,4 @@ insert_final_newline = true
 charset = utf-8
 indent_style = space
 indent_size = 2
+trim_trailing_whitespace = true
diff --git a/.eslintrc.js b/.eslintrc.js
index 03af2975b..bbdfa7de2 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -3,44 +3,49 @@ module.exports = {
 
   extends: [
     'eslint:recommended',
+    'plugin:react/recommended',
+    'plugin:jsx-a11y/recommended',
+    'plugin:import/recommended',
+    'plugin:promise/recommended',
   ],
 
   env: {
     browser: true,
     node: true,
     es6: true,
-    jest: true,
   },
 
   globals: {
     ATTACHMENT_HOST: false,
   },
 
-  parser: '@babel/eslint-parser',
+  parser: '@typescript-eslint/parser',
 
   plugins: [
     'react',
     'jsx-a11y',
     'import',
     'promise',
+    '@typescript-eslint',
   ],
 
   parserOptions: {
     sourceType: 'module',
     ecmaFeatures: {
-      experimentalObjectRestSpread: true,
       jsx: true,
     },
     ecmaVersion: 2021,
+    requireConfigFile: false,
+    babelOptions: {
+      configFile: false,
+      presets: ['@babel/react', '@babel/env'],
+    },
   },
 
   settings: {
     react: {
       version: 'detect',
     },
-    'import/extensions': [
-      '.js',
-    ],
     'import/ignore': [
       'node_modules',
       '\\.(css|scss|json)$',
@@ -48,6 +53,7 @@ module.exports = {
     'import/resolver': {
       node: {
         paths: ['app/javascript'],
+        extensions: ['.js', '.jsx', '.ts', '.tsx'],
       },
     },
   },
@@ -80,8 +86,6 @@ module.exports = {
       },
     ],
     'no-empty': 'off',
-    'no-nested-ternary': 'warn',
-    'no-prototype-builtins': 'off',
     'no-restricted-properties': [
       'error',
       { property: 'substring', message: 'Use .slice instead of .substring.' },
@@ -90,7 +94,8 @@ module.exports = {
     'no-self-assign': 'off',
     'no-trailing-spaces': 'warn',
     'no-unused-expressions': 'error',
-    'no-unused-vars': [
+    'no-unused-vars': 'off',
+    '@typescript-eslint/no-unused-vars': [
       'error',
       {
         vars: 'all',
@@ -98,7 +103,6 @@ module.exports = {
         ignoreRestSiblings: true,
       },
     ],
-    'no-useless-escape': 'off',
     'object-curly-spacing': ['error', 'always'],
     'padded-blocks': [
       'error',
@@ -110,58 +114,46 @@ module.exports = {
     semi: 'error',
     'valid-typeof': 'error',
 
+    'react/jsx-filename-extension': ['error', { extensions: ['.jsx', 'tsx'] }],
     'react/jsx-boolean-value': 'error',
     'react/jsx-closing-bracket-location': ['error', 'line-aligned'],
     'react/jsx-curly-spacing': 'error',
+    'react/display-name': 'off',
     'react/jsx-equals-spacing': 'error',
     'react/jsx-first-prop-new-line': ['error', 'multiline-multiprop'],
     'react/jsx-indent': ['error', 2],
     'react/jsx-no-bind': 'error',
-    'react/jsx-no-duplicate-props': 'error',
-    'react/jsx-no-undef': 'error',
+    'react/jsx-no-target-blank': 'off',
     'react/jsx-tag-spacing': 'error',
-    'react/jsx-uses-react': 'error',
-    'react/jsx-uses-vars': 'error',
     'react/jsx-wrap-multilines': 'error',
-    'react/no-multi-comp': 'off',
-    'react/no-string-refs': 'error',
-    'react/prop-types': 'error',
+    'react/no-deprecated': 'off',
+    'react/no-unknown-property': 'off',
     'react/self-closing-comp': 'error',
 
+    // recommended values found in https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/main/src/index.js
     'jsx-a11y/accessible-emoji': 'warn',
-    'jsx-a11y/alt-text': 'warn',
-    'jsx-a11y/anchor-has-content': 'warn',
-    'jsx-a11y/anchor-is-valid': [
-      'warn',
-      {
-        components: [
-          'Link',
-          'NavLink',
-        ],
-        specialLink: [
-          'to',
-        ],
-        aspect: [
-          'noHref',
-          'invalidHref',
-          'preferButton',
-        ],
-      },
-    ],
-    'jsx-a11y/aria-activedescendant-has-tabindex': 'warn',
-    'jsx-a11y/aria-props': 'warn',
-    'jsx-a11y/aria-proptypes': 'warn',
-    'jsx-a11y/aria-role': 'warn',
-    'jsx-a11y/aria-unsupported-elements': 'warn',
-    'jsx-a11y/heading-has-content': 'warn',
-    'jsx-a11y/html-has-lang': 'warn',
-    'jsx-a11y/iframe-has-title': 'warn',
-    'jsx-a11y/img-redundant-alt': 'warn',
-    'jsx-a11y/interactive-supports-focus': 'warn',
-    'jsx-a11y/label-has-for': 'off',
-    'jsx-a11y/mouse-events-have-key-events': 'warn',
-    'jsx-a11y/no-access-key': 'warn',
-    'jsx-a11y/no-distracting-elements': 'warn',
+    'jsx-a11y/click-events-have-key-events': 'off',
+    'jsx-a11y/label-has-associated-control': 'off',
+    'jsx-a11y/media-has-caption': 'off',
+    'jsx-a11y/no-autofocus': 'off',
+    // recommended rule is:
+    // 'jsx-a11y/no-interactive-element-to-noninteractive-role': [
+    //   'error',
+    //   {
+    //     tr: ['none', 'presentation'],
+    //     canvas: ['img'],
+    //   },
+    // ],
+    'jsx-a11y/no-interactive-element-to-noninteractive-role': 'off',
+    // recommended rule is:
+    // 'jsx-a11y/no-noninteractive-element-interactions': [
+    //   'error',
+    //   {
+    //     body: ['onError', 'onLoad'],
+    //     iframe: ['onError', 'onLoad'],
+    //     img: ['onError', 'onLoad'],
+    //   },
+    // ],
     'jsx-a11y/no-noninteractive-element-interactions': [
       'warn',
       {
@@ -170,8 +162,18 @@ module.exports = {
         ],
       },
     ],
+    // recommended rule is:
+    // 'jsx-a11y/no-noninteractive-tabindex': [
+    //   'error',
+    //   {
+    //     tags: [],
+    //     roles: ['tabpanel'],
+    //     allowExpressionValues: true,
+    //   },
+    // ],
+    'jsx-a11y/no-noninteractive-tabindex': 'off',
     'jsx-a11y/no-onchange': 'warn',
-    'jsx-a11y/no-redundant-roles': 'warn',
+    // recommended is full 'error'
     'jsx-a11y/no-static-element-interactions': [
       'warn',
       {
@@ -180,16 +182,16 @@ module.exports = {
         ],
       },
     ],
-    'jsx-a11y/role-has-required-aria-props': 'warn',
-    'jsx-a11y/role-supports-aria-props': 'off',
-    'jsx-a11y/scope': 'warn',
-    'jsx-a11y/tabindex-no-positive': 'warn',
 
+    // See https://github.com/import-js/eslint-plugin-import/blob/main/config/recommended.js
     'import/extensions': [
       'error',
       'always',
       {
         js: 'never',
+        jsx: 'never',
+        ts: 'never',
+        tsx: 'never',
       },
     ],
     'import/newline-after-import': 'error',
@@ -198,19 +200,71 @@ module.exports = {
       {
         devDependencies: [
           'config/webpack/**',
+          'app/javascript/mastodon/performance.js',
           'app/javascript/mastodon/test_setup.js',
           'app/javascript/**/__tests__/**',
         ],
       },
     ],
-    'import/no-unresolved': 'error',
     'import/no-webpack-loader-syntax': 'error',
 
+    'promise/always-return': 'off',
     'promise/catch-or-return': [
       'error',
       {
         allowFinally: true,
       },
     ],
+    'promise/no-callback-in-promise': 'off',
+    'promise/no-nesting': 'off',
+    'promise/no-promise-in-callback': 'off',
   },
+
+  overrides: [
+    {
+      files: [
+        '*.config.js',
+        '.*rc.js',
+        'ide-helper.js',
+      ],
+
+      env: {
+        commonjs: true,
+      },
+
+      parserOptions: {
+        sourceType: 'script',
+      },
+    },
+    {
+      files: [
+        '**/*.ts',
+        '**/*.tsx',
+      ],
+
+      extends: [
+        'eslint:recommended',
+        'plugin:@typescript-eslint/recommended',
+        'plugin:react/recommended',
+        'plugin:jsx-a11y/recommended',
+        'plugin:import/recommended',
+        'plugin:import/typescript',
+        'plugin:promise/recommended',
+      ],
+
+      rules: {
+        '@typescript-eslint/no-explicit-any': 'off',
+      },
+    },
+    {
+      files: [
+        '**/__tests__/*.js',
+        '**/__tests__/*.jsx',
+      ],
+
+      env: {
+        jest: true,
+      },
+    },
+  ],
 };
diff --git a/.haml-lint.yml b/.haml-lint.yml
index 7853d81d7..12ca46342 100644
--- a/.haml-lint.yml
+++ b/.haml-lint.yml
@@ -1,108 +1,9 @@
-# Whether to ignore frontmatter at the beginning of HAML documents for
-# frameworks such as Jekyll/Middleman
-skip_frontmatter: false
+inherits_from: .haml-lint_todo.yml
 
 exclude:
   - 'vendor/**/*'
-  - 'spec/**/*'
-  - 'lib/templates/**/*'
-  - 'app/views/kaminari/**/*'
+  - lib/templates/haml/scaffold/_form.html.haml
 
 linters:
   AltText:
-    enabled: false
-
-  ClassAttributeWithStaticValue:
-    enabled: true
-
-  ClassesBeforeIds:
-    enabled: true
-
-  ConsecutiveComments:
-    enabled: true
-
-  ConsecutiveSilentScripts:
-    enabled: true
-    max_consecutive: 2
-
-  EmptyObjectReference:
-    enabled: true
-
-  EmptyScript:
-    enabled: true
-
-  FinalNewline:
-    enabled: true
-    present: true
-
-  HtmlAttributes:
-    enabled: true
-
-  ImplicitDiv:
-    enabled: true
-
-  LeadingCommentSpace:
-    enabled: true
-
-  LineLength:
-    enabled: false
-    max: 80
-
-  MultilinePipe:
-    enabled: true
-
-  MultilineScript:
-    enabled: true
-
-  ObjectReferenceAttributes:
-    enabled: true
-
-  RuboCop:
-    enabled: true
-    # These cops are incredibly noisy when it comes to HAML templates, so we
-    # ignore them.
-    ignored_cops:
-      - Lint/BlockAlignment
-      - Lint/EndAlignment
-      - Lint/Void
-      - Metrics/BlockLength
-      - Metrics/LineLength
-      - Style/AlignParameters
-      - Style/BlockNesting
-      - Style/ElseAlignment
-      - Style/EndOfLine
-      - Style/FileName
-      - Style/FinalNewline
-      - Style/FrozenStringLiteralComment
-      - Style/IfUnlessModifier
-      - Style/IndentationWidth
-      - Style/Next
-      - Style/TrailingBlankLines
-      - Style/TrailingWhitespace
-      - Style/WhileUntilModifier
-
-  RubyComments:
-    enabled: true
-
-  SpaceBeforeScript:
-    enabled: true
-
-  SpaceInsideHashAttributes:
-    enabled: true
-    style: space
-
-  Indentation:
-    enabled: true
-    character: space # or tab
-
-  TagName:
-    enabled: true
-
-  TrailingWhitespace:
-    enabled: true
-
-  UnnecessaryInterpolation:
-    enabled: true
-
-  UnnecessaryStringOutput:
     enabled: true
diff --git a/.haml-lint_todo.yml b/.haml-lint_todo.yml
new file mode 100644
index 000000000..c60168390
--- /dev/null
+++ b/.haml-lint_todo.yml
@@ -0,0 +1,106 @@
+# This configuration was generated by
+# `haml-lint --auto-gen-config`
+# on 2023-03-15 00:55:01 -0400 using Haml-Lint version 0.45.0.
+# The point is for the user to remove these configuration records
+# one by one as the lints are removed from the code base.
+# Note that changes in the inspected code, or installation of new
+# versions of Haml-Lint, may require this file to be generated again.
+
+linters:
+  # Offense count: 63
+  RuboCop:
+    exclude:
+      - 'app/views/accounts/_og.html.haml'
+      - 'app/views/admin/account_warnings/_account_warning.html.haml'
+      - 'app/views/admin/accounts/index.html.haml'
+      - 'app/views/admin/accounts/show.html.haml'
+      - 'app/views/admin/announcements/edit.html.haml'
+      - 'app/views/admin/announcements/new.html.haml'
+      - 'app/views/admin/disputes/appeals/_appeal.html.haml'
+      - 'app/views/admin/domain_blocks/edit.html.haml'
+      - 'app/views/admin/domain_blocks/new.html.haml'
+      - 'app/views/admin/ip_blocks/new.html.haml'
+      - 'app/views/admin/reports/actions/preview.html.haml'
+      - 'app/views/admin/reports/index.html.haml'
+      - 'app/views/admin/reports/show.html.haml'
+      - 'app/views/admin/roles/_form.html.haml'
+      - 'app/views/admin/settings/about/show.html.haml'
+      - 'app/views/admin/settings/appearance/show.html.haml'
+      - 'app/views/admin/settings/registrations/show.html.haml'
+      - 'app/views/admin/statuses/show.html.haml'
+      - 'app/views/auth/registrations/new.html.haml'
+      - 'app/views/disputes/strikes/show.html.haml'
+      - 'app/views/filters/_filter_fields.html.haml'
+      - 'app/views/invites/_form.html.haml'
+      - 'app/views/layouts/application.html.haml'
+      - 'app/views/layouts/error.html.haml'
+      - 'app/views/notification_mailer/_status.html.haml'
+      - 'app/views/settings/applications/_fields.html.haml'
+      - 'app/views/settings/imports/show.html.haml'
+      - 'app/views/settings/preferences/appearance/show.html.haml'
+      - 'app/views/settings/preferences/other/show.html.haml'
+      - 'app/views/statuses/_detailed_status.html.haml'
+      - 'app/views/statuses/_poll.html.haml'
+      - 'app/views/statuses/show.html.haml'
+      - 'app/views/statuses_cleanup/show.html.haml'
+      - 'app/views/user_mailer/warning.html.haml'
+
+  # Offense count: 913
+  LineLength:
+    enabled: false
+
+  # Offense count: 22
+  UnnecessaryStringOutput:
+    exclude:
+      - 'app/views/accounts/show.html.haml'
+      - 'app/views/admin/custom_emojis/_custom_emoji.html.haml'
+      - 'app/views/admin/relays/_relay.html.haml'
+      - 'app/views/admin/rules/_rule.html.haml'
+      - 'app/views/admin/statuses/index.html.haml'
+      - 'app/views/auth/registrations/_sessions.html.haml'
+      - 'app/views/disputes/strikes/show.html.haml'
+      - 'app/views/notification_mailer/_status.html.haml'
+      - 'app/views/settings/two_factor_authentication_methods/index.html.haml'
+      - 'app/views/statuses/_detailed_status.html.haml'
+      - 'app/views/statuses/_poll.html.haml'
+      - 'app/views/statuses/_simple_status.html.haml'
+      - 'app/views/user_mailer/suspicious_sign_in.html.haml'
+      - 'app/views/user_mailer/webauthn_credential_added.html.haml'
+      - 'app/views/user_mailer/webauthn_credential_deleted.html.haml'
+      - 'app/views/user_mailer/welcome.html.haml'
+
+  # Offense count: 3
+  ViewLength:
+    exclude:
+      - 'app/views/admin/accounts/show.html.haml'
+      - 'app/views/admin/reports/show.html.haml'
+      - 'app/views/disputes/strikes/show.html.haml'
+
+  # Offense count: 41
+  InstanceVariables:
+    exclude:
+      - 'app/views/admin/reports/_actions.html.haml'
+      - 'app/views/admin/roles/_form.html.haml'
+      - 'app/views/admin/webhooks/_form.html.haml'
+      - 'app/views/auth/registrations/_sessions.html.haml'
+      - 'app/views/auth/registrations/_status.html.haml'
+      - 'app/views/auth/sessions/two_factor/_otp_authentication_form.html.haml'
+      - 'app/views/authorize_interactions/_post_follow_actions.html.haml'
+      - 'app/views/invites/_form.html.haml'
+      - 'app/views/relationships/_account.html.haml'
+      - 'app/views/shared/_og.html.haml'
+      - 'app/views/statuses/_status.html.haml'
+
+  # Offense count: 6
+  ConsecutiveSilentScripts:
+    exclude:
+      - 'app/views/admin/settings/shared/_links.html.haml'
+      - 'app/views/settings/login_activities/_login_activity.html.haml'
+      - 'app/views/statuses/_poll.html.haml'
+
+  # Offense count: 3
+  IdNames:
+    exclude:
+      - 'app/views/authorize_interactions/error.html.haml'
+      - 'app/views/oauth/authorizations/error.html.haml'
+      - 'app/views/shared/_error_messages.html.haml'
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 000000000..d2ae35e84
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+yarn lint-staged
diff --git a/.nvmrc b/.nvmrc
index b6a7d89c6..59ea99ee6 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-16
+16.20
diff --git a/.prettierignore b/.prettierignore
index dc8c53693..36ba57bfb 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -51,15 +51,8 @@
 *~
 *.swp
 
-# Ignore npm debug log
-npm-debug.log
-
-# Ignore yarn log files
-yarn-error.log
-yarn-debug.log
-
-# Ignore vagrant log files
-*-cloudimg-console.log
+# Ignore log files
+*.log
 
 # Ignore Docker option files
 docker-compose.override.yml
@@ -71,9 +64,30 @@ docker-compose.override.yml
 /app/javascript/mastodon/locales
 /config/locales
 
+# Ignore vendored CSS reset
+app/javascript/styles/mastodon/reset.scss
+
+# Ignore Javascript pending https://github.com/mastodon/mastodon/pull/23631
+*.js
+*.jsx
+*.ts
+*.tsx
+
+# Ignore HTML till cleaned and included in CI
+*.html
+
+# Ignore the generated AUTHORS.md
+AUTHORS.md
+
+# Ignore glitch-soc emoji map file
+/app/javascript/flavours/glitch/features/emoji/emoji_map.json
+
 # Ignore glitch-soc locale files
 /app/javascript/flavours/glitch/locales
 /config/locales-glitch
 
-# Ignore glitch-soc emoji map file
-/app/javascript/flavours/glitch/features/emoji/emoji_map.json
+# Ignore glitch-soc vendored CSS reset
+app/javascript/flavours/glitch/styles/reset.scss
+
+# Ignore win95 theme
+app/javascript/styles/win95.scss
diff --git a/.rubocop.yml b/.rubocop.yml
index 3c9223470..e6a0c2d14 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -1,479 +1,261 @@
+# Can be removed once all rules are addressed or moved to this file as documented overrides
+inherit_from: .rubocop_todo.yml
+
+# Used for merging with exclude lists with .rubocop_todo.yml
+inherit_mode:
+  merge:
+    - Exclude
+
 require:
   - rubocop-rails
   - rubocop-rspec
   - rubocop-performance
+  - rubocop-capybara
 
 AllCops:
-  TargetRubyVersion: 2.7
+  TargetRubyVersion: 2.7 # Set to minimum supported version of CI
   DisplayCopNames: true
   DisplayStyleGuide: true
   ExtraDetails: true
   UseCache: true
   CacheRootDirectory: tmp
-  NewCops: enable
+  NewCops: enable # Opt-in to newly added rules
   Exclude:
     - db/schema.rb
-    - 'app/views/**/*'
-    - 'config/**/*'
     - 'bin/*'
     - 'Rakefile'
     - 'node_modules/**/*'
     - 'Vagrantfile'
     - 'vendor/**/*'
-    - 'lib/json_ld/*'
+    - 'lib/json_ld/*' # Generated files
     - 'lib/templates/**/*'
 
-Bundler/OrderedGems:
-  Enabled: false
-
-Layout/AccessModifierIndentation:
-  EnforcedStyle: indent
-
-Layout/EmptyLineAfterMagicComment:
-  Enabled: false
-
-Layout/EmptyLineAfterGuardClause:
-  Enabled: false
-
-Layout/EmptyLineBetweenDefs:
-  AllowAdjacentOneLineDefs: true
-
-Layout/EmptyLinesAroundAttributeAccessor:
-  Enabled: true
-
+# Reason: Prefer Hashes without extreme indentation
+# https://docs.rubocop.org/rubocop/cops_layout.html#layoutfirsthashelementindentation
 Layout/FirstHashElementIndentation:
   EnforcedStyle: consistent
 
-Layout/HashAlignment:
-  Enabled: false
-
-Layout/SpaceAroundMethodCallOperator:
-  Enabled: true
-
-Layout/SpaceInsideHashLiteralBraces:
-  EnforcedStyle: space
-
-Lint/DeprecatedOpenSSLConstant:
-  Enabled: true
-
-Lint/DuplicateElsifCondition:
-  Enabled: true
-
-Lint/MixedRegexpCaptureTypes:
-  Enabled: true
-
-Lint/RaiseException:
-  Enabled: true
-
-Lint/StructNewOverride:
-  Enabled: true
+# Reason: Currently disabled in .rubocop_todo.yml
+# https://docs.rubocop.org/rubocop/cops_layout.html#layoutlinelength
+Layout/LineLength:
+  AllowedPatterns:
+    # Allow comments to be long lines
+    - !ruby/regexp / \# .*$/
+    - !ruby/regexp /^\# .*$/
+  Exclude:
+    - lib/**/*cli*.rb
+    - db/*migrate/**/*
+    - db/seeds/**/*
 
+# Reason:
+# https://docs.rubocop.org/rubocop/cops_lint.html#lintuselessaccessmodifier
 Lint/UselessAccessModifier:
   ContextCreatingMethods:
     - class_methods
 
+# Reason: Currently disabled in .rubocop_todo.yml
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsabcsize
 Metrics/AbcSize:
-  Max: 34 # RuboCop default 17
   Exclude:
     - 'lib/**/*cli*.rb'
     - db/*migrate/**/*
-    - lib/paperclip/color_extractor.rb
-    - app/workers/scheduler/follow_recommendations_scheduler.rb
-    - app/services/activitypub/fetch*_service.rb
-    - lib/paperclip/**/*
-  CountRepeatedAttributes: false
-  AllowedMethods:
-    - update_media_attachments!
-    - account_link_to
-    - attempt_oembed
-    - build_crutches
-    - calculate_scores
-    - cc
-    - dump_actor!
-    - filter_from_home?
-    - hydrate
-    - import_bookmarks!
-    - import_relationships!
-    - initialize
-    - link_to_mention
-    - log_target
-    - matches_time_window?
-    - parse_metadata
-    - perform_statuses_search!
-    - privatize_media_attachments!
-    - process_update
-    - publish_media_attachments!
-    - remotable_attachment
-    - render_initial_state
-    - render_with_cache
-    - searchable_by
-    - self.cached_filters_for
-    - set_fetchable_attributes!
-    - signed_request_actor
-    - statuses_to_delete
-    - update_poll!
 
+# Reason: Some functions cannot be broken up, but others may be refactor candidates
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocklength
 Metrics/BlockLength:
-  Max: 55
+  CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
   Exclude:
     - 'lib/mastodon/*_cli.rb'
-  CountComments: false
-  CountAsOne: [array, heredoc]
-  AllowedMethods:
-    - task
-    - namespace
-    - class_methods
-    - included
-
+    - 'lib/tasks/*.rake'
+    - 'app/models/concerns/account_associations.rb'
+    - 'app/models/concerns/account_interactions.rb'
+    - 'app/models/concerns/ldap_authenticable.rb'
+    - 'app/models/concerns/omniauthable.rb'
+    - 'app/models/concerns/pam_authenticable.rb'
+    - 'app/models/concerns/remotable.rb'
+    - 'app/services/suspend_account_service.rb'
+    - 'app/services/unsuspend_account_service.rb'
+    - 'app/views/accounts/show.rss.ruby'
+    - 'app/views/tags/show.rss.ruby'
+    - 'config/environments/development.rb'
+    - 'config/environments/production.rb'
+    - 'config/initializers/devise.rb'
+    - 'config/initializers/doorkeeper.rb'
+    - 'config/initializers/omniauth.rb'
+    - 'config/initializers/simple_form.rb'
+    - 'config/navigation.rb'
+    - 'config/routes.rb'
+    - 'db/post_migrate/20221101190723_backfill_admin_action_logs.rb'
+    - 'db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb'
+    - 'lib/paperclip/gif_transcoder.rb'
+
+# Reason:
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsblocknesting
 Metrics/BlockNesting:
-  Max: 3
   Exclude:
     - 'lib/mastodon/*_cli.rb'
 
+# Reason: Some Excluded files would be candidates for refactoring but not currently addressed
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsclasslength
 Metrics/ClassLength:
-  CountComments: false
-  Max: 500
-  CountAsOne: [array, heredoc]
+  CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
   Exclude:
     - 'lib/mastodon/*_cli.rb'
-
+    - 'app/controllers/admin/accounts_controller.rb'
+    - 'app/controllers/api/base_controller.rb'
+    - 'app/controllers/api/v1/admin/accounts_controller.rb'
+    - 'app/controllers/application_controller.rb'
+    - 'app/controllers/auth/registrations_controller.rb'
+    - 'app/controllers/auth/sessions_controller.rb'
+    - 'app/lib/activitypub/activity.rb'
+    - 'app/lib/activitypub/activity/create.rb'
+    - 'app/lib/activitypub/tag_manager.rb'
+    - 'app/lib/feed_manager.rb'
+    - 'app/lib/link_details_extractor.rb'
+    - 'app/lib/request.rb'
+    - 'app/lib/text_formatter.rb'
+    - 'app/lib/user_settings_decorator.rb'
+    - 'app/mailers/user_mailer.rb'
+    - 'app/models/account.rb'
+    - 'app/models/admin/account_action.rb'
+    - 'app/models/form/account_batch.rb'
+    - 'app/models/media_attachment.rb'
+    - 'app/models/status.rb'
+    - 'app/models/tag.rb'
+    - 'app/models/user.rb'
+    - 'app/serializers/activitypub/actor_serializer.rb'
+    - 'app/serializers/activitypub/note_serializer.rb'
+    - 'app/serializers/rest/status_serializer.rb'
+    - 'app/services/account_search_service.rb'
+    - 'app/services/activitypub/process_account_service.rb'
+    - 'app/services/activitypub/process_status_update_service.rb'
+    - 'app/services/backup_service.rb'
+    - 'app/services/delete_account_service.rb'
+    - 'app/services/fan_out_on_write_service.rb'
+    - 'app/services/fetch_link_card_service.rb'
+    - 'app/services/import_service.rb'
+    - 'app/services/notify_service.rb'
+    - 'app/services/post_status_service.rb'
+    - 'app/services/update_status_service.rb'
+    - 'lib/paperclip/color_extractor.rb'
+
+# Reason: Currently disabled in .rubocop_todo.yml
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricscyclomaticcomplexity
 Metrics/CyclomaticComplexity:
-  Max: 12
   Exclude:
     - lib/mastodon/*cli*.rb
     - db/*migrate/**/*
-  AllowedMethods:
-    - attempt_oembed
-    - blocked?
-    - build_crutches
-    - calculate_scores
-    - cc
-    - discover_endpoint!
-    - filter_from_home?
-    - hydrate
-    - klass
-    - link_to_mention
-    - log_target
-    - matches_time_window?
-    - patch_for_forwarding!
-    - preprocess_attributes!
-    - process_update
-    - remotable_attachment
-    - scan_text!
-    - self.cached_filters_for
-    - set_fetchable_attributes!
-    - setup_redis_env_url
-    - update_media_attachments!
-
-Layout/LineLength:
-  Max: 140 # RuboCop default 120
-  AllowHeredoc: true
-  AllowURI: true
-  IgnoreCopDirectives: true
-  AllowedPatterns:
-    # Allow comments to be long lines
-    - !ruby/regexp / \# .*$/
-    - !ruby/regexp /^\# .*$/
-  Exclude:
-    - lib/**/*cli*.rb
-    - db/*migrate/**/*
-    - db/seeds/**/*
 
+# Reason: Currently disabled in .rubocop_todo.yml
+# https://docs.rubocop.org/rubocop/cops_metrics.html#metricsmethodlength
 Metrics/MethodLength:
-  CountComments: false
   CountAsOne: [array, heredoc]
-  Max: 25 # RuboCop default 10
   Exclude:
     - 'lib/mastodon/*_cli.rb'
-  AllowedMethods:
-    - account_link_to
-    - attempt_oembed
-    - body_with_limit
-    - build_crutches
-    - cached_filters_for
-    - calculate_scores
-    - check_webfinger!
-    - clean_feeds!
-    - collection_items
-    - collection_presenter
-    - copy_account_notes!
-    - deduplicate_accounts!
-    - deduplicate_conversations!
-    - deduplicate_local_accounts!
-    - deduplicate_statuses!
-    - deduplicate_tags!
-    - deduplicate_users!
-    - discover_endpoint!
-    - extract_extra_uris_with_indices
-    - extract_hashtags_with_indices
-    - extract_mentions_or_lists_with_indices
-    - filter_from_home?
-    - from_elasticsearch
-    - handle_explicit_update!
-    - handle_mark_as_sensitive!
-    - hsl_to_rgb
-    - import_bookmarks!
-    - import_domain_blocks!
-    - import_relationships!
-    - ldap_options
-    - matches_time_window?
-    - outbox_presenter
-    - pam_get_user
-    - parallelize_with_progress
-    - parse_and_transform
-    - patch_for_forwarding!
-    - populate_home
-    - post_process_style
-    - preload_cache_collection_target_statuses
-    - privatize_media_attachments!
-    - provides_callback_for
-    - publish_media_attachments!
-    - relevant_account_timestamp
-    - remotable_attachment
-    - rgb_to_hsl
-    - rss_status_content_format
-    - set_fetchable_attributes!
-    - setup_redis_env_url
-    - signed_request_actor
-    - to_preview_card_attributes
-    - upgrade_storage_filesystem
-    - upgrade_storage_s3
-    - user_settings_params
-    - hydrate
-    - cc
-    - self_destruct
 
+# Reason:
+# https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror
 Metrics/ModuleLength:
-  CountComments: false
-  Max: 200
   CountAsOne: [array, heredoc]
 
-Metrics/ParameterLists:
-  Max: 5 # RuboCop default 5
-  CountKeywordArgs: true # RuboCop default true
-  MaxOptionalParameters: 3 # RuboCop default 3
-  Exclude:
-    - app/models/concerns/account_interactions.rb
-    - app/services/activitypub/fetch_remote_account_service.rb
-    - app/services/activitypub/fetch_remote_actor_service.rb
-
-Metrics/PerceivedComplexity:
-  Max: 16 # RuboCop default 8
-  AllowedMethods:
-    - attempt_oembed
-    - build_crutches
-    - calculate_scores
-    - deduplicate_users!
-    - discover_endpoint!
-    - filter_from_home?
-    - hydrate
-    - patch_for_forwarding!
-    - process_update
-    - remove_orphans
-    - update_media_attachments!
-
-Naming/MemoizedInstanceVariableName:
-  Enabled: false
-
-Naming/MethodParameterName:
-  Enabled: true
-
-Rails:
-  Enabled: true
-
-Rails/ApplicationController:
-  Enabled: false
-  Exclude:
-    - 'app/controllers/well_known/**/*.rb'
-
-Rails/BelongsTo:
-  Enabled: false
-
-Rails/ContentTag:
-  Enabled: false
-
-Rails/EnumHash:
-  Enabled: false
+# Reason: Prevailing style uses numeric status codes, matches RSpec/Rails/HttpStatus
+# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railshttpstatus
+Rails/HttpStatus:
+  EnforcedStyle: numeric
 
+# Reason: Allowed only in the `tootctl` CLI application code
+# https://docs.rubocop.org/rubocop-rails/cops_rails.html#railsexit
 Rails/Exit:
   Exclude:
-    - 'lib/mastodon/*'
+    - 'lib/mastodon/*_cli.rb'
+    - 'lib/mastodon/cli_helper.rb'
     - 'lib/cli.rb'
 
-Rails/FilePath:
-  Enabled: false
-
-Rails/HasAndBelongsToMany:
-  Enabled: false
-
-Rails/HasManyOrHasOneDependent:
-  Enabled: false
-
-Rails/HelperInstanceVariable:
-  Enabled: false
-
-Rails/HttpStatus:
-  Enabled: false
-
-Rails/IndexBy:
-  Enabled: false
-
-Rails/InverseOf:
-  Enabled: false
-
-Rails/LexicallyScopedActionFilter:
-  Enabled: false
-
-Rails/OutputSafety:
-  Enabled: true
-
-Rails/RakeEnvironment:
-  Enabled: false
-
-Rails/RedundantForeignKey:
-  Enabled: false
-
-Rails/SkipsModelValidations:
-  Enabled: false
-
-Rails/UniqueValidationWithoutIndex:
-  Enabled: false
-
-Style/AccessorGrouping:
-  Enabled: true
-
-Style/AccessModifierDeclarations:
-  Enabled: false
-
-Style/ArrayCoercion:
-  Enabled: true
+# Reason: Some single letter camel case files shouldn't be split
+# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath
+RSpec/FilePath:
+  CustomTransform:
+    ActivityPub: activitypub # Ignore the snake_case due to the amount of files to rename
+    DeepL: deepl
+    FetchOEmbedService: fetch_oembed_service
+    JsonLdHelper: jsonld_helper
+    OEmbedController: oembed_controller
+    OStatus: ostatus
+    NodeInfoController: nodeinfo_controller # NodeInfo isn't snake_cased for any of the instances
+  Exclude:
+    - 'spec/config/initializers/rack_attack_spec.rb' # namespaces usually have separate folder
+    - 'spec/lib/sanitize_config_spec.rb' # namespaces usually have separate folder
+    - 'spec/controllers/concerns/account_controller_concern_spec.rb' # Concerns describe ApplicationController and don't fit naming
+    - 'spec/controllers/concerns/export_controller_concern_spec.rb'
+    - 'spec/controllers/concerns/localized_spec.rb'
+    - 'spec/controllers/concerns/rate_limit_headers_spec.rb'
+    - 'spec/controllers/concerns/signature_verification_spec.rb'
+    - 'spec/controllers/concerns/user_tracking_concern_spec.rb'
+
+# Reason:
+# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecnamedsubject
+RSpec/NamedSubject:
+  EnforcedStyle: named_only
 
-Style/BisectedAttrAccessor:
-  Enabled: true
+# Reason: Prevailing style choice
+# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecnottonot
+RSpec/NotToNot:
+  EnforcedStyle: to_not
 
-Style/CaseLikeIf:
-  Enabled: false
+# Reason: Prevailing style uses numeric status codes, matches Rails/HttpStatus
+# https://docs.rubocop.org/rubocop-rspec/cops_rspec_rails.html#rspecrailshttpstatus
+RSpec/Rails/HttpStatus:
+  EnforcedStyle: numeric
 
+# Reason:
+# https://docs.rubocop.org/rubocop/cops_style.html#styleclassandmodulechildren
 Style/ClassAndModuleChildren:
   Enabled: false
 
-Style/CollectionMethods:
-  Enabled: true
-  PreferredMethods:
-    find_all: 'select'
-
+# Reason: Classes mostly self-document with their names
+# https://docs.rubocop.org/rubocop/cops_style.html#styledocumentation
 Style/Documentation:
   Enabled: false
 
-Style/DoubleNegation:
-  Enabled: true
-
-Style/ExpandPathArguments:
-  Enabled: false
-
-Style/ExponentialNotation:
-  Enabled: true
-
-Style/FormatString:
-  Enabled: false
-
-Style/FormatStringToken:
-  Enabled: false
-
-Style/FrozenStringLiteralComment:
-  Enabled: true
-
-Style/GuardClause:
-  Enabled: false
-
-Style/HashAsLastArrayItem:
-  Enabled: false
-
-Style/HashEachMethods:
-  Enabled: true
-
-Style/HashLikeCase:
-  Enabled: true
-
-Style/HashTransformKeys:
-  Enabled: true
-
-Style/HashTransformValues:
-  Enabled: false
-
+# Reason: Enforce modern Ruby style
+# https://docs.rubocop.org/rubocop/cops_style.html#stylehashsyntax
 Style/HashSyntax:
-  Enabled: true
   EnforcedStyle: ruby19_no_mixed_keys
 
-Style/IfUnlessModifier:
-  Enabled: false
-
-Style/InverseMethods:
-  Enabled: false
-
-Style/Lambda:
-  Enabled: false
-
-Style/MutableConstant:
-  Enabled: false
+# Reason:
+# https://docs.rubocop.org/rubocop/cops_style.html#stylenumericliterals
+Style/NumericLiterals:
+  AllowedPatterns:
+    - \d{4}_\d{2}_\d{2}_\d{6} # For DB migration date version number readability
 
+# Reason:
+# https://docs.rubocop.org/rubocop/cops_style.html#stylepercentliteraldelimiters
 Style/PercentLiteralDelimiters:
   PreferredDelimiters:
     '%i': '()'
     '%w': '()'
 
-Style/PerlBackrefs:
-  AutoCorrect: false
-
-Style/RedundantFetchBlock:
-  Enabled: true
-
-Style/RedundantFileExtensionInRequire:
-  Enabled: true
-
-Style/RedundantRegexpCharacterClass:
-  Enabled: false
-
-Style/RedundantRegexpEscape:
-  Enabled: false
-
-Style/RedundantReturn:
-  Enabled: true
-
+# Reason: Prefer less indentation in conditional assignments
+# https://docs.rubocop.org/rubocop/cops_style.html#styleredundantbegin
 Style/RedundantBegin:
   Enabled: false
 
-Style/RegexpLiteral:
-  Enabled: false
-
+# Reason: Overridden to reduce implicit StandardError rescues
+# https://docs.rubocop.org/rubocop/cops_style.html#stylerescuestandarderror
 Style/RescueStandardError:
-  Enabled: true
-
-Style/SignalException:
-  Enabled: false
-
-Style/SlicingWithRange:
-  Enabled: true
+  EnforcedStyle: implicit
 
+# Reason: Originally disabled for CodeClimate, and no config consensus has been found
+# https://docs.rubocop.org/rubocop/cops_style.html#stylesymbolarray
 Style/SymbolArray:
   Enabled: false
 
+# Reason:
+# https://docs.rubocop.org/rubocop/cops_style.html#styletrailingcommainarrayliteral
 Style/TrailingCommaInArrayLiteral:
   EnforcedStyleForMultiline: 'comma'
 
+# Reason:
+# https://docs.rubocop.org/rubocop/cops_style.html#styletrailingcommainhashliteral
 Style/TrailingCommaInHashLiteral:
   EnforcedStyleForMultiline: 'comma'
-
-Style/UnpackFirst:
-  Enabled: false
-
-RSpec/ScatteredSetup:
-  Enabled: false
-RSpec/ImplicitExpect:
-  Enabled: false
-RSpec/NamedSubject:
-  Enabled: false
-RSpec/DescribeClass:
-  Enabled: false
-RSpec/LetSetup:
-  Enabled: false
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
new file mode 100644
index 000000000..dc7e21dc5
--- /dev/null
+++ b/.rubocop_todo.yml
@@ -0,0 +1,2492 @@
+# This configuration was generated by
+# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit --no-offense-counts --no-auto-gen-timestamp`
+# using RuboCop version 1.48.1.
+# The point is for the user to remove these configuration records
+# one by one as the offenses are removed from the code base.
+# Note that changes in the inspected code, or installation of new
+# versions of RuboCop, may require this file to be generated again.
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
+# Include: **/*.gemfile, **/Gemfile, **/gems.rb
+Bundler/OrderedGems:
+  Exclude:
+    - 'Gemfile'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, IndentationWidth.
+# SupportedStyles: with_first_argument, with_fixed_indentation
+Layout/ArgumentAlignment:
+  Exclude:
+    - 'config/initializers/cors.rb'
+    - 'config/initializers/session_store.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: empty_lines, no_empty_lines
+Layout/EmptyLinesAroundBlockBody:
+  Exclude:
+    - 'config/routes.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
+Layout/ExtraSpacing:
+  Exclude:
+    - 'config/initializers/omniauth.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
+# SupportedHashRocketStyles: key, separator, table
+# SupportedColonStyles: key, separator, table
+# SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
+Layout/HashAlignment:
+  Exclude:
+    - 'config/boot.rb'
+    - 'config/environments/production.rb'
+    - 'config/initializers/rack_attack.rb'
+    - 'config/routes.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Width, AllowedPatterns.
+Layout/IndentationWidth:
+  Exclude:
+    - 'config/initializers/ffmpeg.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment.
+Layout/LeadingCommentSpace:
+  Exclude:
+    - 'config/application.rb'
+    - 'config/initializers/omniauth.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
+# SupportedStyles: space, no_space
+# SupportedStylesForEmptyBraces: space, no_space
+Layout/SpaceBeforeBlockBraces:
+  Exclude:
+    - 'config/initializers/paperclip.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: require_no_space, require_space
+Layout/SpaceInLambdaLiteral:
+  Exclude:
+    - 'config/environments/production.rb'
+    - 'config/initializers/content_security_policy.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: space, no_space
+Layout/SpaceInsideStringInterpolation:
+  Exclude:
+    - 'config/initializers/webauthn.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowInHeredoc.
+Layout/TrailingWhitespace:
+  Exclude:
+    - 'config/initializers/paperclip.rb'
+
+# Configuration parameters: AllowedMethods, AllowedPatterns.
+Lint/AmbiguousBlockAssociation:
+  Exclude:
+    - 'spec/controllers/admin/account_moderation_notes_controller_spec.rb'
+    - 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb'
+    - 'spec/controllers/settings/two_factor_authentication/otp_authentication_controller_spec.rb'
+    - 'spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb'
+    - 'spec/services/activitypub/process_status_update_service_spec.rb'
+    - 'spec/services/post_status_service_spec.rb'
+    - 'spec/services/suspend_account_service_spec.rb'
+    - 'spec/services/unsuspend_account_service_spec.rb'
+    - 'spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+Lint/AmbiguousOperatorPrecedence:
+  Exclude:
+    - 'config/initializers/rack_attack.rb'
+
+# Configuration parameters: AllowedMethods.
+# AllowedMethods: enums
+Lint/ConstantDefinitionInBlock:
+  Exclude:
+    - 'spec/controllers/api/base_controller_spec.rb'
+    - 'spec/controllers/application_controller_spec.rb'
+    - 'spec/controllers/concerns/accountable_concern_spec.rb'
+    - 'spec/controllers/concerns/signature_verification_spec.rb'
+    - 'spec/lib/activitypub/adapter_spec.rb'
+    - 'spec/lib/connection_pool/shared_connection_pool_spec.rb'
+    - 'spec/lib/connection_pool/shared_timed_stack_spec.rb'
+    - 'spec/models/concerns/remotable_spec.rb'
+
+# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches.
+Lint/DuplicateBranch:
+  Exclude:
+    - 'app/lib/permalink_redirector.rb'
+    - 'app/models/account_statuses_filter.rb'
+    - 'app/validators/email_mx_validator.rb'
+    - 'app/validators/vote_validator.rb'
+    - 'lib/mastodon/maintenance_cli.rb'
+
+# Configuration parameters: AllowComments, AllowEmptyLambdas.
+Lint/EmptyBlock:
+  Exclude:
+    - 'spec/controllers/api/v2/search_controller_spec.rb'
+    - 'spec/controllers/application_controller_spec.rb'
+    - 'spec/fabricators/access_token_fabricator.rb'
+    - 'spec/fabricators/conversation_fabricator.rb'
+    - 'spec/fabricators/system_key_fabricator.rb'
+    - 'spec/helpers/admin/action_logs_helper_spec.rb'
+    - 'spec/lib/activitypub/adapter_spec.rb'
+    - 'spec/models/account_alias_spec.rb'
+    - 'spec/models/account_deletion_request_spec.rb'
+    - 'spec/models/account_moderation_note_spec.rb'
+    - 'spec/models/announcement_mute_spec.rb'
+    - 'spec/models/announcement_reaction_spec.rb'
+    - 'spec/models/announcement_spec.rb'
+    - 'spec/models/backup_spec.rb'
+    - 'spec/models/conversation_mute_spec.rb'
+    - 'spec/models/custom_filter_keyword_spec.rb'
+    - 'spec/models/custom_filter_spec.rb'
+    - 'spec/models/device_spec.rb'
+    - 'spec/models/encrypted_message_spec.rb'
+    - 'spec/models/featured_tag_spec.rb'
+    - 'spec/models/follow_recommendation_suppression_spec.rb'
+    - 'spec/models/list_account_spec.rb'
+    - 'spec/models/list_spec.rb'
+    - 'spec/models/login_activity_spec.rb'
+    - 'spec/models/mute_spec.rb'
+    - 'spec/models/preview_card_spec.rb'
+    - 'spec/models/preview_card_trend_spec.rb'
+    - 'spec/models/relay_spec.rb'
+    - 'spec/models/scheduled_status_spec.rb'
+    - 'spec/models/status_stat_spec.rb'
+    - 'spec/models/status_trend_spec.rb'
+    - 'spec/models/system_key_spec.rb'
+    - 'spec/models/tag_follow_spec.rb'
+    - 'spec/models/unavailable_domain_spec.rb'
+    - 'spec/models/user_invite_request_spec.rb'
+    - 'spec/models/user_role_spec.rb'
+    - 'spec/models/web/setting_spec.rb'
+
+# Configuration parameters: AllowComments.
+Lint/EmptyClass:
+  Exclude:
+    - 'spec/controllers/api/base_controller_spec.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Lint/NonDeterministicRequireOrder:
+  Exclude:
+    - 'spec/rails_helper.rb'
+
+Lint/NonLocalExitFromIterator:
+  Exclude:
+    - 'app/helpers/jsonld_helper.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Lint/OrAssignmentToConstant:
+  Exclude:
+    - 'lib/sanitize_ext/sanitize_config.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+Lint/SendWithMixinArgument:
+  Exclude:
+    - 'config/application.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
+Lint/UnusedBlockArgument:
+  Exclude:
+    - 'config/initializers/content_security_policy.rb'
+    - 'config/initializers/doorkeeper.rb'
+    - 'config/initializers/paperclip.rb'
+    - 'config/initializers/simple_form.rb'
+
+Lint/UselessAssignment:
+  Exclude:
+    - 'app/services/activitypub/process_status_update_service.rb'
+    - 'config/initializers/omniauth.rb'
+    - 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb'
+    - 'db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb'
+    - 'spec/controllers/api/v1/bookmarks_controller_spec.rb'
+    - 'spec/controllers/api/v1/favourites_controller_spec.rb'
+    - 'spec/controllers/concerns/account_controller_concern_spec.rb'
+    - 'spec/helpers/jsonld_helper_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/models/domain_block_spec.rb'
+    - 'spec/models/status_spec.rb'
+    - 'spec/models/user_spec.rb'
+    - 'spec/models/webauthn_credentials_spec.rb'
+    - 'spec/services/account_search_service_spec.rb'
+    - 'spec/services/post_status_service_spec.rb'
+    - 'spec/services/precompute_feed_service_spec.rb'
+    - 'spec/services/resolve_url_service_spec.rb'
+    - 'spec/views/statuses/show.html.haml_spec.rb'
+
+# Configuration parameters: CheckForMethodsWithNoSideEffects.
+Lint/Void:
+  Exclude:
+    - 'spec/services/resolve_account_service_spec.rb'
+
+# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
+Metrics/AbcSize:
+  Max: 150
+
+# Configuration parameters: CountBlocks, Max.
+Metrics/BlockNesting:
+  Exclude:
+    - 'lib/tasks/mastodon.rake'
+
+# Configuration parameters: AllowedMethods, AllowedPatterns.
+Metrics/CyclomaticComplexity:
+  Max: 25
+
+# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
+Metrics/MethodLength:
+  Max: 58
+
+# Configuration parameters: CountComments, Max, CountAsOne.
+Metrics/ModuleLength:
+  Exclude:
+    - 'app/controllers/concerns/signature_verification.rb'
+    - 'app/helpers/application_helper.rb'
+    - 'app/helpers/jsonld_helper.rb'
+    - 'app/helpers/statuses_helper.rb'
+    - 'app/models/concerns/account_interactions.rb'
+    - 'app/models/concerns/has_user_settings.rb'
+
+# Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters.
+Metrics/ParameterLists:
+  Exclude:
+    - 'app/models/concerns/account_interactions.rb'
+    - 'app/services/activitypub/fetch_remote_account_service.rb'
+    - 'app/services/activitypub/fetch_remote_actor_service.rb'
+    - 'app/services/activitypub/fetch_remote_status_service.rb'
+
+# Configuration parameters: AllowedMethods, AllowedPatterns.
+Metrics/PerceivedComplexity:
+  Max: 28
+
+Naming/AccessorMethodName:
+  Exclude:
+    - 'app/controllers/auth/sessions_controller.rb'
+
+# Configuration parameters: ExpectMatchingDefinition, CheckDefinitionPathHierarchy, CheckDefinitionPathHierarchyRoots, Regex, IgnoreExecutableScripts, AllowedAcronyms.
+# CheckDefinitionPathHierarchyRoots: lib, spec, test, src
+# AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
+Naming/FileName:
+  Exclude:
+    - 'config/locales/sr-Latn.rb'
+
+# Configuration parameters: EnforcedStyleForLeadingUnderscores.
+# SupportedStylesForLeadingUnderscores: disallowed, required, optional
+Naming/MemoizedInstanceVariableName:
+  Exclude:
+    - 'app/controllers/api/v1/bookmarks_controller.rb'
+    - 'app/controllers/api/v1/favourites_controller.rb'
+    - 'app/controllers/concerns/rate_limit_headers.rb'
+    - 'app/lib/activitypub/activity.rb'
+    - 'app/services/resolve_url_service.rb'
+    - 'app/services/search_service.rb'
+    - 'config/initializers/rack_attack.rb'
+
+# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
+# SupportedStyles: snake_case, normalcase, non_integer
+# AllowedIdentifiers: capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
+Naming/VariableNumber:
+  Exclude:
+    - 'db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb'
+    - 'db/migrate/20180514140000_revert_index_change_on_statuses_for_api_v1_accounts_account_id_statuses.rb'
+    - 'db/migrate/20190820003045_update_statuses_index.rb'
+    - 'db/migrate/20190823221802_add_local_index_to_statuses.rb'
+    - 'db/migrate/20200119112504_add_public_index_to_statuses.rb'
+    - 'spec/controllers/activitypub/followers_synchronizations_controller_spec.rb'
+    - 'spec/lib/feed_manager_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/models/concerns/account_interactions_spec.rb'
+    - 'spec/models/custom_emoji_filter_spec.rb'
+    - 'spec/models/domain_block_spec.rb'
+    - 'spec/models/user_spec.rb'
+    - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb'
+
+# Configuration parameters: MinSize.
+Performance/CollectionLiteralInLoop:
+  Exclude:
+    - 'app/models/admin/appeal_filter.rb'
+    - 'app/models/admin/status_filter.rb'
+    - 'app/models/relationship_filter.rb'
+    - 'app/models/trends/preview_card_filter.rb'
+    - 'app/models/trends/status_filter.rb'
+    - 'app/presenters/status_relationships_presenter.rb'
+    - 'app/services/fetch_resource_service.rb'
+    - 'app/services/suspend_account_service.rb'
+    - 'app/services/unsuspend_account_service.rb'
+    - 'config/deploy.rb'
+    - 'lib/mastodon/media_cli.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Performance/Count:
+  Exclude:
+    - 'app/lib/importer/accounts_index_importer.rb'
+    - 'app/lib/importer/tags_index_importer.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: SafeMultiline.
+Performance/DeletePrefix:
+  Exclude:
+    - 'app/controllers/authorize_interactions_controller.rb'
+    - 'app/controllers/concerns/signature_verification.rb'
+    - 'app/controllers/intents_controller.rb'
+    - 'app/lib/activitypub/case_transform.rb'
+    - 'app/lib/permalink_redirector.rb'
+    - 'app/lib/webfinger_resource.rb'
+    - 'app/services/activitypub/fetch_remote_actor_service.rb'
+    - 'app/services/backup_service.rb'
+    - 'app/services/resolve_account_service.rb'
+    - 'app/services/tag_search_service.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Performance/MapCompact:
+  Exclude:
+    - 'app/lib/admin/metrics/dimension.rb'
+    - 'app/lib/admin/metrics/measure.rb'
+    - 'app/lib/feed_manager.rb'
+    - 'app/models/account.rb'
+    - 'app/models/account_statuses_cleanup_policy.rb'
+    - 'app/models/account_suggestions/setting_source.rb'
+    - 'app/models/account_suggestions/source.rb'
+    - 'app/models/follow_recommendation_filter.rb'
+    - 'app/models/notification.rb'
+    - 'app/models/user_role.rb'
+    - 'app/models/webhook.rb'
+    - 'app/services/process_mentions_service.rb'
+    - 'app/validators/existing_username_validator.rb'
+    - 'db/migrate/20200407202420_migrate_unavailable_inboxes.rb'
+    - 'spec/presenters/status_relationships_presenter_spec.rb'
+
+Performance/MethodObjectAsBlock:
+  Exclude:
+    - 'app/models/account_suggestions/source.rb'
+    - 'spec/models/export_spec.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Performance/RedundantEqualityComparisonBlock:
+  Exclude:
+    - 'spec/requests/link_headers_spec.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: MaxKeyValuePairs.
+Performance/RedundantMerge:
+  Exclude:
+    - 'config/initializers/paperclip.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: SafeMultiline.
+Performance/StartWith:
+  Exclude:
+    - 'app/lib/extractor.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: OnlySumOrWithInitialValue.
+Performance/Sum:
+  Exclude:
+    - 'app/lib/activity_tracker.rb'
+    - 'app/models/trends/history.rb'
+    - 'lib/paperclip/color_extractor.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Performance/TimesMap:
+  Exclude:
+    - 'spec/controllers/api/v1/blocks_controller_spec.rb'
+    - 'spec/controllers/api/v1/mutes_controller_spec.rb'
+    - 'spec/lib/feed_manager_spec.rb'
+    - 'spec/lib/request_pool_spec.rb'
+    - 'spec/models/account_spec.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Performance/UnfreezeString:
+  Exclude:
+    - 'app/lib/rss/builder.rb'
+    - 'app/lib/text_formatter.rb'
+    - 'app/validators/status_length_validator.rb'
+    - 'lib/tasks/mastodon.rake'
+
+RSpec/AnyInstance:
+  Exclude:
+    - 'spec/controllers/activitypub/inboxes_controller_spec.rb'
+    - 'spec/controllers/admin/accounts_controller_spec.rb'
+    - 'spec/controllers/admin/resets_controller_spec.rb'
+    - 'spec/controllers/admin/settings/branding_controller_spec.rb'
+    - 'spec/controllers/api/v1/media_controller_spec.rb'
+    - 'spec/controllers/auth/sessions_controller_spec.rb'
+    - 'spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb'
+    - 'spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb'
+    - 'spec/lib/request_spec.rb'
+    - 'spec/lib/status_filter_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/models/setting_spec.rb'
+    - 'spec/services/activitypub/process_collection_service_spec.rb'
+    - 'spec/validators/blacklisted_email_validator_spec.rb'
+    - 'spec/validators/follow_limit_validator_spec.rb'
+    - 'spec/workers/activitypub/delivery_worker_spec.rb'
+    - 'spec/workers/web/push_notification_worker_spec.rb'
+
+RSpec/BeforeAfterAll:
+  Exclude:
+    - 'spec/requests/localization_spec.rb'
+
+# Configuration parameters: Prefixes, AllowedPatterns.
+# Prefixes: when, with, without
+RSpec/ContextWording:
+  Exclude:
+    - 'spec/config/initializers/rack_attack_spec.rb'
+    - 'spec/controllers/accounts_controller_spec.rb'
+    - 'spec/controllers/activitypub/collections_controller_spec.rb'
+    - 'spec/controllers/activitypub/inboxes_controller_spec.rb'
+    - 'spec/controllers/admin/domain_blocks_controller_spec.rb'
+    - 'spec/controllers/admin/reports/actions_controller_spec.rb'
+    - 'spec/controllers/admin/statuses_controller_spec.rb'
+    - 'spec/controllers/api/v1/accounts/relationships_controller_spec.rb'
+    - 'spec/controllers/api/v1/accounts_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb'
+    - 'spec/controllers/api/v1/emails/confirmations_controller_spec.rb'
+    - 'spec/controllers/api/v1/instances/activity_controller_spec.rb'
+    - 'spec/controllers/api/v1/instances/peers_controller_spec.rb'
+    - 'spec/controllers/api/v1/media_controller_spec.rb'
+    - 'spec/controllers/api/v2/filters_controller_spec.rb'
+    - 'spec/controllers/application_controller_spec.rb'
+    - 'spec/controllers/auth/registrations_controller_spec.rb'
+    - 'spec/controllers/auth/sessions_controller_spec.rb'
+    - 'spec/controllers/concerns/cache_concern_spec.rb'
+    - 'spec/controllers/concerns/challengable_concern_spec.rb'
+    - 'spec/controllers/concerns/localized_spec.rb'
+    - 'spec/controllers/concerns/rate_limit_headers_spec.rb'
+    - 'spec/controllers/instance_actors_controller_spec.rb'
+    - 'spec/controllers/settings/applications_controller_spec.rb'
+    - 'spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb'
+    - 'spec/controllers/statuses_controller_spec.rb'
+    - 'spec/helpers/admin/account_moderation_notes_helper_spec.rb'
+    - 'spec/helpers/jsonld_helper_spec.rb'
+    - 'spec/helpers/routing_helper_spec.rb'
+    - 'spec/lib/activitypub/activity/accept_spec.rb'
+    - 'spec/lib/activitypub/activity/announce_spec.rb'
+    - 'spec/lib/activitypub/activity/create_spec.rb'
+    - 'spec/lib/activitypub/activity/follow_spec.rb'
+    - 'spec/lib/activitypub/activity/reject_spec.rb'
+    - 'spec/lib/advanced_text_formatter_spec.rb'
+    - 'spec/lib/emoji_formatter_spec.rb'
+    - 'spec/lib/entity_cache_spec.rb'
+    - 'spec/lib/feed_manager_spec.rb'
+    - 'spec/lib/html_aware_formatter_spec.rb'
+    - 'spec/lib/link_details_extractor_spec.rb'
+    - 'spec/lib/ostatus/tag_manager_spec.rb'
+    - 'spec/lib/scope_transformer_spec.rb'
+    - 'spec/lib/status_cache_hydrator_spec.rb'
+    - 'spec/lib/status_reach_finder_spec.rb'
+    - 'spec/lib/text_formatter_spec.rb'
+    - 'spec/models/account/field_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/models/admin/account_action_spec.rb'
+    - 'spec/models/concerns/account_interactions_spec.rb'
+    - 'spec/models/concerns/remotable_spec.rb'
+    - 'spec/models/custom_emoji_filter_spec.rb'
+    - 'spec/models/custom_emoji_spec.rb'
+    - 'spec/models/email_domain_block_spec.rb'
+    - 'spec/models/media_attachment_spec.rb'
+    - 'spec/models/notification_spec.rb'
+    - 'spec/models/remote_follow_spec.rb'
+    - 'spec/models/report_spec.rb'
+    - 'spec/models/session_activation_spec.rb'
+    - 'spec/models/setting_spec.rb'
+    - 'spec/models/status_spec.rb'
+    - 'spec/models/web/push_subscription_spec.rb'
+    - 'spec/policies/account_moderation_note_policy_spec.rb'
+    - 'spec/policies/account_policy_spec.rb'
+    - 'spec/policies/backup_policy_spec.rb'
+    - 'spec/policies/custom_emoji_policy_spec.rb'
+    - 'spec/policies/domain_block_policy_spec.rb'
+    - 'spec/policies/email_domain_block_policy_spec.rb'
+    - 'spec/policies/instance_policy_spec.rb'
+    - 'spec/policies/invite_policy_spec.rb'
+    - 'spec/policies/relay_policy_spec.rb'
+    - 'spec/policies/report_note_policy_spec.rb'
+    - 'spec/policies/report_policy_spec.rb'
+    - 'spec/policies/settings_policy_spec.rb'
+    - 'spec/policies/tag_policy_spec.rb'
+    - 'spec/policies/user_policy_spec.rb'
+    - 'spec/presenters/account_relationships_presenter_spec.rb'
+    - 'spec/presenters/status_relationships_presenter_spec.rb'
+    - 'spec/services/account_search_service_spec.rb'
+    - 'spec/services/account_statuses_cleanup_service_spec.rb'
+    - 'spec/services/activitypub/fetch_remote_status_service_spec.rb'
+    - 'spec/services/activitypub/process_account_service_spec.rb'
+    - 'spec/services/activitypub/process_status_update_service_spec.rb'
+    - 'spec/services/fetch_link_card_service_spec.rb'
+    - 'spec/services/fetch_oembed_service_spec.rb'
+    - 'spec/services/fetch_remote_status_service_spec.rb'
+    - 'spec/services/follow_service_spec.rb'
+    - 'spec/services/import_service_spec.rb'
+    - 'spec/services/notify_service_spec.rb'
+    - 'spec/services/process_mentions_service_spec.rb'
+    - 'spec/services/reblog_service_spec.rb'
+    - 'spec/services/report_service_spec.rb'
+    - 'spec/services/resolve_account_service_spec.rb'
+    - 'spec/services/resolve_url_service_spec.rb'
+    - 'spec/services/search_service_spec.rb'
+    - 'spec/services/unallow_domain_service_spec.rb'
+    - 'spec/services/verify_link_service_spec.rb'
+    - 'spec/validators/disallowed_hashtags_validator_spec.rb'
+    - 'spec/validators/email_mx_validator_spec.rb'
+    - 'spec/validators/follow_limit_validator_spec.rb'
+    - 'spec/validators/poll_validator_spec.rb'
+    - 'spec/validators/status_pin_validator_spec.rb'
+    - 'spec/validators/unreserved_username_validator_spec.rb'
+    - 'spec/validators/url_validator_spec.rb'
+    - 'spec/workers/move_worker_spec.rb'
+    - 'spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: SkipBlocks, EnforcedStyle.
+# SupportedStyles: described_class, explicit
+RSpec/DescribedClass:
+  Exclude:
+    - 'spec/controllers/concerns/cache_concern_spec.rb'
+    - 'spec/controllers/concerns/challengable_concern_spec.rb'
+    - 'spec/lib/entity_cache_spec.rb'
+    - 'spec/lib/extractor_spec.rb'
+    - 'spec/lib/feed_manager_spec.rb'
+    - 'spec/lib/hash_object_spec.rb'
+    - 'spec/lib/ostatus/tag_manager_spec.rb'
+    - 'spec/lib/request_spec.rb'
+    - 'spec/lib/tag_manager_spec.rb'
+    - 'spec/lib/webfinger_resource_spec.rb'
+    - 'spec/mailers/notification_mailer_spec.rb'
+    - 'spec/mailers/user_mailer_spec.rb'
+    - 'spec/models/account_conversation_spec.rb'
+    - 'spec/models/account_domain_block_spec.rb'
+    - 'spec/models/account_migration_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/models/block_spec.rb'
+    - 'spec/models/domain_block_spec.rb'
+    - 'spec/models/email_domain_block_spec.rb'
+    - 'spec/models/export_spec.rb'
+    - 'spec/models/favourite_spec.rb'
+    - 'spec/models/follow_spec.rb'
+    - 'spec/models/identity_spec.rb'
+    - 'spec/models/import_spec.rb'
+    - 'spec/models/media_attachment_spec.rb'
+    - 'spec/models/notification_spec.rb'
+    - 'spec/models/relationship_filter_spec.rb'
+    - 'spec/models/report_filter_spec.rb'
+    - 'spec/models/session_activation_spec.rb'
+    - 'spec/models/setting_spec.rb'
+    - 'spec/models/site_upload_spec.rb'
+    - 'spec/models/status_pin_spec.rb'
+    - 'spec/models/status_spec.rb'
+    - 'spec/models/user_spec.rb'
+    - 'spec/policies/account_moderation_note_policy_spec.rb'
+    - 'spec/presenters/account_relationships_presenter_spec.rb'
+    - 'spec/presenters/status_relationships_presenter_spec.rb'
+    - 'spec/serializers/activitypub/note_serializer_spec.rb'
+    - 'spec/serializers/activitypub/update_poll_serializer_spec.rb'
+    - 'spec/serializers/rest/account_serializer_spec.rb'
+    - 'spec/services/activitypub/fetch_remote_account_service_spec.rb'
+    - 'spec/services/activitypub/fetch_remote_actor_service_spec.rb'
+    - 'spec/services/activitypub/fetch_remote_key_service_spec.rb'
+    - 'spec/services/after_block_domain_from_account_service_spec.rb'
+    - 'spec/services/authorize_follow_service_spec.rb'
+    - 'spec/services/batched_remove_status_service_spec.rb'
+    - 'spec/services/block_domain_service_spec.rb'
+    - 'spec/services/block_service_spec.rb'
+    - 'spec/services/bootstrap_timeline_service_spec.rb'
+    - 'spec/services/clear_domain_media_service_spec.rb'
+    - 'spec/services/favourite_service_spec.rb'
+    - 'spec/services/follow_service_spec.rb'
+    - 'spec/services/import_service_spec.rb'
+    - 'spec/services/post_status_service_spec.rb'
+    - 'spec/services/precompute_feed_service_spec.rb'
+    - 'spec/services/process_mentions_service_spec.rb'
+    - 'spec/services/purge_domain_service_spec.rb'
+    - 'spec/services/reblog_service_spec.rb'
+    - 'spec/services/reject_follow_service_spec.rb'
+    - 'spec/services/remove_from_followers_service_spec.rb'
+    - 'spec/services/remove_status_service_spec.rb'
+    - 'spec/services/unallow_domain_service_spec.rb'
+    - 'spec/services/unblock_service_spec.rb'
+    - 'spec/services/unfollow_service_spec.rb'
+    - 'spec/services/unmute_service_spec.rb'
+    - 'spec/services/update_account_service_spec.rb'
+    - 'spec/validators/note_length_validator_spec.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+RSpec/EmptyExampleGroup:
+  Exclude:
+    - 'spec/helpers/admin/action_logs_helper_spec.rb'
+    - 'spec/models/account_alias_spec.rb'
+    - 'spec/models/account_deletion_request_spec.rb'
+    - 'spec/models/account_moderation_note_spec.rb'
+    - 'spec/models/announcement_mute_spec.rb'
+    - 'spec/models/announcement_reaction_spec.rb'
+    - 'spec/models/announcement_spec.rb'
+    - 'spec/models/backup_spec.rb'
+    - 'spec/models/conversation_mute_spec.rb'
+    - 'spec/models/custom_filter_keyword_spec.rb'
+    - 'spec/models/custom_filter_spec.rb'
+    - 'spec/models/device_spec.rb'
+    - 'spec/models/encrypted_message_spec.rb'
+    - 'spec/models/featured_tag_spec.rb'
+    - 'spec/models/follow_recommendation_suppression_spec.rb'
+    - 'spec/models/list_account_spec.rb'
+    - 'spec/models/list_spec.rb'
+    - 'spec/models/login_activity_spec.rb'
+    - 'spec/models/mute_spec.rb'
+    - 'spec/models/preview_card_spec.rb'
+    - 'spec/models/preview_card_trend_spec.rb'
+    - 'spec/models/relay_spec.rb'
+    - 'spec/models/scheduled_status_spec.rb'
+    - 'spec/models/status_stat_spec.rb'
+    - 'spec/models/status_trend_spec.rb'
+    - 'spec/models/system_key_spec.rb'
+    - 'spec/models/tag_follow_spec.rb'
+    - 'spec/models/unavailable_domain_spec.rb'
+    - 'spec/models/user_invite_request_spec.rb'
+    - 'spec/models/web/setting_spec.rb'
+    - 'spec/services/unmute_service_spec.rb'
+
+# Configuration parameters: CountAsOne.
+RSpec/ExampleLength:
+  Max: 22
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: method_call, block
+RSpec/ExpectChange:
+  Exclude:
+    - 'spec/controllers/admin/account_moderation_notes_controller_spec.rb'
+    - 'spec/controllers/admin/custom_emojis_controller_spec.rb'
+    - 'spec/controllers/admin/invites_controller_spec.rb'
+    - 'spec/controllers/admin/report_notes_controller_spec.rb'
+    - 'spec/controllers/concerns/accountable_concern_spec.rb'
+    - 'spec/controllers/invites_controller_spec.rb'
+    - 'spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb'
+    - 'spec/models/admin/account_action_spec.rb'
+    - 'spec/services/suspend_account_service_spec.rb'
+    - 'spec/services/unsuspend_account_service_spec.rb'
+    - 'spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb'
+
+RSpec/ExpectInHook:
+  Exclude:
+    - 'spec/controllers/api/v1/media_controller_spec.rb'
+    - 'spec/controllers/settings/applications_controller_spec.rb'
+    - 'spec/lib/status_filter_spec.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: implicit, each, example
+RSpec/HookArgument:
+  Exclude:
+    - 'spec/controllers/api/v1/streaming_controller_spec.rb'
+    - 'spec/controllers/well_known/webfinger_controller_spec.rb'
+    - 'spec/helpers/instance_helper_spec.rb'
+    - 'spec/models/user_spec.rb'
+    - 'spec/rails_helper.rb'
+    - 'spec/serializers/activitypub/note_serializer_spec.rb'
+    - 'spec/serializers/activitypub/update_poll_serializer_spec.rb'
+    - 'spec/services/import_service_spec.rb'
+    - 'spec/spec_helper.rb'
+
+# Configuration parameters: AssignmentOnly.
+RSpec/InstanceVariable:
+  Exclude:
+    - 'spec/controllers/api/v1/streaming_controller_spec.rb'
+    - 'spec/controllers/application_controller_spec.rb'
+    - 'spec/controllers/auth/confirmations_controller_spec.rb'
+    - 'spec/controllers/auth/passwords_controller_spec.rb'
+    - 'spec/controllers/auth/sessions_controller_spec.rb'
+    - 'spec/controllers/concerns/export_controller_concern_spec.rb'
+    - 'spec/controllers/home_controller_spec.rb'
+    - 'spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb'
+    - 'spec/controllers/statuses_cleanup_controller_spec.rb'
+    - 'spec/models/concerns/account_finder_concern_spec.rb'
+    - 'spec/models/concerns/account_interactions_spec.rb'
+    - 'spec/models/concerns/remotable_spec.rb'
+    - 'spec/models/public_feed_spec.rb'
+    - 'spec/serializers/activitypub/note_serializer_spec.rb'
+    - 'spec/serializers/activitypub/update_poll_serializer_spec.rb'
+    - 'spec/services/remove_status_service_spec.rb'
+    - 'spec/services/search_service_spec.rb'
+    - 'spec/services/unblock_domain_service_spec.rb'
+
+RSpec/LeakyConstantDeclaration:
+  Exclude:
+    - 'spec/controllers/api/base_controller_spec.rb'
+    - 'spec/controllers/application_controller_spec.rb'
+    - 'spec/controllers/concerns/accountable_concern_spec.rb'
+    - 'spec/controllers/concerns/signature_verification_spec.rb'
+    - 'spec/lib/activitypub/adapter_spec.rb'
+    - 'spec/lib/connection_pool/shared_connection_pool_spec.rb'
+    - 'spec/lib/connection_pool/shared_timed_stack_spec.rb'
+    - 'spec/models/concerns/remotable_spec.rb'
+
+RSpec/LetSetup:
+  Exclude:
+    - 'spec/controllers/admin/accounts_controller_spec.rb'
+    - 'spec/controllers/admin/action_logs_controller_spec.rb'
+    - 'spec/controllers/admin/instances_controller_spec.rb'
+    - 'spec/controllers/admin/reports/actions_controller_spec.rb'
+    - 'spec/controllers/admin/statuses_controller_spec.rb'
+    - 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/accounts_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/domain_allows_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb'
+    - 'spec/controllers/api/v1/filters_controller_spec.rb'
+    - 'spec/controllers/api/v1/followed_tags_controller_spec.rb'
+    - 'spec/controllers/api/v1/tags_controller_spec.rb'
+    - 'spec/controllers/api/v2/admin/accounts_controller_spec.rb'
+    - 'spec/controllers/api/v2/filters/keywords_controller_spec.rb'
+    - 'spec/controllers/api/v2/filters/statuses_controller_spec.rb'
+    - 'spec/controllers/api/v2/filters_controller_spec.rb'
+    - 'spec/controllers/auth/confirmations_controller_spec.rb'
+    - 'spec/controllers/auth/passwords_controller_spec.rb'
+    - 'spec/controllers/auth/sessions_controller_spec.rb'
+    - 'spec/controllers/follower_accounts_controller_spec.rb'
+    - 'spec/controllers/following_accounts_controller_spec.rb'
+    - 'spec/controllers/oauth/authorized_applications_controller_spec.rb'
+    - 'spec/controllers/oauth/tokens_controller_spec.rb'
+    - 'spec/controllers/tags_controller_spec.rb'
+    - 'spec/lib/activitypub/activity/delete_spec.rb'
+    - 'spec/lib/vacuum/preview_cards_vacuum_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/models/account_statuses_cleanup_policy_spec.rb'
+    - 'spec/models/canonical_email_block_spec.rb'
+    - 'spec/models/status_spec.rb'
+    - 'spec/models/user_spec.rb'
+    - 'spec/services/account_statuses_cleanup_service_spec.rb'
+    - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb'
+    - 'spec/services/activitypub/fetch_remote_status_service_spec.rb'
+    - 'spec/services/activitypub/process_account_service_spec.rb'
+    - 'spec/services/activitypub/process_collection_service_spec.rb'
+    - 'spec/services/batched_remove_status_service_spec.rb'
+    - 'spec/services/block_domain_service_spec.rb'
+    - 'spec/services/delete_account_service_spec.rb'
+    - 'spec/services/import_service_spec.rb'
+    - 'spec/services/notify_service_spec.rb'
+    - 'spec/services/remove_status_service_spec.rb'
+    - 'spec/services/report_service_spec.rb'
+    - 'spec/services/resolve_account_service_spec.rb'
+    - 'spec/services/suspend_account_service_spec.rb'
+    - 'spec/services/unallow_domain_service_spec.rb'
+    - 'spec/services/unsuspend_account_service_spec.rb'
+    - 'spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb'
+    - 'spec/workers/scheduler/user_cleanup_scheduler_spec.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+RSpec/MatchArray:
+  Exclude:
+    - 'spec/controllers/activitypub/followers_synchronizations_controller_spec.rb'
+    - 'spec/controllers/admin/export_domain_blocks_controller_spec.rb'
+    - 'spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb'
+    - 'spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb'
+    - 'spec/controllers/api/v1/accounts/statuses_controller_spec.rb'
+    - 'spec/controllers/api/v1/bookmarks_controller_spec.rb'
+    - 'spec/controllers/api/v1/favourites_controller_spec.rb'
+    - 'spec/controllers/api/v1/reports_controller_spec.rb'
+    - 'spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb'
+    - 'spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb'
+    - 'spec/models/account_filter_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/models/account_statuses_cleanup_policy_spec.rb'
+    - 'spec/models/custom_emoji_filter_spec.rb'
+    - 'spec/models/status_spec.rb'
+    - 'spec/models/user_spec.rb'
+    - 'spec/presenters/familiar_followers_presenter_spec.rb'
+    - 'spec/services/activitypub/fetch_featured_collection_service_spec.rb'
+    - 'spec/services/update_status_service_spec.rb'
+
+RSpec/MessageChain:
+  Exclude:
+    - 'spec/controllers/api/v1/media_controller_spec.rb'
+    - 'spec/models/concerns/remotable_spec.rb'
+    - 'spec/models/session_activation_spec.rb'
+    - 'spec/models/setting_spec.rb'
+
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: have_received, receive
+RSpec/MessageSpies:
+  Exclude:
+    - 'spec/controllers/admin/accounts_controller_spec.rb'
+    - 'spec/controllers/api/base_controller_spec.rb'
+    - 'spec/controllers/auth/registrations_controller_spec.rb'
+    - 'spec/helpers/admin/account_moderation_notes_helper_spec.rb'
+    - 'spec/helpers/application_helper_spec.rb'
+    - 'spec/lib/status_finder_spec.rb'
+    - 'spec/lib/webfinger_resource_spec.rb'
+    - 'spec/models/admin/account_action_spec.rb'
+    - 'spec/models/concerns/remotable_spec.rb'
+    - 'spec/models/follow_request_spec.rb'
+    - 'spec/models/identity_spec.rb'
+    - 'spec/models/session_activation_spec.rb'
+    - 'spec/models/setting_spec.rb'
+    - 'spec/services/activitypub/fetch_replies_service_spec.rb'
+    - 'spec/services/activitypub/process_collection_service_spec.rb'
+    - 'spec/spec_helper.rb'
+    - 'spec/validators/status_length_validator_spec.rb'
+
+RSpec/MissingExampleGroupArgument:
+  Exclude:
+    - 'spec/controllers/accounts_controller_spec.rb'
+    - 'spec/controllers/activitypub/collections_controller_spec.rb'
+    - 'spec/controllers/admin/statuses_controller_spec.rb'
+    - 'spec/controllers/admin/users/roles_controller_spec.rb'
+    - 'spec/controllers/api/v1/accounts_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/account_actions_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/domain_allows_controller_spec.rb'
+    - 'spec/controllers/api/v1/statuses_controller_spec.rb'
+    - 'spec/controllers/application_controller_spec.rb'
+    - 'spec/controllers/auth/registrations_controller_spec.rb'
+    - 'spec/features/log_in_spec.rb'
+    - 'spec/lib/activitypub/activity/undo_spec.rb'
+    - 'spec/lib/status_reach_finder_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/models/email_domain_block_spec.rb'
+    - 'spec/models/trends/statuses_spec.rb'
+    - 'spec/models/trends/tags_spec.rb'
+    - 'spec/models/user_role_spec.rb'
+    - 'spec/models/user_spec.rb'
+    - 'spec/services/fetch_link_card_service_spec.rb'
+    - 'spec/services/notify_service_spec.rb'
+    - 'spec/services/process_mentions_service_spec.rb'
+
+RSpec/MultipleExpectations:
+  Max: 19
+
+# Configuration parameters: AllowSubject.
+RSpec/MultipleMemoizedHelpers:
+  Max: 21
+
+# This cop supports safe autocorrection (--autocorrect).
+RSpec/MultipleSubjects:
+  Exclude:
+    - 'spec/controllers/activitypub/collections_controller_spec.rb'
+    - 'spec/controllers/activitypub/followers_synchronizations_controller_spec.rb'
+    - 'spec/controllers/activitypub/outboxes_controller_spec.rb'
+    - 'spec/controllers/api/web/embeds_controller_spec.rb'
+    - 'spec/controllers/emojis_controller_spec.rb'
+    - 'spec/controllers/follower_accounts_controller_spec.rb'
+    - 'spec/controllers/following_accounts_controller_spec.rb'
+
+# Configuration parameters: AllowedGroups.
+RSpec/NestedGroups:
+  Max: 6
+
+# Configuration parameters: AllowedPatterns.
+# AllowedPatterns: ^expect_, ^assert_
+RSpec/NoExpectationExample:
+  Exclude:
+    - 'spec/controllers/auth/registrations_controller_spec.rb'
+    - 'spec/services/precompute_feed_service_spec.rb'
+
+RSpec/PendingWithoutReason:
+  Exclude:
+    - 'spec/controllers/statuses_controller_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/models/user_spec.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: Strict, EnforcedStyle, AllowedExplicitMatchers.
+# SupportedStyles: inflected, explicit
+RSpec/PredicateMatcher:
+  Exclude:
+    - 'spec/controllers/api/v1/accounts/notes_controller_spec.rb'
+    - 'spec/models/user_spec.rb'
+    - 'spec/services/post_status_service_spec.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: Inferences.
+RSpec/Rails/InferredSpecType:
+  Exclude:
+    - 'spec/controllers/about_controller_spec.rb'
+    - 'spec/controllers/accounts_controller_spec.rb'
+    - 'spec/controllers/activitypub/collections_controller_spec.rb'
+    - 'spec/controllers/activitypub/followers_synchronizations_controller_spec.rb'
+    - 'spec/controllers/activitypub/inboxes_controller_spec.rb'
+    - 'spec/controllers/activitypub/outboxes_controller_spec.rb'
+    - 'spec/controllers/activitypub/replies_controller_spec.rb'
+    - 'spec/controllers/admin/account_moderation_notes_controller_spec.rb'
+    - 'spec/controllers/admin/accounts_controller_spec.rb'
+    - 'spec/controllers/admin/action_logs_controller_spec.rb'
+    - 'spec/controllers/admin/base_controller_spec.rb'
+    - 'spec/controllers/admin/change_emails_controller_spec.rb'
+    - 'spec/controllers/admin/confirmations_controller_spec.rb'
+    - 'spec/controllers/admin/dashboard_controller_spec.rb'
+    - 'spec/controllers/admin/disputes/appeals_controller_spec.rb'
+    - 'spec/controllers/admin/domain_allows_controller_spec.rb'
+    - 'spec/controllers/admin/domain_blocks_controller_spec.rb'
+    - 'spec/controllers/admin/email_domain_blocks_controller_spec.rb'
+    - 'spec/controllers/admin/export_domain_allows_controller_spec.rb'
+    - 'spec/controllers/admin/export_domain_blocks_controller_spec.rb'
+    - 'spec/controllers/admin/instances_controller_spec.rb'
+    - 'spec/controllers/admin/settings/branding_controller_spec.rb'
+    - 'spec/controllers/admin/tags_controller_spec.rb'
+    - 'spec/controllers/api/oembed_controller_spec.rb'
+    - 'spec/controllers/api/v1/accounts/pins_controller_spec.rb'
+    - 'spec/controllers/api/v1/accounts/search_controller_spec.rb'
+    - 'spec/controllers/api/v1/accounts_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/account_actions_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/accounts_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/domain_allows_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/reports_controller_spec.rb'
+    - 'spec/controllers/api/v1/announcements/reactions_controller_spec.rb'
+    - 'spec/controllers/api/v1/announcements_controller_spec.rb'
+    - 'spec/controllers/api/v1/apps_controller_spec.rb'
+    - 'spec/controllers/api/v1/blocks_controller_spec.rb'
+    - 'spec/controllers/api/v1/bookmarks_controller_spec.rb'
+    - 'spec/controllers/api/v1/conversations_controller_spec.rb'
+    - 'spec/controllers/api/v1/custom_emojis_controller_spec.rb'
+    - 'spec/controllers/api/v1/domain_blocks_controller_spec.rb'
+    - 'spec/controllers/api/v1/emails/confirmations_controller_spec.rb'
+    - 'spec/controllers/api/v1/endorsements_controller_spec.rb'
+    - 'spec/controllers/api/v1/favourites_controller_spec.rb'
+    - 'spec/controllers/api/v1/filters_controller_spec.rb'
+    - 'spec/controllers/api/v1/follow_requests_controller_spec.rb'
+    - 'spec/controllers/api/v1/followed_tags_controller_spec.rb'
+    - 'spec/controllers/api/v1/instances/activity_controller_spec.rb'
+    - 'spec/controllers/api/v1/instances/peers_controller_spec.rb'
+    - 'spec/controllers/api/v1/instances_controller_spec.rb'
+    - 'spec/controllers/api/v1/lists_controller_spec.rb'
+    - 'spec/controllers/api/v1/markers_controller_spec.rb'
+    - 'spec/controllers/api/v1/media_controller_spec.rb'
+    - 'spec/controllers/api/v1/mutes_controller_spec.rb'
+    - 'spec/controllers/api/v1/notifications_controller_spec.rb'
+    - 'spec/controllers/api/v1/polls/votes_controller_spec.rb'
+    - 'spec/controllers/api/v1/polls_controller_spec.rb'
+    - 'spec/controllers/api/v1/reports_controller_spec.rb'
+    - 'spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb'
+    - 'spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb'
+    - 'spec/controllers/api/v1/statuses_controller_spec.rb'
+    - 'spec/controllers/api/v1/suggestions_controller_spec.rb'
+    - 'spec/controllers/api/v1/tags_controller_spec.rb'
+    - 'spec/controllers/api/v1/trends/tags_controller_spec.rb'
+    - 'spec/controllers/api/v2/admin/accounts_controller_spec.rb'
+    - 'spec/controllers/api/v2/filters/keywords_controller_spec.rb'
+    - 'spec/controllers/api/v2/filters/statuses_controller_spec.rb'
+    - 'spec/controllers/api/v2/filters_controller_spec.rb'
+    - 'spec/controllers/api/v2/search_controller_spec.rb'
+    - 'spec/controllers/application_controller_spec.rb'
+    - 'spec/controllers/auth/challenges_controller_spec.rb'
+    - 'spec/controllers/auth/confirmations_controller_spec.rb'
+    - 'spec/controllers/auth/passwords_controller_spec.rb'
+    - 'spec/controllers/auth/registrations_controller_spec.rb'
+    - 'spec/controllers/auth/sessions_controller_spec.rb'
+    - 'spec/controllers/concerns/account_controller_concern_spec.rb'
+    - 'spec/controllers/concerns/cache_concern_spec.rb'
+    - 'spec/controllers/concerns/challengable_concern_spec.rb'
+    - 'spec/controllers/concerns/export_controller_concern_spec.rb'
+    - 'spec/controllers/concerns/localized_spec.rb'
+    - 'spec/controllers/concerns/signature_verification_spec.rb'
+    - 'spec/controllers/concerns/user_tracking_concern_spec.rb'
+    - 'spec/controllers/disputes/appeals_controller_spec.rb'
+    - 'spec/controllers/disputes/strikes_controller_spec.rb'
+    - 'spec/controllers/home_controller_spec.rb'
+    - 'spec/controllers/instance_actors_controller_spec.rb'
+    - 'spec/controllers/intents_controller_spec.rb'
+    - 'spec/controllers/oauth/authorizations_controller_spec.rb'
+    - 'spec/controllers/oauth/tokens_controller_spec.rb'
+    - 'spec/controllers/settings/imports_controller_spec.rb'
+    - 'spec/controllers/settings/profiles_controller_spec.rb'
+    - 'spec/controllers/statuses_cleanup_controller_spec.rb'
+    - 'spec/controllers/tags_controller_spec.rb'
+    - 'spec/controllers/well_known/host_meta_controller_spec.rb'
+    - 'spec/controllers/well_known/nodeinfo_controller_spec.rb'
+    - 'spec/controllers/well_known/webfinger_controller_spec.rb'
+    - 'spec/helpers/accounts_helper_spec.rb'
+    - 'spec/helpers/admin/account_moderation_notes_helper_spec.rb'
+    - 'spec/helpers/admin/action_logs_helper_spec.rb'
+    - 'spec/helpers/flashes_helper_spec.rb'
+    - 'spec/helpers/formatting_helper_spec.rb'
+    - 'spec/helpers/home_helper_spec.rb'
+    - 'spec/helpers/routing_helper_spec.rb'
+    - 'spec/mailers/admin_mailer_spec.rb'
+    - 'spec/mailers/notification_mailer_spec.rb'
+    - 'spec/mailers/user_mailer_spec.rb'
+    - 'spec/models/account/field_spec.rb'
+    - 'spec/models/account_alias_spec.rb'
+    - 'spec/models/account_conversation_spec.rb'
+    - 'spec/models/account_deletion_request_spec.rb'
+    - 'spec/models/account_domain_block_spec.rb'
+    - 'spec/models/account_migration_spec.rb'
+    - 'spec/models/account_moderation_note_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/models/account_statuses_cleanup_policy_spec.rb'
+    - 'spec/models/admin/account_action_spec.rb'
+    - 'spec/models/admin/action_log_spec.rb'
+    - 'spec/models/announcement_mute_spec.rb'
+    - 'spec/models/announcement_reaction_spec.rb'
+    - 'spec/models/announcement_spec.rb'
+    - 'spec/models/backup_spec.rb'
+    - 'spec/models/block_spec.rb'
+    - 'spec/models/canonical_email_block_spec.rb'
+    - 'spec/models/conversation_mute_spec.rb'
+    - 'spec/models/conversation_spec.rb'
+    - 'spec/models/custom_emoji_spec.rb'
+    - 'spec/models/custom_filter_keyword_spec.rb'
+    - 'spec/models/custom_filter_spec.rb'
+    - 'spec/models/device_spec.rb'
+    - 'spec/models/domain_block_spec.rb'
+    - 'spec/models/email_domain_block_spec.rb'
+    - 'spec/models/encrypted_message_spec.rb'
+    - 'spec/models/favourite_spec.rb'
+    - 'spec/models/featured_tag_spec.rb'
+    - 'spec/models/follow_recommendation_suppression_spec.rb'
+    - 'spec/models/follow_request_spec.rb'
+    - 'spec/models/follow_spec.rb'
+    - 'spec/models/home_feed_spec.rb'
+    - 'spec/models/identity_spec.rb'
+    - 'spec/models/import_spec.rb'
+    - 'spec/models/invite_spec.rb'
+    - 'spec/models/list_account_spec.rb'
+    - 'spec/models/list_spec.rb'
+    - 'spec/models/login_activity_spec.rb'
+    - 'spec/models/media_attachment_spec.rb'
+    - 'spec/models/mention_spec.rb'
+    - 'spec/models/mute_spec.rb'
+    - 'spec/models/notification_spec.rb'
+    - 'spec/models/poll_vote_spec.rb'
+    - 'spec/models/preview_card_spec.rb'
+    - 'spec/models/preview_card_trend_spec.rb'
+    - 'spec/models/public_feed_spec.rb'
+    - 'spec/models/relay_spec.rb'
+    - 'spec/models/scheduled_status_spec.rb'
+    - 'spec/models/session_activation_spec.rb'
+    - 'spec/models/setting_spec.rb'
+    - 'spec/models/site_upload_spec.rb'
+    - 'spec/models/status_pin_spec.rb'
+    - 'spec/models/status_spec.rb'
+    - 'spec/models/status_stat_spec.rb'
+    - 'spec/models/status_trend_spec.rb'
+    - 'spec/models/system_key_spec.rb'
+    - 'spec/models/tag_follow_spec.rb'
+    - 'spec/models/unavailable_domain_spec.rb'
+    - 'spec/models/user_invite_request_spec.rb'
+    - 'spec/models/user_role_spec.rb'
+    - 'spec/models/user_spec.rb'
+    - 'spec/models/web/push_subscription_spec.rb'
+    - 'spec/models/web/setting_spec.rb'
+    - 'spec/models/webauthn_credentials_spec.rb'
+    - 'spec/models/webhook_spec.rb'
+
+RSpec/RepeatedExample:
+  Exclude:
+    - 'spec/policies/status_policy_spec.rb'
+
+RSpec/RepeatedExampleGroupBody:
+  Exclude:
+    - 'spec/controllers/statuses_controller_spec.rb'
+
+RSpec/RepeatedExampleGroupDescription:
+  Exclude:
+    - 'spec/controllers/admin/reports/actions_controller_spec.rb'
+    - 'spec/policies/report_note_policy_spec.rb'
+
+RSpec/ScatteredSetup:
+  Exclude:
+    - 'spec/controllers/activitypub/followers_synchronizations_controller_spec.rb'
+    - 'spec/controllers/activitypub/outboxes_controller_spec.rb'
+    - 'spec/controllers/admin/disputes/appeals_controller_spec.rb'
+    - 'spec/controllers/auth/registrations_controller_spec.rb'
+    - 'spec/services/activitypub/process_account_service_spec.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+RSpec/SharedContext:
+  Exclude:
+    - 'spec/services/unsuspend_account_service_spec.rb'
+
+RSpec/StubbedMock:
+  Exclude:
+    - 'spec/controllers/api/base_controller_spec.rb'
+    - 'spec/controllers/api/v1/media_controller_spec.rb'
+    - 'spec/controllers/auth/registrations_controller_spec.rb'
+    - 'spec/helpers/application_helper_spec.rb'
+    - 'spec/lib/status_filter_spec.rb'
+    - 'spec/lib/status_finder_spec.rb'
+    - 'spec/lib/webfinger_resource_spec.rb'
+    - 'spec/services/activitypub/process_collection_service_spec.rb'
+
+RSpec/SubjectDeclaration:
+  Exclude:
+    - 'spec/controllers/admin/domain_blocks_controller_spec.rb'
+    - 'spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb'
+    - 'spec/models/account_migration_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/models/relationship_filter_spec.rb'
+    - 'spec/models/user_role_spec.rb'
+    - 'spec/policies/account_moderation_note_policy_spec.rb'
+    - 'spec/policies/account_policy_spec.rb'
+    - 'spec/policies/backup_policy_spec.rb'
+    - 'spec/policies/custom_emoji_policy_spec.rb'
+    - 'spec/policies/domain_block_policy_spec.rb'
+    - 'spec/policies/email_domain_block_policy_spec.rb'
+    - 'spec/policies/instance_policy_spec.rb'
+    - 'spec/policies/invite_policy_spec.rb'
+    - 'spec/policies/relay_policy_spec.rb'
+    - 'spec/policies/report_note_policy_spec.rb'
+    - 'spec/policies/report_policy_spec.rb'
+    - 'spec/policies/settings_policy_spec.rb'
+    - 'spec/policies/tag_policy_spec.rb'
+    - 'spec/policies/user_policy_spec.rb'
+    - 'spec/services/activitypub/process_account_service_spec.rb'
+
+RSpec/SubjectStub:
+  Exclude:
+    - 'spec/services/unallow_domain_service_spec.rb'
+    - 'spec/validators/blacklisted_email_validator_spec.rb'
+
+# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
+RSpec/VerifiedDoubles:
+  Exclude:
+    - 'spec/controllers/admin/change_emails_controller_spec.rb'
+    - 'spec/controllers/admin/confirmations_controller_spec.rb'
+    - 'spec/controllers/admin/disputes/appeals_controller_spec.rb'
+    - 'spec/controllers/admin/domain_allows_controller_spec.rb'
+    - 'spec/controllers/admin/domain_blocks_controller_spec.rb'
+    - 'spec/controllers/api/v1/reports_controller_spec.rb'
+    - 'spec/controllers/api/web/embeds_controller_spec.rb'
+    - 'spec/controllers/auth/sessions_controller_spec.rb'
+    - 'spec/controllers/disputes/appeals_controller_spec.rb'
+    - 'spec/controllers/settings/imports_controller_spec.rb'
+    - 'spec/helpers/statuses_helper_spec.rb'
+    - 'spec/lib/suspicious_sign_in_detector_spec.rb'
+    - 'spec/models/account/field_spec.rb'
+    - 'spec/models/session_activation_spec.rb'
+    - 'spec/models/setting_spec.rb'
+    - 'spec/services/account_search_service_spec.rb'
+    - 'spec/services/post_status_service_spec.rb'
+    - 'spec/services/search_service_spec.rb'
+    - 'spec/validators/blacklisted_email_validator_spec.rb'
+    - 'spec/validators/disallowed_hashtags_validator_spec.rb'
+    - 'spec/validators/email_mx_validator_spec.rb'
+    - 'spec/validators/follow_limit_validator_spec.rb'
+    - 'spec/validators/note_length_validator_spec.rb'
+    - 'spec/validators/poll_validator_spec.rb'
+    - 'spec/validators/status_length_validator_spec.rb'
+    - 'spec/validators/status_pin_validator_spec.rb'
+    - 'spec/validators/unique_username_validator_spec.rb'
+    - 'spec/validators/unreserved_username_validator_spec.rb'
+    - 'spec/validators/url_validator_spec.rb'
+    - 'spec/views/statuses/show.html.haml_spec.rb'
+    - 'spec/workers/activitypub/processing_worker_spec.rb'
+    - 'spec/workers/admin/domain_purge_worker_spec.rb'
+    - 'spec/workers/domain_block_worker_spec.rb'
+    - 'spec/workers/domain_clear_media_worker_spec.rb'
+    - 'spec/workers/feed_insert_worker_spec.rb'
+    - 'spec/workers/regeneration_worker_spec.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: ExpectedOrder, Include.
+# ExpectedOrder: index, show, new, edit, create, update, destroy
+# Include: app/controllers/**/*.rb
+Rails/ActionOrder:
+  Exclude:
+    - 'app/controllers/admin/announcements_controller.rb'
+    - 'app/controllers/admin/roles_controller.rb'
+    - 'app/controllers/admin/rules_controller.rb'
+    - 'app/controllers/admin/warning_presets_controller.rb'
+    - 'app/controllers/admin/webhooks_controller.rb'
+    - 'app/controllers/api/v1/admin/domain_allows_controller.rb'
+    - 'app/controllers/api/v1/admin/domain_blocks_controller.rb'
+    - 'app/controllers/api/v1/admin/email_domain_blocks_controller.rb'
+    - 'app/controllers/api/v1/admin/ip_blocks_controller.rb'
+    - 'app/controllers/api/v1/filters_controller.rb'
+    - 'app/controllers/api/v1/media_controller.rb'
+    - 'app/controllers/api/v1/push/subscriptions_controller.rb'
+    - 'app/controllers/api/v2/filters/keywords_controller.rb'
+    - 'app/controllers/api/v2/filters/statuses_controller.rb'
+    - 'app/controllers/api/v2/filters_controller.rb'
+    - 'app/controllers/auth/registrations_controller.rb'
+    - 'app/controllers/filters_controller.rb'
+    - 'app/controllers/settings/applications_controller.rb'
+    - 'app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Include.
+# Include: app/models/**/*.rb
+Rails/ActiveRecordCallbacksOrder:
+  Exclude:
+    - 'app/models/account.rb'
+    - 'app/models/account_conversation.rb'
+    - 'app/models/announcement_reaction.rb'
+    - 'app/models/block.rb'
+    - 'app/models/media_attachment.rb'
+    - 'app/models/session_activation.rb'
+    - 'app/models/status.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Rails/ApplicationController:
+  Exclude:
+    - 'app/controllers/health_controller.rb'
+    - 'app/controllers/well_known/host_meta_controller.rb'
+    - 'app/controllers/well_known/nodeinfo_controller.rb'
+    - 'app/controllers/well_known/webfinger_controller.rb'
+
+# Configuration parameters: Database, Include.
+# SupportedDatabases: mysql, postgresql
+# Include: db/migrate/*.rb
+Rails/BulkChangeTable:
+  Exclude:
+    - 'db/migrate/20160222143943_add_profile_fields_to_accounts.rb'
+    - 'db/migrate/20160223162837_add_metadata_to_statuses.rb'
+    - 'db/migrate/20160305115639_add_devise_to_users.rb'
+    - 'db/migrate/20160314164231_add_owner_to_application.rb'
+    - 'db/migrate/20160926213048_remove_owner_from_application.rb'
+    - 'db/migrate/20161003142332_add_confirmable_to_users.rb'
+    - 'db/migrate/20170112154826_migrate_settings.rb'
+    - 'db/migrate/20170127165745_add_devise_two_factor_to_users.rb'
+    - 'db/migrate/20170322143850_change_primary_key_to_bigint_on_statuses.rb'
+    - 'db/migrate/20170330021336_add_counter_caches.rb'
+    - 'db/migrate/20170425202925_add_oembed_to_preview_cards.rb'
+    - 'db/migrate/20170427011934_re_add_owner_to_application.rb'
+    - 'db/migrate/20170520145338_change_language_filter_to_opt_out.rb'
+    - 'db/migrate/20170624134742_add_description_to_session_activations.rb'
+    - 'db/migrate/20170718211102_add_activitypub_to_accounts.rb'
+    - 'db/migrate/20171006142024_add_uri_to_custom_emojis.rb'
+    - 'db/migrate/20180812123222_change_relays_enabled.rb'
+    - 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb'
+    - 'db/migrate/20190805123746_add_capabilities_to_tags.rb'
+    - 'db/migrate/20190807135426_add_comments_to_domain_blocks.rb'
+    - 'db/migrate/20190815225426_add_last_status_at_to_tags.rb'
+    - 'db/migrate/20190901035623_add_max_score_to_tags.rb'
+    - 'db/migrate/20200417125749_add_storage_schema_version.rb'
+    - 'db/migrate/20200608113046_add_sign_in_token_to_users.rb'
+    - 'db/migrate/20211112011713_add_language_to_preview_cards.rb'
+    - 'db/migrate/20211231080958_add_category_to_reports.rb'
+    - 'db/migrate/20220202200743_add_trendable_to_accounts.rb'
+    - 'db/migrate/20220224010024_add_ips_to_email_domain_blocks.rb'
+    - 'db/migrate/20220227041951_add_last_used_at_to_oauth_access_tokens.rb'
+    - 'db/migrate/20220303000827_add_ordered_media_attachment_ids_to_status_edits.rb'
+    - 'db/migrate/20220824164433_add_human_identifier_to_admin_action_logs.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Rails/CompactBlank:
+  Exclude:
+    - 'app/helpers/application_helper.rb'
+    - 'app/helpers/statuses_helper.rb'
+    - 'app/models/concerns/attachmentable.rb'
+    - 'app/models/poll.rb'
+    - 'app/services/import_service.rb'
+    - 'config/initializers/paperclip.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+Rails/ContentTag:
+  Exclude:
+    - 'app/helpers/application_helper.rb'
+    - 'app/helpers/branding_helper.rb'
+
+# Configuration parameters: Include.
+# Include: db/migrate/*.rb
+Rails/CreateTableWithTimestamps:
+  Exclude:
+    - 'db/migrate/20170508230434_create_conversation_mutes.rb'
+    - 'db/migrate/20170823162448_create_status_pins.rb'
+    - 'db/migrate/20171116161857_create_list_accounts.rb'
+    - 'db/migrate/20180929222014_create_account_conversations.rb'
+    - 'db/migrate/20181007025445_create_pghero_space_stats.rb'
+    - 'db/migrate/20190103124649_create_scheduled_statuses.rb'
+    - 'db/migrate/20220824233535_create_status_trends.rb'
+    - 'db/migrate/20221006061337_create_preview_card_trends.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: Severity.
+Rails/DeprecatedActiveModelErrorsMethods:
+  Exclude:
+    - 'lib/mastodon/accounts_cli.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Severity.
+Rails/DuplicateAssociation:
+  Exclude:
+    - 'app/serializers/activitypub/collection_serializer.rb'
+    - 'app/serializers/activitypub/note_serializer.rb'
+
+# Configuration parameters: Include.
+# Include: app/**/*.rb, config/**/*.rb, lib/**/*.rb
+Rails/Exit:
+  Exclude:
+    - 'config/boot.rb'
+
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: slashes, arguments
+Rails/FilePath:
+  Exclude:
+    - 'app/lib/themes.rb'
+    - 'app/models/setting.rb'
+    - 'app/validators/reaction_validator.rb'
+    - 'config/environments/test.rb'
+    - 'config/initializers/locale.rb'
+    - 'db/migrate/20170716191202_add_hide_notifications_to_mute.rb'
+    - 'db/migrate/20171005171936_add_disabled_to_custom_emojis.rb'
+    - 'db/migrate/20171028221157_add_reblogs_to_follows.rb'
+    - 'db/migrate/20171107143332_add_memorial_to_accounts.rb'
+    - 'db/migrate/20171107143624_add_disabled_to_users.rb'
+    - 'db/migrate/20171109012327_add_moderator_to_accounts.rb'
+    - 'db/migrate/20171130000000_add_embed_url_to_preview_cards.rb'
+    - 'db/migrate/20180615122121_add_autofollow_to_invites.rb'
+    - 'db/migrate/20180707154237_add_whole_word_to_custom_filter.rb'
+    - 'db/migrate/20180814171349_add_confidential_to_doorkeeper_application.rb'
+    - 'db/migrate/20181010141500_add_silent_to_mentions.rb'
+    - 'db/migrate/20181017170937_add_reject_reports_to_domain_blocks.rb'
+    - 'db/migrate/20181018205649_add_unread_to_account_conversations.rb'
+    - 'db/migrate/20181127130500_identity_id_to_bigint.rb'
+    - 'db/migrate/20181127165847_add_show_replies_to_lists.rb'
+    - 'db/migrate/20190201012802_add_overwrite_to_imports.rb'
+    - 'db/migrate/20190306145741_add_lock_version_to_polls.rb'
+    - 'db/migrate/20190307234537_add_approved_to_users.rb'
+    - 'db/migrate/20191001213028_add_lock_version_to_account_stats.rb'
+    - 'db/migrate/20191212003415_increase_backup_size.rb'
+    - 'db/migrate/20200312144258_add_title_to_account_warning_presets.rb'
+    - 'db/migrate/20200620164023_add_fixed_lowercase_index_to_accounts.rb'
+    - 'db/migrate/20200917192924_add_notify_to_follows.rb'
+    - 'db/migrate/20201218054746_add_obfuscate_to_domain_blocks.rb'
+    - 'db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb'
+    - 'db/migrate/20211231080958_add_category_to_reports.rb'
+    - 'db/migrate/20220613110834_add_action_to_custom_filters.rb'
+    - 'db/post_migrate/20220307083603_optimize_null_index_conversations_uri.rb'
+    - 'db/post_migrate/20220310060545_optimize_null_index_statuses_in_reply_to_account_id.rb'
+    - 'db/post_migrate/20220310060556_optimize_null_index_statuses_in_reply_to_id.rb'
+    - 'db/post_migrate/20220310060614_optimize_null_index_media_attachments_scheduled_status_id.rb'
+    - 'db/post_migrate/20220310060626_optimize_null_index_media_attachments_shortcode.rb'
+    - 'db/post_migrate/20220310060641_optimize_null_index_users_reset_password_token.rb'
+    - 'db/post_migrate/20220310060653_optimize_null_index_users_created_by_application_id.rb'
+    - 'db/post_migrate/20220310060706_optimize_null_index_statuses_uri.rb'
+    - 'db/post_migrate/20220310060722_optimize_null_index_accounts_moved_to_account_id.rb'
+    - 'db/post_migrate/20220310060740_optimize_null_index_oauth_access_tokens_refresh_token.rb'
+    - 'db/post_migrate/20220310060750_optimize_null_index_accounts_url.rb'
+    - 'db/post_migrate/20220310060809_optimize_null_index_oauth_access_tokens_resource_owner_id.rb'
+    - 'db/post_migrate/20220310060833_optimize_null_index_announcement_reactions_custom_emoji_id.rb'
+    - 'db/post_migrate/20220310060854_optimize_null_index_appeals_approved_by_account_id.rb'
+    - 'db/post_migrate/20220310060913_optimize_null_index_account_migrations_target_account_id.rb'
+    - 'db/post_migrate/20220310060926_optimize_null_index_appeals_rejected_by_account_id.rb'
+    - 'db/post_migrate/20220310060939_optimize_null_index_list_accounts_follow_id.rb'
+    - 'db/post_migrate/20220310060959_optimize_null_index_web_push_subscriptions_access_token_id.rb'
+    - 'db/post_migrate/20220613110802_remove_whole_word_from_custom_filters.rb'
+    - 'db/post_migrate/20220613110903_remove_irreversible_from_custom_filters.rb'
+    - 'db/post_migrate/20220617202502_migrate_roles.rb'
+    - 'db/seeds.rb'
+    - 'db/seeds/03_roles.rb'
+    - 'lib/tasks/branding.rake'
+    - 'lib/tasks/emojis.rake'
+    - 'lib/tasks/repo.rake'
+    - 'spec/controllers/admin/custom_emojis_controller_spec.rb'
+    - 'spec/fabricators/custom_emoji_fabricator.rb'
+    - 'spec/fabricators/site_upload_fabricator.rb'
+    - 'spec/rails_helper.rb'
+    - 'spec/spec_helper.rb'
+
+# Configuration parameters: Include.
+# Include: app/models/**/*.rb
+Rails/HasAndBelongsToMany:
+  Exclude:
+    - 'app/models/concerns/account_associations.rb'
+    - 'app/models/preview_card.rb'
+    - 'app/models/status.rb'
+    - 'app/models/tag.rb'
+
+# Configuration parameters: Include.
+# Include: app/models/**/*.rb
+Rails/HasManyOrHasOneDependent:
+  Exclude:
+    - 'app/models/concerns/account_counters.rb'
+    - 'app/models/conversation.rb'
+    - 'app/models/custom_emoji.rb'
+    - 'app/models/custom_emoji_category.rb'
+    - 'app/models/domain_block.rb'
+    - 'app/models/invite.rb'
+    - 'app/models/status.rb'
+    - 'app/models/user.rb'
+    - 'app/models/web/push_subscription.rb'
+
+# Configuration parameters: Include.
+# Include: app/helpers/**/*.rb
+Rails/HelperInstanceVariable:
+  Exclude:
+    - 'app/helpers/application_helper.rb'
+    - 'app/helpers/instance_helper.rb'
+    - 'app/helpers/jsonld_helper.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: Include.
+# Include: spec/**/*, test/**/*
+Rails/HttpPositionalArguments:
+  Exclude:
+    - 'spec/config/initializers/rack_attack_spec.rb'
+
+# Configuration parameters: Include.
+# Include: spec/**/*.rb, test/**/*.rb
+Rails/I18nLocaleAssignment:
+  Exclude:
+    - 'spec/controllers/auth/registrations_controller_spec.rb'
+    - 'spec/helpers/application_helper_spec.rb'
+    - 'spec/requests/localization_spec.rb'
+
+Rails/I18nLocaleTexts:
+  Exclude:
+    - 'lib/tasks/mastodon.rake'
+    - 'spec/helpers/flashes_helper_spec.rb'
+
+# Configuration parameters: IgnoreScopes, Include.
+# Include: app/models/**/*.rb
+Rails/InverseOf:
+  Exclude:
+    - 'app/models/appeal.rb'
+    - 'app/models/concerns/account_interactions.rb'
+    - 'app/models/custom_emoji.rb'
+    - 'app/models/domain_block.rb'
+    - 'app/models/follow_recommendation.rb'
+    - 'app/models/instance.rb'
+    - 'app/models/notification.rb'
+    - 'app/models/status.rb'
+
+# Configuration parameters: Include.
+# Include: app/controllers/**/*.rb, app/mailers/**/*.rb
+Rails/LexicallyScopedActionFilter:
+  Exclude:
+    - 'app/controllers/auth/passwords_controller.rb'
+    - 'app/controllers/auth/registrations_controller.rb'
+    - 'app/controllers/auth/sessions_controller.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Rails/NegateInclude:
+  Exclude:
+    - 'app/controllers/concerns/signature_verification.rb'
+    - 'app/helpers/jsonld_helper.rb'
+    - 'app/lib/activitypub/activity/create.rb'
+    - 'app/lib/activitypub/activity/move.rb'
+    - 'app/lib/feed_manager.rb'
+    - 'app/lib/link_details_extractor.rb'
+    - 'app/models/concerns/attachmentable.rb'
+    - 'app/models/concerns/remotable.rb'
+    - 'app/models/custom_filter.rb'
+    - 'app/models/webhook.rb'
+    - 'app/services/activitypub/process_status_update_service.rb'
+    - 'app/services/fetch_link_card_service.rb'
+    - 'app/services/search_service.rb'
+    - 'app/workers/web/push_notification_worker.rb'
+    - 'lib/paperclip/color_extractor.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: Include.
+# Include: app/**/*.rb, config/**/*.rb, db/**/*.rb, lib/**/*.rb
+Rails/Output:
+  Exclude:
+    - 'lib/mastodon/ip_blocks_cli.rb'
+
+Rails/OutputSafety:
+  Exclude:
+    - 'config/initializers/simple_form.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: NotNilAndNotEmpty, NotBlank, UnlessBlank.
+Rails/Present:
+  Exclude:
+    - 'config/initializers/content_security_policy.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: Include.
+# Include: **/Rakefile, **/*.rake
+Rails/RakeEnvironment:
+  Exclude:
+    - 'lib/tasks/auto_annotate_models.rake'
+    - 'lib/tasks/db.rake'
+    - 'lib/tasks/emojis.rake'
+    - 'lib/tasks/mastodon.rake'
+    - 'lib/tasks/repo.rake'
+    - 'lib/tasks/statistics.rake'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: Include.
+# Include: spec/controllers/**/*.rb, spec/requests/**/*.rb, test/controllers/**/*.rb, test/integration/**/*.rb
+Rails/ResponseParsedBody:
+  Exclude:
+    - 'spec/controllers/follower_accounts_controller_spec.rb'
+    - 'spec/controllers/following_accounts_controller_spec.rb'
+    - 'spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb'
+
+# Configuration parameters: Include.
+# Include: db/**/*.rb
+Rails/ReversibleMigration:
+  Exclude:
+    - 'db/migrate/20160223164502_make_uris_nullable_in_statuses.rb'
+    - 'db/migrate/20161122163057_remove_unneeded_indexes.rb'
+    - 'db/migrate/20170205175257_remove_devices.rb'
+    - 'db/migrate/20170322143850_change_primary_key_to_bigint_on_statuses.rb'
+    - 'db/migrate/20170520145338_change_language_filter_to_opt_out.rb'
+    - 'db/migrate/20170609145826_remove_default_language_from_statuses.rb'
+    - 'db/migrate/20170711225116_fix_null_booleans.rb'
+    - 'db/migrate/20171129172043_add_index_on_stream_entries.rb'
+    - 'db/migrate/20171212195226_remove_duplicate_indexes_in_lists.rb'
+    - 'db/migrate/20171226094803_more_faster_index_on_notifications.rb'
+    - 'db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb'
+    - 'db/migrate/20180617162849_remove_unused_indexes.rb'
+    - 'db/migrate/20220827195229_change_canonical_email_blocks_nullable.rb'
+
+# Configuration parameters: ForbiddenMethods, AllowedMethods.
+# ForbiddenMethods: decrement!, decrement_counter, increment!, increment_counter, insert, insert!, insert_all, insert_all!, toggle!, touch, touch_all, update_all, update_attribute, update_column, update_columns, update_counters, upsert, upsert_all
+Rails/SkipsModelValidations:
+  Exclude:
+    - 'app/controllers/admin/invites_controller.rb'
+    - 'app/controllers/concerns/session_tracking_concern.rb'
+    - 'app/models/concerns/account_merging.rb'
+    - 'app/models/concerns/expireable.rb'
+    - 'app/models/status.rb'
+    - 'app/models/trends/links.rb'
+    - 'app/models/trends/preview_card_batch.rb'
+    - 'app/models/trends/preview_card_provider_batch.rb'
+    - 'app/models/trends/status_batch.rb'
+    - 'app/models/trends/statuses.rb'
+    - 'app/models/trends/tag_batch.rb'
+    - 'app/models/trends/tags.rb'
+    - 'app/models/user.rb'
+    - 'app/services/activitypub/process_status_update_service.rb'
+    - 'app/services/approve_appeal_service.rb'
+    - 'app/services/block_domain_service.rb'
+    - 'app/services/delete_account_service.rb'
+    - 'app/services/process_mentions_service.rb'
+    - 'app/services/unallow_domain_service.rb'
+    - 'app/services/unblock_domain_service.rb'
+    - 'app/services/update_status_service.rb'
+    - 'app/workers/activitypub/post_upgrade_worker.rb'
+    - 'app/workers/move_worker.rb'
+    - 'app/workers/scheduler/ip_cleanup_scheduler.rb'
+    - 'app/workers/scheduler/scheduled_statuses_scheduler.rb'
+    - 'db/migrate/20161203164520_add_from_account_id_to_notifications.rb'
+    - 'db/migrate/20170105224407_add_shortcode_to_media_attachments.rb'
+    - 'db/migrate/20170209184350_add_reply_to_statuses.rb'
+    - 'db/migrate/20170304202101_add_type_to_media_attachments.rb'
+    - 'db/migrate/20180528141303_fix_accounts_unique_index.rb'
+    - 'db/migrate/20180609104432_migrate_web_push_subscriptions2.rb'
+    - 'db/migrate/20181207011115_downcase_custom_emoji_domains.rb'
+    - 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb'
+    - 'db/migrate/20191007013357_update_pt_locales.rb'
+    - 'db/migrate/20220316233212_update_kurdish_locales.rb'
+    - 'db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb'
+    - 'db/post_migrate/20200917193528_migrate_notifications_type.rb'
+    - 'db/post_migrate/20201017234926_fill_account_suspension_origin.rb'
+    - 'db/post_migrate/20220617202502_migrate_roles.rb'
+    - 'db/post_migrate/20221101190723_backfill_admin_action_logs.rb'
+    - 'db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb'
+    - 'lib/cli.rb'
+    - 'lib/mastodon/accounts_cli.rb'
+    - 'lib/mastodon/maintenance_cli.rb'
+    - 'spec/controllers/api/v1/admin/accounts_controller_spec.rb'
+    - 'spec/lib/activitypub/activity/follow_spec.rb'
+    - 'spec/services/follow_service_spec.rb'
+    - 'spec/services/update_account_service_spec.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Rails/SquishedSQLHeredocs:
+  Exclude:
+    - 'db/migrate/20170920024819_status_ids_to_timestamp_ids.rb'
+    - 'db/migrate/20180608213548_reject_following_blocked_users.rb'
+    - 'db/post_migrate/20190519130537_remove_boosts_widening_audience.rb'
+    - 'lib/mastodon/snowflake.rb'
+    - 'lib/tasks/tests.rake'
+
+Rails/TransactionExitStatement:
+  Exclude:
+    - 'app/lib/activitypub/activity/announce.rb'
+    - 'app/lib/activitypub/activity/create.rb'
+    - 'app/lib/activitypub/activity/delete.rb'
+    - 'app/services/activitypub/process_account_service.rb'
+
+# Configuration parameters: Include.
+# Include: app/models/**/*.rb
+Rails/UniqueValidationWithoutIndex:
+  Exclude:
+    - 'app/models/account_alias.rb'
+    - 'app/models/custom_filter_status.rb'
+    - 'app/models/identity.rb'
+    - 'app/models/webauthn_credential.rb'
+
+# Configuration parameters: Include.
+# Include: app/models/**/*.rb
+Rails/UnusedIgnoredColumns:
+  Exclude:
+    - 'app/models/account.rb'
+    - 'app/models/account_stat.rb'
+    - 'app/models/admin/action_log.rb'
+    - 'app/models/custom_filter.rb'
+    - 'app/models/email_domain_block.rb'
+    - 'app/models/report.rb'
+    - 'app/models/status_edit.rb'
+    - 'app/models/user.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: exists, where
+Rails/WhereExists:
+  Exclude:
+    - 'app/controllers/activitypub/inboxes_controller.rb'
+    - 'app/controllers/admin/email_domain_blocks_controller.rb'
+    - 'app/controllers/auth/registrations_controller.rb'
+    - 'app/lib/activitypub/activity/create.rb'
+    - 'app/lib/delivery_failure_tracker.rb'
+    - 'app/lib/feed_manager.rb'
+    - 'app/lib/status_cache_hydrator.rb'
+    - 'app/lib/suspicious_sign_in_detector.rb'
+    - 'app/models/concerns/account_interactions.rb'
+    - 'app/models/featured_tag.rb'
+    - 'app/models/poll.rb'
+    - 'app/models/session_activation.rb'
+    - 'app/models/status.rb'
+    - 'app/models/user.rb'
+    - 'app/policies/status_policy.rb'
+    - 'app/serializers/rest/announcement_serializer.rb'
+    - 'app/serializers/rest/tag_serializer.rb'
+    - 'app/services/activitypub/fetch_remote_status_service.rb'
+    - 'app/services/app_sign_up_service.rb'
+    - 'app/services/vote_service.rb'
+    - 'app/validators/reaction_validator.rb'
+    - 'app/validators/vote_validator.rb'
+    - 'app/workers/move_worker.rb'
+    - 'db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb'
+    - 'lib/mastodon/email_domain_blocks_cli.rb'
+    - 'lib/tasks/tests.rake'
+    - 'spec/controllers/api/v1/accounts/notes_controller_spec.rb'
+    - 'spec/controllers/api/v1/tags_controller_spec.rb'
+    - 'spec/models/account_spec.rb'
+    - 'spec/services/activitypub/process_collection_service_spec.rb'
+    - 'spec/services/post_status_service_spec.rb'
+    - 'spec/services/purge_domain_service_spec.rb'
+    - 'spec/services/unallow_domain_service_spec.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowOnConstant, AllowOnSelfClass.
+Style/CaseEquality:
+  Exclude:
+    - 'config/initializers/trusted_proxies.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: MinBranchesCount.
+Style/CaseLikeIf:
+  Exclude:
+    - 'app/controllers/concerns/signature_verification.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowedMethods, AllowedPatterns.
+# AllowedMethods: ==, equal?, eql?
+Style/ClassEqualityComparison:
+  Exclude:
+    - 'app/helpers/jsonld_helper.rb'
+    - 'app/serializers/activitypub/outbox_serializer.rb'
+
+Style/ClassVars:
+  Exclude:
+    - 'config/initializers/devise.rb'
+
+Style/CombinableLoops:
+  Exclude:
+    - 'app/models/form/custom_emoji_batch.rb'
+    - 'app/models/form/ip_block_batch.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/ConcatArrayLiterals:
+  Exclude:
+    - 'app/lib/feed_manager.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowedVars.
+Style/FetchEnvVar:
+  Exclude:
+    - 'app/helpers/application_helper.rb'
+    - 'app/lib/redis_configuration.rb'
+    - 'app/lib/translation_service.rb'
+    - 'config/environments/development.rb'
+    - 'config/environments/production.rb'
+    - 'config/initializers/2_whitelist_mode.rb'
+    - 'config/initializers/blacklists.rb'
+    - 'config/initializers/cache_buster.rb'
+    - 'config/initializers/content_security_policy.rb'
+    - 'config/initializers/devise.rb'
+    - 'config/initializers/omniauth.rb'
+    - 'config/initializers/paperclip.rb'
+    - 'config/initializers/vapid.rb'
+    - 'lib/mastodon/premailer_webpack_strategy.rb'
+    - 'lib/mastodon/redis_config.rb'
+    - 'lib/tasks/repo.rake'
+    - 'spec/features/profile_spec.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, AllowedMethods, AllowedPatterns.
+# SupportedStyles: annotated, template, unannotated
+# AllowedMethods: redirect
+Style/FormatStringToken:
+  Exclude:
+    - 'app/models/privacy_policy.rb'
+    - 'config/initializers/devise.rb'
+    - 'lib/mastodon/maintenance_cli.rb'
+    - 'lib/paperclip/color_extractor.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: always, always_true, never
+Style/FrozenStringLiteralComment:
+  Exclude:
+    - 'app/views/accounts/show.rss.ruby'
+    - 'app/views/tags/show.rss.ruby'
+    - 'app/views/well_known/host_meta/show.xml.ruby'
+    - 'config/application.rb'
+    - 'config/boot.rb'
+    - 'config/environment.rb'
+    - 'config/environments/development.rb'
+    - 'config/environments/production.rb'
+    - 'config/environments/test.rb'
+    - 'config/initializers/0_post_deployment_migrations.rb'
+    - 'config/initializers/active_model_serializers.rb'
+    - 'config/initializers/application_controller_renderer.rb'
+    - 'config/initializers/assets.rb'
+    - 'config/initializers/backtrace_silencers.rb'
+    - 'config/initializers/cache_logging.rb'
+    - 'config/initializers/chewy.rb'
+    - 'config/initializers/content_security_policy.rb'
+    - 'config/initializers/cookies_serializer.rb'
+    - 'config/initializers/cors.rb'
+    - 'config/initializers/devise.rb'
+    - 'config/initializers/doorkeeper.rb'
+    - 'config/initializers/fast_blank.rb'
+    - 'config/initializers/ffmpeg.rb'
+    - 'config/initializers/filter_parameter_logging.rb'
+    - 'config/initializers/http_client_proxy.rb'
+    - 'config/initializers/httplog.rb'
+    - 'config/initializers/inflections.rb'
+    - 'config/initializers/mail_delivery_job.rb'
+    - 'config/initializers/makara.rb'
+    - 'config/initializers/mime_types.rb'
+    - 'config/initializers/oj.rb'
+    - 'config/initializers/omniauth.rb'
+    - 'config/initializers/open_uri_redirection.rb'
+    - 'config/initializers/permissions_policy.rb'
+    - 'config/initializers/pghero.rb'
+    - 'config/initializers/preload_link_headers.rb'
+    - 'config/initializers/premailer_rails.rb'
+    - 'config/initializers/rack_attack_logging.rb'
+    - 'config/initializers/redis.rb'
+    - 'config/initializers/session_store.rb'
+    - 'config/initializers/simple_form.rb'
+    - 'config/initializers/stoplight.rb'
+    - 'config/initializers/trusted_proxies.rb'
+    - 'config/initializers/twitter_regex.rb'
+    - 'config/initializers/webauthn.rb'
+    - 'config/initializers/wrap_parameters.rb'
+    - 'config/locales/sr-Latn.rb'
+    - 'config/locales/sr.rb'
+    - 'config/puma.rb'
+    - 'db/migrate/20160220174730_create_accounts.rb'
+    - 'db/migrate/20160220211917_create_statuses.rb'
+    - 'db/migrate/20160221003140_create_users.rb'
+    - 'db/migrate/20160221003621_create_follows.rb'
+    - 'db/migrate/20160222122600_create_stream_entries.rb'
+    - 'db/migrate/20160222143943_add_profile_fields_to_accounts.rb'
+    - 'db/migrate/20160223162837_add_metadata_to_statuses.rb'
+    - 'db/migrate/20160223164502_make_uris_nullable_in_statuses.rb'
+    - 'db/migrate/20160223165723_add_url_to_statuses.rb'
+    - 'db/migrate/20160223165855_add_url_to_accounts.rb'
+    - 'db/migrate/20160223171800_create_favourites.rb'
+    - 'db/migrate/20160224223247_create_mentions.rb'
+    - 'db/migrate/20160227230233_add_attachment_avatar_to_accounts.rb'
+    - 'db/migrate/20160305115639_add_devise_to_users.rb'
+    - 'db/migrate/20160306172223_create_doorkeeper_tables.rb'
+    - 'db/migrate/20160312193225_add_attachment_header_to_accounts.rb'
+    - 'db/migrate/20160314164231_add_owner_to_application.rb'
+    - 'db/migrate/20160316103650_add_missing_indices.rb'
+    - 'db/migrate/20160322193748_add_avatar_remote_url_to_accounts.rb'
+    - 'db/migrate/20160325130944_add_admin_to_users.rb'
+    - 'db/migrate/20160826155805_add_superapp_to_oauth_applications.rb'
+    - 'db/migrate/20160905150353_create_media_attachments.rb'
+    - 'db/migrate/20160919221059_add_subscription_expires_at_to_accounts.rb'
+    - 'db/migrate/20160920003904_remove_verify_token_from_accounts.rb'
+    - 'db/migrate/20160926213048_remove_owner_from_application.rb'
+    - 'db/migrate/20161003142332_add_confirmable_to_users.rb'
+    - 'db/migrate/20161003145426_create_blocks.rb'
+    - 'db/migrate/20161006213403_rails_settings_migration.rb'
+    - 'db/migrate/20161009120834_create_domain_blocks.rb'
+    - 'db/migrate/20161027172456_add_silenced_to_accounts.rb'
+    - 'db/migrate/20161104173623_create_tags.rb'
+    - 'db/migrate/20161105130633_create_statuses_tags_join_table.rb'
+    - 'db/migrate/20161116162355_add_locale_to_users.rb'
+    - 'db/migrate/20161119211120_create_notifications.rb'
+    - 'db/migrate/20161122163057_remove_unneeded_indexes.rb'
+    - 'db/migrate/20161123093447_add_sensitive_to_statuses.rb'
+    - 'db/migrate/20161128103007_create_subscriptions.rb'
+    - 'db/migrate/20161130142058_add_last_successful_delivery_at_to_subscriptions.rb'
+    - 'db/migrate/20161130185319_add_visibility_to_statuses.rb'
+    - 'db/migrate/20161202132159_add_in_reply_to_account_id_to_statuses.rb'
+    - 'db/migrate/20161203164520_add_from_account_id_to_notifications.rb'
+    - 'db/migrate/20161205214545_add_suspended_to_accounts.rb'
+    - 'db/migrate/20161221152630_add_hidden_to_stream_entries.rb'
+    - 'db/migrate/20161222201034_add_locked_to_accounts.rb'
+    - 'db/migrate/20161222204147_create_follow_requests.rb'
+    - 'db/migrate/20170105224407_add_shortcode_to_media_attachments.rb'
+    - 'db/migrate/20170109120109_create_web_settings.rb'
+    - 'db/migrate/20170112154826_migrate_settings.rb'
+    - 'db/migrate/20170114194937_add_application_to_statuses.rb'
+    - 'db/migrate/20170114203041_add_website_to_oauth_application.rb'
+    - 'db/migrate/20170119214911_create_preview_cards.rb'
+    - 'db/migrate/20170123162658_add_severity_to_domain_blocks.rb'
+    - 'db/migrate/20170123203248_add_reject_media_to_domain_blocks.rb'
+    - 'db/migrate/20170125145934_add_spoiler_text_to_statuses.rb'
+    - 'db/migrate/20170127165745_add_devise_two_factor_to_users.rb'
+    - 'db/migrate/20170205175257_remove_devices.rb'
+    - 'db/migrate/20170209184350_add_reply_to_statuses.rb'
+    - 'db/migrate/20170214110202_create_reports.rb'
+    - 'db/migrate/20170217012631_add_reblog_of_id_foreign_key_to_statuses.rb'
+    - 'db/migrate/20170301222600_create_mutes.rb'
+    - 'db/migrate/20170303212857_add_last_emailed_at_to_users.rb'
+    - 'db/migrate/20170304202101_add_type_to_media_attachments.rb'
+    - 'db/migrate/20170317193015_add_search_index_to_accounts.rb'
+    - 'db/migrate/20170318214217_add_header_remote_url_to_accounts.rb'
+    - 'db/migrate/20170322021028_add_lowercase_index_to_accounts.rb'
+    - 'db/migrate/20170322143850_change_primary_key_to_bigint_on_statuses.rb'
+    - 'db/migrate/20170322162804_add_search_index_to_tags.rb'
+    - 'db/migrate/20170330021336_add_counter_caches.rb'
+    - 'db/migrate/20170330163835_create_imports.rb'
+    - 'db/migrate/20170330164118_add_attachment_data_to_imports.rb'
+    - 'db/migrate/20170403172249_add_action_taken_by_account_id_to_reports.rb'
+    - 'db/migrate/20170405112956_add_index_on_mentions_status_id.rb'
+    - 'db/migrate/20170406215816_add_notifications_and_favourites_indices.rb'
+    - 'db/migrate/20170409170753_add_last_webfingered_at_to_accounts.rb'
+    - 'db/migrate/20170414080609_add_devise_two_factor_backupable_to_users.rb'
+    - 'db/migrate/20170414132105_add_language_to_statuses.rb'
+    - 'db/migrate/20170418160728_add_indexes_to_reports_for_accounts.rb'
+    - 'db/migrate/20170423005413_add_allowed_languages_to_user.rb'
+    - 'db/migrate/20170424003227_create_account_domain_blocks.rb'
+    - 'db/migrate/20170424112722_add_status_id_index_to_statuses_tags.rb'
+    - 'db/migrate/20170425131920_add_media_attachment_meta.rb'
+    - 'db/migrate/20170425202925_add_oembed_to_preview_cards.rb'
+    - 'db/migrate/20170427011934_re_add_owner_to_application.rb'
+    - 'db/migrate/20170506235850_create_conversations.rb'
+    - 'db/migrate/20170507000211_add_conversation_id_to_statuses.rb'
+    - 'db/migrate/20170507141759_optimize_index_subscriptions.rb'
+    - 'db/migrate/20170508230434_create_conversation_mutes.rb'
+    - 'db/migrate/20170516072309_add_index_accounts_on_uri.rb'
+    - 'db/migrate/20170520145338_change_language_filter_to_opt_out.rb'
+    - 'db/migrate/20170601210557_add_index_on_media_attachments_account_id.rb'
+    - 'db/migrate/20170604144747_add_foreign_keys_for_accounts.rb'
+    - 'db/migrate/20170606113804_change_tag_search_index_to_btree.rb'
+    - 'db/migrate/20170609145826_remove_default_language_from_statuses.rb'
+    - 'db/migrate/20170610000000_add_statuses_index_on_account_id_id.rb'
+    - 'db/migrate/20170623152212_create_session_activations.rb'
+    - 'db/migrate/20170624134742_add_description_to_session_activations.rb'
+    - 'db/migrate/20170625140443_add_access_token_id_to_session_activations.rb'
+    - 'db/migrate/20170711225116_fix_null_booleans.rb'
+    - 'db/migrate/20170713112503_make_tag_search_case_insensitive.rb'
+    - 'db/migrate/20170713175513_create_web_push_subscriptions.rb'
+    - 'db/migrate/20170713190709_add_web_push_subscription_to_session_activations.rb'
+    - 'db/migrate/20170714184731_add_domain_to_subscriptions.rb'
+    - 'db/migrate/20170716191202_add_hide_notifications_to_mute.rb'
+    - 'db/migrate/20170718211102_add_activitypub_to_accounts.rb'
+    - 'db/migrate/20170720000000_add_index_favourites_on_account_id_and_id.rb'
+    - 'db/migrate/20170823162448_create_status_pins.rb'
+    - 'db/migrate/20170824103029_add_timestamps_to_status_pins.rb'
+    - 'db/migrate/20170829215220_remove_status_pins_account_index.rb'
+    - 'db/migrate/20170901141119_truncate_preview_cards.rb'
+    - 'db/migrate/20170901142658_create_join_table_preview_cards_statuses.rb'
+    - 'db/migrate/20170905044538_add_index_id_account_id_activity_type_on_notifications.rb'
+    - 'db/migrate/20170905165803_add_local_to_statuses.rb'
+    - 'db/migrate/20170913000752_create_site_uploads.rb'
+    - 'db/migrate/20170917153509_create_custom_emojis.rb'
+    - 'db/migrate/20170918125918_ids_to_bigints.rb'
+    - 'db/migrate/20170920024819_status_ids_to_timestamp_ids.rb'
+    - 'db/migrate/20170920032311_fix_reblogs_in_feeds.rb'
+    - 'db/migrate/20170924022025_ids_to_bigints2.rb'
+    - 'db/migrate/20170927215609_add_description_to_media_attachments.rb'
+    - 'db/migrate/20170928082043_create_email_domain_blocks.rb'
+    - 'db/migrate/20171005102658_create_account_moderation_notes.rb'
+    - 'db/migrate/20171005171936_add_disabled_to_custom_emojis.rb'
+    - 'db/migrate/20171006142024_add_uri_to_custom_emojis.rb'
+    - 'db/migrate/20171010023049_add_foreign_key_to_account_moderation_notes.rb'
+    - 'db/migrate/20171010025614_change_accounts_nonnullable_in_account_moderation_notes.rb'
+    - 'db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb'
+    - 'db/migrate/20171028221157_add_reblogs_to_follows.rb'
+    - 'db/migrate/20171107143332_add_memorial_to_accounts.rb'
+    - 'db/migrate/20171107143624_add_disabled_to_users.rb'
+    - 'db/migrate/20171109012327_add_moderator_to_accounts.rb'
+    - 'db/migrate/20171114080328_add_index_domain_to_email_domain_blocks.rb'
+    - 'db/migrate/20171114231651_create_lists.rb'
+    - 'db/migrate/20171116161857_create_list_accounts.rb'
+    - 'db/migrate/20171118012443_add_moved_to_account_id_to_accounts.rb'
+    - 'db/migrate/20171119172437_create_admin_action_logs.rb'
+    - 'db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb'
+    - 'db/migrate/20171125024930_create_invites.rb'
+    - 'db/migrate/20171125031751_add_invite_id_to_users.rb'
+    - 'db/migrate/20171125185353_add_index_reblog_of_id_and_account_to_statuses.rb'
+    - 'db/migrate/20171125190735_remove_old_reblog_index_on_statuses.rb'
+    - 'db/migrate/20171129172043_add_index_on_stream_entries.rb'
+    - 'db/migrate/20171130000000_add_embed_url_to_preview_cards.rb'
+    - 'db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb'
+    - 'db/migrate/20171212195226_remove_duplicate_indexes_in_lists.rb'
+    - 'db/migrate/20171226094803_more_faster_index_on_notifications.rb'
+    - 'db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb'
+    - 'db/migrate/20180109143959_add_remember_token_to_users.rb'
+    - 'db/migrate/20180204034416_create_identities.rb'
+    - 'db/migrate/20180206000000_change_user_id_nonnullable.rb'
+    - 'db/migrate/20180211015820_create_backups.rb'
+    - 'db/migrate/20180304013859_add_featured_collection_url_to_accounts.rb'
+    - 'db/migrate/20180310000000_change_columns_in_notifications_nonnullable.rb'
+    - 'db/migrate/20180402031200_add_assigned_account_id_to_reports.rb'
+    - 'db/migrate/20180402040909_create_report_notes.rb'
+    - 'db/migrate/20180410204633_add_fields_to_accounts.rb'
+    - 'db/migrate/20180416210259_add_uri_to_relationships.rb'
+    - 'db/migrate/20180506221944_add_actor_type_to_accounts.rb'
+    - 'db/migrate/20180510214435_add_access_token_id_to_web_push_subscriptions.rb'
+    - 'db/migrate/20180510230049_migrate_web_push_subscriptions.rb'
+    - 'db/migrate/20180528141303_fix_accounts_unique_index.rb'
+    - 'db/migrate/20180608213548_reject_following_blocked_users.rb'
+    - 'db/migrate/20180609104432_migrate_web_push_subscriptions2.rb'
+    - 'db/migrate/20180615122121_add_autofollow_to_invites.rb'
+    - 'db/migrate/20180616192031_add_chosen_languages_to_users.rb'
+    - 'db/migrate/20180617162849_remove_unused_indexes.rb'
+    - 'db/migrate/20180628181026_create_custom_filters.rb'
+    - 'db/migrate/20180707154237_add_whole_word_to_custom_filter.rb'
+    - 'db/migrate/20180711152640_create_relays.rb'
+    - 'db/migrate/20180808175627_create_account_pins.rb'
+    - 'db/migrate/20180812123222_change_relays_enabled.rb'
+    - 'db/migrate/20180812162710_create_status_stats.rb'
+    - 'db/migrate/20180812173710_copy_status_stats.rb'
+    - 'db/migrate/20180814171349_add_confidential_to_doorkeeper_application.rb'
+    - 'db/migrate/20180831171112_create_bookmarks.rb'
+    - 'db/migrate/20180929222014_create_account_conversations.rb'
+    - 'db/migrate/20181007025445_create_pghero_space_stats.rb'
+    - 'db/migrate/20181010141500_add_silent_to_mentions.rb'
+    - 'db/migrate/20181017170937_add_reject_reports_to_domain_blocks.rb'
+    - 'db/migrate/20181018205649_add_unread_to_account_conversations.rb'
+    - 'db/migrate/20181024224956_migrate_account_conversations.rb'
+    - 'db/migrate/20181026034033_remove_faux_remote_account_duplicates.rb'
+    - 'db/migrate/20181116165755_create_account_stats.rb'
+    - 'db/migrate/20181116173541_copy_account_stats.rb'
+    - 'db/migrate/20181127130500_identity_id_to_bigint.rb'
+    - 'db/migrate/20181127165847_add_show_replies_to_lists.rb'
+    - 'db/migrate/20181203003808_create_accounts_tags_join_table.rb'
+    - 'db/migrate/20181203021853_add_discoverable_to_accounts.rb'
+    - 'db/migrate/20181204193439_add_last_status_at_to_account_stats.rb'
+    - 'db/migrate/20181204215309_create_account_tag_stats.rb'
+    - 'db/migrate/20181207011115_downcase_custom_emoji_domains.rb'
+    - 'db/migrate/20181213184704_create_account_warnings.rb'
+    - 'db/migrate/20181213185533_create_account_warning_presets.rb'
+    - 'db/migrate/20181219235220_add_created_by_application_id_to_users.rb'
+    - 'db/migrate/20181226021420_add_also_known_as_to_accounts.rb'
+    - 'db/migrate/20190103124649_create_scheduled_statuses.rb'
+    - 'db/migrate/20190103124754_add_scheduled_status_id_to_media_attachments.rb'
+    - 'db/migrate/20190117114553_create_tombstones.rb'
+    - 'db/migrate/20190201012802_add_overwrite_to_imports.rb'
+    - 'db/migrate/20190203180359_create_featured_tags.rb'
+    - 'db/migrate/20190225031541_create_polls.rb'
+    - 'db/migrate/20190225031625_create_poll_votes.rb'
+    - 'db/migrate/20190226003449_add_poll_id_to_statuses.rb'
+    - 'db/migrate/20190304152020_add_uri_to_poll_votes.rb'
+    - 'db/migrate/20190306145741_add_lock_version_to_polls.rb'
+    - 'db/migrate/20190307234537_add_approved_to_users.rb'
+    - 'db/migrate/20190314181829_migrate_open_registrations_setting.rb'
+    - 'db/migrate/20190316190352_create_account_identity_proofs.rb'
+    - 'db/migrate/20190317135723_add_uri_to_reports.rb'
+    - 'db/migrate/20190403141604_add_comment_to_invites.rb'
+    - 'db/migrate/20190409054914_create_user_invite_requests.rb'
+    - 'db/migrate/20190420025523_add_blurhash_to_media_attachments.rb'
+    - 'db/migrate/20190509164208_add_by_moderator_to_tombstone.rb'
+    - 'db/migrate/20190511134027_add_silenced_at_suspended_at_to_accounts.rb'
+    - 'db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb'
+    - 'db/migrate/20190627222225_create_custom_emoji_categories.rb'
+    - 'db/migrate/20190627222826_add_category_id_to_custom_emojis.rb'
+    - 'db/migrate/20190701022101_add_trust_level_to_accounts.rb'
+    - 'db/migrate/20190705002136_create_domain_allows.rb'
+    - 'db/migrate/20190715164535_add_instance_actor.rb'
+    - 'db/migrate/20190726175042_add_case_insensitive_index_to_tags.rb'
+    - 'db/migrate/20190729185330_add_score_to_tags.rb'
+    - 'db/migrate/20190805123746_add_capabilities_to_tags.rb'
+    - 'db/migrate/20190807135426_add_comments_to_domain_blocks.rb'
+    - 'db/migrate/20190815225426_add_last_status_at_to_tags.rb'
+    - 'db/migrate/20190819134503_add_deleted_at_to_statuses.rb'
+    - 'db/migrate/20190820003045_update_statuses_index.rb'
+    - 'db/migrate/20190823221802_add_local_index_to_statuses.rb'
+    - 'db/migrate/20190901035623_add_max_score_to_tags.rb'
+    - 'db/migrate/20190904222339_create_markers.rb'
+    - 'db/migrate/20190914202517_create_account_migrations.rb'
+    - 'db/migrate/20190915194355_create_account_aliases.rb'
+    - 'db/migrate/20190927232842_add_voters_count_to_polls.rb'
+    - 'db/migrate/20191001213028_add_lock_version_to_account_stats.rb'
+    - 'db/migrate/20191007013357_update_pt_locales.rb'
+    - 'db/migrate/20191031163205_change_list_account_follow_nullable.rb'
+    - 'db/migrate/20191212003415_increase_backup_size.rb'
+    - 'db/migrate/20191212163405_add_hide_collections_to_accounts.rb'
+    - 'db/migrate/20191218153258_create_announcements.rb'
+    - 'db/migrate/20200113125135_create_announcement_mutes.rb'
+    - 'db/migrate/20200114113335_create_announcement_reactions.rb'
+    - 'db/migrate/20200119112504_add_public_index_to_statuses.rb'
+    - 'db/migrate/20200126203551_add_published_at_to_announcements.rb'
+    - 'db/migrate/20200306035625_add_processing_to_media_attachments.rb'
+    - 'db/migrate/20200309150742_add_forwarded_to_reports.rb'
+    - 'db/migrate/20200312144258_add_title_to_account_warning_presets.rb'
+    - 'db/migrate/20200312162302_add_status_ids_to_announcements.rb'
+    - 'db/migrate/20200312185443_add_parent_id_to_email_domain_blocks.rb'
+    - 'db/migrate/20200317021758_add_expires_at_to_mutes.rb'
+    - 'db/migrate/20200407201300_create_unavailable_domains.rb'
+    - 'db/migrate/20200407202420_migrate_unavailable_inboxes.rb'
+    - 'db/migrate/20200417125749_add_storage_schema_version.rb'
+    - 'db/migrate/20200508212852_reset_unique_jobs_locks.rb'
+    - 'db/migrate/20200510110808_reset_web_app_secret.rb'
+    - 'db/migrate/20200510181721_remove_duplicated_indexes_pghero.rb'
+    - 'db/migrate/20200516180352_create_devices.rb'
+    - 'db/migrate/20200516183822_create_one_time_keys.rb'
+    - 'db/migrate/20200518083523_create_encrypted_messages.rb'
+    - 'db/migrate/20200521180606_encrypted_message_ids_to_timestamp_ids.rb'
+    - 'db/migrate/20200529214050_add_devices_url_to_accounts.rb'
+    - 'db/migrate/20200601222558_create_system_keys.rb'
+    - 'db/migrate/20200605155027_add_blurhash_to_preview_cards.rb'
+    - 'db/migrate/20200608113046_add_sign_in_token_to_users.rb'
+    - 'db/migrate/20200614002136_add_sensitized_to_accounts.rb'
+    - 'db/migrate/20200620164023_add_fixed_lowercase_index_to_accounts.rb'
+    - 'db/migrate/20200622213645_media_attachment_ids_to_timestamp_ids.rb'
+    - 'db/migrate/20200627125810_add_thumbnail_columns_to_media_attachments.rb'
+    - 'db/migrate/20200628133322_create_account_notes.rb'
+    - 'db/migrate/20200630190240_create_webauthn_credentials.rb'
+    - 'db/migrate/20200630190544_add_webauthn_id_to_users.rb'
+    - 'db/migrate/20200908193330_create_account_deletion_requests.rb'
+    - 'db/migrate/20200917192924_add_notify_to_follows.rb'
+    - 'db/migrate/20200917193034_add_type_to_notifications.rb'
+    - 'db/migrate/20200917222316_add_index_notifications_on_type.rb'
+    - 'db/migrate/20201008202037_create_ip_blocks.rb'
+    - 'db/migrate/20201008220312_add_sign_up_ip_to_users.rb'
+    - 'db/migrate/20201017233919_add_suspension_origin_to_accounts.rb'
+    - 'db/migrate/20201206004238_create_instances.rb'
+    - 'db/migrate/20201218054746_add_obfuscate_to_domain_blocks.rb'
+    - 'db/migrate/20210221045109_create_rules.rb'
+    - 'db/migrate/20210306164523_account_ids_to_timestamp_ids.rb'
+    - 'db/migrate/20210322164601_create_account_summaries.rb'
+    - 'db/migrate/20210323114347_create_follow_recommendations.rb'
+    - 'db/migrate/20210324171613_create_follow_recommendation_suppressions.rb'
+    - 'db/migrate/20210416200740_create_canonical_email_blocks.rb'
+    - 'db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb'
+    - 'db/migrate/20210425135952_add_index_on_media_attachments_account_id_status_id.rb'
+    - 'db/migrate/20210505174616_update_follow_recommendations_to_version_2.rb'
+    - 'db/migrate/20210609202149_create_login_activities.rb'
+    - 'db/migrate/20210616214526_create_user_ips.rb'
+    - 'db/migrate/20210621221010_add_skip_sign_in_token_to_users.rb'
+    - 'db/migrate/20210630000137_fix_canonical_email_blocks_foreign_key.rb'
+    - 'db/migrate/20210722120340_create_account_statuses_cleanup_policies.rb'
+    - 'db/migrate/20210904215403_add_edited_at_to_statuses.rb'
+    - 'db/migrate/20210908220918_create_status_edits.rb'
+    - 'db/migrate/20211031031021_create_preview_card_providers.rb'
+    - 'db/migrate/20211112011713_add_language_to_preview_cards.rb'
+    - 'db/migrate/20211115032527_add_trendable_to_preview_cards.rb'
+    - 'db/migrate/20211123212714_add_link_type_to_preview_cards.rb'
+    - 'db/migrate/20211213040746_update_account_summaries_to_version_2.rb'
+    - 'db/migrate/20211231080958_add_category_to_reports.rb'
+    - 'db/migrate/20220105163928_remove_mentions_status_id_index.rb'
+    - 'db/migrate/20220115125126_add_report_id_to_account_warnings.rb'
+    - 'db/migrate/20220115125341_fix_account_warning_actions.rb'
+    - 'db/migrate/20220116202951_add_deleted_at_index_on_statuses.rb'
+    - 'db/migrate/20220124141035_create_appeals.rb'
+    - 'db/migrate/20220202200743_add_trendable_to_accounts.rb'
+    - 'db/migrate/20220202200926_add_trendable_to_statuses.rb'
+    - 'db/migrate/20220210153119_add_overruled_at_to_account_warnings.rb'
+    - 'db/migrate/20220224010024_add_ips_to_email_domain_blocks.rb'
+    - 'db/migrate/20220227041951_add_last_used_at_to_oauth_access_tokens.rb'
+    - 'db/migrate/20220302232632_add_ordered_media_attachment_ids_to_statuses.rb'
+    - 'db/migrate/20220303000827_add_ordered_media_attachment_ids_to_status_edits.rb'
+    - 'db/migrate/20220304195405_migrate_hide_network_preference.rb'
+    - 'db/migrate/20220307094650_fix_featured_tags_constraints.rb'
+    - 'db/migrate/20220309213005_fix_reblog_deleted_at.rb'
+    - 'db/migrate/20220316233212_update_kurdish_locales.rb'
+    - 'db/migrate/20220428112511_add_index_statuses_on_account_id.rb'
+    - 'db/migrate/20220428112727_add_index_statuses_pins_on_status_id.rb'
+    - 'db/migrate/20220428114454_add_index_reports_on_assigned_account_id.rb'
+    - 'db/migrate/20220428114902_add_index_reports_on_action_taken_by_account_id.rb'
+    - 'db/migrate/20220606044941_create_webhooks.rb'
+    - 'db/migrate/20220611210335_create_user_roles.rb'
+    - 'db/migrate/20220611212541_add_role_id_to_users.rb'
+    - 'db/migrate/20220710102457_add_display_name_to_tags.rb'
+    - 'db/migrate/20220714171049_create_tag_follows.rb'
+    - 'db/migrate/20220824164433_add_human_identifier_to_admin_action_logs.rb'
+    - 'db/migrate/20220824233535_create_status_trends.rb'
+    - 'db/migrate/20220827195229_change_canonical_email_blocks_nullable.rb'
+    - 'db/migrate/20220829192633_add_languages_to_follows.rb'
+    - 'db/migrate/20220829192658_add_languages_to_follow_requests.rb'
+    - 'db/migrate/20221006061337_create_preview_card_trends.rb'
+    - 'db/migrate/20221012181003_add_blurhash_to_site_uploads.rb'
+    - 'db/migrate/20221021055441_add_index_featured_tags_on_account_id_and_tag_id.rb'
+    - 'db/migrate/20221025171544_add_index_ip_blocks_on_ip.rb'
+    - 'db/migrate/20221104133904_add_name_to_featured_tags.rb'
+    - 'db/post_migrate/20190519130537_remove_boosts_widening_audience.rb'
+    - 'db/post_migrate/20210308133107_remove_subscription_expires_at_from_accounts.rb'
+    - 'db/post_migrate/20220118183123_remove_rememberable_from_users.rb'
+    - 'db/seeds/01_web_app.rb'
+    - 'db/seeds/02_instance_actor.rb'
+    - 'db/seeds/03_roles.rb'
+    - 'db/seeds/04_admin.rb'
+    - 'lib/rails/engine_extensions.rb'
+    - 'lib/tasks/branding.rake'
+    - 'spec/fabricators_spec.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/GlobalStdStream:
+  Exclude:
+    - 'config/boot.rb'
+    - 'config/environments/development.rb'
+    - 'config/environments/production.rb'
+
+# Configuration parameters: AllowedVariables.
+Style/GlobalVars:
+  Exclude:
+    - 'config/initializers/statsd.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: MinBodyLength, AllowConsecutiveConditionals.
+Style/GuardClause:
+  Exclude:
+    - 'app/controllers/admin/confirmations_controller.rb'
+    - 'app/controllers/auth/confirmations_controller.rb'
+    - 'app/controllers/auth/passwords_controller.rb'
+    - 'app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb'
+    - 'app/lib/activitypub/activity/block.rb'
+    - 'app/lib/connection_pool/shared_connection_pool.rb'
+    - 'app/lib/request.rb'
+    - 'app/lib/request_pool.rb'
+    - 'app/lib/webfinger.rb'
+    - 'app/lib/webfinger_resource.rb'
+    - 'app/models/concerns/account_counters.rb'
+    - 'app/models/concerns/ldap_authenticable.rb'
+    - 'app/models/tag.rb'
+    - 'app/models/user.rb'
+    - 'app/services/fan_out_on_write_service.rb'
+    - 'app/services/post_status_service.rb'
+    - 'app/services/process_hashtags_service.rb'
+    - 'app/workers/move_worker.rb'
+    - 'app/workers/redownload_avatar_worker.rb'
+    - 'app/workers/redownload_header_worker.rb'
+    - 'app/workers/redownload_media_worker.rb'
+    - 'app/workers/remote_account_refresh_worker.rb'
+    - 'config/initializers/devise.rb'
+    - 'db/migrate/20170901141119_truncate_preview_cards.rb'
+    - 'db/post_migrate/20220704024901_migrate_settings_to_user_roles.rb'
+    - 'lib/devise/two_factor_ldap_authenticatable.rb'
+    - 'lib/devise/two_factor_pam_authenticatable.rb'
+    - 'lib/mastodon/accounts_cli.rb'
+    - 'lib/mastodon/maintenance_cli.rb'
+    - 'lib/mastodon/media_cli.rb'
+    - 'lib/paperclip/attachment_extensions.rb'
+    - 'lib/tasks/repo.rake'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: braces, no_braces
+Style/HashAsLastArrayItem:
+  Exclude:
+    - 'app/controllers/admin/statuses_controller.rb'
+    - 'app/controllers/api/v1/statuses_controller.rb'
+    - 'app/models/account.rb'
+    - 'app/models/concerns/account_counters.rb'
+    - 'app/models/concerns/status_threading_concern.rb'
+    - 'app/models/status.rb'
+    - 'app/services/batched_remove_status_service.rb'
+    - 'app/services/notify_service.rb'
+    - 'db/migrate/20181024224956_migrate_account_conversations.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
+# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
+# SupportedShorthandSyntax: always, never, either, consistent
+Style/HashSyntax:
+  Exclude:
+    - 'app/helpers/application_helper.rb'
+    - 'app/models/media_attachment.rb'
+    - 'lib/terrapin/multi_pipe_extensions.rb'
+    - 'spec/controllers/admin/reports/actions_controller_spec.rb'
+    - 'spec/controllers/admin/statuses_controller_spec.rb'
+    - 'spec/controllers/concerns/signature_verification_spec.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/HashTransformValues:
+  Exclude:
+    - 'app/serializers/rest/web_push_subscription_serializer.rb'
+    - 'app/services/import_service.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+Style/IfUnlessModifier:
+  Exclude:
+    - 'config/environments/production.rb'
+    - 'config/initializers/devise.rb'
+    - 'config/initializers/ffmpeg.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: InverseMethods, InverseBlocks.
+Style/InverseMethods:
+  Exclude:
+    - 'app/controllers/concerns/signature_verification.rb'
+    - 'app/helpers/jsonld_helper.rb'
+    - 'app/lib/activitypub/activity/create.rb'
+    - 'app/lib/activitypub/activity/move.rb'
+    - 'app/lib/feed_manager.rb'
+    - 'app/lib/link_details_extractor.rb'
+    - 'app/models/concerns/attachmentable.rb'
+    - 'app/models/concerns/remotable.rb'
+    - 'app/models/custom_filter.rb'
+    - 'app/models/webhook.rb'
+    - 'app/services/activitypub/process_status_update_service.rb'
+    - 'app/services/fetch_link_card_service.rb'
+    - 'app/services/search_service.rb'
+    - 'app/services/update_account_service.rb'
+    - 'app/workers/web/push_notification_worker.rb'
+    - 'lib/paperclip/color_extractor.rb'
+    - 'spec/controllers/activitypub/replies_controller_spec.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: line_count_dependent, lambda, literal
+Style/Lambda:
+  Exclude:
+    - 'config/initializers/simple_form.rb'
+    - 'config/routes.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/MapToHash:
+  Exclude:
+    - 'app/models/status.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: literals, strict
+Style/MutableConstant:
+  Exclude:
+    - 'app/lib/link_details_extractor.rb'
+    - 'app/models/account.rb'
+    - 'app/models/custom_emoji.rb'
+    - 'app/models/tag.rb'
+    - 'app/services/account_search_service.rb'
+    - 'app/services/delete_account_service.rb'
+    - 'app/services/fetch_link_card_service.rb'
+    - 'app/services/resolve_url_service.rb'
+    - 'config/initializers/twitter_regex.rb'
+    - 'lib/mastodon/snowflake.rb'
+    - 'spec/controllers/api/base_controller_spec.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+Style/NilLambda:
+  Exclude:
+    - 'config/initializers/paperclip.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: MinDigits, Strict, AllowedNumbers, AllowedPatterns.
+Style/NumericLiterals:
+  Exclude:
+    - 'config/initializers/strong_migrations.rb'
+
+# Configuration parameters: AllowedMethods.
+# AllowedMethods: respond_to_missing?
+Style/OptionalBooleanParameter:
+  Exclude:
+    - 'app/helpers/admin/account_moderation_notes_helper.rb'
+    - 'app/helpers/jsonld_helper.rb'
+    - 'app/lib/admin/system_check/message.rb'
+    - 'app/lib/request.rb'
+    - 'app/lib/webfinger.rb'
+    - 'app/services/block_domain_service.rb'
+    - 'app/services/fetch_resource_service.rb'
+    - 'app/workers/domain_block_worker.rb'
+    - 'app/workers/unfollow_follow_worker.rb'
+    - 'lib/mastodon/redis_config.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: PreferredDelimiters.
+Style/PercentLiteralDelimiters:
+  Exclude:
+    - 'config/deploy.rb'
+    - 'config/initializers/doorkeeper.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: short, verbose
+Style/PreferredHashMethods:
+  Exclude:
+    - 'config/initializers/paperclip.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+Style/RedundantConstantBase:
+  Exclude:
+    - 'config/environments/production.rb'
+    - 'config/initializers/sidekiq.rb'
+    - 'config/initializers/statsd.rb'
+    - 'config/locales/sr-Latn.rb'
+    - 'config/locales/sr.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: SafeForConstants.
+Style/RedundantFetchBlock:
+  Exclude:
+    - 'config/initializers/1_hosts.rb'
+    - 'config/initializers/chewy.rb'
+    - 'config/initializers/devise.rb'
+    - 'config/initializers/paperclip.rb'
+    - 'config/puma.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+Style/RedundantRegexpCharacterClass:
+  Exclude:
+    - 'app/lib/link_details_extractor.rb'
+    - 'app/lib/tag_manager.rb'
+    - 'app/models/domain_allow.rb'
+    - 'app/models/domain_block.rb'
+    - 'app/services/fetch_oembed_service.rb'
+    - 'config/initializers/rack_attack.rb'
+    - 'lib/tasks/emojis.rake'
+    - 'lib/tasks/mastodon.rake'
+
+# This cop supports safe autocorrection (--autocorrect).
+Style/RedundantRegexpEscape:
+  Exclude:
+    - 'app/lib/webfinger_resource.rb'
+    - 'app/models/account.rb'
+    - 'app/models/tag.rb'
+    - 'app/services/fetch_link_card_service.rb'
+    - 'config/initializers/twitter_regex.rb'
+    - 'lib/paperclip/color_extractor.rb'
+    - 'lib/tasks/mastodon.rake'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, AllowInnerSlashes.
+# SupportedStyles: slashes, percent_r, mixed
+Style/RegexpLiteral:
+  Exclude:
+    - 'app/lib/link_details_extractor.rb'
+    - 'app/lib/permalink_redirector.rb'
+    - 'app/lib/plain_text_formatter.rb'
+    - 'app/lib/tag_manager.rb'
+    - 'app/lib/text_formatter.rb'
+    - 'app/models/account.rb'
+    - 'app/models/domain_allow.rb'
+    - 'app/models/domain_block.rb'
+    - 'app/models/site_upload.rb'
+    - 'app/models/tag.rb'
+    - 'app/services/backup_service.rb'
+    - 'app/services/fetch_oembed_service.rb'
+    - 'app/services/search_service.rb'
+    - 'config/initializers/rack_attack.rb'
+    - 'config/initializers/twitter_regex.rb'
+    - 'config/routes.rb'
+    - 'lib/mastodon/premailer_webpack_strategy.rb'
+    - 'lib/tasks/mastodon.rake'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods, MaxChainLength.
+# AllowedMethods: present?, blank?, presence, try, try!
+Style/SafeNavigation:
+  Exclude:
+    - 'app/models/concerns/account_finder_concern.rb'
+    - 'app/models/status.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowAsExpressionSeparator.
+Style/Semicolon:
+  Exclude:
+    - 'spec/services/activitypub/process_status_update_service_spec.rb'
+    - 'spec/validators/blacklisted_email_validator_spec.rb'
+    - 'spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: only_raise, only_fail, semantic
+Style/SignalException:
+  Exclude:
+    - 'lib/devise/two_factor_ldap_authenticatable.rb'
+    - 'lib/devise/two_factor_pam_authenticatable.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/SingleArgumentDig:
+  Exclude:
+    - 'lib/webpacker/manifest_extensions.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+Style/SlicingWithRange:
+  Exclude:
+    - 'app/lib/emoji_formatter.rb'
+    - 'app/lib/text_formatter.rb'
+    - 'app/models/account_alias.rb'
+    - 'app/models/domain_block.rb'
+    - 'app/models/email_domain_block.rb'
+    - 'app/models/preview_card_provider.rb'
+    - 'app/validators/status_length_validator.rb'
+    - 'db/migrate/20190726175042_add_case_insensitive_index_to_tags.rb'
+    - 'lib/active_record/batches.rb'
+    - 'lib/mastodon/premailer_webpack_strategy.rb'
+    - 'lib/tasks/repo.rake'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle.
+# SupportedStyles: require_parentheses, require_no_parentheses
+Style/StabbyLambdaParentheses:
+  Exclude:
+    - 'config/environments/production.rb'
+    - 'config/initializers/content_security_policy.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+Style/StderrPuts:
+  Exclude:
+    - 'config/boot.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: Mode.
+Style/StringConcatenation:
+  Exclude:
+    - 'config/initializers/paperclip.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
+# SupportedStyles: single_quotes, double_quotes
+Style/StringLiterals:
+  Exclude:
+    - 'config/environments/production.rb'
+    - 'config/initializers/backtrace_silencers.rb'
+    - 'config/initializers/http_client_proxy.rb'
+    - 'config/initializers/rack_attack.rb'
+    - 'config/initializers/webauthn.rb'
+    - 'config/routes.rb'
+
+# This cop supports unsafe autocorrection (--autocorrect-all).
+# Configuration parameters: AllowMethodsWithArguments, AllowedMethods, AllowedPatterns, AllowComments.
+# AllowedMethods: define_method, mail, respond_to
+Style/SymbolProc:
+  Exclude:
+    - 'config/initializers/omniauth.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyle, AllowSafeAssignment.
+# SupportedStyles: require_parentheses, require_no_parentheses, require_parentheses_when_complex
+Style/TernaryParentheses:
+  Exclude:
+    - 'config/environments/development.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyleForMultiline.
+# SupportedStylesForMultiline: comma, consistent_comma, no_comma
+Style/TrailingCommaInArguments:
+  Exclude:
+    - 'config/initializers/paperclip.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: EnforcedStyleForMultiline.
+# SupportedStylesForMultiline: comma, consistent_comma, no_comma
+Style/TrailingCommaInHashLiteral:
+  Exclude:
+    - 'config/environments/production.rb'
+    - 'config/environments/test.rb'
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: WordRegex.
+# SupportedStyles: percent, brackets
+Style/WordArray:
+  EnforcedStyle: percent
+  MinSize: 6
+
+# This cop supports safe autocorrection (--autocorrect).
+# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
+# URISchemes: http, https
+Layout/LineLength:
+  Max: 701
diff --git a/.ruby-version b/.ruby-version
index b0f2dcb32..be94e6f53 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-3.0.4
+3.2.2
diff --git a/.yarnclean b/.yarnclean
index 0cc2b50d7..21eb734a6 100644
--- a/.yarnclean
+++ b/.yarnclean
@@ -44,3 +44,6 @@ Gruntfile.js
 # for specific ignore
 !.svgo.yml
 !sass-lint/**/*.yml
+
+# breaks lint-staged or generally anything using https://github.com/eemeli/yaml/issues/384
+!**/yaml/dist/**/doc
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b1ad9e5fd..91a2c48a1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,289 @@
-Changelog
-=========
+# Changelog
 
 All notable changes to this project will be documented in this file.
 
+## [4.1.2] - 2023-04-04
+
+### Fixed
+
+- Fix crash in `tootctl` commands making use of parallelization when Elasticsearch is enabled ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24182), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24377))
+- Fix crash in `db:setup` when Elasticsearch is enabled ([rrgeorge](https://github.com/mastodon/mastodon/pull/24302))
+- Fix user archive takeout when using OpenStack Swift or S3 providers with no ACL support ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24200))
+- Fix invalid/expired invites being processed on sign-up ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24337))
+
+### Security
+
+- Update Ruby to 3.0.6 due to ReDoS vulnerabilities ([saizai](https://github.com/mastodon/mastodon/pull/24334))
+- Fix unescaped user input in LDAP query ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24379))
+
+## [4.1.1] - 2023-03-16
+
+### Added
+
+- Add redirection from paths with url-encoded `@` to their decoded form ([thijskh](https://github.com/mastodon/mastodon/pull/23593))
+- Add `lang` attribute to native language names in language picker in Web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23749))
+- Add headers to outgoing mails to avoid auto-replies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23597))
+- Add support for refreshing many accounts at once with `tootctl accounts refresh` ([9p4](https://github.com/mastodon/mastodon/pull/23304))
+- Add confirmation modal when clicking to edit a post with a non-empty compose form ([PauloVilarinho](https://github.com/mastodon/mastodon/pull/23936))
+- Add support for the HAproxy PROXY protocol through the `PROXY_PROTO_V1` environment variable ([CSDUMMI](https://github.com/mastodon/mastodon/pull/24064))
+- Add `SENDFILE_HEADER` environment variable ([Gargron](https://github.com/mastodon/mastodon/pull/24123))
+- Add cache headers to static files served through Rails ([Gargron](https://github.com/mastodon/mastodon/pull/24120))
+
+### Changed
+
+- Increase contrast of upload progress bar background ([toolmantim](https://github.com/mastodon/mastodon/pull/23836))
+- Change post auto-deletion throttling constants to better scale with server size ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23320))
+- Change order of bookmark and favourite sidebar entries in single-column UI for consistency ([TerryGarcia](https://github.com/mastodon/mastodon/pull/23701))
+- Change `ActivityPub::DeliveryWorker` retries to be spread out more ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21956))
+
+### Fixed
+
+- Fix “Remove all followers from the selected domains” also removing follows and notifications ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23805))
+- Fix streaming metrics format ([emilweth](https://github.com/mastodon/mastodon/pull/23519), [emilweth](https://github.com/mastodon/mastodon/pull/23520))
+- Fix case-sensitive check for previously used hashtags in hashtag autocompletion ([deanveloper](https://github.com/mastodon/mastodon/pull/23526))
+- Fix focus point of already-attached media not saving after edit ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23566))
+- Fix sidebar behavior in settings/admin UI on mobile ([wxt2005](https://github.com/mastodon/mastodon/pull/23764))
+- Fix inefficiency when searching accounts per username in admin interface ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23801))
+- Fix duplicate “Publish” button on mobile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23804))
+- Fix server error when failing to follow back followers from `/relationships` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23787))
+- Fix server error when attempting to display the edit history of a trendable post in the admin interface ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23574))
+- Fix `tootctl accounts migrate` crashing because of a typo ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23567))
+- Fix original account being unfollowed on migration before the follow request to the new account could be sent ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21957))
+- Fix the “Back” button in column headers sometimes leaving Mastodon ([c960657](https://github.com/mastodon/mastodon/pull/23953))
+- Fix pgBouncer resetting application name on every transaction ([Gargron](https://github.com/mastodon/mastodon/pull/23958))
+- Fix unconfirmed accounts being counted as active users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23803))
+- Fix `/api/v1/streaming` sub-paths not being redirected ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23988))
+- Fix drag'n'drop upload area text that spans multiple lines not being centered ([vintprox](https://github.com/mastodon/mastodon/pull/24029))
+- Fix sidekiq jobs not triggering Elasticsearch index updates ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24046))
+- Fix tags being unnecessarily stripped from plain-text short site description ([c960657](https://github.com/mastodon/mastodon/pull/23975))
+- Fix HTML entities not being un-escaped in extracted plain-text from remote posts ([c960657](https://github.com/mastodon/mastodon/pull/24019))
+- Fix dashboard crash on ElasticSearch server error ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23751))
+- Fix incorrect post links in strikes when the account is remote ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23611))
+- Fix misleading error code when receiving invalid WebAuthn credentials ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23568))
+- Fix duplicate mails being sent when the SMTP server is too slow to close the connection ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23750))
+
+### Security
+
+- Change user backups to use expiring URLs for download when possible ([Gargron](https://github.com/mastodon/mastodon/pull/24136))
+- Add warning for object storage misconfiguration ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24137))
+
+## [4.1.0] - 2023-02-10
+
+### Added
+
+- **Add support for importing/exporting server-wide domain blocks** ([enbylenore](https://github.com/mastodon/mastodon/pull/20597), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/21471), [dariusk](https://github.com/mastodon/mastodon/pull/22803), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/21470))
+- **Add listing of followed hashtags** ([connorshea](https://github.com/mastodon/mastodon/pull/21773))
+- **Add support for editing media description and focus point of already-sent posts** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20878))
+  - Previously, you could add and remove attachments, but not edit media description of already-attached media
+  - REST API changes:
+    - `PUT /api/v1/statuses/:id` now takes an extra `media_attributes[]` array parameter with the `id` of the updated media and their updated `description`, `focus`, and `thumbnail`
+- **Add follow request banner on account header** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20785))
+  - REST API changes:
+    - `Relationship` entities have an extra `requested_by` boolean attribute representing whether the represented user has requested to follow you
+- **Add confirmation screen when handling reports** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22375), [Gargron](https://github.com/mastodon/mastodon/pull/23156), [tribela](https://github.com/mastodon/mastodon/pull/23178))
+- Add option to make the landing page be `/about` even when trends are enabled ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20808))
+- Add `noindex` setting back to the admin interface ([prplecake](https://github.com/mastodon/mastodon/pull/22205))
+- Add instance peers API endpoint toggle back to the admin interface ([dariusk](https://github.com/mastodon/mastodon/pull/22810))
+- Add instance activity API endpoint toggle back to the admin interface ([dariusk](https://github.com/mastodon/mastodon/pull/22833))
+- Add setting for status page URL ([Gargron](https://github.com/mastodon/mastodon/pull/23390), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23499))
+  - REST API changes:
+    - Add `configuration.urls.status` attribute to the object returned by `GET /api/v1/instance`
+- Add `account.approved` webhook ([Saiv46](https://github.com/mastodon/mastodon/pull/22938))
+- Add 12 hours option to polls ([Pleclown](https://github.com/mastodon/mastodon/pull/21131))
+- Add dropdown menu item to open admin interface for remote domains ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21895))
+- Add `--remove-headers`, `--prune-profiles` and `--include-follows` flags to `tootctl media remove` ([evanphilip](https://github.com/mastodon/mastodon/pull/22149))
+- Add `--email` and `--dry-run` options to `tootctl accounts delete` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22328))
+- Add `tootctl accounts migrate` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22330))
+- Add `tootctl accounts prune` ([tribela](https://github.com/mastodon/mastodon/pull/18397))
+- Add `tootctl domains purge` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22063))
+- Add `SIDEKIQ_CONCURRENCY` environment variable ([muffinista](https://github.com/mastodon/mastodon/pull/19589))
+- Add `DB_POOL` environment variable support for streaming server ([Gargron](https://github.com/mastodon/mastodon/pull/23470))
+- Add `MIN_THREADS` environment variable to set minimum Puma threads ([jimeh](https://github.com/mastodon/mastodon/pull/21048))
+- Add explanation text to log-in page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20946))
+- Add user profile OpenGraph tag on post pages ([bramus](https://github.com/mastodon/mastodon/pull/21423))
+- Add maskable icon support for Android ([workeffortwaste](https://github.com/mastodon/mastodon/pull/20904))
+- Add Belarusian to supported languages ([Mixaill](https://github.com/mastodon/mastodon/pull/22022))
+- Add Western Frisian to supported languages ([ykzts](https://github.com/mastodon/mastodon/pull/18602))
+- Add Montenegrin to the language picker ([ayefries](https://github.com/mastodon/mastodon/pull/21013))
+- Add Southern Sami and Lule Sami to the language picker ([Jullan-M](https://github.com/mastodon/mastodon/pull/21262))
+- Add logging for Rails cache timeouts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21667))
+- Add color highlight for active hashtag “follow” button ([MFTabriz](https://github.com/mastodon/mastodon/pull/21629))
+- Add brotli compression to `assets:precompile` ([Izorkin](https://github.com/mastodon/mastodon/pull/19025))
+- Add “disabled” account filter to the `/admin/accounts` UI ([tribela](https://github.com/mastodon/mastodon/pull/21282))
+- Add transparency to modal background for accessibility ([edent](https://github.com/mastodon/mastodon/pull/18081))
+- Add `lang` attribute to image description textarea and poll option field ([c960657](https://github.com/mastodon/mastodon/pull/23293))
+- Add `spellcheck` attribute to Content Warning and poll option input fields ([c960657](https://github.com/mastodon/mastodon/pull/23395))
+- Add `title` attribute to video elements in media attachments ([bramus](https://github.com/mastodon/mastodon/pull/21420))
+- Add left and right margins to emojis ([dsblank](https://github.com/mastodon/mastodon/pull/20464))
+- Add `roles` attribute to `Account` entities in REST API ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23255), [tribela](https://github.com/mastodon/mastodon/pull/23428))
+- Add `reading:autoplay:gifs` to `/api/v1/preferences` ([j-f1](https://github.com/mastodon/mastodon/pull/22706))
+- Add `hide_collections` parameter to `/api/v1/accounts/credentials` ([CarlSchwan](https://github.com/mastodon/mastodon/pull/22790))
+- Add `policy` attribute to web push subscription objects in REST API at `/api/v1/push/subscriptions` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23210))
+- Add metrics endpoint to streaming API ([Gargron](https://github.com/mastodon/mastodon/pull/23388), [Gargron](https://github.com/mastodon/mastodon/pull/23469))
+- Add more specific error messages to HTTP signature verification ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21617))
+- Add Storj DCS to cloud object storage options in the `mastodon:setup` rake task ([jtolio](https://github.com/mastodon/mastodon/pull/21929))
+- Add checkmark symbol in the checkbox for sensitive media ([sidp](https://github.com/mastodon/mastodon/pull/22795))
+- Add missing accessibility attributes to logout link in modals ([kytta](https://github.com/mastodon/mastodon/pull/22549))
+- Add missing accessibility attributes to “Hide image” button in `MediaGallery` ([hs4man21](https://github.com/mastodon/mastodon/pull/22513))
+- Add missing accessibility attributes to hide content warning field when disabled ([hs4man21](https://github.com/mastodon/mastodon/pull/22568))
+- Add `aria-hidden` to footer circle dividers to improve accessibility ([hs4man21](https://github.com/mastodon/mastodon/pull/22576))
+- Add `lang` attribute to compose form inputs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23240))
+
+### Changed
+
+- **Ensure exact match is the first result in hashtag searches** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21315))
+- Change account search to return followed accounts first ([dariusk](https://github.com/mastodon/mastodon/pull/22956))
+- Change batch account suspension to create a strike ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20897))
+- Change default reply language to match the default language when replying to a translated post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22272))
+- Change misleading wording about waitlists ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20850))
+- Increase width of the unread notification border ([connorshea](https://github.com/mastodon/mastodon/pull/21692))
+- Change new post notification button on profiles to make it more apparent when it is enabled ([tribela](https://github.com/mastodon/mastodon/pull/22541))
+- Change trending tags admin interface to always show batch action controls ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23013))
+- Change wording of some OAuth scope descriptions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22491))
+- Change wording of admin report handling actions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18388))
+- Change confirm prompts for relationships management ([tribela](https://github.com/mastodon/mastodon/pull/19411))
+- Change language surrounding disability in prompts for media descriptions ([hs4man21](https://github.com/mastodon/mastodon/pull/20923))
+- Change confusing wording in the sign in banner ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22490))
+- Change `POST /settings/applications/:id` to regenerate token on scopes change ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23359))
+- Change account moderation notes to make links clickable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22553))
+- Change link previews for statuses to never use avatar as fallback ([Gargron](https://github.com/mastodon/mastodon/pull/23376))
+- Change email address input to be read-only for logged-in users when requesting a new confirmation e-mail ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23247))
+- Change notifications per page from 15 to 40 in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/23348))
+- Change number of stored items in home feed from 400 to 800 ([Gargron](https://github.com/mastodon/mastodon/pull/23349))
+- Change API rate limits from 300/5min per user to 1500/5min per user, 300/5min per app ([Gargron](https://github.com/mastodon/mastodon/pull/23347))
+- Save avatar or header correctly even if the other one fails ([tribela](https://github.com/mastodon/mastodon/pull/18465))
+- Change `referrer-policy` to `same-origin` application-wide ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23014), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23037))
+- Add 'private' to `Cache-Control`, match Rails expectations ([daxtens](https://github.com/mastodon/mastodon/pull/20608))
+- Make the button that expands the compose form differentiable from the button that publishes a post ([Tak](https://github.com/mastodon/mastodon/pull/20864))
+- Change automatic post deletion configuration to be accessible to moved users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20774))
+- Make tag following idempotent ([trwnh](https://github.com/mastodon/mastodon/pull/20860), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/21285))
+- Use buildx functions for faster builds ([inductor](https://github.com/mastodon/mastodon/pull/20692))
+- Split off Dockerfile components for faster builds ([moritzheiber](https://github.com/mastodon/mastodon/pull/20933), [ineffyble](https://github.com/mastodon/mastodon/pull/20948), [BtbN](https://github.com/mastodon/mastodon/pull/21028))
+- Change last occurrence of “silence” to “limit” in UI text ([cincodenada](https://github.com/mastodon/mastodon/pull/20637))
+- Change “hide toot” to “hide post” ([seanthegeek](https://github.com/mastodon/mastodon/pull/22385))
+- Don't allow URLs that contain non-normalized paths to be verified ([dgl](https://github.com/mastodon/mastodon/pull/20999))
+- Change the “Trending now” header to be a link to the Explore page ([connorshea](https://github.com/mastodon/mastodon/pull/21759))
+- Change PostgreSQL connection timeout from 2 minutes to 15 seconds ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21790))
+- Make handle more easily selectable on profile page ([cadars](https://github.com/mastodon/mastodon/pull/21479))
+- Allow admins to refresh remotely-suspended accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22327))
+- Change dropdown menu to contain “Copy link to post” even for non-public posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21316))
+- Allow adding relays in secure mode and limited federation mode ([ineffyble](https://github.com/mastodon/mastodon/pull/22324))
+- Change timestamps to be displayed using the user's timezone throughout the moderation interface ([FrancisMurillo](https://github.com/mastodon/mastodon/pull/21878), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/22555))
+- Change CSP directives on API to be tight and concise ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20960))
+- Change web UI to not autofocus the compose form ([raboof](https://github.com/mastodon/mastodon/pull/16517), [Akkiesoft](https://github.com/mastodon/mastodon/pull/23094))
+- Change idempotency key handling for posting when database access is slow ([lambda](https://github.com/mastodon/mastodon/pull/21840))
+- Change remote media files to be downloaded outside of transactions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21796))
+- Improve contrast of charts in “poll has ended” notifications ([j-f1](https://github.com/mastodon/mastodon/pull/22575))
+- Change OEmbed detection and validation to be somewhat more lenient ([ineffyble](https://github.com/mastodon/mastodon/pull/22533))
+- Widen ElasticSearch version detection to not display a warning for OpenSearch ([VyrCossont](https://github.com/mastodon/mastodon/pull/22422), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23064))
+- Change link verification to allow pages larger than 1MB as long as the link is in the first 1MB ([untitaker](https://github.com/mastodon/mastodon/pull/22879))
+- Update default Node.js version to Node.js 16 ([ineffyble](https://github.com/mastodon/mastodon/pull/22223), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/22342))
+
+### Removed
+
+- Officially remove support for Ruby 2.6 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21477))
+- Remove `object-fit` polyfill used for old versions of Microsoft Edge ([shuuji3](https://github.com/mastodon/mastodon/pull/22693))
+- Remove `intersection-observer` polyfill for old Safari support ([shuuji3](https://github.com/mastodon/mastodon/pull/23284))
+- Remove empty `title` tag from mailer layout ([nametoolong](https://github.com/mastodon/mastodon/pull/23078))
+- Remove post count and last posts from ActivityPub representation of hashtag collections ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23460))
+
+### Fixed
+
+- **Fix changing domain block severity not undoing individual account effects** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22135))
+- Fix suspension worker crashing on S3-compatible setups without ACL support ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22487))
+- Fix possible race conditions when suspending/unsuspending accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22363))
+- Fix being stuck in edit mode when deleting the edited posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22126))
+- Fix attached media uploads not being cleared when replying to a post ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23504))
+- Fix filters not being applied to some notification types ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23211))
+- Fix incorrect link in push notifications for some event types ([elizabeth-dev](https://github.com/mastodon/mastodon/pull/23286))
+- Fix some performance issues with `/admin/instances` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21907))
+- Fix some pre-4.0 admin audit logs ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22091))
+- Fix moderation audit log items for warnings having incorrect links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23242))
+- Fix account activation being sometimes triggered before email confirmation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23245))
+- Fix missing OAuth scopes for admin APIs ([trwnh](https://github.com/mastodon/mastodon/pull/20918), [trwnh](https://github.com/mastodon/mastodon/pull/20979))
+- Fix voter count not being cleared when a poll is reset ([afontenot](https://github.com/mastodon/mastodon/pull/21700))
+- Fix attachments of edited posts not being fetched ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21565))
+- Fix irreversible and whole_word parameters handling in `/api/v1/filters` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21988))
+- Fix 500 error when marking posts as sensitive while some of them are deleted ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22134))
+- Fix expanded posts not always being scrolled into view ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21797))
+- Fix not being able to scroll the remote interaction modal on small screens ([xendke](https://github.com/mastodon/mastodon/pull/21763))
+- Fix not being able to scroll in post history modal ([cadars](https://github.com/mastodon/mastodon/pull/23396))
+- Fix audio player volume control on Safari ([minacle](https://github.com/mastodon/mastodon/pull/23187))
+- Fix disappearing “Explore” tabs on Safari ([nyura](https://github.com/mastodon/mastodon/pull/20917), [ykzts](https://github.com/mastodon/mastodon/pull/20982))
+- Fix wrong padding in RTL layout ([Gargron](https://github.com/mastodon/mastodon/pull/23157))
+- Fix drag & drop upload area display in single-column mode ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23217))
+- Fix being unable to get a single EmailDomainBlock from the admin API ([trwnh](https://github.com/mastodon/mastodon/pull/20846))
+- Fix admin-set follow recommandations being case-sensitive ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23500))
+- Fix unserialized `role` on account entities in admin API ([Gargron](https://github.com/mastodon/mastodon/pull/23290))
+- Fix pagination of followed tags ([trwnh](https://github.com/mastodon/mastodon/pull/20861))
+- Fix dropdown menu positions when scrolling ([sidp](https://github.com/mastodon/mastodon/pull/22916), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23062))
+- Fix email with empty domain name labels passing validation ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23246))
+- Fix mysterious registration failure when “Require a reason to join” is set with open registrations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22127))
+- Fix attachment rendering of edited posts in OpenGraph ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22270))
+- Fix invalid/empty RSS feed link on account pages ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20772))
+- Fix error in `VerifyLinkService` when processing links with no href ([joshuap](https://github.com/mastodon/mastodon/pull/20741))
+- Fix error in `VerifyLinkService` when processing links with invalid URLs ([untitaker](https://github.com/mastodon/mastodon/pull/23204))
+- Fix media uploads with FFmpeg 5 ([dead10ck](https://github.com/mastodon/mastodon/pull/21191))
+- Fix sensitive flag not being set when replying to a post with a content warning under certain conditions ([kedamaDQ](https://github.com/mastodon/mastodon/pull/21724))
+- Fix misleading message briefly showing up when loading follow requests under some conditions ([c960657](https://github.com/mastodon/mastodon/pull/23386))
+- Fix “Share @:user's profile” profile menu item not working ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21490))
+- Fix crash and incorrect behavior in `tootctl domains crawl` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19004))
+- Fix autoplay on iOS ([jamesadney](https://github.com/mastodon/mastodon/pull/21422))
+- Fix user clean-up scheduler crash when an unconfirmed account has a moderation note ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23318))
+- Fix spaces not being stripped in admin account search ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21324))
+- Fix spaces not being stripped when adding relays ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22655))
+- Fix infinite loading spinner instead of soft 404 for non-existing remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21303))
+- Fix minor visual issue with the top border of verified account fields ([j-f1](https://github.com/mastodon/mastodon/pull/22006))
+- Fix pending account approval and rejection not being recorded in the admin audit log ([FrancisMurillo](https://github.com/mastodon/mastodon/pull/22088))
+- Fix “Sign up” button with closed registrations not opening modal on mobile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22060))
+- Fix UI header overflowing on mobile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21783))
+- Fix 500 error when trying to migrate to an invalid address ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21462))
+- Fix crash when trying to fetch unobtainable avatar of user using external authentication ([lochiiconnectivity](https://github.com/mastodon/mastodon/pull/22462))
+- Fix processing error on incoming malformed JSON-LD under some situations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23416))
+- Fix potential duplicate posts in Explore tab ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22121))
+- Fix deprecation warning in `tootctl accounts rotate` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22120))
+- Fix styling of featured tags in light theme ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23252))
+- Fix missing style in warning and strike cards ([AtelierSnek](https://github.com/mastodon/mastodon/pull/22177), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/22302))
+- Fix wasteful request to `/api/v1/custom_emojis` when not logged in ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22326))
+- Fix replies sometimes being delivered to user-blocked domains ([tribela](https://github.com/mastodon/mastodon/pull/22117))
+- Fix admin dashboard crash when using some ElasticSearch replacements ([cortices](https://github.com/mastodon/mastodon/pull/21006))
+- Fix profile avatar being slightly offset into left border ([RiedleroD](https://github.com/mastodon/mastodon/pull/20994))
+- Fix N+1 queries in `NotificationsController` ([nametoolong](https://github.com/mastodon/mastodon/pull/21202))
+- Fix being unable to react to announcements with the keycap number sign emoji ([kescherCode](https://github.com/mastodon/mastodon/pull/22231))
+- Fix height computation of post embeds ([hodgesmr](https://github.com/mastodon/mastodon/pull/22141))
+- Fix accessibility issue of the search bar due to hidden placeholder ([alexstine](https://github.com/mastodon/mastodon/pull/21275))
+- Fix layout change handler not being removed due to a typo ([nschonni](https://github.com/mastodon/mastodon/pull/21829))
+- Fix typo in the default `S3_HOSTNAME` used in the `mastodon:setup` rake task ([danp](https://github.com/mastodon/mastodon/pull/19932))
+- Fix the top action bar appearing in the multi-column layout ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20943))
+- Fix inability to use local LibreTranslate without setting `ALLOWED_PRIVATE_ADDRESSES` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21926))
+- Fix punycoded local domains not being prettified in initial state ([Tritlo](https://github.com/mastodon/mastodon/pull/21440))
+- Fix CSP violation warning by removing inline CSS from SVG logo ([luxiaba](https://github.com/mastodon/mastodon/pull/20814))
+- Fix margin for search field on medium window size ([minacle](https://github.com/mastodon/mastodon/pull/21606))
+- Fix search popout scrolling with the page in single-column mode ([rgroothuijsen](https://github.com/mastodon/mastodon/pull/16463))
+- Fix minor post cache hydration discrepancy ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/19879))
+- Fix `・` detection in hashtags ([parthoghosh24](https://github.com/mastodon/mastodon/pull/22888))
+- Fix hashtag follows bypassing user blocks ([tribela](https://github.com/mastodon/mastodon/pull/22849))
+- Fix moved accounts being incorrectly redirected to account settings when trying to view a remote profile ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22497))
+- Fix site upload validations ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22479))
+- Fix “Add new domain block” button using last submitted search value instead of the current one ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22485))
+- Fix misleading hashtag warning when posting with “Followers only” or “Mentioned people only” visibility ([n0toose](https://github.com/mastodon/mastodon/pull/22827))
+- Fix embedded posts with videos grabbing focus ([Akkiesoft](https://github.com/mastodon/mastodon/pull/22778))
+- Fix `$` not being escaped in `.env.production` files generated by the `mastodon:setup` rake task ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23012), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/23072))
+- Fix sanitizer parsing link text as HTML when stripping unsupported links ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22558))
+- Fix `scheduled_at` input not using `datetime-local` when editing announcements ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21896))
+- Fix REST API serializer for `Account` not including `moved` when the moved account has itself moved ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22483))
+- Fix `/api/v1/admin/trends/tags` using wrong serializer ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18943))
+- Fix situations in which instance actor can be set to a Mastodon-incompatible name ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22307))
+
+### Security
+
+- Add `form-action` CSP directive ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20781), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/20958), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/20962))
+- Fix unbounded recursion in account discovery ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/22025))
+- Revoke all authorized applications on password reset ([FrancisMurillo](https://github.com/mastodon/mastodon/pull/21325))
+- Fix unbounded recursion in post discovery ([ClearlyClaire,nametoolong](https://github.com/mastodon/mastodon/pull/23506))
+
 ## [4.0.2] - 2022-11-15
+
 ### Fixed
 
 - Fix wrong color on mentions hidden behind content warning in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/20724))
@@ -11,6 +291,7 @@ All notable changes to this project will be documented in this file.
 - Fix `unsafe-eval` being used when `wasm-unsafe-eval` is enough in Content Security Policy ([Gargron](https://github.com/mastodon/mastodon/pull/20729), [prplecake](https://github.com/mastodon/mastodon/pull/20606))
 
 ## [4.0.1] - 2022-11-14
+
 ### Fixed
 
 - Fix nodes order being sometimes mangled when rewriting emoji ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20677))
@@ -214,6 +495,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix out-of-bound reads in blurhash transcoder ([delroth](https://github.com/mastodon/mastodon/pull/20388))
 
 ## [3.5.3] - 2022-05-26
+
 ### Added
 
 - **Add language dropdown to compose form in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/18420), [ykzts](https://github.com/mastodon/mastodon/pull/18460))
@@ -261,6 +543,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix confirmation redirect to app without `Location` header ([Gargron](https://github.com/mastodon/mastodon/pull/18523))
 
 ## [3.5.2] - 2022-05-04
+
 ### Added
 
 - Add warning on direct messages screen in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/18289))
@@ -313,6 +596,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix error in alias settings page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18004))
 
 ## [3.5.1] - 2022-04-08
+
 ### Added
 
 - Add pagination for trending statuses in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17976))
@@ -356,6 +640,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix error when indexing statuses into Elasticsearch ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17912))
 
 ## [3.5.0] - 2022-03-30
+
 ### Added
 
 - **Add support for incoming edited posts** ([Gargron](https://github.com/mastodon/mastodon/pull/16697), [Gargron](https://github.com/mastodon/mastodon/pull/17727), [Gargron](https://github.com/mastodon/mastodon/pull/17728), [Gargron](https://github.com/mastodon/mastodon/pull/17320), [Gargron](https://github.com/mastodon/mastodon/pull/17404), [Gargron](https://github.com/mastodon/mastodon/pull/17390), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17335), [Gargron](https://github.com/mastodon/mastodon/pull/17696), [Gargron](https://github.com/mastodon/mastodon/pull/17745), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17740), [Gargron](https://github.com/mastodon/mastodon/pull/17697), [Gargron](https://github.com/mastodon/mastodon/pull/17648), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17531), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17499), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17498), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17380), [Gargron](https://github.com/mastodon/mastodon/pull/17373), [Gargron](https://github.com/mastodon/mastodon/pull/17334), [Gargron](https://github.com/mastodon/mastodon/pull/17333), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17699), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17748))
@@ -555,6 +840,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix being able to bypass e-mail restrictions ([Gargron](https://github.com/mastodon/mastodon/pull/17909))
 
 ## [3.4.6] - 2022-02-03
+
 ### Fixed
 
 - Fix `mastodon:webpush:generate_vapid_key` task requiring a functional environment ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17338))
@@ -569,6 +855,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Disable legacy XSS filtering ([Wonderfall](https://github.com/mastodon/mastodon/pull/17289))
 
 ## [3.4.5] - 2022-01-31
+
 ### Added
 
 - Add more advanced migration tests ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17393))
@@ -583,6 +870,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix followers synchronization mechanism ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16510))
 
 ## [3.4.4] - 2021-11-26
+
 ### Fixed
 
 - Fix error when suspending user with an already blocked canonical email ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17036))
@@ -600,11 +888,13 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix handling of recursive toots in WebUI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17041))
 
 ## [3.4.3] - 2021-11-06
+
 ### Fixed
 
 - Fix login being broken due to inaccurately applied backport fix in 3.4.2 ([Gargron](https://github.com/mastodon/mastodon/commit/5c47a18c8df3231aa25c6d1f140a71a7fac9cbf9))
 
 ## [3.4.2] - 2021-11-06
+
 ### Added
 
 - Add `configuration` attribute to `GET /api/v1/instance` ([Gargron](https://github.com/mastodon/mastodon/pull/16485))
@@ -648,6 +938,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix revoking a specific session not working ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16943))
 
 ## [3.4.1] - 2021-06-03
+
 ### Added
 
 - Add new emoji assets from Twemoji 13.1.0 ([Gargron](https://github.com/mastodon/mastodon/pull/16345))
@@ -667,6 +958,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix mailer jobs for deleted notifications erroring out ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16294))
 
 ## [3.4.0] - 2021-05-16
+
 ### Added
 
 - **Add follow recommendations for onboarding** ([Gargron](https://github.com/mastodon/mastodon/pull/15945), [Gargron](https://github.com/mastodon/mastodon/pull/16161), [Gargron](https://github.com/mastodon/mastodon/pull/16060), [Gargron](https://github.com/mastodon/mastodon/pull/16077), [Gargron](https://github.com/mastodon/mastodon/pull/16078), [Gargron](https://github.com/mastodon/mastodon/pull/16160), [Gargron](https://github.com/mastodon/mastodon/pull/16079), [noellabo](https://github.com/mastodon/mastodon/pull/16044), [noellabo](https://github.com/mastodon/mastodon/pull/16045), [Gargron](https://github.com/mastodon/mastodon/pull/16152), [Gargron](https://github.com/mastodon/mastodon/pull/16153), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16082), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16173), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16159), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/16189))
@@ -702,7 +994,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
   - This method allows an app through which a user signed-up to request a new confirmation e-mail to be sent, or to change the e-mail of the account before it is confirmed
 - Add `GET /api/v1/accounts/lookup` to REST API ([Gargron](https://github.com/mastodon/mastodon/pull/15740), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/15750))
   - This method allows to quickly convert a username of a known account to an ID that can be used with the REST API, or to check if a username is available
-  for sign-up
+    for sign-up
 - Add `policy` param to `POST /api/v1/push/subscriptions` in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/16040))
   - This param allows an app to control from whom notifications should be delivered as push notifications to the app
 - Add `details` to error response for `POST /api/v1/accounts` in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/15803))
@@ -812,6 +1104,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix app name, website and redirect URIs not having a maximum length ([Gargron](https://github.com/mastodon/mastodon/pull/16042))
 
 ## [3.3.0] - 2020-12-27
+
 ### Added
 
 - **Add hotkeys for audio/video control in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/15158), [Gargron](https://github.com/mastodon/mastodon/pull/15198))
@@ -988,6 +1281,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix resolving accounts sometimes creating duplicate records for a given ActivityPub identifier ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15364))
 
 ## [3.2.2] - 2020-12-19
+
 ### Added
 
 - Add `tootctl maintenance fix-duplicates` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14860), [Gargron](https://github.com/mastodon/mastodon/pull/15223))
@@ -1014,6 +1308,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix resolving accounts sometimes creating duplicate records for a given ActivityPub identifier ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/15364))
 
 ## [3.2.1] - 2020-10-19
+
 ### Added
 
 - Add support for latest HTTP Signatures spec draft ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14556))
@@ -1043,6 +1338,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix files served as `application/octet-stream` being rejected without attempting mime type detection ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14452))
 
 ## [3.2.0] - 2020-07-27
+
 ### Added
 
 - Add `SMTP_SSL` environment variable ([OmmyZhang](https://github.com/mastodon/mastodon/pull/14309))
@@ -1178,7 +1474,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix unique username constraint for local users not being enforced in database ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14099))
 - Fix unnecessary gap under video modal in web UI ([mfmfuyu](https://github.com/mastodon/mastodon/pull/14098))
 - Fix 2FA and sign in token pages not respecting user locale ([mfmfuyu](https://github.com/mastodon/mastodon/pull/14087))
-- Fix unapproved users being able to view profiles when in limited-federation mode *and* requiring approval for sign-ups ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14093))
+- Fix unapproved users being able to view profiles when in limited-federation mode _and_ requiring approval for sign-ups ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14093))
 - Fix initial audio volume not corresponding to what's displayed in audio player in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14057))
 - Fix timelines sometimes jumping when closing modals in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14019))
 - Fix memory usage of downloading remote files ([Gargron](https://github.com/mastodon/mastodon/pull/14184), [Gargron](https://github.com/mastodon/mastodon/pull/14181), [noellabo](https://github.com/mastodon/mastodon/pull/14356))
@@ -1196,6 +1492,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
   - Clear out media attachments in a separate worker (slow)
 
 ## [3.1.5] - 2020-07-07
+
 ### Security
 
 - Fix media attachment enumeration ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/14254))
@@ -1203,6 +1500,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix other sessions not being logged out on password change ([Gargron](https://github.com/mastodon/mastodon/pull/14252))
 
 ## [3.1.4] - 2020-05-14
+
 ### Added
 
 - Add `vi` to available locales ([taicv](https://github.com/mastodon/mastodon/pull/13542))
@@ -1241,7 +1539,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix regression in `tootctl media remove-orphans` ([Gargron](https://github.com/mastodon/mastodon/pull/13405))
 - Fix old unique jobs digests not having been cleaned up ([Gargron](https://github.com/mastodon/mastodon/pull/13683))
 - Fix own following/followers not showing muted users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13614))
-- Fix list of followed people ignoring sorting on Follows & Followers page  ([taras2358](https://github.com/mastodon/mastodon/pull/13676))
+- Fix list of followed people ignoring sorting on Follows & Followers page ([taras2358](https://github.com/mastodon/mastodon/pull/13676))
 - Fix wrong pgHero Content-Security-Policy when `CDN_HOST` is set ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13595))
 - Fix needlessly deduplicating usernames on collisions with remote accounts when signing-up through SAML/CAS ([kaiyou](https://github.com/mastodon/mastodon/pull/13581))
 - Fix page incorrectly scrolling when bringing up dropdown menus in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13574))
@@ -1270,6 +1568,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
   - The issue only affects developers of apps who are shared between multiple users, such as server-side apps like cross-posters
 
 ## [3.1.3] - 2020-04-05
+
 ### Added
 
 - Add ability to filter audit log in admin UI ([Gargron](https://github.com/mastodon/mastodon/pull/13381))
@@ -1343,6 +1642,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix re-sending of e-mail confirmation not being rate limited ([Gargron](https://github.com/mastodon/mastodon/pull/13360))
 
 ## [v3.1.2] - 2020-02-27
+
 ### Added
 
 - Add `--reset-password` option to `tootctl accounts modify` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/13126))
@@ -1369,11 +1669,13 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix leak of arbitrary statuses through unfavourite action in REST API ([Gargron](https://github.com/mastodon/mastodon/pull/13161))
 
 ## [3.1.1] - 2020-02-10
+
 ### Fixed
 
 - Fix yanked dependency preventing installation ([mayaeh](https://github.com/mastodon/mastodon/pull/13059))
 
 ## [3.1.0] - 2020-02-09
+
 ### Added
 
 - Add bookmarks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/7107), [Gargron](https://github.com/mastodon/mastodon/pull/12494), [Gomasy](https://github.com/mastodon/mastodon/pull/12381))
@@ -1538,6 +1840,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix settings pages being cacheable by the browser ([Gargron](https://github.com/mastodon/mastodon/pull/12714))
 
 ## [3.0.1] - 2019-10-10
+
 ### Added
 
 - Add `tootctl media usage` command ([Gargron](https://github.com/mastodon/mastodon/pull/12115))
@@ -1571,6 +1874,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix `tootctl accounts cull` advertising unused option flag ([Kjwon15](https://github.com/mastodon/mastodon/pull/12074))
 
 ## [3.0.0] - 2019-10-03
+
 ### Added
 
 - Add "not available" label to unloaded media attachments in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/11715), [Gargron](https://github.com/mastodon/mastodon/pull/11745))
@@ -1767,6 +2071,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix performance of GIF re-encoding and always strip EXIF data from videos ([Gargron](https://github.com/mastodon/mastodon/pull/12057))
 
 ## [2.9.3] - 2019-08-10
+
 ### Added
 
 - Add GIF and WebP support for custom emojis ([Gargron](https://github.com/mastodon/mastodon/pull/11519))
@@ -1826,6 +2131,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix blocked domains still being able to fill database with account records ([Gargron](https://github.com/mastodon/mastodon/pull/11219))
 
 ## [2.9.2] - 2019-06-22
+
 ### Added
 
 - Add `short_description` and `approval_required` to `GET /api/v1/instance` ([Gargron](https://github.com/mastodon/mastodon/pull/11146))
@@ -1840,6 +2146,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix audio not being downloaded from remote servers ([Gargron](https://github.com/mastodon/mastodon/pull/11145))
 
 ## [2.9.1] - 2019-06-22
+
 ### Added
 
 - Add moderation API ([Gargron](https://github.com/mastodon/mastodon/pull/9387))
@@ -1865,6 +2172,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix scrolling behaviour in compose form ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/11093))
 
 ## [2.9.0] - 2019-06-13
+
 ### Added
 
 - **Add single-column mode in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/10807), [Gargron](https://github.com/mastodon/mastodon/pull/10848), [Gargron](https://github.com/mastodon/mastodon/pull/11003), [Gargron](https://github.com/mastodon/mastodon/pull/10961), [Hanage999](https://github.com/mastodon/mastodon/pull/10915), [noellabo](https://github.com/mastodon/mastodon/pull/10917), [abcang](https://github.com/mastodon/mastodon/pull/10859), [Gargron](https://github.com/mastodon/mastodon/pull/10820), [Gargron](https://github.com/mastodon/mastodon/pull/10835), [Gargron](https://github.com/mastodon/mastodon/pull/10809), [Gargron](https://github.com/mastodon/mastodon/pull/10963), [noellabo](https://github.com/mastodon/mastodon/pull/10883), [Hanage999](https://github.com/mastodon/mastodon/pull/10839))
@@ -1919,6 +2227,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix login sometimes redirecting to paths that are not pages ([Gargron](https://github.com/mastodon/mastodon/pull/11019))
 
 ## [2.8.4] - 2019-05-24
+
 ### Fixed
 
 - Fix delivery not retrying on some inbox errors that should be retriable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10812))
@@ -1930,6 +2239,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Require specific OAuth scopes for specific endpoints of the streaming API, instead of merely requiring a token for all endpoints, and allow using WebSockets protocol negotiation to specify the access token instead of using a query string ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10818))
 
 ## [2.8.3] - 2019-05-19
+
 ### Added
 
 - Add `og:image:alt` OpenGraph tag ([BenLubar](https://github.com/mastodon/mastodon/pull/10779))
@@ -1952,6 +2262,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix "invited by" not showing up in admin UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10791))
 
 ## [2.8.2] - 2019-05-05
+
 ### Added
 
 - Add `SOURCE_TAG` environment variable ([ushitora-anqou](https://github.com/mastodon/mastodon/pull/10698))
@@ -1964,6 +2275,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix closing video modal scrolling timelines to top ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10695))
 
 ## [2.8.1] - 2019-05-04
+
 ### Added
 
 - Add link to existing domain block when trying to block an already-blocked domain ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10663))
@@ -2003,6 +2315,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix confirmation modals being too narrow for a secondary action button ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10586))
 
 ## [2.8.0] - 2019-04-10
+
 ### Added
 
 - Add polls ([Gargron](https://github.com/mastodon/mastodon/pull/10111), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10155), [Gargron](https://github.com/mastodon/mastodon/pull/10184), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10196), [Gargron](https://github.com/mastodon/mastodon/pull/10248), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10255), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10322), [Gargron](https://github.com/mastodon/mastodon/pull/10138), [Gargron](https://github.com/mastodon/mastodon/pull/10139), [Gargron](https://github.com/mastodon/mastodon/pull/10144), [Gargron](https://github.com/mastodon/mastodon/pull/10145),[Gargron](https://github.com/mastodon/mastodon/pull/10146), [Gargron](https://github.com/mastodon/mastodon/pull/10148), [Gargron](https://github.com/mastodon/mastodon/pull/10151), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10150), [Gargron](https://github.com/mastodon/mastodon/pull/10168), [Gargron](https://github.com/mastodon/mastodon/pull/10165), [Gargron](https://github.com/mastodon/mastodon/pull/10172), [Gargron](https://github.com/mastodon/mastodon/pull/10170), [Gargron](https://github.com/mastodon/mastodon/pull/10171), [Gargron](https://github.com/mastodon/mastodon/pull/10186), [Gargron](https://github.com/mastodon/mastodon/pull/10189), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10200), [rinsuki](https://github.com/mastodon/mastodon/pull/10203), [Gargron](https://github.com/mastodon/mastodon/pull/10213), [Gargron](https://github.com/mastodon/mastodon/pull/10246), [Gargron](https://github.com/mastodon/mastodon/pull/10265), [Gargron](https://github.com/mastodon/mastodon/pull/10261), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10333), [Gargron](https://github.com/mastodon/mastodon/pull/10352), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10140), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10142), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10141), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10162), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10161), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10158), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10156), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10160), [Gargron](https://github.com/mastodon/mastodon/pull/10185), [Gargron](https://github.com/mastodon/mastodon/pull/10188), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10195), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10208), [Gargron](https://github.com/mastodon/mastodon/pull/10187), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10214), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/10209))
@@ -2086,6 +2399,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix `tootctl accounts cull` sometimes removing accounts that are temporarily unreachable ([BenLubar](https://github.com/mastodon/mastodon/pull/10460))
 
 ## [2.7.4] - 2019-03-05
+
 ### Fixed
 
 - Fix web UI not cleaning up notifications after block ([Gargron](https://github.com/mastodon/mastodon/pull/10108))
@@ -2100,6 +2414,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix edit profile page crash for suspended-then-unsuspended users ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10178))
 
 ## [2.7.3] - 2019-02-23
+
 ### Added
 
 - Add domain filter to the admin federation page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10071))
@@ -2117,6 +2432,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Change custom emojis to randomize stored file name ([hinaloe](https://github.com/mastodon/mastodon/pull/10090))
 
 ## [2.7.2] - 2019-02-17
+
 ### Added
 
 - Add support for IPv6 in e-mail validation ([zoc](https://github.com/mastodon/mastodon/pull/10009))
@@ -2158,6 +2474,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Change error graphic to hover-to-play ([Gargron](https://github.com/mastodon/mastodon/pull/10055))
 
 ## [2.7.1] - 2019-01-28
+
 ### Fixed
 
 - Fix SSO authentication not working due to missing agreement boolean ([Gargron](https://github.com/mastodon/mastodon/pull/9915))
@@ -2172,6 +2489,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix missing strong style for landing page description ([Kjwon15](https://github.com/mastodon/mastodon/pull/9892))
 
 ## [2.7.0] - 2019-01-20
+
 ### Added
 
 - Add link for adding a user to a list from their profile ([namelessGonbai](https://github.com/mastodon/mastodon/pull/9062))
@@ -2301,6 +2619,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Add tombstones for remote statuses to prevent replay attacks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9830))
 
 ## [2.6.5] - 2018-12-01
+
 ### Changed
 
 - Change lists to display replies to others on the list and list owner ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9324))
@@ -2310,11 +2629,13 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix failures caused by commonly-used JSON-LD contexts being unavailable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9412))
 
 ## [2.6.4] - 2018-11-30
+
 ### Fixed
 
 - Fix yarn dependencies not installing due to yanked event-stream package ([Gargron](https://github.com/mastodon/mastodon/pull/9401))
 
 ## [2.6.3] - 2018-11-30
+
 ### Added
 
 - Add hyphen to characters allowed in remote usernames ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/9345))
@@ -2334,6 +2655,7 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix TLS handshake timeout not being enforced ([Gargron](https://github.com/mastodon/mastodon/pull/9381))
 
 ## [2.6.2] - 2018-11-23
+
 ### Added
 
 - Add Page to whitelisted ActivityPub types ([mbajur](https://github.com/mastodon/mastodon/pull/9188))
@@ -2368,12 +2690,14 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix HTTP connection timeout of 10s not being enforced ([Gargron](https://github.com/mastodon/mastodon/pull/9329))
 
 ## [2.6.1] - 2018-10-30
+
 ### Fixed
 
 - Fix resolving resources by URL not working due to a regression in [valerauko](https://github.com/mastodon/mastodon/pull/9132) ([Gargron](https://github.com/mastodon/mastodon/pull/9171))
 - Fix reducer error in web UI when a conversation has no last status ([Gargron](https://github.com/mastodon/mastodon/pull/9173))
 
 ## [2.6.0] - 2018-10-30
+
 ### Added
 
 - Add link ownership verification ([Gargron](https://github.com/mastodon/mastodon/pull/8703))
@@ -2478,11 +2802,13 @@ Some of the features in this release have been funded through the [NGI0 Discover
 - Fix handling of content types with profile ([valerauko](https://github.com/mastodon/mastodon/pull/9132))
 
 ## [2.5.2] - 2018-10-12
+
 ### Security
 
 - Fix XSS vulnerability ([Gargron](https://github.com/mastodon/mastodon/pull/8959))
 
 ## [2.5.1] - 2018-10-07
+
 ### Fixed
 
 - Fix database migrations for PostgreSQL below 9.5 ([Gargron](https://github.com/mastodon/mastodon/pull/8903))
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index b3eafdc48..2ee2e538b 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
 
 Examples of behavior that contributes to creating a positive environment include:
 
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
+- Using welcoming and inclusive language
+- Being respectful of differing viewpoints and experiences
+- Gracefully accepting constructive criticism
+- Focusing on what is best for the community
+- Showing empathy towards other community members
 
 Examples of unacceptable behavior by participants include:
 
-* The use of sexualized language or imagery and unwelcome sexual attention or advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a professional setting
+- The use of sexualized language or imagery and unwelcome sexual attention or advances
+- Trolling, insulting/derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or electronic address, without explicit permission
+- Other conduct which could reasonably be considered inappropriate in a professional setting
 
 ## Our Responsibilities
 
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ed670f5a6..a232915b6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,10 +1,10 @@
-#  Contributing to Mastodon Glitch Edition  #
+# Contributing to Mastodon Glitch Edition
 
 Thank you for your interest in contributing to the `glitch-soc` project!
 Here are some guidelines, and ways you can help.
 
->   (This document is a bit of a work-in-progress, so please bear with us.
->   If you don't see what you're looking for here, please don't hesitate to reach out!)
+> (This document is a bit of a work-in-progress, so please bear with us.
+> If you don't see what you're looking for here, please don't hesitate to reach out!)
 
 ## Translations
 
@@ -12,34 +12,32 @@ You can submit glitch-soc-specific translations via [Crowdin](https://crowdin.co
 
 [![Crowdin](https://badges.crowdin.net/glitch-soc/localized.svg)](https://crowdin.com/project/glitch-soc)
 
-##  Planning  ##
+## Planning
 
 Right now a lot of the planning for this project takes place in our development Discord, or through GitHub Issues and Projects.
 We're working on ways to improve the planning structure and better solicit feedback, and if you feel like you can help in this respect, feel free to give us a holler.
 
-##  Documentation  ##
+## Documentation
 
 The documentation for this repository is available at [`glitch-soc/docs`](https://github.com/glitch-soc/docs) (online at [glitch-soc.github.io/docs/](https://glitch-soc.github.io/docs/)).
 Right now, we've mostly focused on the features that make this fork different from upstream in some manner.
 Adding screenshots, improving descriptions, and so forth are all ways to help contribute to the project even if you don't know any code.
 
-##  Frontend Development  ##
+## Frontend Development
 
 Check out [the documentation here](https://glitch-soc.github.io/docs/contributing/frontend/) for more information.
 
-##  Backend Development  ##
+## Backend Development
 
 See the guidelines below.
 
- - - -
+---
 
 You should also try to follow the guidelines set out in the original `CONTRIBUTING.md` from `mastodon/mastodon`, reproduced below.
 
 <blockquote>
 
-CONTRIBUTING
-=======
-Contributing
+# Contributing
 
 Thank you for considering contributing to Mastodon 🐘
 
@@ -68,9 +66,9 @@ You can submit translations via [Crowdin](https://crowdin.com/project/mastodon).
 
 Example:
 
-|Not ideal|Better|
-|---|----|
-|Fixed NoMethodError in RemovalWorker|Fix nil error when removing statuses caused by race condition|
+| Not ideal                            | Better                                                        |
+| ------------------------------------ | ------------------------------------------------------------- |
+| Fixed NoMethodError in RemovalWorker | Fix nil error when removing statuses caused by race condition |
 
 It is not always possible to phrase every change in such a manner, but it is desired.
 
@@ -82,8 +80,6 @@ It is not always possible to phrase every change in such a manner, but it is des
 - Code style rules (rubocop, eslint)
 - Normalization of locale files (i18n-tasks)
 
-**Note**: You may need to log in and authorise the GitHub account your fork of this repository belongs to with CircleCI to enable some of the automated checks to run.
-
 ## Documentation
 
 The [Mastodon documentation](https://docs.joinmastodon.org) is a statically generated site. You can [submit merge requests to mastodon/documentation](https://github.com/mastodon/documentation).
diff --git a/Capfile b/Capfile
index bf3ae7e24..86efa5bac 100644
--- a/Capfile
+++ b/Capfile
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 require 'capistrano/setup'
 require 'capistrano/deploy'
 require 'capistrano/scm/git'
diff --git a/Dockerfile b/Dockerfile
index df2251425..bee45dd81 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,8 +1,8 @@
 # syntax=docker/dockerfile:1.4
 # This needs to be bullseye-slim because the Ruby image is built on bullseye-slim
-ARG NODE_VERSION="16.18.1-bullseye-slim"
+ARG NODE_VERSION="16.20-bullseye-slim"
 
-FROM ghcr.io/moritzheiber/ruby-jemalloc:3.0.4-slim as ruby
+FROM ghcr.io/moritzheiber/ruby-jemalloc:3.2.2-slim as ruby
 FROM node:${NODE_VERSION} as build
 
 COPY --link --from=ruby /opt/ruby /opt/ruby
@@ -18,7 +18,6 @@ COPY Gemfile* package.json yarn.lock /opt/mastodon/
 # hadolint ignore=DL3008
 RUN apt-get update && \
     apt-get install -y --no-install-recommends build-essential \
-        ca-certificates \
         git \
         libicu-dev \
         libidn11-dev \
@@ -37,7 +36,8 @@ RUN apt-get update && \
     bundle config set --local without 'development test' && \
     bundle config set silence_root_warning true && \
     bundle install -j"$(nproc)" && \
-    yarn install --pure-lockfile --network-timeout 600000
+    yarn install --pure-lockfile --production --network-timeout 600000 && \
+    yarn cache clean
 
 FROM node:${NODE_VERSION}
 
@@ -93,8 +93,7 @@ ARG SOURCE_COMMIT
 ENV SOURCE_TAG $SOURCE_COMMIT
 
 # Precompile assets
-RUN OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder rails assets:precompile && \
-    yarn cache clean
+RUN OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder rails assets:precompile
 
 # Empty temporary files
 USER root
diff --git a/Gemfile b/Gemfile
index 930ff6c4a..d175d7412 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,36 +1,35 @@
 # frozen_string_literal: true
 
 source 'https://rubygems.org'
-ruby '>= 2.7.0', '< 3.1.0'
+ruby '>= 2.7.0', '< 3.3.0'
 
 gem 'pkg-config', '~> 1.5'
-gem 'rexml', '~> 3.2'
 
-gem 'puma', '~> 5.6'
+gem 'puma', '~> 6.2'
 gem 'rails', '~> 6.1.7'
 gem 'sprockets', '~> 3.7.2'
 gem 'thor', '~> 1.2'
-gem 'rack', '~> 2.2.5'
+gem 'rack', '~> 2.2.6'
 
-gem 'hamlit-rails', '~> 0.2'
+gem 'haml-rails', '~>2.0'
 gem 'pg', '~> 1.4'
 gem 'makara', '~> 0.5'
-gem 'pghero', '~> 2.8'
+gem 'pghero'
 gem 'dotenv-rails', '~> 2.8'
 
-gem 'aws-sdk-s3', '~> 1.117', require: false
+gem 'aws-sdk-s3', '~> 1.120', require: false
 gem 'fog-core', '<= 2.4.0'
 gem 'fog-openstack', '~> 0.3', require: false
-gem 'kt-paperclip', '~> 7.1'
+gem 'kt-paperclip', '~> 7.1', github: 'kreeti/kt-paperclip', ref: '11abf222dc31bff71160a1d138b445214f434b2b'
 gem 'blurhash', '~> 0.1'
 
 gem 'active_model_serializers', '~> 0.10'
 gem 'addressable', '~> 2.8'
-gem 'bootsnap', '~> 1.15.0', require: false
+gem 'bootsnap', '~> 1.16.0', require: false
 gem 'browser'
 gem 'charlock_holmes', '~> 0.7.7'
 gem 'chewy', '~> 7.2'
-gem 'devise', '~> 4.8'
+gem 'devise', '~> 4.9'
 gem 'devise-two-factor', '~> 4.0'
 
 group :pam_authentication, optional: true do
@@ -40,7 +39,7 @@ end
 gem 'net-ldap', '~> 0.17'
 gem 'omniauth-cas', '~> 2.0'
 gem 'omniauth-saml', '~> 1.10'
-gem 'gitlab-omniauth-openid-connect', '~>0.10.0', require: 'omniauth_openid_connect'
+gem 'omniauth_openid_connect', '~> 0.6.1'
 gem 'omniauth', '~> 1.9'
 gem 'omniauth-rails_csrf_protection', '~> 0.1'
 
@@ -60,9 +59,9 @@ gem 'idn-ruby', require: 'idn'
 gem 'kaminari', '~> 1.2'
 gem 'link_header', '~> 0.0'
 gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar'
-gem 'nokogiri', '~> 1.13'
+gem 'nokogiri', '~> 1.14'
 gem 'nsa', '~> 0.2'
-gem 'oj', '~> 3.13'
+gem 'oj', '~> 3.14'
 gem 'ox', '~> 2.14'
 gem 'parslet'
 gem 'posix-spawn'
@@ -70,10 +69,10 @@ gem 'public_suffix', '~> 5.0'
 gem 'pundit', '~> 2.3'
 gem 'premailer-rails'
 gem 'rack-attack', '~> 6.6'
-gem 'rack-cors', '~> 1.1', require: 'rack/cors'
+gem 'rack-cors', '~> 2.0', require: 'rack/cors'
 gem 'rails-i18n', '~> 6.0'
-gem 'rails-settings-cached', '~> 0.6'
-gem 'redcarpet', '~> 3.5'
+gem 'rails-settings-cached', '~> 0.6', git: 'https://github.com/mastodon/rails-settings-cached.git', branch: 'v0.6.6-aliases-true'
+gem 'redcarpet', '~> 3.6'
 gem 'redis', '~> 4.5', require: ['redis', 'redis/connection/hiredis']
 gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
 gem 'rqrcode', '~> 2.1'
@@ -81,20 +80,20 @@ gem 'ruby-progressbar', '~> 1.11'
 gem 'sanitize', '~> 6.0'
 gem 'scenic', '~> 1.7'
 gem 'sidekiq', '~> 6.5'
-gem 'sidekiq-scheduler', '~> 4.0'
+gem 'sidekiq-scheduler', '~> 5.0'
 gem 'sidekiq-unique-jobs', '~> 7.1'
 gem 'sidekiq-bulk', '~> 0.2.0'
 gem 'simple-navigation', '~> 4.4'
-gem 'simple_form', '~> 5.1'
+gem 'simple_form', '~> 5.2'
 gem 'sprockets-rails', '~> 3.4', require: 'sprockets/railtie'
 gem 'stoplight', '~> 3.0.1'
-gem 'strong_migrations', '~> 0.7'
+gem 'strong_migrations', '~> 0.8'
 gem 'tty-prompt', '~> 0.23', require: false
 gem 'twitter-text', '~> 3.1.0'
-gem 'tzinfo-data', '~> 1.2022'
+gem 'tzinfo-data', '~> 1.2023'
 gem 'webpacker', '~> 5.4'
 gem 'webpush', github: 'ClearlyClaire/webpush', ref: 'f14a4d52e201128b1b00245d11b6de80d6cfdcd9'
-gem 'webauthn', '~> 2.5'
+gem 'webauthn', '~> 3.0'
 
 gem 'json-ld'
 gem 'json-ld-preloaded', '~> 3.2'
@@ -104,9 +103,10 @@ group :development, :test do
   gem 'fabrication', '~> 2.30'
   gem 'fuubar', '~> 2.5'
   gem 'i18n-tasks', '~> 1.0', require: false
-  gem 'pry-byebug', '~> 3.10'
-  gem 'pry-rails', '~> 0.3'
-  gem 'rspec-rails', '~> 5.1'
+  gem 'rspec-rails', '~> 6.0'
+  gem 'rspec_chunked', '~> 0.6'
+
+  gem 'rubocop-capybara', require: false
   gem 'rubocop-performance', require: false
   gem 'rubocop-rails', require: false
   gem 'rubocop-rspec', require: false
@@ -118,11 +118,11 @@ group :production, :test do
 end
 
 group :test do
-  gem 'capybara', '~> 3.38'
-  gem 'climate_control', '~> 0.2'
+  gem 'capybara', '~> 3.39'
+  gem 'climate_control'
   gem 'faker', '~> 3.1'
   gem 'json-schema', '~> 3.0'
-  gem 'rack-test', '~> 2.0'  
+  gem 'rack-test', '~> 2.1'
   gem 'rails-controller-testing', '~> 1.0'
   gem 'rspec_junit_formatter', '~> 0.6'
   gem 'rspec-sidekiq', '~> 3.1'
@@ -131,16 +131,15 @@ group :test do
 end
 
 group :development do
-  gem 'active_record_query_trace', '~> 1.8'
   gem 'annotate', '~> 3.2'
   gem 'better_errors', '~> 2.9'
   gem 'binding_of_caller', '~> 1.0'
-  gem 'bullet', '~> 7.0'
   gem 'letter_opener', '~> 1.8'
   gem 'letter_opener_web', '~> 2.0'
   gem 'memory_profiler'
   gem 'brakeman', '~> 5.4', require: false
   gem 'bundler-audit', '~> 0.9', require: false
+  gem 'haml_lint', require: false
 
   gem 'capistrano', '~> 3.17'
   gem 'capistrano-rails', '~> 1.6'
@@ -160,3 +159,5 @@ gem 'xorcist', '~> 1.1'
 
 gem 'hcaptcha', '~> 7.1'
 gem 'cocoon', '~> 1.2'
+
+gem 'net-http', '~> 0.3.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index 80c46f07c..5b05b79d6 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -7,43 +7,63 @@ GIT
       hkdf (~> 0.2)
       jwt (~> 2.0)
 
+GIT
+  remote: https://github.com/kreeti/kt-paperclip.git
+  revision: 11abf222dc31bff71160a1d138b445214f434b2b
+  ref: 11abf222dc31bff71160a1d138b445214f434b2b
+  specs:
+    kt-paperclip (7.1.1)
+      activemodel (>= 4.2.0)
+      activesupport (>= 4.2.0)
+      marcel (~> 1.0.1)
+      mime-types
+      terrapin (~> 0.6.0)
+
+GIT
+  remote: https://github.com/mastodon/rails-settings-cached.git
+  revision: 86328ef0bd04ce21cc0504ff5e334591e8c2ccab
+  branch: v0.6.6-aliases-true
+  specs:
+    rails-settings-cached (0.6.6)
+      rails (>= 4.2.0)
+
 GEM
   remote: https://rubygems.org/
   specs:
-    actioncable (6.1.7)
-      actionpack (= 6.1.7)
-      activesupport (= 6.1.7)
+    actioncable (6.1.7.3)
+      actionpack (= 6.1.7.3)
+      activesupport (= 6.1.7.3)
       nio4r (~> 2.0)
       websocket-driver (>= 0.6.1)
-    actionmailbox (6.1.7)
-      actionpack (= 6.1.7)
-      activejob (= 6.1.7)
-      activerecord (= 6.1.7)
-      activestorage (= 6.1.7)
-      activesupport (= 6.1.7)
+    actionmailbox (6.1.7.3)
+      actionpack (= 6.1.7.3)
+      activejob (= 6.1.7.3)
+      activerecord (= 6.1.7.3)
+      activestorage (= 6.1.7.3)
+      activesupport (= 6.1.7.3)
       mail (>= 2.7.1)
-    actionmailer (6.1.7)
-      actionpack (= 6.1.7)
-      actionview (= 6.1.7)
-      activejob (= 6.1.7)
-      activesupport (= 6.1.7)
+    actionmailer (6.1.7.3)
+      actionpack (= 6.1.7.3)
+      actionview (= 6.1.7.3)
+      activejob (= 6.1.7.3)
+      activesupport (= 6.1.7.3)
       mail (~> 2.5, >= 2.5.4)
       rails-dom-testing (~> 2.0)
-    actionpack (6.1.7)
-      actionview (= 6.1.7)
-      activesupport (= 6.1.7)
+    actionpack (6.1.7.3)
+      actionview (= 6.1.7.3)
+      activesupport (= 6.1.7.3)
       rack (~> 2.0, >= 2.0.9)
       rack-test (>= 0.6.3)
       rails-dom-testing (~> 2.0)
       rails-html-sanitizer (~> 1.0, >= 1.2.0)
-    actiontext (6.1.7)
-      actionpack (= 6.1.7)
-      activerecord (= 6.1.7)
-      activestorage (= 6.1.7)
-      activesupport (= 6.1.7)
+    actiontext (6.1.7.3)
+      actionpack (= 6.1.7.3)
+      activerecord (= 6.1.7.3)
+      activestorage (= 6.1.7.3)
+      activesupport (= 6.1.7.3)
       nokogiri (>= 1.8.5)
-    actionview (6.1.7)
-      activesupport (= 6.1.7)
+    actionview (6.1.7.3)
+      activesupport (= 6.1.7.3)
       builder (~> 3.1)
       erubi (~> 1.4)
       rails-dom-testing (~> 2.0)
@@ -53,29 +73,28 @@ GEM
       activemodel (>= 4.1, < 7.1)
       case_transform (>= 0.2)
       jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
-    active_record_query_trace (1.8)
-    activejob (6.1.7)
-      activesupport (= 6.1.7)
+    activejob (6.1.7.3)
+      activesupport (= 6.1.7.3)
       globalid (>= 0.3.6)
-    activemodel (6.1.7)
-      activesupport (= 6.1.7)
-    activerecord (6.1.7)
-      activemodel (= 6.1.7)
-      activesupport (= 6.1.7)
-    activestorage (6.1.7)
-      actionpack (= 6.1.7)
-      activejob (= 6.1.7)
-      activerecord (= 6.1.7)
-      activesupport (= 6.1.7)
+    activemodel (6.1.7.3)
+      activesupport (= 6.1.7.3)
+    activerecord (6.1.7.3)
+      activemodel (= 6.1.7.3)
+      activesupport (= 6.1.7.3)
+    activestorage (6.1.7.3)
+      actionpack (= 6.1.7.3)
+      activejob (= 6.1.7.3)
+      activerecord (= 6.1.7.3)
+      activesupport (= 6.1.7.3)
       marcel (~> 1.0)
       mini_mime (>= 1.1.0)
-    activesupport (6.1.7)
+    activesupport (6.1.7.3)
       concurrent-ruby (~> 1.0, >= 1.0.2)
       i18n (>= 1.6, < 2)
       minitest (>= 5.1)
       tzinfo (~> 2.0)
       zeitwerk (~> 2.3)
-    addressable (2.8.1)
+    addressable (2.8.2)
       public_suffix (>= 2.0.2, < 6.0)
     aes_key_wrap (1.1.0)
     airbrussh (1.4.1)
@@ -90,22 +109,22 @@ GEM
     attr_required (1.0.1)
     awrence (1.2.1)
     aws-eventstream (1.2.0)
-    aws-partitions (1.670.0)
-    aws-sdk-core (3.168.2)
+    aws-partitions (1.739.0)
+    aws-sdk-core (3.171.0)
       aws-eventstream (~> 1, >= 1.0.2)
       aws-partitions (~> 1, >= 1.651.0)
       aws-sigv4 (~> 1.5)
       jmespath (~> 1, >= 1.6.1)
-    aws-sdk-kms (1.60.0)
+    aws-sdk-kms (1.63.0)
       aws-sdk-core (~> 3, >= 3.165.0)
       aws-sigv4 (~> 1.1)
-    aws-sdk-s3 (1.117.2)
+    aws-sdk-s3 (1.120.0)
       aws-sdk-core (~> 3, >= 3.165.0)
       aws-sdk-kms (~> 1)
       aws-sigv4 (~> 1.4)
     aws-sigv4 (1.5.2)
       aws-eventstream (~> 1, >= 1.0.2)
-    bcrypt (3.1.17)
+    bcrypt (3.1.18)
     better_errors (2.9.1)
       coderay (>= 1.0.0)
       erubi (>= 1.0.0)
@@ -117,27 +136,22 @@ GEM
       erubi (~> 1.4)
       parser (>= 2.4)
       smart_properties
-    bindata (2.4.10)
+    bindata (2.4.15)
     binding_of_caller (1.0.0)
       debug_inspector (>= 0.0.1)
-    blurhash (0.1.6)
-      ffi (~> 1.14)
-    bootsnap (1.15.0)
+    blurhash (0.1.7)
+    bootsnap (1.16.0)
       msgpack (~> 1.2)
     brakeman (5.4.0)
-    browser (4.2.0)
+    browser (5.3.1)
     brpoplpush-redis_script (0.1.3)
       concurrent-ruby (~> 1.0, >= 1.0.5)
       redis (>= 1.0, < 6)
     builder (3.2.4)
-    bullet (7.0.7)
-      activesupport (>= 3.0.0)
-      uniform_notifier (~> 1.11)
     bundler-audit (0.9.1)
       bundler (>= 1.2.0, < 3)
       thor (~> 1.0)
-    byebug (11.1.3)
-    capistrano (3.17.1)
+    capistrano (3.17.2)
       airbrussh (>= 1.0.0)
       i18n
       rake (>= 10.0.0)
@@ -152,7 +166,7 @@ GEM
       sshkit (~> 1.3)
     capistrano-yarn (2.0.2)
       capistrano (~> 3.0)
-    capybara (3.38.0)
+    capybara (3.39.0)
       addressable
       matrix
       mini_mime (>= 0.1.3)
@@ -165,7 +179,7 @@ GEM
       activesupport
     cbor (0.5.9.6)
     charlock_holmes (0.7.7)
-    chewy (7.2.4)
+    chewy (7.2.7)
       activesupport (>= 5.2)
       elasticsearch (>= 7.12.0, < 7.14.0)
       elasticsearch-dsl
@@ -174,9 +188,9 @@ GEM
     cocoon (1.2.15)
     coderay (1.1.3)
     color_diff (0.1)
-    concurrent-ruby (1.1.10)
+    concurrent-ruby (1.2.2)
     connection_pool (2.3.0)
-    cose (1.2.1)
+    cose (1.3.0)
       cbor (~> 0.5.9)
       openssl-signature_algorithm (~> 1.0)
     crack (0.4.5)
@@ -184,8 +198,9 @@ GEM
     crass (1.0.6)
     css_parser (1.12.0)
       addressable
+    date (3.3.3)
     debug_inspector (1.0.0)
-    devise (4.8.1)
+    devise (4.9.2)
       bcrypt (~> 3.0)
       orm_adapter (~> 0.1)
       railties (>= 4.1.0)
@@ -206,7 +221,7 @@ GEM
     docile (1.4.0)
     domain_name (0.5.20190701)
       unf (>= 0.0.5, < 1.0.0)
-    doorkeeper (5.6.2)
+    doorkeeper (5.6.6)
       railties (>= 5)
     dotenv (2.8.1)
     dotenv-rails (2.8.1)
@@ -223,14 +238,14 @@ GEM
       faraday (~> 1)
       multi_json
     encryptor (3.0.0)
-    erubi (1.11.0)
+    erubi (1.12.0)
     et-orbi (1.2.7)
       tzinfo
     excon (0.95.0)
     fabrication (2.30.0)
-    faker (3.1.0)
+    faker (3.1.1)
       i18n (>= 1.8.11, < 2)
-    faraday (1.9.3)
+    faraday (1.10.3)
       faraday-em_http (~> 1.0)
       faraday-em_synchrony (~> 1.0)
       faraday-excon (~> 1.1)
@@ -246,8 +261,8 @@ GEM
     faraday-em_synchrony (1.0.0)
     faraday-excon (1.1.0)
     faraday-httpclient (1.0.1)
-    faraday-multipart (1.0.3)
-      multipart-post (>= 1.2, < 3)
+    faraday-multipart (1.0.4)
+      multipart-post (~> 2)
     faraday-net_http (1.0.1)
     faraday-net_http_persistent (1.2.0)
     faraday-patron (1.0.0)
@@ -272,27 +287,29 @@ GEM
       fog-json (>= 1.0)
       ipaddress (>= 0.8)
     formatador (0.3.0)
-    fugit (1.7.1)
+    fugit (1.8.1)
       et-orbi (~> 1, >= 1.2.7)
       raabro (~> 1.4)
     fuubar (2.5.1)
       rspec-core (~> 3.0)
       ruby-progressbar (~> 1.4)
-    gitlab-omniauth-openid-connect (0.10.0)
-      addressable (~> 2.7)
-      omniauth (>= 1.9, < 3)
-      openid_connect (~> 1.2)
-    globalid (1.0.0)
+    globalid (1.1.0)
       activesupport (>= 5.0)
-    hamlit (2.13.0)
+    haml (6.1.1)
       temple (>= 0.8.2)
       thor
       tilt
-    hamlit-rails (0.2.3)
-      actionpack (>= 4.0.1)
-      activesupport (>= 4.0.1)
-      hamlit (>= 1.2.0)
-      railties (>= 4.0.1)
+    haml-rails (2.1.0)
+      actionpack (>= 5.1)
+      activesupport (>= 5.1)
+      haml (>= 4.0.6)
+      railties (>= 5.1)
+    haml_lint (0.45.0)
+      haml (>= 4.0, < 6.2)
+      parallel (~> 1.10)
+      rainbow
+      rubocop (>= 0.50.0)
+      sysexits (~> 1.1)
     hashdiff (1.0.1)
     hashie (5.0.0)
     hcaptcha (7.1.0)
@@ -332,10 +349,11 @@ GEM
     jmespath (1.6.2)
     json (2.6.3)
     json-canonicalization (0.3.0)
-    json-jwt (1.14.0)
+    json-jwt (1.15.3)
       activesupport (>= 4.2)
       aes_key_wrap
       bindata
+      httpclient
     json-ld (3.2.3)
       htmlentities (~> 4.3)
       json-canonicalization (~> 0.3)
@@ -349,7 +367,7 @@ GEM
     json-schema (3.0.0)
       addressable (>= 2.8)
     jsonapi-renderer (0.2.2)
-    jwt (2.5.0)
+    jwt (2.7.0)
     kaminari (1.2.2)
       activesupport (>= 4.1.0)
       kaminari-actionview (= 1.2.2)
@@ -362,12 +380,6 @@ GEM
       activerecord
       kaminari-core (= 1.2.2)
     kaminari-core (1.2.2)
-    kt-paperclip (7.1.1)
-      activemodel (>= 4.2.0)
-      activesupport (>= 4.2.0)
-      marcel (~> 1.0.1)
-      mime-types
-      terrapin (~> 0.6.0)
     launchy (2.5.0)
       addressable (~> 2.7)
     letter_opener (1.8.1)
@@ -386,11 +398,14 @@ GEM
       activesupport (>= 4)
       railties (>= 4)
       request_store (~> 1.0)
-    loofah (2.19.1)
+    loofah (2.20.0)
       crass (~> 1.0.2)
       nokogiri (>= 1.5.9)
-    mail (2.7.1)
+    mail (2.8.1)
       mini_mime (>= 0.1.1)
+      net-imap
+      net-pop
+      net-smtp
     makara (0.5.1)
       activerecord (>= 5.2.0)
     marcel (1.0.2)
@@ -403,21 +418,28 @@ GEM
       mime-types-data (~> 3.2015)
     mime-types-data (3.2022.0105)
     mini_mime (1.1.2)
-    mini_portile2 (2.8.0)
-    minitest (5.17.0)
+    mini_portile2 (2.8.1)
+    minitest (5.18.0)
     msgpack (1.6.0)
     multi_json (1.15.0)
-    multipart-post (2.1.1)
+    multipart-post (2.3.0)
+    net-http (0.3.2)
+      uri
+    net-imap (0.3.4)
+      date
+      net-protocol
     net-ldap (0.17.1)
-    net-protocol (0.1.3)
+    net-pop (0.1.2)
+      net-protocol
+    net-protocol (0.2.1)
       timeout
-    net-scp (4.0.0.rc1)
+    net-scp (4.0.0)
       net-ssh (>= 2.6.5, < 8.0.0)
     net-smtp (0.3.3)
       net-protocol
     net-ssh (7.0.1)
-    nio4r (2.5.8)
-    nokogiri (1.13.10)
+    nio4r (2.5.9)
+    nokogiri (1.14.2)
       mini_portile2 (~> 2.8.0)
       racc (~> 1.4)
     nsa (0.2.8)
@@ -425,7 +447,7 @@ GEM
       concurrent-ruby (~> 1.0, >= 1.0.2)
       sidekiq (>= 3.5)
       statsd-ruby (~> 1.4, >= 1.4.0)
-    oj (3.13.23)
+    oj (3.14.2)
     omniauth (1.9.2)
       hashie (>= 3.4.6)
       rack (>= 1.6.2, < 3)
@@ -439,30 +461,34 @@ GEM
     omniauth-saml (1.10.3)
       omniauth (~> 1.3, >= 1.3.2)
       ruby-saml (~> 1.9)
-    openid_connect (1.3.0)
+    omniauth_openid_connect (0.6.1)
+      omniauth (>= 1.9, < 3)
+      openid_connect (~> 1.1)
+    openid_connect (1.4.2)
       activemodel
       attr_required (>= 1.0.0)
-      json-jwt (>= 1.5.0)
-      rack-oauth2 (>= 1.6.1)
-      swd (>= 1.0.0)
+      json-jwt (>= 1.15.0)
+      net-smtp
+      rack-oauth2 (~> 1.21)
+      swd (~> 1.3)
       tzinfo
       validate_email
       validate_url
-      webfinger (>= 1.0.1)
-    openssl (3.0.0)
-    openssl-signature_algorithm (1.2.1)
-      openssl (> 2.0, < 3.1)
+      webfinger (~> 1.2)
+    openssl (3.1.0)
+    openssl-signature_algorithm (1.3.0)
+      openssl (> 2.0)
     orm_adapter (0.5.0)
-    ox (2.14.12)
+    ox (2.14.14)
     parallel (1.22.1)
-    parser (3.1.3.0)
+    parser (3.2.2.0)
       ast (~> 2.4.1)
     parslet (2.0.0)
     pastel (0.8.0)
       tty-color (~> 0.5)
-    pg (1.4.5)
-    pghero (2.8.3)
-      activerecord (>= 5)
+    pg (1.4.6)
+    pghero (3.3.1)
+      activerecord (>= 6)
     pkg-config (1.5.1)
     posix-spawn (0.3.15)
     premailer (1.18.0)
@@ -474,50 +500,42 @@ GEM
       net-smtp
       premailer (~> 1.7, >= 1.7.9)
     private_address_check (0.5.0)
-    pry (0.14.1)
-      coderay (~> 1.1)
-      method_source (~> 1.0)
-    pry-byebug (3.10.1)
-      byebug (~> 11.0)
-      pry (>= 0.13, < 0.15)
-    pry-rails (0.3.9)
-      pry (>= 0.10.4)
     public_suffix (5.0.1)
-    puma (5.6.5)
+    puma (6.2.1)
       nio4r (~> 2.0)
     pundit (2.3.0)
       activesupport (>= 3.0.0)
     raabro (1.4.0)
-    racc (1.6.1)
-    rack (2.2.5)
+    racc (1.6.2)
+    rack (2.2.6.4)
     rack-attack (6.6.1)
       rack (>= 1.0, < 3)
-    rack-cors (1.1.1)
+    rack-cors (2.0.1)
       rack (>= 2.0.0)
-    rack-oauth2 (1.19.0)
+    rack-oauth2 (1.21.3)
       activesupport
       attr_required
       httpclient
       json-jwt (>= 1.11.0)
       rack (>= 2.1.0)
-    rack-proxy (0.7.0)
+    rack-proxy (0.7.6)
       rack
-    rack-test (2.0.2)
+    rack-test (2.1.0)
       rack (>= 1.3)
-    rails (6.1.7)
-      actioncable (= 6.1.7)
-      actionmailbox (= 6.1.7)
-      actionmailer (= 6.1.7)
-      actionpack (= 6.1.7)
-      actiontext (= 6.1.7)
-      actionview (= 6.1.7)
-      activejob (= 6.1.7)
-      activemodel (= 6.1.7)
-      activerecord (= 6.1.7)
-      activestorage (= 6.1.7)
-      activesupport (= 6.1.7)
+    rails (6.1.7.3)
+      actioncable (= 6.1.7.3)
+      actionmailbox (= 6.1.7.3)
+      actionmailer (= 6.1.7.3)
+      actionpack (= 6.1.7.3)
+      actiontext (= 6.1.7.3)
+      actionview (= 6.1.7.3)
+      activejob (= 6.1.7.3)
+      activemodel (= 6.1.7.3)
+      activerecord (= 6.1.7.3)
+      activestorage (= 6.1.7.3)
+      activesupport (= 6.1.7.3)
       bundler (>= 1.15.0)
-      railties (= 6.1.7)
+      railties (= 6.1.7.3)
       sprockets-rails (>= 2.0.0)
     rails-controller-testing (1.0.5)
       actionpack (>= 5.0.1.rc1)
@@ -526,16 +544,14 @@ GEM
     rails-dom-testing (2.0.3)
       activesupport (>= 4.2.0)
       nokogiri (>= 1.6)
-    rails-html-sanitizer (1.4.4)
+    rails-html-sanitizer (1.5.0)
       loofah (~> 2.19, >= 2.19.1)
     rails-i18n (6.0.0)
       i18n (>= 0.7, < 2)
       railties (>= 6.0.0, < 7)
-    rails-settings-cached (0.6.6)
-      rails (>= 4.2.0)
-    railties (6.1.7)
-      actionpack (= 6.1.7)
-      activesupport (= 6.1.7)
+    railties (6.1.7.3)
+      actionpack (= 6.1.7.3)
+      activesupport (= 6.1.7.3)
       method_source
       rake (>= 12.2)
       thor (~> 1.0)
@@ -545,18 +561,18 @@ GEM
       link_header (~> 0.0, >= 0.0.8)
     rdf-normalize (0.5.1)
       rdf (~> 3.2)
-    redcarpet (3.5.1)
-    redis (4.5.1)
+    redcarpet (3.6.0)
+    redis (4.8.1)
     redis-namespace (1.10.0)
       redis (>= 4)
     redlock (1.3.2)
       redis (>= 3.0.0, < 6.0)
-    regexp_parser (2.6.1)
+    regexp_parser (2.7.0)
     request_store (1.5.1)
       rack (>= 1.4)
-    responders (3.0.1)
-      actionpack (>= 5.0)
-      railties (>= 5.0)
+    responders (3.1.0)
+      actionpack (>= 5.2)
+      railties (>= 5.2)
     rexml (3.2.5)
     rotp (6.2.0)
     rpam2 (4.0.2)
@@ -564,50 +580,54 @@ GEM
       chunky_png (~> 1.0)
       rqrcode_core (~> 1.0)
     rqrcode_core (1.2.0)
-    rspec-core (3.11.0)
-      rspec-support (~> 3.11.0)
-    rspec-expectations (3.11.0)
+    rspec-core (3.12.1)
+      rspec-support (~> 3.12.0)
+    rspec-expectations (3.12.2)
       diff-lcs (>= 1.2.0, < 2.0)
-      rspec-support (~> 3.11.0)
-    rspec-mocks (3.11.1)
+      rspec-support (~> 3.12.0)
+    rspec-mocks (3.12.3)
       diff-lcs (>= 1.2.0, < 2.0)
-      rspec-support (~> 3.11.0)
-    rspec-rails (5.1.2)
-      actionpack (>= 5.2)
-      activesupport (>= 5.2)
-      railties (>= 5.2)
-      rspec-core (~> 3.10)
-      rspec-expectations (~> 3.10)
-      rspec-mocks (~> 3.10)
-      rspec-support (~> 3.10)
+      rspec-support (~> 3.12.0)
+    rspec-rails (6.0.1)
+      actionpack (>= 6.1)
+      activesupport (>= 6.1)
+      railties (>= 6.1)
+      rspec-core (~> 3.11)
+      rspec-expectations (~> 3.11)
+      rspec-mocks (~> 3.11)
+      rspec-support (~> 3.11)
     rspec-sidekiq (3.1.0)
       rspec-core (~> 3.0, >= 3.0.0)
       sidekiq (>= 2.4.0)
-    rspec-support (3.11.1)
+    rspec-support (3.12.0)
+    rspec_chunked (0.6)
     rspec_junit_formatter (0.6.0)
       rspec-core (>= 2, < 4, != 2.12.0)
-    rubocop (1.42.0)
+    rubocop (1.49.0)
       json (~> 2.3)
       parallel (~> 1.10)
-      parser (>= 3.1.2.1)
+      parser (>= 3.2.0.0)
       rainbow (>= 2.2.2, < 4.0)
       regexp_parser (>= 1.8, < 3.0)
       rexml (>= 3.2.5, < 4.0)
-      rubocop-ast (>= 1.24.1, < 2.0)
+      rubocop-ast (>= 1.28.0, < 2.0)
       ruby-progressbar (~> 1.7)
-      unicode-display_width (>= 1.4.0, < 3.0)
-    rubocop-ast (1.24.1)
-      parser (>= 3.1.1.0)
-    rubocop-performance (1.15.2)
+      unicode-display_width (>= 2.4.0, < 3.0)
+    rubocop-ast (1.28.0)
+      parser (>= 3.2.1.0)
+    rubocop-capybara (2.17.1)
+      rubocop (~> 1.41)
+    rubocop-performance (1.16.0)
       rubocop (>= 1.7.0, < 2.0)
       rubocop-ast (>= 0.4.0)
-    rubocop-rails (2.17.4)
+    rubocop-rails (2.18.0)
       activesupport (>= 4.2.0)
       rack (>= 1.1)
       rubocop (>= 1.33.0, < 2.0)
-    rubocop-rspec (2.16.0)
+    rubocop-rspec (2.19.0)
       rubocop (~> 1.33)
-    ruby-progressbar (1.11.0)
+      rubocop-capybara (~> 2.17)
+    ruby-progressbar (1.13.0)
     ruby-saml (1.13.0)
       nokogiri (>= 1.10.5)
       rexml
@@ -616,7 +636,7 @@ GEM
       fugit (~> 1.1, >= 1.1.6)
     safety_net_attestation (0.4.0)
       jwt (~> 2.0)
-    sanitize (6.0.0)
+    sanitize (6.0.1)
       crass (~> 1.0.2)
       nokogiri (>= 1.12.0)
     scenic (1.7.0)
@@ -629,10 +649,9 @@ GEM
       redis (>= 4.5.0, < 5)
     sidekiq-bulk (0.2.0)
       sidekiq
-    sidekiq-scheduler (4.0.3)
-      redis (>= 4.2.0)
+    sidekiq-scheduler (5.0.2)
       rufus-scheduler (~> 3.2)
-      sidekiq (>= 4, < 7)
+      sidekiq (>= 6, < 8)
       tilt (>= 1.4.0)
     sidekiq-unique-jobs (7.1.29)
       brpoplpush-redis_script (> 0.1.1, <= 2.0.0)
@@ -642,7 +661,7 @@ GEM
       thor (>= 0.20, < 3.0)
     simple-navigation (4.4.0)
       activesupport (>= 2.3.2)
-    simple_form (5.1.0)
+    simple_form (5.2.0)
       actionpack (>= 5.2)
       activemodel (>= 5.2)
     simplecov (0.22.0)
@@ -659,30 +678,31 @@ GEM
       actionpack (>= 5.2)
       activesupport (>= 5.2)
       sprockets (>= 3.0.0)
-    sshkit (1.21.2)
+    sshkit (1.21.4)
       net-scp (>= 1.1.2)
       net-ssh (>= 2.8.0)
-    stackprof (0.2.23)
+    stackprof (0.2.24)
     statsd-ruby (1.5.0)
     stoplight (3.0.1)
       redlock (~> 1.0)
-    strong_migrations (0.7.9)
-      activerecord (>= 5)
+    strong_migrations (0.8.0)
+      activerecord (>= 5.2)
     swd (1.3.0)
       activesupport (>= 3)
       attr_required (>= 0.0.5)
       httpclient (>= 2.4)
-    temple (0.8.2)
+    sysexits (1.2.0)
+    temple (0.10.0)
     terminal-table (3.0.2)
       unicode-display_width (>= 1.1.1, < 3)
     terrapin (0.6.0)
       climate_control (>= 0.0.3, < 1.0)
     thor (1.2.1)
-    tilt (2.0.11)
-    timeout (0.3.0)
-    tpm-key_attestation (0.11.0)
+    tilt (2.1.0)
+    timeout (0.3.2)
+    tpm-key_attestation (0.12.0)
       bindata (~> 2.4)
-      openssl (> 2.0, < 3.1)
+      openssl (> 2.0)
       openssl-signature_algorithm (~> 1.0)
     tty-color (0.6.0)
     tty-cursor (0.7.1)
@@ -697,15 +717,15 @@ GEM
     twitter-text (3.1.0)
       idn-ruby
       unf (~> 0.1.0)
-    tzinfo (2.0.5)
+    tzinfo (2.0.6)
       concurrent-ruby (~> 1.0)
-    tzinfo-data (1.2022.7)
+    tzinfo-data (1.2023.3)
       tzinfo (>= 1.0.0)
     unf (0.1.4)
       unf_ext
     unf_ext (0.0.8.2)
-    unicode-display_width (2.3.0)
-    uniform_notifier (1.16.0)
+    unicode-display_width (2.4.2)
+    uri (0.12.1)
     validate_email (0.1.6)
       activemodel (>= 3.0)
       mail (>= 2.2.5)
@@ -714,15 +734,15 @@ GEM
       public_suffix
     warden (1.2.9)
       rack (>= 2.0.9)
-    webauthn (2.5.2)
+    webauthn (3.0.0)
       android_key_attestation (~> 0.3.0)
       awrence (~> 1.1)
       bindata (~> 2.4)
       cbor (~> 0.5.9)
       cose (~> 1.1)
-      openssl (>= 2.2, < 3.1)
+      openssl (>= 2.2)
       safety_net_attestation (~> 0.4.0)
-      tpm-key_attestation (~> 0.11.0)
+      tpm-key_attestation (~> 0.12.0)
     webfinger (1.2.0)
       activesupport
       httpclient (>= 2.4)
@@ -730,7 +750,7 @@ GEM
       addressable (>= 2.8.0)
       crack (>= 0.3.2)
       hashdiff (>= 0.4.0, < 2.0.0)
-    webpacker (5.4.3)
+    webpacker (5.4.4)
       activesupport (>= 5.2)
       rack-proxy (>= 0.6.1)
       railties (>= 5.2)
@@ -742,38 +762,36 @@ GEM
     xorcist (1.1.3)
     xpath (3.2.0)
       nokogiri (~> 1.8)
-    zeitwerk (2.6.6)
+    zeitwerk (2.6.7)
 
 PLATFORMS
   ruby
 
 DEPENDENCIES
   active_model_serializers (~> 0.10)
-  active_record_query_trace (~> 1.8)
   addressable (~> 2.8)
   annotate (~> 3.2)
-  aws-sdk-s3 (~> 1.117)
+  aws-sdk-s3 (~> 1.120)
   better_errors (~> 2.9)
   binding_of_caller (~> 1.0)
   blurhash (~> 0.1)
-  bootsnap (~> 1.15.0)
+  bootsnap (~> 1.16.0)
   brakeman (~> 5.4)
   browser
-  bullet (~> 7.0)
   bundler-audit (~> 0.9)
   capistrano (~> 3.17)
   capistrano-rails (~> 1.6)
   capistrano-rbenv (~> 2.2)
   capistrano-yarn (~> 2.0)
-  capybara (~> 3.38)
+  capybara (~> 3.39)
   charlock_holmes (~> 0.7.7)
   chewy (~> 7.2)
-  climate_control (~> 0.2)
+  climate_control
   cocoon (~> 1.2)
   color_diff (~> 0.1)
   concurrent-ruby
   connection_pool
-  devise (~> 4.8)
+  devise (~> 4.9)
   devise-two-factor (~> 4.0)
   devise_pam_authenticatable2 (~> 9.2)
   discard (~> 1.2)
@@ -787,8 +805,8 @@ DEPENDENCIES
   fog-core (<= 2.4.0)
   fog-openstack (~> 0.3)
   fuubar (~> 2.5)
-  gitlab-omniauth-openid-connect (~> 0.10.0)
-  hamlit-rails (~> 0.2)
+  haml-rails (~> 2.0)
+  haml_lint
   hcaptcha (~> 7.1)
   hiredis (~> 0.6)
   htmlentities (~> 4.3)
@@ -801,7 +819,7 @@ DEPENDENCIES
   json-ld-preloaded (~> 3.2)
   json-schema (~> 3.0)
   kaminari (~> 1.2)
-  kt-paperclip (~> 7.1)
+  kt-paperclip (~> 7.1)!
   letter_opener (~> 1.8)
   letter_opener_web (~> 2.0)
   link_header (~> 0.0)
@@ -810,45 +828,46 @@ DEPENDENCIES
   mario-redis-lock (~> 1.2)
   memory_profiler
   mime-types (~> 3.4.1)
+  net-http (~> 0.3.2)
   net-ldap (~> 0.17)
-  nokogiri (~> 1.13)
+  nokogiri (~> 1.14)
   nsa (~> 0.2)
-  oj (~> 3.13)
+  oj (~> 3.14)
   omniauth (~> 1.9)
   omniauth-cas (~> 2.0)
   omniauth-rails_csrf_protection (~> 0.1)
   omniauth-saml (~> 1.10)
+  omniauth_openid_connect (~> 0.6.1)
   ox (~> 2.14)
   parslet
   pg (~> 1.4)
-  pghero (~> 2.8)
+  pghero
   pkg-config (~> 1.5)
   posix-spawn
   premailer-rails
   private_address_check (~> 0.5)
-  pry-byebug (~> 3.10)
-  pry-rails (~> 0.3)
   public_suffix (~> 5.0)
-  puma (~> 5.6)
+  puma (~> 6.2)
   pundit (~> 2.3)
-  rack (~> 2.2.5)
+  rack (~> 2.2.6)
   rack-attack (~> 6.6)
-  rack-cors (~> 1.1)
-  rack-test (~> 2.0)
+  rack-cors (~> 2.0)
+  rack-test (~> 2.1)
   rails (~> 6.1.7)
   rails-controller-testing (~> 1.0)
   rails-i18n (~> 6.0)
-  rails-settings-cached (~> 0.6)
+  rails-settings-cached (~> 0.6)!
   rdf-normalize (~> 0.5)
-  redcarpet (~> 3.5)
+  redcarpet (~> 3.6)
   redis (~> 4.5)
   redis-namespace (~> 1.10)
-  rexml (~> 3.2)
   rqrcode (~> 2.1)
-  rspec-rails (~> 5.1)
+  rspec-rails (~> 6.0)
   rspec-sidekiq (~> 3.1)
+  rspec_chunked (~> 0.6)
   rspec_junit_formatter (~> 0.6)
   rubocop
+  rubocop-capybara
   rubocop-performance
   rubocop-rails
   rubocop-rspec
@@ -857,21 +876,21 @@ DEPENDENCIES
   scenic (~> 1.7)
   sidekiq (~> 6.5)
   sidekiq-bulk (~> 0.2.0)
-  sidekiq-scheduler (~> 4.0)
+  sidekiq-scheduler (~> 5.0)
   sidekiq-unique-jobs (~> 7.1)
   simple-navigation (~> 4.4)
-  simple_form (~> 5.1)
+  simple_form (~> 5.2)
   simplecov (~> 0.22)
   sprockets (~> 3.7.2)
   sprockets-rails (~> 3.4)
   stackprof
   stoplight (~> 3.0.1)
-  strong_migrations (~> 0.7)
+  strong_migrations (~> 0.8)
   thor (~> 1.2)
   tty-prompt (~> 0.23)
   twitter-text (~> 3.1.0)
-  tzinfo-data (~> 1.2022)
-  webauthn (~> 2.5)
+  tzinfo-data (~> 1.2023)
+  webauthn (~> 3.0)
   webmock (~> 3.18)
   webpacker (~> 5.4)
   webpush!
diff --git a/README.md b/README.md
index 375e96646..c6f2d8bf4 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-![Mastodon](https://i.imgur.com/NhZc40l.png)
-========
+# Mastodon - Pluralcafe Glitch Edition
 
 [![Site Status](https://img.shields.io/website?label=plural.cafe&logo=mastodon&url=https%3A%2F%2Fplural.cafe)](https://plural.cafe)
 ![License](https://shields.io/github/license/pluralcafe/mastodon)
diff --git a/SECURITY.md b/SECURITY.md
index ccc7c1034..6a51c126a 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,7 +2,7 @@
 
 If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you can reach us at <security@joinmastodon.org>.
 
-You should *not* report such issues on GitHub or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
+You should _not_ report such issues on GitHub or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
 
 ## Scope
 
@@ -11,7 +11,8 @@ A "vulnerability in Mastodon" is a vulnerability in the code distributed through
 ## Supported Versions
 
 | Version | Supported |
-| ------- | ----------|
+| ------- | --------- |
+| 4.1.x   | Yes       |
 | 4.0.x   | Yes       |
 | 3.5.x   | Yes       |
 | < 3.5   | No        |
diff --git a/app/controllers/admin/account_actions_controller.rb b/app/controllers/admin/account_actions_controller.rb
index 3f2e28b6a..e89404b60 100644
--- a/app/controllers/admin/account_actions_controller.rb
+++ b/app/controllers/admin/account_actions_controller.rb
@@ -21,7 +21,7 @@ module Admin
       account_action.save!
 
       if account_action.with_report?
-        redirect_to admin_reports_path
+        redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: params[:report_id])
       else
         redirect_to admin_account_path(@account.id)
       end
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
index 924b623ad..099512248 100644
--- a/app/controllers/admin/dashboard_controller.rb
+++ b/app/controllers/admin/dashboard_controller.rb
@@ -18,13 +18,11 @@ module Admin
     private
 
     def redis_info
-      @redis_info ||= begin
-        if redis.is_a?(Redis::Namespace)
-          redis.redis.info
-        else
-          redis.info
-        end
-      end
+      @redis_info ||= if redis.is_a?(Redis::Namespace)
+                        redis.redis.info
+                      else
+                        redis.info
+                      end
     end
   end
 end
diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb
index 74764640b..750f5c995 100644
--- a/app/controllers/admin/domain_blocks_controller.rb
+++ b/app/controllers/admin/domain_blocks_controller.rb
@@ -2,7 +2,7 @@
 
 module Admin
   class DomainBlocksController < BaseController
-    before_action :set_domain_block, only: [:show, :destroy, :edit, :update]
+    before_action :set_domain_block, only: [:destroy, :edit, :update]
 
     def batch
       authorize :domain_block, :create?
@@ -90,9 +90,7 @@ module Admin
     end
 
     def action_from_button
-      if params[:save]
-        'save'
-      end
+      'save' if params[:save]
     end
   end
 end
diff --git a/app/controllers/admin/email_domain_blocks_controller.rb b/app/controllers/admin/email_domain_blocks_controller.rb
index a0a43de19..4a3228ec3 100644
--- a/app/controllers/admin/email_domain_blocks_controller.rb
+++ b/app/controllers/admin/email_domain_blocks_controller.rb
@@ -2,8 +2,6 @@
 
 module Admin
   class EmailDomainBlocksController < BaseController
-    before_action :set_email_domain_block, only: [:show, :destroy]
-
     def index
       authorize :email_domain_block, :index?
 
@@ -59,10 +57,6 @@ module Admin
 
     private
 
-    def set_email_domain_block
-      @email_domain_block = EmailDomainBlock.find(params[:id])
-    end
-
     def set_resolved_records
       Resolv::DNS.open do |dns|
         dns.timeouts = 5
diff --git a/app/controllers/admin/export_domain_allows_controller.rb b/app/controllers/admin/export_domain_allows_controller.rb
index 57fb12c62..adfc39da2 100644
--- a/app/controllers/admin/export_domain_allows_controller.rb
+++ b/app/controllers/admin/export_domain_allows_controller.rb
@@ -23,9 +23,7 @@ module Admin
         @import = Admin::Import.new(import_params)
         return render :new unless @import.validate
 
-        parse_import_data!(export_headers)
-
-        @data.take(Admin::Import::ROWS_PROCESSING_LIMIT).each do |row|
+        @import.csv_rows.each do |row|
           domain = row['#domain'].strip
           next if DomainAllow.allowed?(domain)
 
diff --git a/app/controllers/admin/export_domain_blocks_controller.rb b/app/controllers/admin/export_domain_blocks_controller.rb
index fb0cd05d2..816422d4f 100644
--- a/app/controllers/admin/export_domain_blocks_controller.rb
+++ b/app/controllers/admin/export_domain_blocks_controller.rb
@@ -23,24 +23,30 @@ module Admin
       @import = Admin::Import.new(import_params)
       return render :new unless @import.validate
 
-      parse_import_data!(export_headers)
-
       @global_private_comment = I18n.t('admin.export_domain_blocks.import.private_comment_template', source: @import.data_file_name, date: I18n.l(Time.now.utc))
 
       @form = Form::DomainBlockBatch.new
-      @domain_blocks = @data.take(Admin::Import::ROWS_PROCESSING_LIMIT).filter_map do |row|
+      @domain_blocks = @import.csv_rows.filter_map do |row|
         domain = row['#domain'].strip
         next if DomainBlock.rule_for(domain).present?
 
         domain_block = DomainBlock.new(domain: domain,
-                                       severity: row['#severity'].strip,
-                                       reject_media: row['#reject_media'].strip,
-                                       reject_reports: row['#reject_reports'].strip,
+                                       severity: row.fetch('#severity', :suspend),
+                                       reject_media: row.fetch('#reject_media', false),
+                                       reject_reports: row.fetch('#reject_reports', false),
                                        private_comment: @global_private_comment,
-                                       public_comment: row['#public_comment']&.strip,
-                                       obfuscate: row['#obfuscate'].strip)
+                                       public_comment: row['#public_comment'],
+                                       obfuscate: row.fetch('#obfuscate', false))
+
+        if domain_block.invalid?
+          flash.now[:alert] = I18n.t('admin.export_domain_blocks.invalid_domain_block', error: domain_block.errors.full_messages.join(', '))
+          next
+        end
 
-        domain_block if domain_block.valid?
+        domain_block
+      rescue ArgumentError => e
+        flash.now[:alert] = I18n.t('admin.export_domain_blocks.invalid_domain_block', error: e.message)
+        next
       end
 
       @warning_domains = Instance.where(domain: @domain_blocks.map(&:domain)).where('EXISTS (SELECT 1 FROM follows JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id WHERE accounts.domain = instances.domain)').pluck(:domain)
diff --git a/app/controllers/admin/reports/actions_controller.rb b/app/controllers/admin/reports/actions_controller.rb
index 5cb5c744f..554f7906f 100644
--- a/app/controllers/admin/reports/actions_controller.rb
+++ b/app/controllers/admin/reports/actions_controller.rb
@@ -3,6 +3,11 @@
 class Admin::Reports::ActionsController < Admin::BaseController
   before_action :set_report
 
+  def preview
+    authorize @report, :show?
+    @moderation_action = action_from_button
+  end
+
   def create
     authorize @report, :show?
 
@@ -13,7 +18,8 @@ class Admin::Reports::ActionsController < Admin::BaseController
         status_ids: @report.status_ids,
         current_account: current_account,
         report_id: @report.id,
-        send_email_notification: !@report.spam?
+        send_email_notification: !@report.spam?,
+        text: params[:text]
       )
 
       status_batch_action.save!
@@ -23,13 +29,16 @@ class Admin::Reports::ActionsController < Admin::BaseController
         report_id: @report.id,
         target_account: @report.target_account,
         current_account: current_account,
-        send_email_notification: !@report.spam?
+        send_email_notification: !@report.spam?,
+        text: params[:text]
       )
 
       account_action.save!
+    else
+      return redirect_to admin_report_path(@report), alert: I18n.t('admin.reports.unknown_action_msg', action: action_from_button)
     end
 
-    redirect_to admin_reports_path
+    redirect_to admin_reports_path, notice: I18n.t('admin.reports.processed_msg', id: @report.id)
   end
 
   private
@@ -47,6 +56,8 @@ class Admin::Reports::ActionsController < Admin::BaseController
       'silence'
     elsif params[:suspend]
       'suspend'
+    elsif params[:moderation_action]
+      params[:moderation_action]
     end
   end
 end
diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb
index 94b707771..7c7d70fd3 100644
--- a/app/controllers/api/v1/accounts/credentials_controller.rb
+++ b/app/controllers/api/v1/accounts/credentials_controller.rb
@@ -13,7 +13,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
   def update
     @account = current_account
     UpdateAccountService.new.call(@account, account_params, raise_error: true)
-    UserSettingsDecorator.new(current_user).update(user_settings_params) if user_settings_params
+    current_user.update(user_params) if user_params
     ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
     render json: @account, serializer: REST::CredentialAccountSerializer
   end
@@ -34,15 +34,17 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
     )
   end
 
-  def user_settings_params
+  def user_params
     return nil if params[:source].blank?
 
     source_params = params.require(:source)
 
     {
-      'setting_default_privacy' => source_params.fetch(:privacy, @account.user.setting_default_privacy),
-      'setting_default_sensitive' => source_params.fetch(:sensitive, @account.user.setting_default_sensitive),
-      'setting_default_language' => source_params.fetch(:language, @account.user.setting_default_language),
+      settings_attributes: {
+        default_privacy: source_params.fetch(:privacy, @account.user.setting_default_privacy),
+        default_sensitive: source_params.fetch(:sensitive, @account.user.setting_default_sensitive),
+        default_language: source_params.fetch(:language, @account.user.setting_default_language),
+      },
     }
   end
 end
diff --git a/app/controllers/api/v1/accounts/follower_accounts_controller.rb b/app/controllers/api/v1/accounts/follower_accounts_controller.rb
index 208e06ed6..0daa3fe1e 100644
--- a/app/controllers/api/v1/accounts/follower_accounts_controller.rb
+++ b/app/controllers/api/v1/accounts/follower_accounts_controller.rb
@@ -45,15 +45,11 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_account_followers_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_account_followers_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless @accounts.empty?
-      api_v1_account_followers_url pagination_params(since_id: pagination_since_id)
-    end
+    api_v1_account_followers_url pagination_params(since_id: pagination_since_id) unless @accounts.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/accounts/following_accounts_controller.rb b/app/controllers/api/v1/accounts/following_accounts_controller.rb
index 155ca0907..abac80557 100644
--- a/app/controllers/api/v1/accounts/following_accounts_controller.rb
+++ b/app/controllers/api/v1/accounts/following_accounts_controller.rb
@@ -45,15 +45,11 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_account_following_index_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_account_following_index_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless @accounts.empty?
-      api_v1_account_following_index_url pagination_params(since_id: pagination_since_id)
-    end
+    api_v1_account_following_index_url pagination_params(since_id: pagination_since_id) unless @accounts.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb
index a68d266a7..58e668dca 100644
--- a/app/controllers/api/v1/accounts/statuses_controller.rb
+++ b/app/controllers/api/v1/accounts/statuses_controller.rb
@@ -40,15 +40,11 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_account_statuses_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_account_statuses_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless @statuses.empty?
-      api_v1_account_statuses_url pagination_params(min_id: pagination_since_id)
-    end
+    api_v1_account_statuses_url pagination_params(min_id: pagination_since_id) unless @statuses.empty?
   end
 
   def records_continue?
diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb
index be84720aa..7dff66efa 100644
--- a/app/controllers/api/v1/accounts_controller.rb
+++ b/app/controllers/api/v1/accounts_controller.rb
@@ -30,7 +30,7 @@ class Api::V1::AccountsController < Api::BaseController
     self.response_body = Oj.dump(response.body)
     self.status        = response.status
   rescue ActiveRecord::RecordInvalid => e
-    render json: ValidationErrorFormatter.new(e, 'account.username': :username, 'invite_request.text': :reason).as_json, status: :unprocessable_entity
+    render json: ValidationErrorFormatter.new(e, 'account.username': :username, 'invite_request.text': :reason).as_json, status: 422
   end
 
   def follow
diff --git a/app/controllers/api/v1/admin/accounts_controller.rb b/app/controllers/api/v1/admin/accounts_controller.rb
index f48300072..ff9cae639 100644
--- a/app/controllers/api/v1/admin/accounts_controller.rb
+++ b/app/controllers/api/v1/admin/accounts_controller.rb
@@ -120,9 +120,7 @@ class Api::V1::Admin::AccountsController < Api::BaseController
       translated_params[:status] = status.to_s if params[status].present?
     end
 
-    if params[:staff].present?
-      translated_params[:role_ids] = UserRole.that_can(:manage_reports).map(&:id)
-    end
+    translated_params[:role_ids] = UserRole.that_can(:manage_reports).map(&:id) if params[:staff].present?
 
     translated_params
   end
diff --git a/app/controllers/api/v1/admin/trends/tags_controller.rb b/app/controllers/api/v1/admin/trends/tags_controller.rb
index f3c0c4b6b..e77df3021 100644
--- a/app/controllers/api/v1/admin/trends/tags_controller.rb
+++ b/app/controllers/api/v1/admin/trends/tags_controller.rb
@@ -3,6 +3,14 @@
 class Api::V1::Admin::Trends::TagsController < Api::V1::Trends::TagsController
   before_action -> { authorize_if_got_token! :'admin:read' }
 
+  def index
+    if current_user&.can?(:manage_taxonomies)
+      render json: @tags, each_serializer: REST::Admin::TagSerializer
+    else
+      super
+    end
+  end
+
   private
 
   def enabled?
diff --git a/app/controllers/api/v1/announcements_controller.rb b/app/controllers/api/v1/announcements_controller.rb
index ee79fc19f..82e9cf7de 100644
--- a/app/controllers/api/v1/announcements_controller.rb
+++ b/app/controllers/api/v1/announcements_controller.rb
@@ -18,9 +18,7 @@ class Api::V1::AnnouncementsController < Api::BaseController
   private
 
   def set_announcements
-    @announcements = begin
-      Announcement.published.chronological
-    end
+    @announcements = Announcement.published.chronological
   end
 
   def set_announcement
diff --git a/app/controllers/api/v1/blocks_controller.rb b/app/controllers/api/v1/blocks_controller.rb
index a65e762c9..06a8bfa89 100644
--- a/app/controllers/api/v1/blocks_controller.rb
+++ b/app/controllers/api/v1/blocks_controller.rb
@@ -33,15 +33,11 @@ class Api::V1::BlocksController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_blocks_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_blocks_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless paginated_blocks.empty?
-      api_v1_blocks_url pagination_params(since_id: pagination_since_id)
-    end
+    api_v1_blocks_url pagination_params(since_id: pagination_since_id) unless paginated_blocks.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/conversations_controller.rb b/app/controllers/api/v1/conversations_controller.rb
index 6c7583403..9034e8a2f 100644
--- a/app/controllers/api/v1/conversations_controller.rb
+++ b/app/controllers/api/v1/conversations_controller.rb
@@ -40,15 +40,11 @@ class Api::V1::ConversationsController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_conversations_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_conversations_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless @conversations.empty?
-      api_v1_conversations_url pagination_params(min_id: pagination_since_id)
-    end
+    api_v1_conversations_url pagination_params(min_id: pagination_since_id) unless @conversations.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/domain_blocks_controller.rb b/app/controllers/api/v1/domain_blocks_controller.rb
index 1891261b9..34def3c44 100644
--- a/app/controllers/api/v1/domain_blocks_controller.rb
+++ b/app/controllers/api/v1/domain_blocks_controller.rb
@@ -43,15 +43,11 @@ class Api::V1::DomainBlocksController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_domain_blocks_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_domain_blocks_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless @blocks.empty?
-      api_v1_domain_blocks_url pagination_params(since_id: pagination_since_id)
-    end
+    api_v1_domain_blocks_url pagination_params(since_id: pagination_since_id) unless @blocks.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/emails/confirmations_controller.rb b/app/controllers/api/v1/emails/confirmations_controller.rb
index 3faaea2fb..32fb8e39f 100644
--- a/app/controllers/api/v1/emails/confirmations_controller.rb
+++ b/app/controllers/api/v1/emails/confirmations_controller.rb
@@ -15,10 +15,10 @@ class Api::V1::Emails::ConfirmationsController < Api::BaseController
   private
 
   def require_user_owned_by_application!
-    render json: { error: 'This method is only available to the application the user originally signed-up with' }, status: :forbidden unless current_user && current_user.created_by_application_id == doorkeeper_token.application_id
+    render json: { error: 'This method is only available to the application the user originally signed-up with' }, status: 403 unless current_user && current_user.created_by_application_id == doorkeeper_token.application_id
   end
 
   def require_user_not_confirmed!
-    render json: { error: 'This method is only available while the e-mail is awaiting confirmation' }, status: :forbidden unless !current_user.confirmed? || current_user.unconfirmed_email.present?
+    render json: { error: 'This method is only available while the e-mail is awaiting confirmation' }, status: 403 unless !current_user.confirmed? || current_user.unconfirmed_email.present?
   end
 end
diff --git a/app/controllers/api/v1/endorsements_controller.rb b/app/controllers/api/v1/endorsements_controller.rb
index 9e80f468a..46e3fcd64 100644
--- a/app/controllers/api/v1/endorsements_controller.rb
+++ b/app/controllers/api/v1/endorsements_controller.rb
@@ -35,17 +35,13 @@ class Api::V1::EndorsementsController < Api::BaseController
   def next_path
     return if unlimited?
 
-    if records_continue?
-      api_v1_endorsements_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_endorsements_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
     return if unlimited?
 
-    unless @accounts.empty?
-      api_v1_endorsements_url pagination_params(since_id: pagination_since_id)
-    end
+    api_v1_endorsements_url pagination_params(since_id: pagination_since_id) unless @accounts.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/favourites_controller.rb b/app/controllers/api/v1/favourites_controller.rb
index 2a873696c..bd7f3d775 100644
--- a/app/controllers/api/v1/favourites_controller.rb
+++ b/app/controllers/api/v1/favourites_controller.rb
@@ -36,15 +36,11 @@ class Api::V1::FavouritesController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_favourites_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_favourites_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless results.empty?
-      api_v1_favourites_url pagination_params(min_id: pagination_since_id)
-    end
+    api_v1_favourites_url pagination_params(min_id: pagination_since_id) unless results.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/follow_requests_controller.rb b/app/controllers/api/v1/follow_requests_controller.rb
index 54ff0e11d..7c197ce6b 100644
--- a/app/controllers/api/v1/follow_requests_controller.rb
+++ b/app/controllers/api/v1/follow_requests_controller.rb
@@ -53,15 +53,11 @@ class Api::V1::FollowRequestsController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_follow_requests_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_follow_requests_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless @accounts.empty?
-      api_v1_follow_requests_url pagination_params(since_id: pagination_since_id)
-    end
+    api_v1_follow_requests_url pagination_params(since_id: pagination_since_id) unless @accounts.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/instances/translation_languages_controller.rb b/app/controllers/api/v1/instances/translation_languages_controller.rb
new file mode 100644
index 000000000..3910a499e
--- /dev/null
+++ b/app/controllers/api/v1/instances/translation_languages_controller.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class Api::V1::Instances::TranslationLanguagesController < Api::BaseController
+  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
+
+  before_action :set_languages
+
+  def show
+    expires_in 1.day, public: true
+    render json: @languages
+  end
+
+  private
+
+  def set_languages
+    if TranslationService.configured?
+      @languages = Rails.cache.fetch('translation_service/languages', expires_in: 7.days, race_condition_ttl: 1.hour) { TranslationService.configured.languages }
+      @languages['und'] = @languages.delete(nil) if @languages.key?(nil)
+    else
+      @languages = {}
+    end
+  end
+end
diff --git a/app/controllers/api/v1/lists/accounts_controller.rb b/app/controllers/api/v1/lists/accounts_controller.rb
index b66ea9bfe..8e12cb7b6 100644
--- a/app/controllers/api/v1/lists/accounts_controller.rb
+++ b/app/controllers/api/v1/lists/accounts_controller.rb
@@ -62,17 +62,13 @@ class Api::V1::Lists::AccountsController < Api::BaseController
   def next_path
     return if unlimited?
 
-    if records_continue?
-      api_v1_list_accounts_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_list_accounts_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
     return if unlimited?
 
-    unless @accounts.empty?
-      api_v1_list_accounts_url pagination_params(since_id: pagination_since_id)
-    end
+    api_v1_list_accounts_url pagination_params(since_id: pagination_since_id) unless @accounts.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/mutes_controller.rb b/app/controllers/api/v1/mutes_controller.rb
index 6cde53a2a..555485823 100644
--- a/app/controllers/api/v1/mutes_controller.rb
+++ b/app/controllers/api/v1/mutes_controller.rb
@@ -33,15 +33,11 @@ class Api::V1::MutesController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_mutes_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_mutes_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless paginated_mutes.empty?
-      api_v1_mutes_url pagination_params(since_id: pagination_since_id)
-    end
+    api_v1_mutes_url pagination_params(since_id: pagination_since_id) unless paginated_mutes.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/notifications_controller.rb b/app/controllers/api/v1/notifications_controller.rb
index a6ed359c9..7a64d1300 100644
--- a/app/controllers/api/v1/notifications_controller.rb
+++ b/app/controllers/api/v1/notifications_controller.rb
@@ -6,7 +6,7 @@ class Api::V1::NotificationsController < Api::BaseController
   before_action :require_user!
   after_action :insert_pagination_headers, only: :index
 
-  DEFAULT_NOTIFICATIONS_LIMIT = 15
+  DEFAULT_NOTIFICATIONS_LIMIT = 40
 
   def index
     @notifications = load_notifications
@@ -28,7 +28,7 @@ class Api::V1::NotificationsController < Api::BaseController
   end
 
   def dismiss
-    current_account.notifications.find_by!(id: params[:id]).destroy!
+    current_account.notifications.find(params[:id]).destroy!
     render_empty
   end
 
@@ -67,15 +67,11 @@ class Api::V1::NotificationsController < Api::BaseController
   end
 
   def next_path
-    unless @notifications.empty?
-      api_v1_notifications_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_notifications_url pagination_params(max_id: pagination_max_id) unless @notifications.empty?
   end
 
   def prev_path
-    unless @notifications.empty?
-      api_v1_notifications_url pagination_params(min_id: pagination_since_id)
-    end
+    api_v1_notifications_url pagination_params(min_id: pagination_since_id) unless @notifications.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/scheduled_statuses_controller.rb b/app/controllers/api/v1/scheduled_statuses_controller.rb
index f90642a73..2220b6d22 100644
--- a/app/controllers/api/v1/scheduled_statuses_controller.rb
+++ b/app/controllers/api/v1/scheduled_statuses_controller.rb
@@ -52,15 +52,11 @@ class Api::V1::ScheduledStatusesController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_scheduled_statuses_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_scheduled_statuses_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless @statuses.empty?
-      api_v1_scheduled_statuses_url pagination_params(min_id: pagination_since_id)
-    end
+    api_v1_scheduled_statuses_url pagination_params(min_id: pagination_since_id) unless @statuses.empty?
   end
 
   def records_continue?
diff --git a/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb b/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
index 2b614a837..b138fa265 100644
--- a/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
+++ b/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb
@@ -41,15 +41,11 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_status_favourited_by_index_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_status_favourited_by_index_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless @accounts.empty?
-      api_v1_status_favourited_by_index_url pagination_params(since_id: pagination_since_id)
-    end
+    api_v1_status_favourited_by_index_url pagination_params(since_id: pagination_since_id) unless @accounts.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb b/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
index 24db30fcc..4b545f982 100644
--- a/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
+++ b/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb
@@ -37,15 +37,11 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_status_reblogged_by_index_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_status_reblogged_by_index_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless @accounts.empty?
-      api_v1_status_reblogged_by_index_url pagination_params(since_id: pagination_since_id)
-    end
+    api_v1_status_reblogged_by_index_url pagination_params(since_id: pagination_since_id) unless @accounts.empty?
   end
 
   def pagination_max_id
diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb
index e2e48f633..8dcf6331e 100644
--- a/app/controllers/api/v1/statuses_controller.rb
+++ b/app/controllers/api/v1/statuses_controller.rb
@@ -64,11 +64,18 @@ class Api::V1::StatusesController < Api::BaseController
       application: doorkeeper_token.application,
       poll: status_params[:poll],
       content_type: status_params[:content_type],
+      allowed_mentions: status_params[:allowed_mentions],
       idempotency: request.headers['Idempotency-Key'],
       with_rate_limit: true
     )
 
     render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
+  rescue PostStatusService::UnexpectedMentionsError => e
+    unexpected_accounts = ActiveModel::Serializer::CollectionSerializer.new(
+      e.accounts,
+      serializer: REST::AccountSerializer
+    )
+    render json: { error: e.message, unexpected_accounts: unexpected_accounts }, status: 422
   end
 
   def update
@@ -80,6 +87,7 @@ class Api::V1::StatusesController < Api::BaseController
       current_account.id,
       text: status_params[:status],
       media_ids: status_params[:media_ids],
+      media_attributes: status_params[:media_attributes],
       sensitive: status_params[:sensitive],
       language: status_params[:language],
       spoiler_text: status_params[:spoiler_text],
@@ -130,7 +138,14 @@ class Api::V1::StatusesController < Api::BaseController
       :language,
       :scheduled_at,
       :content_type,
+      allowed_mentions: [],
       media_ids: [],
+      media_attributes: [
+        :id,
+        :thumbnail,
+        :description,
+        :focus,
+      ],
       poll: [
         :multiple,
         :hide_totals,
diff --git a/app/controllers/api/v1/streaming_controller.rb b/app/controllers/api/v1/streaming_controller.rb
index 7cd60615a..0cdd00d62 100644
--- a/app/controllers/api/v1/streaming_controller.rb
+++ b/app/controllers/api/v1/streaming_controller.rb
@@ -2,10 +2,10 @@
 
 class Api::V1::StreamingController < Api::BaseController
   def index
-    if Rails.configuration.x.streaming_api_base_url != request.host
-      redirect_to streaming_api_url, status: 301
-    else
+    if Rails.configuration.x.streaming_api_base_url == request.host
       not_found
+    else
+      redirect_to streaming_api_url, status: 301, allow_other_host: true
     end
   end
 
diff --git a/app/controllers/api/v1/tags_controller.rb b/app/controllers/api/v1/tags_controller.rb
index 272362c31..a08fd2187 100644
--- a/app/controllers/api/v1/tags_controller.rb
+++ b/app/controllers/api/v1/tags_controller.rb
@@ -25,6 +25,7 @@ class Api::V1::TagsController < Api::BaseController
 
   def set_or_create_tag
     return not_found unless Tag::HASHTAG_NAME_RE.match?(params[:id])
+
     @tag = Tag.find_normalized(params[:id]) || Tag.new(name: Tag.normalize(params[:id]), display_name: params[:id])
   end
 end
diff --git a/app/controllers/api/v1/timelines/public_controller.rb b/app/controllers/api/v1/timelines/public_controller.rb
index 493fe4776..4675af921 100644
--- a/app/controllers/api/v1/timelines/public_controller.rb
+++ b/app/controllers/api/v1/timelines/public_controller.rb
@@ -40,7 +40,7 @@ class Api::V1::Timelines::PublicController < Api::BaseController
       only_media: truthy_param?(:only_media),
       allow_local_only: truthy_param?(:allow_local_only),
       with_replies: Setting.show_replies_in_public_timelines,
-      with_reblogs: Setting.show_reblogs_in_public_timelines,
+      with_reblogs: Setting.show_reblogs_in_public_timelines
     )
   end
 
diff --git a/app/controllers/api/v1/trends/links_controller.rb b/app/controllers/api/v1/trends/links_controller.rb
index 8ff3b364e..3ce20fb78 100644
--- a/app/controllers/api/v1/trends/links_controller.rb
+++ b/app/controllers/api/v1/trends/links_controller.rb
@@ -18,13 +18,11 @@ class Api::V1::Trends::LinksController < Api::BaseController
   end
 
   def set_links
-    @links = begin
-      if enabled?
-        links_from_trends.offset(offset_param).limit(limit_param(DEFAULT_LINKS_LIMIT))
-      else
-        []
-      end
-    end
+    @links = if enabled?
+               links_from_trends.offset(offset_param).limit(limit_param(DEFAULT_LINKS_LIMIT))
+             else
+               []
+             end
   end
 
   def links_from_trends
diff --git a/app/controllers/api/v1/trends/statuses_controller.rb b/app/controllers/api/v1/trends/statuses_controller.rb
index c275d5fc8..3aab92477 100644
--- a/app/controllers/api/v1/trends/statuses_controller.rb
+++ b/app/controllers/api/v1/trends/statuses_controller.rb
@@ -16,13 +16,11 @@ class Api::V1::Trends::StatusesController < Api::BaseController
   end
 
   def set_statuses
-    @statuses = begin
-      if enabled?
-        cache_collection(statuses_from_trends.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
-      else
-        []
-      end
-    end
+    @statuses = if enabled?
+                  cache_collection(statuses_from_trends.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT)), Status)
+                else
+                  []
+                end
   end
 
   def statuses_from_trends
diff --git a/app/controllers/api/v1/trends/tags_controller.rb b/app/controllers/api/v1/trends/tags_controller.rb
index 885a4ad7e..9dd9abdfe 100644
--- a/app/controllers/api/v1/trends/tags_controller.rb
+++ b/app/controllers/api/v1/trends/tags_controller.rb
@@ -18,13 +18,11 @@ class Api::V1::Trends::TagsController < Api::BaseController
   end
 
   def set_tags
-    @tags = begin
-      if enabled?
-        tags_from_trends.offset(offset_param).limit(limit_param(DEFAULT_TAGS_LIMIT))
-      else
-        []
-      end
-    end
+    @tags = if enabled?
+              tags_from_trends.offset(offset_param).limit(limit_param(DEFAULT_TAGS_LIMIT))
+            else
+              []
+            end
   end
 
   def tags_from_trends
diff --git a/app/controllers/api/v2/admin/accounts_controller.rb b/app/controllers/api/v2/admin/accounts_controller.rb
index b25831aa0..0c451f778 100644
--- a/app/controllers/api/v2/admin/accounts_controller.rb
+++ b/app/controllers/api/v2/admin/accounts_controller.rb
@@ -25,9 +25,7 @@ class Api::V2::Admin::AccountsController < Api::V1::Admin::AccountsController
   def translated_filter_params
     translated_params = filter_params.slice(*AccountFilter::KEYS)
 
-    if params[:permissions] == 'staff'
-      translated_params[:role_ids] = UserRole.that_can(:manage_reports).map(&:id)
-    end
+    translated_params[:role_ids] = UserRole.that_can(:manage_reports).map(&:id) if params[:permissions] == 'staff'
 
     translated_params
   end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index ee3c5204d..906761f6f 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -18,6 +18,8 @@ class ApplicationController < ActionController::Base
   helper_method :current_skin
   helper_method :single_user_mode?
   helper_method :use_seamless_external_login?
+  helper_method :omniauth_only?
+  helper_method :sso_account_settings
   helper_method :whitelist_mode?
 
   rescue_from ActionController::ParameterMissing, Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request
@@ -63,7 +65,11 @@ class ApplicationController < ActionController::Base
   end
 
   def after_sign_out_path_for(_resource_or_scope)
-    new_user_session_path
+    if ENV['OMNIAUTH_ONLY'] == 'true' && ENV['OIDC_ENABLED'] == 'true'
+      '/auth/auth/openid_connect/logout'
+    else
+      new_user_session_path
+    end
   end
 
   protected
@@ -116,6 +122,14 @@ class ApplicationController < ActionController::Base
     Devise.pam_authentication || Devise.ldap_authentication
   end
 
+  def omniauth_only?
+    ENV['OMNIAUTH_ONLY'] == 'true'
+  end
+
+  def sso_account_settings
+    ENV.fetch('SSO_ACCOUNT_SETTINGS')
+  end
+
   def current_account
     return @current_account if defined?(@current_account)
 
diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb
index 0817a905c..620fb621d 100644
--- a/app/controllers/auth/confirmations_controller.rb
+++ b/app/controllers/auth/confirmations_controller.rb
@@ -15,12 +15,6 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
 
   skip_before_action :require_functional!
 
-  def new
-    super
-
-    resource.email = current_user.unconfirmed_email || current_user.email if user_signed_in?
-  end
-
   def show
     old_session_values = session.to_hash
     reset_session
@@ -29,6 +23,12 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
     super
   end
 
+  def new
+    super
+
+    resource.email = current_user.unconfirmed_email || current_user.email if user_signed_in?
+  end
+
   def confirm_captcha
     check_captcha! do |message|
       flash.now[:alert] = message
@@ -51,6 +51,7 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController
     # step.
     confirmation_token = params[:confirmation_token]
     return if confirmation_token.nil?
+
     @confirmation_user = User.find_first_by_auth_conditions(confirmation_token: confirmation_token)
   end
 
diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb
index 3d7962de5..9e0fb942a 100644
--- a/app/controllers/auth/omniauth_callbacks_controller.rb
+++ b/app/controllers/auth/omniauth_callbacks_controller.rb
@@ -33,7 +33,7 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
 
   def after_sign_in_path_for(resource)
     if resource.email_present?
-      root_path
+      stored_location_for(resource) || root_path
     else
       auth_setup_path(missing_email: '1')
     end
diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb
index 40c38bc6d..d2f1bea93 100644
--- a/app/controllers/auth/registrations_controller.rb
+++ b/app/controllers/auth/registrations_controller.rb
@@ -31,9 +31,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
 
   def update
     super do |resource|
-      if resource.saved_change_to_encrypted_password?
-        resource.clear_other_sessions(current_session.session_id)
-      end
+      resource.clear_other_sessions(current_session.session_id) if resource.saved_change_to_encrypted_password?
     end
   end
 
@@ -49,7 +47,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
     super(hash)
 
     resource.locale                 = I18n.locale
-    resource.invite_code            = params[:invite_code] if resource.invite_code.blank?
+    resource.invite_code            = @invite&.code if resource.invite_code.blank?
     resource.registration_form_time = session[:registration_form_time]
     resource.sign_up_ip             = request.remote_ip
 
diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb
index 16c18baa2..b1abb9f1d 100644
--- a/app/controllers/auth/sessions_controller.rb
+++ b/app/controllers/auth/sessions_controller.rb
@@ -53,9 +53,9 @@ class Auth::SessionsController < Devise::SessionsController
 
       session[:webauthn_challenge] = options_for_get.challenge
 
-      render json: options_for_get, status: :ok
+      render json: options_for_get, status: 200
     else
-      render json: { error: t('webauthn_credentials.not_enabled') }, status: :unauthorized
+      render json: { error: t('webauthn_credentials.not_enabled') }, status: 401
     end
   end
 
@@ -115,9 +115,7 @@ class Auth::SessionsController < Devise::SessionsController
   def home_paths(resource)
     paths = [about_path]
 
-    if single_user_mode? && resource.is_a?(User)
-      paths << short_account_path(username: resource.account)
-    end
+    paths << short_account_path(username: resource.account) if single_user_mode? && resource.is_a?(User)
 
     paths
   end
diff --git a/app/controllers/backups_controller.rb b/app/controllers/backups_controller.rb
new file mode 100644
index 000000000..5891da6f6
--- /dev/null
+++ b/app/controllers/backups_controller.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class BackupsController < ApplicationController
+  include RoutingHelper
+
+  skip_before_action :require_functional!
+
+  before_action :authenticate_user!
+  before_action :set_backup
+
+  def download
+    case Paperclip::Attachment.default_options[:storage]
+    when :s3
+      redirect_to @backup.dump.expiring_url(10)
+    when :fog
+      if Paperclip::Attachment.default_options.dig(:fog_credentials, :openstack_temp_url_key).present?
+        redirect_to @backup.dump.expiring_url(Time.now.utc + 10)
+      else
+        redirect_to full_asset_url(@backup.dump.url)
+      end
+    when :filesystem
+      redirect_to full_asset_url(@backup.dump.url)
+    end
+  end
+
+  private
+
+  def set_backup
+    @backup = current_user.backups.find(params[:id])
+  end
+end
diff --git a/app/controllers/concerns/account_controller_concern.rb b/app/controllers/concerns/account_controller_concern.rb
index 2f7d84df0..e9cff22ca 100644
--- a/app/controllers/concerns/account_controller_concern.rb
+++ b/app/controllers/concerns/account_controller_concern.rb
@@ -10,7 +10,8 @@ module AccountControllerConcern
 
   included do
     before_action :set_instance_presenter
-    before_action :set_link_headers, if: -> { request.format.nil? || request.format == :html }
+
+    after_action :set_link_headers, if: -> { request.format.nil? || request.format == :html }
   end
 
   private
diff --git a/app/controllers/concerns/admin_export_controller_concern.rb b/app/controllers/concerns/admin_export_controller_concern.rb
index b40c76557..4ac48a04b 100644
--- a/app/controllers/concerns/admin_export_controller_concern.rb
+++ b/app/controllers/concerns/admin_export_controller_concern.rb
@@ -26,14 +26,4 @@ module AdminExportControllerConcern
   def import_params
     params.require(:admin_import).permit(:data)
   end
-
-  def import_data_path
-    params[:admin_import][:data].path
-  end
-
-  def parse_import_data!(default_headers)
-    data = CSV.read(import_data_path, headers: true, encoding: 'UTF-8')
-    data = CSV.read(import_data_path, headers: default_headers, encoding: 'UTF-8') unless data.headers&.first&.strip&.include?(default_headers[0])
-    @data = data.reject(&:blank?)
-  end
 end
diff --git a/app/controllers/concerns/cache_concern.rb b/app/controllers/concerns/cache_concern.rb
index 05e431b19..a5a9ba3e1 100644
--- a/app/controllers/concerns/cache_concern.rb
+++ b/app/controllers/concerns/cache_concern.rb
@@ -3,6 +3,158 @@
 module CacheConcern
   extend ActiveSupport::Concern
 
+  module ActiveRecordCoder
+    EMPTY_HASH = {}.freeze
+
+    class << self
+      def dump(record)
+        instances = InstanceTracker.new
+        serialized_associations = serialize_associations(record, instances)
+        serialized_records = instances.map { |r| serialize_record(r) }
+        [serialized_associations, *serialized_records]
+      end
+
+      def load(payload)
+        instances = InstanceTracker.new
+        serialized_associations, *serialized_records = payload
+        serialized_records.each { |attrs| instances.push(deserialize_record(*attrs)) }
+        deserialize_associations(serialized_associations, instances)
+      end
+
+      private
+
+      # Records without associations, or which have already been visited before,
+      # are serialized by their id alone.
+      #
+      # Records with associations are serialized as a two-element array including
+      # their id and the record's association cache.
+      #
+      def serialize_associations(record, instances)
+        return unless record
+
+        if (id = instances.lookup(record))
+          payload = id
+        else
+          payload = instances.push(record)
+
+          cached_associations = record.class.reflect_on_all_associations.select do |reflection|
+            record.association_cached?(reflection.name)
+          end
+
+          unless cached_associations.empty?
+            serialized_associations = cached_associations.map do |reflection|
+              association = record.association(reflection.name)
+
+              serialized_target = if reflection.collection?
+                                    association.target.map { |target_record| serialize_associations(target_record, instances) }
+                                  else
+                                    serialize_associations(association.target, instances)
+                                  end
+
+              [reflection.name, serialized_target]
+            end
+
+            payload = [payload, serialized_associations]
+          end
+        end
+
+        payload
+      end
+
+      def deserialize_associations(payload, instances)
+        return unless payload
+
+        id, associations = payload
+        record = instances.fetch(id)
+
+        associations&.each do |name, serialized_target|
+          begin
+            association = record.association(name)
+          rescue ActiveRecord::AssociationNotFoundError
+            raise AssociationMissingError, "undefined association: #{name}"
+          end
+
+          target = if association.reflection.collection?
+                     serialized_target.map! { |serialized_record| deserialize_associations(serialized_record, instances) }
+                   else
+                     deserialize_associations(serialized_target, instances)
+                   end
+
+          association.target = target
+        end
+
+        record
+      end
+
+      def serialize_record(record)
+        arguments = [record.class.name, attributes_for_database(record)]
+        arguments << true if record.new_record?
+        arguments
+      end
+
+      if Rails.gem_version >= Gem::Version.new('7.0')
+        def attributes_for_database(record)
+          attributes = record.attributes_for_database
+          attributes.transform_values! { |attr| attr.is_a?(::ActiveModel::Type::Binary::Data) ? attr.to_s : attr }
+          attributes
+        end
+      else
+        def attributes_for_database(record)
+          attributes = record.instance_variable_get(:@attributes).send(:attributes).transform_values(&:value_for_database)
+          attributes.transform_values! { |attr| attr.is_a?(::ActiveModel::Type::Binary::Data) ? attr.to_s : attr }
+          attributes
+        end
+      end
+
+      def deserialize_record(class_name, attributes_from_database, new_record = false) # rubocop:disable Style/OptionalBooleanParameter
+        begin
+          klass = Object.const_get(class_name)
+        rescue NameError
+          raise ClassMissingError, "undefined class: #{class_name}"
+        end
+
+        # Ideally we'd like to call `klass.instantiate`, however it doesn't allow to pass
+        # wether the record was persisted or not.
+        attributes = klass.attributes_builder.build_from_database(attributes_from_database, EMPTY_HASH)
+        klass.allocate.init_with_attributes(attributes, new_record)
+      end
+    end
+
+    class Error < StandardError
+    end
+
+    class ClassMissingError < Error
+    end
+
+    class AssociationMissingError < Error
+    end
+
+    class InstanceTracker
+      def initialize
+        @instances = []
+        @ids = {}.compare_by_identity
+      end
+
+      def map(&block)
+        @instances.map(&block)
+      end
+
+      def fetch(...)
+        @instances.fetch(...)
+      end
+
+      def push(instance)
+        id = @ids[instance] = @instances.size
+        @instances << instance
+        id
+      end
+
+      def lookup(instance)
+        @ids[instance]
+      end
+    end
+  end
+
   def render_with_cache(**options)
     raise ArgumentError, 'only JSON render calls are supported' unless options.key?(:json) || block_given?
 
@@ -34,8 +186,13 @@ module CacheConcern
     raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation)
     return [] if raw.empty?
 
-    cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id)
-    uncached_ids           = raw.map(&:id) - cached_keys_with_value.keys
+    cached_keys_with_value = begin
+      Rails.cache.read_multi(*raw).transform_keys(&:id).transform_values { |r| ActiveRecordCoder.load(r) }
+    rescue ActiveRecordCoder::Error
+      {} # The serialization format may have changed, let's pretend it's a cache miss.
+    end
+
+    uncached_ids = raw.map(&:id) - cached_keys_with_value.keys
 
     klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!)
 
@@ -43,7 +200,7 @@ module CacheConcern
       uncached = klass.where(id: uncached_ids).with_includes.index_by(&:id)
 
       uncached.each_value do |item|
-        Rails.cache.write(item, item)
+        Rails.cache.write(item, ActiveRecordCoder.dump(item))
       end
     end
 
diff --git a/app/controllers/concerns/rate_limit_headers.rb b/app/controllers/concerns/rate_limit_headers.rb
index b8696df73..30702f00e 100644
--- a/app/controllers/concerns/rate_limit_headers.rb
+++ b/app/controllers/concerns/rate_limit_headers.rb
@@ -6,13 +6,11 @@ module RateLimitHeaders
   class_methods do
     def override_rate_limit_headers(method_name, options = {})
       around_action(only: method_name, if: :current_account) do |_controller, block|
-        begin
-          block.call
-        ensure
-          rate_limiter = RateLimiter.new(current_account, options)
-          rate_limit_headers = rate_limiter.to_headers
-          response.headers.merge!(rate_limit_headers) unless response.headers['X-RateLimit-Remaining'].present? && rate_limit_headers['X-RateLimit-Remaining'].to_i > response.headers['X-RateLimit-Remaining'].to_i
-        end
+        block.call
+      ensure
+        rate_limiter = RateLimiter.new(current_account, options)
+        rate_limit_headers = rate_limiter.to_headers
+        response.headers.merge!(rate_limit_headers) unless response.headers['X-RateLimit-Remaining'].present? && rate_limit_headers['X-RateLimit-Remaining'].to_i > response.headers['X-RateLimit-Remaining'].to_i
       end
     end
   end
@@ -67,6 +65,6 @@ module RateLimitHeaders
   end
 
   def reset_period_offset
-    api_throttle_data[:period] - request_time.to_i % api_throttle_data[:period]
+    api_throttle_data[:period] - (request_time.to_i % api_throttle_data[:period])
   end
 end
diff --git a/app/controllers/concerns/session_tracking_concern.rb b/app/controllers/concerns/session_tracking_concern.rb
index eaaa4ac59..3f56c0d02 100644
--- a/app/controllers/concerns/session_tracking_concern.rb
+++ b/app/controllers/concerns/session_tracking_concern.rb
@@ -13,6 +13,7 @@ module SessionTrackingConcern
 
   def set_session_activity
     return unless session_needs_update?
+
     current_session.touch
   end
 
diff --git a/app/controllers/concerns/signature_verification.rb b/app/controllers/concerns/signature_verification.rb
index 4502da698..931725943 100644
--- a/app/controllers/concerns/signature_verification.rb
+++ b/app/controllers/concerns/signature_verification.rb
@@ -46,11 +46,11 @@ module SignatureVerification
   end
 
   def require_account_signature!
-    render plain: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account
+    render json: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account
   end
 
   def require_actor_signature!
-    render plain: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_actor
+    render json: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_actor
   end
 
   def signed_request?
@@ -97,11 +97,11 @@ module SignatureVerification
 
     actor = stoplight_wrap_request { actor_refresh_key!(actor) }
 
-    raise SignatureVerificationError, "Public key not found for key #{signature_params['keyId']}" if actor.nil?
+    raise SignatureVerificationError, "Could not refresh public key #{signature_params['keyId']}" if actor.nil?
 
     return actor unless verify_signature(actor, signature, compare_signed_string).nil?
 
-    fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)"
+    fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri} using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)", signed_string: compare_signed_string, signature: signature_params['signature']
   rescue SignatureVerificationError => e
     fail_with! e.message
   rescue HTTP::Error, OpenSSL::SSL::SSLError => e
@@ -118,8 +118,8 @@ module SignatureVerification
 
   private
 
-  def fail_with!(message)
-    @signature_verification_failure_reason = message
+  def fail_with!(message, **options)
+    @signature_verification_failure_reason = { error: message }.merge(options)
     @signed_request_actor = nil
   end
 
@@ -138,7 +138,7 @@ module SignatureVerification
   end
 
   def signed_headers
-    signature_params.fetch('headers', signature_algorithm == 'hs2019' ? '(created)' : 'date').downcase.split(' ')
+    signature_params.fetch('headers', signature_algorithm == 'hs2019' ? '(created)' : 'date').downcase.split
   end
 
   def verify_signature_strength!
@@ -165,6 +165,7 @@ module SignatureVerification
     end
 
     raise SignatureVerificationError, "Invalid Digest value. The provided Digest value is not a SHA-256 digest. Given digest: #{sha256[1]}" if digest_size != 32
+
     raise SignatureVerificationError, "Invalid Digest value. Computed SHA-256 digest: #{body_digest}; given: #{sha256[1]}"
   end
 
@@ -209,8 +210,8 @@ module SignatureVerification
       end
 
       expires_time = Time.at(signature_params['expires'].to_i).utc if signature_params['expires'].present?
-    rescue ArgumentError
-      return false
+    rescue ArgumentError => e
+      raise SignatureVerificationError, "Invalid Date header: #{e.message}"
     end
 
     expires_time ||= created_time + 5.minutes unless created_time.nil?
@@ -227,7 +228,7 @@ module SignatureVerification
   end
 
   def to_header_name(name)
-    name.split(/-/).map(&:capitalize).join('-')
+    name.split('-').map(&:capitalize).join('-')
   end
 
   def missing_required_signature_parameters?
diff --git a/app/controllers/concerns/two_factor_authentication_concern.rb b/app/controllers/concerns/two_factor_authentication_concern.rb
index c9477a1d4..b30cd354d 100644
--- a/app/controllers/concerns/two_factor_authentication_concern.rb
+++ b/app/controllers/concerns/two_factor_authentication_concern.rb
@@ -57,10 +57,10 @@ module TwoFactorAuthenticationConcern
 
     if valid_webauthn_credential?(user, webauthn_credential)
       on_authentication_success(user, :webauthn)
-      render json: { redirect_path: after_sign_in_path_for(user) }, status: :ok
+      render json: { redirect_path: after_sign_in_path_for(user) }, status: 200
     else
       on_authentication_failure(user, :webauthn, :invalid_credential)
-      render json: { error: t('webauthn_credentials.invalid_credential') }, status: :unprocessable_entity
+      render json: { error: t('webauthn_credentials.invalid_credential') }, status: 422
     end
   end
 
@@ -81,13 +81,11 @@ module TwoFactorAuthenticationConcern
 
     @body_classes     = 'lighter'
     @webauthn_enabled = user.webauthn_enabled?
-    @scheme_type      = begin
-      if user.webauthn_enabled? && user_params[:otp_attempt].blank?
-        'webauthn'
-      else
-        'totp'
-      end
-    end
+    @scheme_type      = if user.webauthn_enabled? && user_params[:otp_attempt].blank?
+                          'webauthn'
+                        else
+                          'totp'
+                        end
 
     set_locale { render :two_factor }
   end
diff --git a/app/controllers/filters/statuses_controller.rb b/app/controllers/filters/statuses_controller.rb
index 4f63de7b6..86d11fcb9 100644
--- a/app/controllers/filters/statuses_controller.rb
+++ b/app/controllers/filters/statuses_controller.rb
@@ -43,9 +43,7 @@ class Filters::StatusesController < ApplicationController
   end
 
   def action_from_button
-    if params[:remove]
-      'remove'
-    end
+    'remove' if params[:remove]
   end
 
   def set_body_classes
diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb
index f9160d8c4..37c5dcb99 100644
--- a/app/controllers/media_controller.rb
+++ b/app/controllers/media_controller.rb
@@ -33,7 +33,7 @@ class MediaController < ApplicationController
 
     scope = MediaAttachment.local.attached
     # If id is 19 characters long, it's a shortcode, otherwise it's an identifier
-    @media_attachment = id.size == 19 ? scope.find_by!(shortcode: id) : scope.find_by!(id: id)
+    @media_attachment = id.size == 19 ? scope.find_by!(shortcode: id) : scope.find(id)
   end
 
   def verify_permitted_status!
diff --git a/app/controllers/media_proxy_controller.rb b/app/controllers/media_proxy_controller.rb
index 3b228722f..f29b69a24 100644
--- a/app/controllers/media_proxy_controller.rb
+++ b/app/controllers/media_proxy_controller.rb
@@ -23,7 +23,7 @@ class MediaProxyController < ApplicationController
       redownload! if @media_attachment.needs_redownload? && !reject_media?
     end
 
-    redirect_to full_asset_url(@media_attachment.file.url(version))
+    redirect_to full_asset_url(@media_attachment.file.url(version)), allow_other_host: true
   end
 
   private
diff --git a/app/controllers/relationships_controller.rb b/app/controllers/relationships_controller.rb
index d40770726..52cf1e0c1 100644
--- a/app/controllers/relationships_controller.rb
+++ b/app/controllers/relationships_controller.rb
@@ -20,6 +20,8 @@ class RelationshipsController < ApplicationController
     @form.save
   rescue ActionController::ParameterMissing
     # Do nothing
+  rescue Mastodon::NotPermittedError, ActiveRecord::RecordNotFound
+    flash[:alert] = I18n.t('relationships.follow_failure') if action_from_button == 'follow'
   ensure
     redirect_to relationships_path(filter_params)
   end
@@ -61,8 +63,8 @@ class RelationshipsController < ApplicationController
       'unfollow'
     elsif params[:remove_from_followers]
       'remove_from_followers'
-    elsif params[:block_domains]
-      'block_domains'
+    elsif params[:block_domains] || params[:remove_domains_from_followers]
+      'remove_domains_from_followers'
     end
   end
 
diff --git a/app/controllers/settings/applications_controller.rb b/app/controllers/settings/applications_controller.rb
index d3ac268d8..e6e137c2b 100644
--- a/app/controllers/settings/applications_controller.rb
+++ b/app/controllers/settings/applications_controller.rb
@@ -29,7 +29,13 @@ class Settings::ApplicationsController < Settings::BaseController
 
   def update
     if @application.update(application_params)
-      redirect_to settings_applications_path, notice: I18n.t('generic.changes_saved_msg')
+      if @application.scopes_previously_changed?
+        @access_token = current_user.token_for_app(@application)
+        @access_token.destroy
+        redirect_to settings_application_path(@application), notice: I18n.t('applications.token_regenerated')
+      else
+        redirect_to settings_application_path(@application), notice: I18n.t('generic.changes_saved_msg')
+      end
     else
       render :show
     end
diff --git a/app/controllers/settings/flavours_controller.rb b/app/controllers/settings/flavours_controller.rb
index 62c52eee9..b179b9429 100644
--- a/app/controllers/settings/flavours_controller.rb
+++ b/app/controllers/settings/flavours_controller.rb
@@ -12,27 +12,15 @@ class Settings::FlavoursController < Settings::BaseController
   end
 
   def show
-    unless Themes.instance.flavours.include?(params[:flavour]) || (params[:flavour] == current_flavour)
-      redirect_to action: 'show', flavour: current_flavour
-    end
+    redirect_to action: 'show', flavour: current_flavour unless Themes.instance.flavours.include?(params[:flavour]) || (params[:flavour] == current_flavour)
 
     @listing = Themes.instance.flavours
     @selected = params[:flavour]
   end
 
   def update
-    user_settings.update(user_settings_params)
+    current_user.settings.update(flavour: params.require(:flavour), skin: params.dig(:user, :setting_skin))
+    current_user.save
     redirect_to action: 'show', flavour: params[:flavour]
   end
-
-  private
-
-  def user_settings
-    UserSettingsDecorator.new(current_user)
-  end
-
-  def user_settings_params
-    { setting_flavour: params.require(:flavour),
-      setting_skin: params.dig(:user, :setting_skin) }.with_indifferent_access
-  end
 end
diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
index 4c1336436..281deb64d 100644
--- a/app/controllers/settings/preferences_controller.rb
+++ b/app/controllers/settings/preferences_controller.rb
@@ -4,8 +4,6 @@ class Settings::PreferencesController < Settings::BaseController
   def show; end
 
   def update
-    user_settings.update(user_settings_params.to_h)
-
     if current_user.update(user_params)
       I18n.locale = current_user.locale
       redirect_to after_update_redirect_path, notice: I18n.t('generic.changes_saved_msg')
@@ -20,46 +18,7 @@ class Settings::PreferencesController < Settings::BaseController
     settings_preferences_path
   end
 
-  def user_settings
-    UserSettingsDecorator.new(current_user)
-  end
-
   def user_params
-    params.require(:user).permit(
-      :locale,
-      chosen_languages: []
-    )
-  end
-
-  def user_settings_params
-    params.require(:user).permit(
-      :setting_default_privacy,
-      :setting_default_sensitive,
-      :setting_default_language,
-      :setting_unfollow_modal,
-      :setting_boost_modal,
-      :setting_favourite_modal,
-      :setting_delete_modal,
-      :setting_auto_play_gif,
-      :setting_display_media,
-      :setting_expand_spoilers,
-      :setting_reduce_motion,
-      :setting_disable_swiping,
-      :setting_system_font_ui,
-      :setting_system_emoji_font,
-      :setting_noindex,
-      :setting_hide_followers_count,
-      :setting_aggregate_reblogs,
-      :setting_show_application,
-      :setting_advanced_layout,
-      :setting_default_content_type,
-      :setting_use_blurhash,
-      :setting_use_pending_items,
-      :setting_trends,
-      :setting_crop_images,
-      :setting_always_send_emails,
-      notification_emails: %i(follow follow_request reblog favourite mention report pending_account trending_tag trending_link trending_status appeal),
-      interactions: %i(must_be_follower must_be_following must_be_following_dm)
-    )
+    params.require(:user).permit(:locale, chosen_languages: [], settings_attributes: UserSettings.keys)
   end
 end
diff --git a/app/controllers/settings/two_factor_authentication/otp_authentication_controller.rb b/app/controllers/settings/two_factor_authentication/otp_authentication_controller.rb
index cbba842a9..0bff01ec2 100644
--- a/app/controllers/settings/two_factor_authentication/otp_authentication_controller.rb
+++ b/app/controllers/settings/two_factor_authentication/otp_authentication_controller.rb
@@ -22,18 +22,9 @@ module Settings
 
       private
 
-      def confirmation_params
-        params.require(:form_two_factor_confirmation).permit(:otp_attempt)
-      end
-
       def verify_otp_not_enabled
         redirect_to settings_two_factor_authentication_methods_path if current_user.otp_enabled?
       end
-
-      def acceptable_code?
-        current_user.validate_and_consume_otp!(confirmation_params[:otp_attempt]) ||
-          current_user.invalidate_otp_backup_code!(confirmation_params[:otp_attempt])
-      end
     end
   end
 end
diff --git a/app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb b/app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb
index 7e2d43dcd..5a9029a42 100644
--- a/app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb
+++ b/app/controllers/settings/two_factor_authentication/webauthn_credentials_controller.rb
@@ -27,7 +27,7 @@ module Settings
 
         session[:webauthn_challenge] = options_for_create.challenge
 
-        render json: options_for_create, status: :ok
+        render json: options_for_create, status: 200
       end
 
       def create
@@ -52,7 +52,7 @@ module Settings
             end
           else
             flash[:error] = I18n.t('webauthn_credentials.create.error')
-            status = :internal_server_error
+            status = :unprocessable_entity
           end
         else
           flash[:error] = t('webauthn_credentials.create.error')
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
index e5221df3a..15c081264 100644
--- a/app/controllers/statuses_controller.rb
+++ b/app/controllers/statuses_controller.rb
@@ -9,11 +9,12 @@ class StatusesController < ApplicationController
   before_action :require_account_signature!, only: [:show, :activity], if: -> { request.format == :json && authorized_fetch_mode? }
   before_action :set_status
   before_action :set_instance_presenter
-  before_action :set_link_headers
   before_action :redirect_to_original, only: :show
   before_action :set_cache_headers
   before_action :set_body_classes, only: :embed
 
+  after_action :set_link_headers
+
   skip_around_action :set_locale, if: -> { request.format == :json }
   skip_before_action :require_functional!, only: [:show, :embed], unless: :whitelist_mode?
 
@@ -71,6 +72,6 @@ class StatusesController < ApplicationController
   end
 
   def redirect_to_original
-    redirect_to ActivityPub::TagManager.instance.url_for(@status.reblog) if @status.reblog?
+    redirect_to(ActivityPub::TagManager.instance.url_for(@status.reblog), allow_other_host: true) if @status.reblog?
   end
 end
diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb
index 65017acba..4b747c9ad 100644
--- a/app/controllers/tags_controller.rb
+++ b/app/controllers/tags_controller.rb
@@ -11,7 +11,7 @@ class TagsController < ApplicationController
   before_action :authenticate_user!, if: :whitelist_mode?
   before_action :set_local
   before_action :set_tag
-  before_action :set_statuses
+  before_action :set_statuses, if: -> { request.format == :rss }
   before_action :set_instance_presenter
 
   skip_before_action :require_functional!, unless: :whitelist_mode?
@@ -44,12 +44,7 @@ class TagsController < ApplicationController
   end
 
   def set_statuses
-    case request.format&.to_sym
-    when :json
-      @statuses = cache_collection(TagFeed.new(@tag, current_account, local: @local).get(PAGE_SIZE, params[:max_id], params[:since_id], params[:min_id]), Status)
-    when :rss
-      @statuses = cache_collection(TagFeed.new(@tag, nil, local: @local).get(limit_param), Status)
-    end
+    @statuses = cache_collection(TagFeed.new(@tag, nil, local: @local).get(limit_param), Status)
   end
 
   def set_instance_presenter
@@ -63,9 +58,7 @@ class TagsController < ApplicationController
   def collection_presenter
     ActivityPub::CollectionPresenter.new(
       id: tag_url(@tag),
-      type: :ordered,
-      size: @tag.statuses.count,
-      items: @statuses.map { |status| ActivityPub::TagManager.instance.uri_for(status) }
+      type: :ordered
     )
   end
 end
diff --git a/app/helpers/accounts_helper.rb b/app/helpers/accounts_helper.rb
index e15aee6df..b8277ee17 100644
--- a/app/helpers/accounts_helper.rb
+++ b/app/helpers/accounts_helper.rb
@@ -28,7 +28,7 @@ module AccountsHelper
   end
 
   def hide_followers_count?(account)
-    Setting.hide_followers_count || account.user&.setting_hide_followers_count
+    Setting.hide_followers_count || account.user&.settings&.[]('hide_followers_count')
   end
 
   def account_description(account)
diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb
index 215ecea0d..4018ef6b1 100644
--- a/app/helpers/admin/action_logs_helper.rb
+++ b/app/helpers/admin/action_logs_helper.rb
@@ -20,7 +20,7 @@ module Admin::ActionLogsHelper
     when 'Status'
       link_to log.human_identifier, log.permalink
     when 'AccountWarning'
-      link_to log.human_identifier, admin_account_path(log.target_id)
+      link_to log.human_identifier, disputes_strike_path(log.target_id)
     when 'Announcement'
       link_to truncate(log.human_identifier), edit_admin_announcement_path(log.target_id)
     when 'IpBlock', 'Instance', 'CustomEmoji'
diff --git a/app/helpers/admin/announcements_helper.rb b/app/helpers/admin/announcements_helper.rb
deleted file mode 100644
index 0c053ddec..000000000
--- a/app/helpers/admin/announcements_helper.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# frozen_string_literal: true
-
-module Admin::AnnouncementsHelper
-  def time_range(announcement)
-    if announcement.all_day?
-      safe_join([l(announcement.starts_at.to_date), ' - ', l(announcement.ends_at.to_date)])
-    else
-      safe_join([l(announcement.starts_at), ' - ', l(announcement.ends_at)])
-    end
-  end
-end
diff --git a/app/helpers/admin/dashboard_helper.rb b/app/helpers/admin/dashboard_helper.rb
index c21d41341..6096ff138 100644
--- a/app/helpers/admin/dashboard_helper.rb
+++ b/app/helpers/admin/dashboard_helper.rb
@@ -19,19 +19,17 @@ module Admin::DashboardHelper
   end
 
   def relevant_account_timestamp(account)
-    timestamp, exact = begin
-      if account.user_current_sign_in_at && account.user_current_sign_in_at < 24.hours.ago
-        [account.user_current_sign_in_at, true]
-      elsif account.user_current_sign_in_at
-        [account.user_current_sign_in_at, false]
-      elsif account.user_pending?
-        [account.user_created_at, true]
-      elsif account.last_status_at.present?
-        [account.last_status_at, true]
-      else
-        [nil, false]
-      end
-    end
+    timestamp, exact = if account.user_current_sign_in_at && account.user_current_sign_in_at < 24.hours.ago
+                         [account.user_current_sign_in_at, true]
+                       elsif account.user_current_sign_in_at
+                         [account.user_current_sign_in_at, false]
+                       elsif account.user_pending?
+                         [account.user_created_at, true]
+                       elsif account.last_status_at.present?
+                         [account.last_status_at, true]
+                       else
+                         [nil, false]
+                       end
 
     return '-' if timestamp.nil?
     return t('generic.today') unless exact
diff --git a/app/helpers/admin/trends/statuses_helper.rb b/app/helpers/admin/trends/statuses_helper.rb
index 214c1e2a6..79fee44dc 100644
--- a/app/helpers/admin/trends/statuses_helper.rb
+++ b/app/helpers/admin/trends/statuses_helper.rb
@@ -2,13 +2,11 @@
 
 module Admin::Trends::StatusesHelper
   def one_line_preview(status)
-    text = begin
-      if status.local?
-        status.text.split("\n").first
-      else
-        Nokogiri::HTML(status.text).css('html > body > *').first&.text
-      end
-    end
+    text = if status.local?
+             status.text.split("\n").first
+           else
+             Nokogiri::HTML(status.text).css('html > body > *').first&.text
+           end
 
     return '' if text.blank?
 
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index af453825b..2cac2de59 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -67,7 +67,7 @@ module ApplicationHelper
   def link_to_login(name = nil, html_options = nil, &block)
     target = new_user_session_path
 
-    html_options = name if block_given?
+    html_options = name if block
 
     if omniauth_only? && Devise.mappings[:user].omniauthable? && User.omniauth_providers.size == 1
       target = omniauth_authorize_path(:user, User.omniauth_providers[0])
@@ -75,7 +75,7 @@ module ApplicationHelper
       html_options[:method] = :post
     end
 
-    if block_given?
+    if block
       link_to(target, html_options, &block)
     else
       link_to(name, target, html_options)
@@ -105,13 +105,14 @@ module ApplicationHelper
 
   def can?(action, record)
     return false if record.nil?
+
     policy(record).public_send("#{action}?")
   end
 
   def fa_icon(icon, attributes = {})
     class_names = attributes[:class]&.split(' ') || []
     class_names << 'fa'
-    class_names += icon.split(' ').map { |cl| "fa-#{cl}" }
+    class_names += icon.split.map { |cl| "fa-#{cl}" }
 
     content_tag(:i, nil, attributes.merge(class: class_names.join(' ')))
   end
@@ -163,7 +164,7 @@ module ApplicationHelper
   end
 
   def body_classes
-    output = (@body_classes || '').split(' ')
+    output = (@body_classes || '').split
     output << "flavour-#{current_flavour.parameterize}"
     output << "skin-#{current_skin.parameterize}"
     output << 'system-font' if current_account&.user&.setting_system_font_ui
@@ -217,9 +218,7 @@ module ApplicationHelper
       state_params[:moved_to_account] = current_account.moved_to_account
     end
 
-    if single_user_mode?
-      state_params[:owner] = Account.local.without_suspended.where('id > 0').first
-    end
+    state_params[:owner] = Account.local.without_suspended.where('id > 0').first if single_user_mode?
 
     json = ActiveModelSerializers::SerializableResource.new(InitialStatePresenter.new(state_params), serializer: InitialStateSerializer).to_json
     # rubocop:disable Rails/OutputSafety
diff --git a/app/helpers/branding_helper.rb b/app/helpers/branding_helper.rb
index ad7702aea..548c95411 100644
--- a/app/helpers/branding_helper.rb
+++ b/app/helpers/branding_helper.rb
@@ -23,14 +23,12 @@ module BrandingHelper
   end
 
   def render_symbol(version = :icon)
-    path = begin
-      case version
-      when :icon
-        'logo-symbol-icon.svg'
-      when :wordmark
-        'logo-symbol-wordmark.svg'
-      end
-    end
+    path = case version
+           when :icon
+             'logo-symbol-icon.svg'
+           when :wordmark
+             'logo-symbol-wordmark.svg'
+           end
 
     render(file: Rails.root.join('app', 'javascript', 'images', path)).html_safe # rubocop:disable Rails/OutputSafety
   end
diff --git a/app/helpers/domain_control_helper.rb b/app/helpers/domain_control_helper.rb
index ac60cad29..ffcf375ea 100644
--- a/app/helpers/domain_control_helper.rb
+++ b/app/helpers/domain_control_helper.rb
@@ -4,13 +4,11 @@ module DomainControlHelper
   def domain_not_allowed?(uri_or_domain)
     return if uri_or_domain.blank?
 
-    domain = begin
-      if uri_or_domain.include?('://')
-        Addressable::URI.parse(uri_or_domain).host
-      else
-        uri_or_domain
-      end
-    end
+    domain = if uri_or_domain.include?('://')
+               Addressable::URI.parse(uri_or_domain).host
+             else
+               uri_or_domain
+             end
 
     if whitelist_mode?
       !DomainAllow.allowed?(domain)
diff --git a/app/helpers/email_helper.rb b/app/helpers/email_helper.rb
index 360783c62..0800601f9 100644
--- a/app/helpers/email_helper.rb
+++ b/app/helpers/email_helper.rb
@@ -7,7 +7,7 @@ module EmailHelper
 
   def email_to_canonical_email(str)
     username, domain = str.downcase.split('@', 2)
-    username, = username.gsub('.', '').split('+', 2)
+    username, = username.delete('.').split('+', 2)
 
     "#{username}@#{domain}"
   end
diff --git a/app/helpers/formatting_helper.rb b/app/helpers/formatting_helper.rb
index 05c003037..5b2ac1a2a 100644
--- a/app/helpers/formatting_helper.rb
+++ b/app/helpers/formatting_helper.rb
@@ -21,30 +21,26 @@ module FormattingHelper
   def rss_status_content_format(status)
     html = status_content_format(status)
 
-    before_html = begin
-      if status.spoiler_text?
-        tag.p do
-          tag.strong do
-            I18n.t('rss.content_warning', locale: available_locale_or_nil(status.language) || I18n.default_locale)
-          end
-
-          status.spoiler_text
-        end + tag.hr
-      end
-    end
-
-    after_html = begin
-      if status.preloadable_poll
-        tag.p do
-          safe_join(
-            status.preloadable_poll.options.map do |o|
-              tag.send(status.preloadable_poll.multiple? ? 'checkbox' : 'radio', o, disabled: true)
-            end,
-            tag.br
-          )
-        end
-      end
-    end
+    before_html = if status.spoiler_text?
+                    tag.p do
+                      tag.strong do
+                        I18n.t('rss.content_warning', locale: available_locale_or_nil(status.language) || I18n.default_locale)
+                      end
+
+                      status.spoiler_text
+                    end + tag.hr
+                  end
+
+    after_html = if status.preloadable_poll
+                   tag.p do
+                     safe_join(
+                       status.preloadable_poll.options.map do |o|
+                         tag.send(status.preloadable_poll.multiple? ? 'checkbox' : 'radio', o, disabled: true)
+                       end,
+                       tag.br
+                     )
+                   end
+                 end
 
     prerender_custom_emojis(
       safe_join([before_html, html, after_html]),
diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb
index f41104709..c5b83326d 100644
--- a/app/helpers/home_helper.rb
+++ b/app/helpers/home_helper.rb
@@ -8,7 +8,7 @@ module HomeHelper
   end
 
   def account_link_to(account, button = '', path: nil)
-    content_tag(:div, class: 'account') do
+    content_tag(:div, class: 'account account--minimal') do
       content_tag(:div, class: 'account__wrapper') do
         section = if account.nil?
                     content_tag(:div, class: 'account__display-name') do
@@ -41,9 +41,9 @@ module HomeHelper
 
   def obscured_counter(count)
     if count <= 0
-      0
+      '0'
     elsif count == 1
-      1
+      '1'
     else
       '1+'
     end
@@ -57,14 +57,6 @@ module HomeHelper
     end
   end
 
-  def optional_link_to(condition, path, options = {}, &block)
-    if condition
-      link_to(path, options, &block)
-    else
-      content_tag(:div, &block)
-    end
-  end
-
   def sign_up_message
     if closed_registrations?
       t('auth.registration_closed', instance: site_hostname)
diff --git a/app/helpers/instance_helper.rb b/app/helpers/instance_helper.rb
index daacb535b..bedfe6f30 100644
--- a/app/helpers/instance_helper.rb
+++ b/app/helpers/instance_helper.rb
@@ -10,13 +10,11 @@ module InstanceHelper
   end
 
   def description_for_sign_up
-    prefix = begin
-      if @invite.present?
-        I18n.t('auth.description.prefix_invited_by_user', name: @invite.user.account.username)
-      else
-        I18n.t('auth.description.prefix_sign_up')
-      end
-    end
+    prefix = if @invite.present?
+               I18n.t('auth.description.prefix_invited_by_user', name: @invite.user.account.username)
+             else
+               I18n.t('auth.description.prefix_sign_up')
+             end
 
     safe_join([prefix, I18n.t('auth.description.suffix')], ' ')
   end
diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb
index 102e4b132..24362b61e 100644
--- a/app/helpers/jsonld_helper.rb
+++ b/app/helpers/jsonld_helper.rb
@@ -26,15 +26,13 @@ module JsonLdHelper
   # The url attribute can be a string, an array of strings, or an array of objects.
   # The objects could include a mimeType. Not-included mimeType means it's text/html.
   def url_to_href(value, preferred_type = nil)
-    single_value = begin
-      if value.is_a?(Array) && !value.first.is_a?(String)
-        value.find { |link| preferred_type.nil? || ((link['mimeType'].presence || 'text/html') == preferred_type) }
-      elsif value.is_a?(Array)
-        value.first
-      else
-        value
-      end
-    end
+    single_value = if value.is_a?(Array) && !value.first.is_a?(String)
+                     value.find { |link| preferred_type.nil? || ((link['mimeType'].presence || 'text/html') == preferred_type) }
+                   elsif value.is_a?(Array)
+                     value.first
+                   else
+                     value
+                   end
 
     if single_value.nil? || single_value.is_a?(String)
       single_value
@@ -213,7 +211,7 @@ module JsonLdHelper
     end
   end
 
-  def load_jsonld_context(url, _options = {}, &_block)
+  def load_jsonld_context(url, _options = {}, &block)
     json = Rails.cache.fetch("jsonld:context:#{url}", expires_in: 30.days, raw: true) do
       request = Request.new(:get, url)
       request.add_headers('Accept' => 'application/ld+json')
@@ -226,6 +224,6 @@ module JsonLdHelper
 
     doc = JSON::LD::API::RemoteDocument.new(json, documentUrl: url)
 
-    block_given? ? yield(doc) : doc
+    block ? yield(doc) : doc
   end
 end
diff --git a/app/helpers/languages_helper.rb b/app/helpers/languages_helper.rb
index 27e8b376c..c367ae1cd 100644
--- a/app/helpers/languages_helper.rb
+++ b/app/helpers/languages_helper.rb
@@ -1,5 +1,6 @@
 # frozen_string_literal: true
-# rubocop:disable Metrics/ModuleLength, Style/WordArray
+
+# rubocop:disable Metrics/ModuleLength
 
 module LanguagesHelper
   ISO_639_1 = {
@@ -199,6 +200,8 @@ module LanguagesHelper
     sco: ['Scots', 'Scots'].freeze,
     sma: ['Southern Sami', 'Åarjelsaemien Gïele'].freeze,
     smj: ['Lule Sami', 'Julevsámegiella'].freeze,
+    szl: ['Silesian', 'ślůnsko godka'].freeze,
+    tai: ['Tai', 'ภาษาไท or ภาษาไต'].freeze,
     tok: ['Toki Pona', 'toki pona'].freeze,
     zba: ['Balaibalan', 'باليبلن'].freeze,
     zgh: ['Standard Moroccan Tamazight', 'ⵜⴰⵎⴰⵣⵉⵖⵜ'].freeze,
@@ -214,8 +217,10 @@ module LanguagesHelper
   # names, but for some translations, we need the names of the
   # regional variants specifically
   REGIONAL_LOCALE_NAMES = {
+    'en-GB': 'English (British)',
     'es-AR': 'Español (Argentina)',
     'es-MX': 'Español (México)',
+    'fr-QC': 'Français (Canadien)',
     'pt-BR': 'Português (Brasil)',
     'pt-PT': 'Português (Portugal)',
     'sr-Latn': 'Srpski (latinica)',
@@ -274,4 +279,4 @@ module LanguagesHelper
   end
 end
 
-# rubocop:enable Metrics/ModuleLength, Style/WordArray
+# rubocop:enable Metrics/ModuleLength
diff --git a/app/javascript/core/embed.js b/app/javascript/core/embed.js
index 9083eb7a3..d1e8f6b10 100644
--- a/app/javascript/core/embed.js
+++ b/app/javascript/core/embed.js
@@ -15,7 +15,7 @@ window.addEventListener('message', e => {
       id: data.id,
       height: document.getElementsByTagName('html')[0].scrollHeight,
     }, '*');
-  };
+  }
 
   if (['interactive', 'complete'].includes(document.readyState)) {
     setEmbedHeight();
diff --git a/app/javascript/core/settings.js b/app/javascript/core/settings.js
index d5bb9532c..d578463a3 100644
--- a/app/javascript/core/settings.js
+++ b/app/javascript/core/settings.js
@@ -2,7 +2,9 @@
 
 import 'packs/public-path';
 import escapeTextContentForBrowser from 'escape-html';
+
 const { delegate } = require('@rails/ujs');
+
 import emojify from '../mastodon/features/emoji/emoji';
 
 delegate(document, '#account_display_name', 'input', ({ target }) => {
@@ -65,7 +67,7 @@ delegate(document, '.input-copy button', 'click', ({ target }) => {
       input.blur();
       target.parentNode.classList.add('copied');
 
-    setTimeout(() => {
+      setTimeout(() => {
         target.parentNode.classList.remove('copied');
       }, 700);
     }
diff --git a/app/javascript/flavours/glitch/actions/account_notes.js b/app/javascript/flavours/glitch/actions/account_notes.js
index 059ed9e80..62a6b4cbb 100644
--- a/app/javascript/flavours/glitch/actions/account_notes.js
+++ b/app/javascript/flavours/glitch/actions/account_notes.js
@@ -21,27 +21,27 @@ export function submitAccountNote() {
       dispatch(submitAccountNoteSuccess(response.data));
     }).catch(error => dispatch(submitAccountNoteFail(error)));
   };
-};
+}
 
 export function submitAccountNoteRequest() {
   return {
     type: ACCOUNT_NOTE_SUBMIT_REQUEST,
   };
-};
+}
 
 export function submitAccountNoteSuccess(relationship) {
   return {
     type: ACCOUNT_NOTE_SUBMIT_SUCCESS,
     relationship,
   };
-};
+}
 
 export function submitAccountNoteFail(error) {
   return {
     type: ACCOUNT_NOTE_SUBMIT_FAIL,
     error,
   };
-};
+}
 
 export function initEditAccountNote(account) {
   return (dispatch, getState) => {
@@ -53,17 +53,17 @@ export function initEditAccountNote(account) {
       comment,
     });
   };
-};
+}
 
 export function cancelAccountNote() {
   return {
     type: ACCOUNT_NOTE_CANCEL,
   };
-};
+}
 
 export function changeAccountNoteComment(comment) {
   return {
     type: ACCOUNT_NOTE_CHANGE_COMMENT,
     comment,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/accounts.js b/app/javascript/flavours/glitch/actions/accounts.js
index dc670e50a..6b5b2ade5 100644
--- a/app/javascript/flavours/glitch/actions/accounts.js
+++ b/app/javascript/flavours/glitch/actions/accounts.js
@@ -108,7 +108,7 @@ export function fetchAccount(id) {
       dispatch(fetchAccountFail(id, error));
     });
   };
-};
+}
 
 export const lookupAccount = acct => (dispatch, getState) => {
   dispatch(lookupAccountRequest(acct));
@@ -143,13 +143,13 @@ export function fetchAccountRequest(id) {
     type: ACCOUNT_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchAccountSuccess() {
   return {
     type: ACCOUNT_FETCH_SUCCESS,
   };
-};
+}
 
 export function fetchAccountFail(id, error) {
   return {
@@ -158,7 +158,7 @@ export function fetchAccountFail(id, error) {
     error,
     skipAlert: true,
   };
-};
+}
 
 export function followAccount(id, options = { reblogs: true }) {
   return (dispatch, getState) => {
@@ -173,7 +173,7 @@ export function followAccount(id, options = { reblogs: true }) {
       dispatch(followAccountFail(error, locked));
     });
   };
-};
+}
 
 export function unfollowAccount(id) {
   return (dispatch, getState) => {
@@ -185,7 +185,7 @@ export function unfollowAccount(id) {
       dispatch(unfollowAccountFail(error));
     });
   };
-};
+}
 
 export function followAccountRequest(id, locked) {
   return {
@@ -194,7 +194,7 @@ export function followAccountRequest(id, locked) {
     locked,
     skipLoading: true,
   };
-};
+}
 
 export function followAccountSuccess(relationship, alreadyFollowing) {
   return {
@@ -203,7 +203,7 @@ export function followAccountSuccess(relationship, alreadyFollowing) {
     alreadyFollowing,
     skipLoading: true,
   };
-};
+}
 
 export function followAccountFail(error, locked) {
   return {
@@ -212,7 +212,7 @@ export function followAccountFail(error, locked) {
     locked,
     skipLoading: true,
   };
-};
+}
 
 export function unfollowAccountRequest(id) {
   return {
@@ -220,7 +220,7 @@ export function unfollowAccountRequest(id) {
     id,
     skipLoading: true,
   };
-};
+}
 
 export function unfollowAccountSuccess(relationship, statuses) {
   return {
@@ -229,7 +229,7 @@ export function unfollowAccountSuccess(relationship, statuses) {
     statuses,
     skipLoading: true,
   };
-};
+}
 
 export function unfollowAccountFail(error) {
   return {
@@ -237,7 +237,7 @@ export function unfollowAccountFail(error) {
     error,
     skipLoading: true,
   };
-};
+}
 
 export function blockAccount(id) {
   return (dispatch, getState) => {
@@ -250,7 +250,7 @@ export function blockAccount(id) {
       dispatch(blockAccountFail(id, error));
     });
   };
-};
+}
 
 export function unblockAccount(id) {
   return (dispatch, getState) => {
@@ -262,14 +262,14 @@ export function unblockAccount(id) {
       dispatch(unblockAccountFail(id, error));
     });
   };
-};
+}
 
 export function blockAccountRequest(id) {
   return {
     type: ACCOUNT_BLOCK_REQUEST,
     id,
   };
-};
+}
 
 export function blockAccountSuccess(relationship, statuses) {
   return {
@@ -277,35 +277,35 @@ export function blockAccountSuccess(relationship, statuses) {
     relationship,
     statuses,
   };
-};
+}
 
 export function blockAccountFail(error) {
   return {
     type: ACCOUNT_BLOCK_FAIL,
     error,
   };
-};
+}
 
 export function unblockAccountRequest(id) {
   return {
     type: ACCOUNT_UNBLOCK_REQUEST,
     id,
   };
-};
+}
 
 export function unblockAccountSuccess(relationship) {
   return {
     type: ACCOUNT_UNBLOCK_SUCCESS,
     relationship,
   };
-};
+}
 
 export function unblockAccountFail(error) {
   return {
     type: ACCOUNT_UNBLOCK_FAIL,
     error,
   };
-};
+}
 
 
 export function muteAccount(id, notifications, duration=0) {
@@ -319,7 +319,7 @@ export function muteAccount(id, notifications, duration=0) {
       dispatch(muteAccountFail(id, error));
     });
   };
-};
+}
 
 export function unmuteAccount(id) {
   return (dispatch, getState) => {
@@ -331,14 +331,14 @@ export function unmuteAccount(id) {
       dispatch(unmuteAccountFail(id, error));
     });
   };
-};
+}
 
 export function muteAccountRequest(id) {
   return {
     type: ACCOUNT_MUTE_REQUEST,
     id,
   };
-};
+}
 
 export function muteAccountSuccess(relationship, statuses) {
   return {
@@ -346,35 +346,35 @@ export function muteAccountSuccess(relationship, statuses) {
     relationship,
     statuses,
   };
-};
+}
 
 export function muteAccountFail(error) {
   return {
     type: ACCOUNT_MUTE_FAIL,
     error,
   };
-};
+}
 
 export function unmuteAccountRequest(id) {
   return {
     type: ACCOUNT_UNMUTE_REQUEST,
     id,
   };
-};
+}
 
 export function unmuteAccountSuccess(relationship) {
   return {
     type: ACCOUNT_UNMUTE_SUCCESS,
     relationship,
   };
-};
+}
 
 export function unmuteAccountFail(error) {
   return {
     type: ACCOUNT_UNMUTE_FAIL,
     error,
   };
-};
+}
 
 
 export function fetchFollowers(id) {
@@ -391,14 +391,14 @@ export function fetchFollowers(id) {
       dispatch(fetchFollowersFail(id, error));
     });
   };
-};
+}
 
 export function fetchFollowersRequest(id) {
   return {
     type: FOLLOWERS_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchFollowersSuccess(id, accounts, next) {
   return {
@@ -407,7 +407,7 @@ export function fetchFollowersSuccess(id, accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchFollowersFail(id, error) {
   return {
@@ -416,7 +416,7 @@ export function fetchFollowersFail(id, error) {
     error,
     skipNotFound: true,
   };
-};
+}
 
 export function expandFollowers(id) {
   return (dispatch, getState) => {
@@ -438,14 +438,14 @@ export function expandFollowers(id) {
       dispatch(expandFollowersFail(id, error));
     });
   };
-};
+}
 
 export function expandFollowersRequest(id) {
   return {
     type: FOLLOWERS_EXPAND_REQUEST,
     id,
   };
-};
+}
 
 export function expandFollowersSuccess(id, accounts, next) {
   return {
@@ -454,7 +454,7 @@ export function expandFollowersSuccess(id, accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function expandFollowersFail(id, error) {
   return {
@@ -462,7 +462,7 @@ export function expandFollowersFail(id, error) {
     id,
     error,
   };
-};
+}
 
 export function fetchFollowing(id) {
   return (dispatch, getState) => {
@@ -478,14 +478,14 @@ export function fetchFollowing(id) {
       dispatch(fetchFollowingFail(id, error));
     });
   };
-};
+}
 
 export function fetchFollowingRequest(id) {
   return {
     type: FOLLOWING_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchFollowingSuccess(id, accounts, next) {
   return {
@@ -494,7 +494,7 @@ export function fetchFollowingSuccess(id, accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchFollowingFail(id, error) {
   return {
@@ -503,7 +503,7 @@ export function fetchFollowingFail(id, error) {
     error,
     skipNotFound: true,
   };
-};
+}
 
 export function expandFollowing(id) {
   return (dispatch, getState) => {
@@ -525,14 +525,14 @@ export function expandFollowing(id) {
       dispatch(expandFollowingFail(id, error));
     });
   };
-};
+}
 
 export function expandFollowingRequest(id) {
   return {
     type: FOLLOWING_EXPAND_REQUEST,
     id,
   };
-};
+}
 
 export function expandFollowingSuccess(id, accounts, next) {
   return {
@@ -541,7 +541,7 @@ export function expandFollowingSuccess(id, accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function expandFollowingFail(id, error) {
   return {
@@ -549,7 +549,7 @@ export function expandFollowingFail(id, error) {
     id,
     error,
   };
-};
+}
 
 export function fetchRelationships(accountIds) {
   return (dispatch, getState) => {
@@ -570,7 +570,7 @@ export function fetchRelationships(accountIds) {
       dispatch(fetchRelationshipsFail(error));
     });
   };
-};
+}
 
 export function fetchRelationshipsRequest(ids) {
   return {
@@ -578,7 +578,7 @@ export function fetchRelationshipsRequest(ids) {
     ids,
     skipLoading: true,
   };
-};
+}
 
 export function fetchRelationshipsSuccess(relationships) {
   return {
@@ -586,7 +586,7 @@ export function fetchRelationshipsSuccess(relationships) {
     relationships,
     skipLoading: true,
   };
-};
+}
 
 export function fetchRelationshipsFail(error) {
   return {
@@ -595,7 +595,7 @@ export function fetchRelationshipsFail(error) {
     skipLoading: true,
     skipNotFound: true,
   };
-};
+}
 
 export function fetchFollowRequests() {
   return (dispatch, getState) => {
@@ -607,13 +607,13 @@ export function fetchFollowRequests() {
       dispatch(fetchFollowRequestsSuccess(response.data, next ? next.uri : null));
     }).catch(error => dispatch(fetchFollowRequestsFail(error)));
   };
-};
+}
 
 export function fetchFollowRequestsRequest() {
   return {
     type: FOLLOW_REQUESTS_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchFollowRequestsSuccess(accounts, next) {
   return {
@@ -621,14 +621,14 @@ export function fetchFollowRequestsSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchFollowRequestsFail(error) {
   return {
     type: FOLLOW_REQUESTS_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function expandFollowRequests() {
   return (dispatch, getState) => {
@@ -646,13 +646,13 @@ export function expandFollowRequests() {
       dispatch(expandFollowRequestsSuccess(response.data, next ? next.uri : null));
     }).catch(error => dispatch(expandFollowRequestsFail(error)));
   };
-};
+}
 
 export function expandFollowRequestsRequest() {
   return {
     type: FOLLOW_REQUESTS_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandFollowRequestsSuccess(accounts, next) {
   return {
@@ -660,14 +660,14 @@ export function expandFollowRequestsSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function expandFollowRequestsFail(error) {
   return {
     type: FOLLOW_REQUESTS_EXPAND_FAIL,
     error,
   };
-};
+}
 
 export function authorizeFollowRequest(id) {
   return (dispatch, getState) => {
@@ -678,21 +678,21 @@ export function authorizeFollowRequest(id) {
       .then(() => dispatch(authorizeFollowRequestSuccess(id)))
       .catch(error => dispatch(authorizeFollowRequestFail(id, error)));
   };
-};
+}
 
 export function authorizeFollowRequestRequest(id) {
   return {
     type: FOLLOW_REQUEST_AUTHORIZE_REQUEST,
     id,
   };
-};
+}
 
 export function authorizeFollowRequestSuccess(id) {
   return {
     type: FOLLOW_REQUEST_AUTHORIZE_SUCCESS,
     id,
   };
-};
+}
 
 export function authorizeFollowRequestFail(id, error) {
   return {
@@ -700,7 +700,7 @@ export function authorizeFollowRequestFail(id, error) {
     id,
     error,
   };
-};
+}
 
 
 export function rejectFollowRequest(id) {
@@ -712,21 +712,21 @@ export function rejectFollowRequest(id) {
       .then(() => dispatch(rejectFollowRequestSuccess(id)))
       .catch(error => dispatch(rejectFollowRequestFail(id, error)));
   };
-};
+}
 
 export function rejectFollowRequestRequest(id) {
   return {
     type: FOLLOW_REQUEST_REJECT_REQUEST,
     id,
   };
-};
+}
 
 export function rejectFollowRequestSuccess(id) {
   return {
     type: FOLLOW_REQUEST_REJECT_SUCCESS,
     id,
   };
-};
+}
 
 export function rejectFollowRequestFail(id, error) {
   return {
@@ -734,7 +734,7 @@ export function rejectFollowRequestFail(id, error) {
     id,
     error,
   };
-};
+}
 
 export function pinAccount(id) {
   return (dispatch, getState) => {
@@ -746,7 +746,7 @@ export function pinAccount(id) {
       dispatch(pinAccountFail(error));
     });
   };
-};
+}
 
 export function unpinAccount(id) {
   return (dispatch, getState) => {
@@ -758,49 +758,49 @@ export function unpinAccount(id) {
       dispatch(unpinAccountFail(error));
     });
   };
-};
+}
 
 export function pinAccountRequest(id) {
   return {
     type: ACCOUNT_PIN_REQUEST,
     id,
   };
-};
+}
 
 export function pinAccountSuccess(relationship) {
   return {
     type: ACCOUNT_PIN_SUCCESS,
     relationship,
   };
-};
+}
 
 export function pinAccountFail(error) {
   return {
     type: ACCOUNT_PIN_FAIL,
     error,
   };
-};
+}
 
 export function unpinAccountRequest(id) {
   return {
     type: ACCOUNT_UNPIN_REQUEST,
     id,
   };
-};
+}
 
 export function unpinAccountSuccess(relationship) {
   return {
     type: ACCOUNT_UNPIN_SUCCESS,
     relationship,
   };
-};
+}
 
 export function unpinAccountFail(error) {
   return {
     type: ACCOUNT_UNPIN_FAIL,
     error,
   };
-};
+}
 
 export const revealAccount = id => ({
   type: ACCOUNT_REVEAL,
@@ -811,18 +811,18 @@ export function fetchPinnedAccounts() {
   return (dispatch, getState) => {
     dispatch(fetchPinnedAccountsRequest());
 
-    api(getState).get(`/api/v1/endorsements`, { params: { limit: 0 } }).then(response => {
+    api(getState).get('/api/v1/endorsements', { params: { limit: 0 } }).then(response => {
       dispatch(importFetchedAccounts(response.data));
       dispatch(fetchPinnedAccountsSuccess(response.data));
     }).catch(err => dispatch(fetchPinnedAccountsFail(err)));
   };
-};
+}
 
 export function fetchPinnedAccountsRequest() {
   return {
     type: PINNED_ACCOUNTS_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchPinnedAccountsSuccess(accounts, next) {
   return {
@@ -830,14 +830,14 @@ export function fetchPinnedAccountsSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchPinnedAccountsFail(error) {
   return {
     type: PINNED_ACCOUNTS_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function fetchPinnedAccountsSuggestions(q) {
   return (dispatch, getState) => {
@@ -853,7 +853,7 @@ export function fetchPinnedAccountsSuggestions(q) {
       dispatch(fetchPinnedAccountsSuggestionsReady(q, response.data));
     });
   };
-};
+}
 
 export function fetchPinnedAccountsSuggestionsReady(query, accounts) {
   return {
@@ -861,24 +861,24 @@ export function fetchPinnedAccountsSuggestionsReady(query, accounts) {
     query,
     accounts,
   };
-};
+}
 
 export function clearPinnedAccountsSuggestions() {
   return {
     type: PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CLEAR,
   };
-};
+}
 
 export function changePinnedAccountsSuggestions(value) {
   return {
     type: PINNED_ACCOUNTS_EDITOR_SUGGESTIONS_CHANGE,
     value,
-  }
-};
+  };
+}
 
 export function resetPinnedAccountsEditor() {
   return {
     type: PINNED_ACCOUNTS_EDITOR_RESET,
   };
-};
+}
 
diff --git a/app/javascript/flavours/glitch/actions/alerts.js b/app/javascript/flavours/glitch/actions/alerts.js
index 1670f9c10..0220b0af5 100644
--- a/app/javascript/flavours/glitch/actions/alerts.js
+++ b/app/javascript/flavours/glitch/actions/alerts.js
@@ -17,13 +17,13 @@ export function dismissAlert(alert) {
     type: ALERT_DISMISS,
     alert,
   };
-};
+}
 
 export function clearAlert() {
   return {
     type: ALERT_CLEAR,
   };
-};
+}
 
 export function showAlert(title = messages.unexpectedTitle, message = messages.unexpectedMessage, message_values = undefined) {
   return {
@@ -32,7 +32,7 @@ export function showAlert(title = messages.unexpectedTitle, message = messages.u
     message,
     message_values,
   };
-};
+}
 
 export function showAlertForError(error, skipNotFound = false) {
   if (error.response) {
diff --git a/app/javascript/flavours/glitch/actions/blocks.js b/app/javascript/flavours/glitch/actions/blocks.js
index fd9881302..192aa3ce4 100644
--- a/app/javascript/flavours/glitch/actions/blocks.js
+++ b/app/javascript/flavours/glitch/actions/blocks.js
@@ -24,13 +24,13 @@ export function fetchBlocks() {
       dispatch(fetchRelationships(response.data.map(item => item.id)));
     }).catch(error => dispatch(fetchBlocksFail(error)));
   };
-};
+}
 
 export function fetchBlocksRequest() {
   return {
     type: BLOCKS_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchBlocksSuccess(accounts, next) {
   return {
@@ -38,14 +38,14 @@ export function fetchBlocksSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchBlocksFail(error) {
   return {
     type: BLOCKS_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function expandBlocks() {
   return (dispatch, getState) => {
@@ -64,13 +64,13 @@ export function expandBlocks() {
       dispatch(fetchRelationships(response.data.map(item => item.id)));
     }).catch(error => dispatch(expandBlocksFail(error)));
   };
-};
+}
 
 export function expandBlocksRequest() {
   return {
     type: BLOCKS_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandBlocksSuccess(accounts, next) {
   return {
@@ -78,14 +78,14 @@ export function expandBlocksSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function expandBlocksFail(error) {
   return {
     type: BLOCKS_EXPAND_FAIL,
     error,
   };
-};
+}
 
 export function initBlockModal(account) {
   return dispatch => {
diff --git a/app/javascript/flavours/glitch/actions/bookmarks.js b/app/javascript/flavours/glitch/actions/bookmarks.js
index 544ed2ff2..3c8eec546 100644
--- a/app/javascript/flavours/glitch/actions/bookmarks.js
+++ b/app/javascript/flavours/glitch/actions/bookmarks.js
@@ -25,13 +25,13 @@ export function fetchBookmarkedStatuses() {
       dispatch(fetchBookmarkedStatusesFail(error));
     });
   };
-};
+}
 
 export function fetchBookmarkedStatusesRequest() {
   return {
     type: BOOKMARKED_STATUSES_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchBookmarkedStatusesSuccess(statuses, next) {
   return {
@@ -39,14 +39,14 @@ export function fetchBookmarkedStatusesSuccess(statuses, next) {
     statuses,
     next,
   };
-};
+}
 
 export function fetchBookmarkedStatusesFail(error) {
   return {
     type: BOOKMARKED_STATUSES_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function expandBookmarkedStatuses() {
   return (dispatch, getState) => {
@@ -66,13 +66,13 @@ export function expandBookmarkedStatuses() {
       dispatch(expandBookmarkedStatusesFail(error));
     });
   };
-};
+}
 
 export function expandBookmarkedStatusesRequest() {
   return {
     type: BOOKMARKED_STATUSES_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandBookmarkedStatusesSuccess(statuses, next) {
   return {
@@ -80,11 +80,11 @@ export function expandBookmarkedStatusesSuccess(statuses, next) {
     statuses,
     next,
   };
-};
+}
 
 export function expandBookmarkedStatusesFail(error) {
   return {
     type: BOOKMARKED_STATUSES_EXPAND_FAIL,
     error,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/boosts.js b/app/javascript/flavours/glitch/actions/boosts.js
index 6e14065d6..c0f0f3acc 100644
--- a/app/javascript/flavours/glitch/actions/boosts.js
+++ b/app/javascript/flavours/glitch/actions/boosts.js
@@ -11,7 +11,7 @@ export function initBoostModal(props) {
 
     dispatch({
       type: BOOSTS_INIT_MODAL,
-      privacy
+      privacy,
     });
 
     dispatch(openModal('BOOST', props));
diff --git a/app/javascript/flavours/glitch/actions/columns.js b/app/javascript/flavours/glitch/actions/columns.js
index 9b87415fb..302c3f0f9 100644
--- a/app/javascript/flavours/glitch/actions/columns.js
+++ b/app/javascript/flavours/glitch/actions/columns.js
@@ -15,7 +15,7 @@ export function addColumn(id, params) {
 
     dispatch(saveSettings());
   };
-};
+}
 
 export function removeColumn(uuid) {
   return dispatch => {
@@ -26,7 +26,7 @@ export function removeColumn(uuid) {
 
     dispatch(saveSettings());
   };
-};
+}
 
 export function moveColumn(uuid, direction) {
   return dispatch => {
@@ -38,7 +38,7 @@ export function moveColumn(uuid, direction) {
 
     dispatch(saveSettings());
   };
-};
+}
 
 export function changeColumnParams(uuid, path, value) {
   return dispatch => {
diff --git a/app/javascript/flavours/glitch/actions/compose.js b/app/javascript/flavours/glitch/actions/compose.js
index 7a4af4cda..9c0ef83df 100644
--- a/app/javascript/flavours/glitch/actions/compose.js
+++ b/app/javascript/flavours/glitch/actions/compose.js
@@ -101,20 +101,20 @@ export function setComposeToStatus(status, text, spoiler_text, content_type) {
     spoiler_text,
     content_type,
   };
-};
+}
 
 export function changeCompose(text) {
   return {
     type: COMPOSE_CHANGE,
     text: text,
   };
-};
+}
 
 export function cycleElefriendCompose() {
   return {
     type: COMPOSE_CYCLE_ELEFRIEND,
   };
-};
+}
 
 export function replyCompose(status, routerHistory) {
   return (dispatch, getState) => {
@@ -127,19 +127,19 @@ export function replyCompose(status, routerHistory) {
 
     ensureComposeIsVisible(getState, routerHistory);
   };
-};
+}
 
 export function cancelReplyCompose() {
   return {
     type: COMPOSE_REPLY_CANCEL,
   };
-};
+}
 
 export function resetCompose() {
   return {
     type: COMPOSE_RESET,
   };
-};
+}
 
 export function mentionCompose(account, routerHistory) {
   return (dispatch, getState) => {
@@ -150,7 +150,7 @@ export function mentionCompose(account, routerHistory) {
 
     ensureComposeIsVisible(getState, routerHistory);
   };
-};
+}
 
 export function directCompose(account, routerHistory) {
   return (dispatch, getState) => {
@@ -161,7 +161,7 @@ export function directCompose(account, routerHistory) {
 
     ensureComposeIsVisible(getState, routerHistory);
   };
-};
+}
 
 export function submitCompose(routerHistory) {
   return function (dispatch, getState) {
@@ -181,6 +181,26 @@ export function submitCompose(routerHistory) {
 
     dispatch(submitComposeRequest());
 
+    // If we're editing a post with media attachments, those have not
+    // necessarily been changed on the server. Do it now in the same
+    // API call.
+    let media_attributes;
+    if (statusId !== null) {
+      media_attributes = media.map(item => {
+        let focus;
+
+        if (item.getIn(['meta', 'focus'])) {
+          focus = `${item.getIn(['meta', 'focus', 'x']).toFixed(2)},${item.getIn(['meta', 'focus', 'y']).toFixed(2)}`;
+        }
+
+        return {
+          id: item.get('id'),
+          description: item.get('description'),
+          focus,
+        };
+      });
+    }
+
     api(getState).request({
       url: statusId === null ? '/api/v1/statuses' : `/api/v1/statuses/${statusId}`,
       method: statusId === null ? 'post' : 'put',
@@ -189,6 +209,7 @@ export function submitCompose(routerHistory) {
         content_type: getState().getIn(['compose', 'content_type']),
         in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
         media_ids: media.map(item => item.get('id')),
+        media_attributes,
         sensitive: getState().getIn(['compose', 'sensitive']) || (spoilerText.length > 0 && media.size !== 0),
         spoiler_text: spoilerText,
         visibility: getState().getIn(['compose', 'privacy']),
@@ -244,34 +265,34 @@ export function submitCompose(routerHistory) {
       dispatch(submitComposeFail(error));
     });
   };
-};
+}
 
 export function submitComposeRequest() {
   return {
     type: COMPOSE_SUBMIT_REQUEST,
   };
-};
+}
 
 export function submitComposeSuccess(status) {
   return {
     type: COMPOSE_SUBMIT_SUCCESS,
     status: status,
   };
-};
+}
 
 export function submitComposeFail(error) {
   return {
     type: COMPOSE_SUBMIT_FAIL,
     error: error,
   };
-};
+}
 
 export function doodleSet(options) {
   return {
     type: COMPOSE_DOODLE_SET,
     options: options,
   };
-};
+}
 
 export function uploadCompose(files) {
   return function (dispatch, getState) {
@@ -334,9 +355,9 @@ export function uploadCompose(files) {
           }
         });
       }).catch(error => dispatch(uploadComposeFail(error)));
-    };
+    }
   };
-};
+}
 
 export const uploadComposeProcessing = () => ({
   type: COMPOSE_UPLOAD_PROCESSING,
@@ -394,14 +415,14 @@ export function initMediaEditModal(id) {
 
     dispatch(openModal('FOCAL_POINT', { id }));
   };
-};
+}
 
 export function onChangeMediaDescription(description) {
   return {
     type: COMPOSE_CHANGE_MEDIA_DESCRIPTION,
     description,
   };
-};
+}
 
 export function onChangeMediaFocus(focusX, focusY) {
   return {
@@ -409,34 +430,55 @@ export function onChangeMediaFocus(focusX, focusY) {
     focusX,
     focusY,
   };
-};
+}
 
 export function changeUploadCompose(id, params) {
   return (dispatch, getState) => {
     dispatch(changeUploadComposeRequest());
 
-    api(getState).put(`/api/v1/media/${id}`, params).then(response => {
-      dispatch(changeUploadComposeSuccess(response.data));
-    }).catch(error => {
-      dispatch(changeUploadComposeFail(id, error));
-    });
+    let media = getState().getIn(['compose', 'media_attachments']).find((item) => item.get('id') === id);
+
+    // Editing already-attached media is deferred to editing the post itself.
+    // For simplicity's sake, fake an API reply.
+    if (media && !media.get('unattached')) {
+      let { description, focus } = params;
+      const data = media.toJS();
+
+      if (description) {
+        data.description = description;
+      }
+
+      if (focus) {
+        focus = focus.split(',');
+        data.meta = { focus: { x: parseFloat(focus[0]), y: parseFloat(focus[1]) } };
+      }
+
+      dispatch(changeUploadComposeSuccess(data, true));
+    } else {
+      api(getState).put(`/api/v1/media/${id}`, params).then(response => {
+        dispatch(changeUploadComposeSuccess(response.data, false));
+      }).catch(error => {
+        dispatch(changeUploadComposeFail(id, error));
+      });
+    }
   };
-};
+}
 
 export function changeUploadComposeRequest() {
   return {
     type: COMPOSE_UPLOAD_CHANGE_REQUEST,
     skipLoading: true,
   };
-};
+}
 
-export function changeUploadComposeSuccess(media) {
+export function changeUploadComposeSuccess(media, attached) {
   return {
     type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
     media: media,
+    attached: attached,
     skipLoading: true,
   };
-};
+}
 
 export function changeUploadComposeFail(error) {
   return {
@@ -444,14 +486,14 @@ export function changeUploadComposeFail(error) {
     error: error,
     skipLoading: true,
   };
-};
+}
 
 export function uploadComposeRequest() {
   return {
     type: COMPOSE_UPLOAD_REQUEST,
     skipLoading: true,
   };
-};
+}
 
 export function uploadComposeProgress(loaded, total) {
   return {
@@ -459,7 +501,7 @@ export function uploadComposeProgress(loaded, total) {
     loaded: loaded,
     total: total,
   };
-};
+}
 
 export function uploadComposeSuccess(media, file) {
   return {
@@ -468,7 +510,7 @@ export function uploadComposeSuccess(media, file) {
     file: file,
     skipLoading: true,
   };
-};
+}
 
 export function uploadComposeFail(error) {
   return {
@@ -476,14 +518,14 @@ export function uploadComposeFail(error) {
     error: error,
     skipLoading: true,
   };
-};
+}
 
 export function undoUploadCompose(media_id) {
   return {
     type: COMPOSE_UPLOAD_UNDO,
     media_id: media_id,
   };
-};
+}
 
 export function clearComposeSuggestions() {
   if (fetchComposeSuggestionsAccountsController) {
@@ -492,7 +534,7 @@ export function clearComposeSuggestions() {
   return {
     type: COMPOSE_SUGGESTIONS_CLEAR,
   };
-};
+}
 
 const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
   if (fetchComposeSuggestionsAccountsController) {
@@ -569,7 +611,7 @@ export function fetchComposeSuggestions(token) {
       break;
     }
   };
-};
+}
 
 export function readyComposeSuggestionsEmojis(token, emojis) {
   return {
@@ -577,7 +619,7 @@ export function readyComposeSuggestionsEmojis(token, emojis) {
     token,
     emojis,
   };
-};
+}
 
 export function readyComposeSuggestionsAccounts(token, accounts) {
   return {
@@ -585,7 +627,7 @@ export function readyComposeSuggestionsAccounts(token, accounts) {
     token,
     accounts,
   };
-};
+}
 
 export const readyComposeSuggestionsTags = (token, tags) => ({
   type: COMPOSE_SUGGESTIONS_READY,
@@ -625,7 +667,7 @@ export function selectComposeSuggestion(position, token, suggestion, path) {
       });
     }
   };
-};
+}
 
 export function updateSuggestionTags(token) {
   return {
@@ -673,13 +715,13 @@ export function mountCompose() {
   return {
     type: COMPOSE_MOUNT,
   };
-};
+}
 
 export function unmountCompose() {
   return {
     type: COMPOSE_UNMOUNT,
   };
-};
+}
 
 export function changeComposeAdvancedOption(option, value) {
   return {
@@ -693,7 +735,7 @@ export function changeComposeSensitivity() {
   return {
     type: COMPOSE_SENSITIVITY_CHANGE,
   };
-};
+}
 
 export const changeComposeLanguage = language => ({
   type: COMPOSE_LANGUAGE_CHANGE,
@@ -704,28 +746,28 @@ export function changeComposeSpoilerness() {
   return {
     type: COMPOSE_SPOILERNESS_CHANGE,
   };
-};
+}
 
 export function changeComposeSpoilerText(text) {
   return {
     type: COMPOSE_SPOILER_TEXT_CHANGE,
     text,
   };
-};
+}
 
 export function changeComposeVisibility(value) {
   return {
     type: COMPOSE_VISIBILITY_CHANGE,
     value,
   };
-};
+}
 
 export function changeComposeContentType(value) {
   return {
     type: COMPOSE_CONTENT_TYPE_CHANGE,
     value,
   };
-};
+}
 
 export function insertEmojiCompose(position, emoji) {
   return {
@@ -733,26 +775,26 @@ export function insertEmojiCompose(position, emoji) {
     position,
     emoji,
   };
-};
+}
 
 export function addPoll() {
   return {
     type: COMPOSE_POLL_ADD,
   };
-};
+}
 
 export function removePoll() {
   return {
     type: COMPOSE_POLL_REMOVE,
   };
-};
+}
 
 export function addPollOption(title) {
   return {
     type: COMPOSE_POLL_OPTION_ADD,
     title,
   };
-};
+}
 
 export function changePollOption(index, title) {
   return {
@@ -760,14 +802,14 @@ export function changePollOption(index, title) {
     index,
     title,
   };
-};
+}
 
 export function removePollOption(index) {
   return {
     type: COMPOSE_POLL_OPTION_REMOVE,
     index,
   };
-};
+}
 
 export function changePollSettings(expiresIn, isMultiple) {
   return {
@@ -775,4 +817,4 @@ export function changePollSettings(expiresIn, isMultiple) {
     expiresIn,
     isMultiple,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/custom_emojis.js b/app/javascript/flavours/glitch/actions/custom_emojis.js
index 7b7d0091b..9ec8156b1 100644
--- a/app/javascript/flavours/glitch/actions/custom_emojis.js
+++ b/app/javascript/flavours/glitch/actions/custom_emojis.js
@@ -14,14 +14,14 @@ export function fetchCustomEmojis() {
       dispatch(fetchCustomEmojisFail(error));
     });
   };
-};
+}
 
 export function fetchCustomEmojisRequest() {
   return {
     type: CUSTOM_EMOJIS_FETCH_REQUEST,
     skipLoading: true,
   };
-};
+}
 
 export function fetchCustomEmojisSuccess(custom_emojis) {
   return {
@@ -29,7 +29,7 @@ export function fetchCustomEmojisSuccess(custom_emojis) {
     custom_emojis,
     skipLoading: true,
   };
-};
+}
 
 export function fetchCustomEmojisFail(error) {
   return {
@@ -37,4 +37,4 @@ export function fetchCustomEmojisFail(error) {
     error,
     skipLoading: true,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/domain_blocks.js b/app/javascript/flavours/glitch/actions/domain_blocks.js
index 34a33a654..d06de20a2 100644
--- a/app/javascript/flavours/glitch/actions/domain_blocks.js
+++ b/app/javascript/flavours/glitch/actions/domain_blocks.js
@@ -29,14 +29,14 @@ export function blockDomain(domain) {
       dispatch(blockDomainFail(domain, err));
     });
   };
-};
+}
 
 export function blockDomainRequest(domain) {
   return {
     type: DOMAIN_BLOCK_REQUEST,
     domain,
   };
-};
+}
 
 export function blockDomainSuccess(domain, accounts) {
   return {
@@ -44,7 +44,7 @@ export function blockDomainSuccess(domain, accounts) {
     domain,
     accounts,
   };
-};
+}
 
 export function blockDomainFail(domain, error) {
   return {
@@ -52,7 +52,7 @@ export function blockDomainFail(domain, error) {
     domain,
     error,
   };
-};
+}
 
 export function unblockDomain(domain) {
   return (dispatch, getState) => {
@@ -66,14 +66,14 @@ export function unblockDomain(domain) {
       dispatch(unblockDomainFail(domain, err));
     });
   };
-};
+}
 
 export function unblockDomainRequest(domain) {
   return {
     type: DOMAIN_UNBLOCK_REQUEST,
     domain,
   };
-};
+}
 
 export function unblockDomainSuccess(domain, accounts) {
   return {
@@ -81,7 +81,7 @@ export function unblockDomainSuccess(domain, accounts) {
     domain,
     accounts,
   };
-};
+}
 
 export function unblockDomainFail(domain, error) {
   return {
@@ -89,7 +89,7 @@ export function unblockDomainFail(domain, error) {
     domain,
     error,
   };
-};
+}
 
 export function fetchDomainBlocks() {
   return (dispatch, getState) => {
@@ -102,13 +102,13 @@ export function fetchDomainBlocks() {
       dispatch(fetchDomainBlocksFail(err));
     });
   };
-};
+}
 
 export function fetchDomainBlocksRequest() {
   return {
     type: DOMAIN_BLOCKS_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchDomainBlocksSuccess(domains, next) {
   return {
@@ -116,14 +116,14 @@ export function fetchDomainBlocksSuccess(domains, next) {
     domains,
     next,
   };
-};
+}
 
 export function fetchDomainBlocksFail(error) {
   return {
     type: DOMAIN_BLOCKS_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function expandDomainBlocks() {
   return (dispatch, getState) => {
@@ -142,13 +142,13 @@ export function expandDomainBlocks() {
       dispatch(expandDomainBlocksFail(err));
     });
   };
-};
+}
 
 export function expandDomainBlocksRequest() {
   return {
     type: DOMAIN_BLOCKS_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandDomainBlocksSuccess(domains, next) {
   return {
@@ -156,11 +156,11 @@ export function expandDomainBlocksSuccess(domains, next) {
     domains,
     next,
   };
-};
+}
 
 export function expandDomainBlocksFail(error) {
   return {
     type: DOMAIN_BLOCKS_EXPAND_FAIL,
     error,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/emojis.js b/app/javascript/flavours/glitch/actions/emojis.js
index 7cd9d4b7b..3b5d53996 100644
--- a/app/javascript/flavours/glitch/actions/emojis.js
+++ b/app/javascript/flavours/glitch/actions/emojis.js
@@ -11,4 +11,4 @@ export function useEmoji(emoji) {
 
     dispatch(saveSettings());
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/favourites.js b/app/javascript/flavours/glitch/actions/favourites.js
index 9448b1efe..7388e0c58 100644
--- a/app/javascript/flavours/glitch/actions/favourites.js
+++ b/app/javascript/flavours/glitch/actions/favourites.js
@@ -25,14 +25,14 @@ export function fetchFavouritedStatuses() {
       dispatch(fetchFavouritedStatusesFail(error));
     });
   };
-};
+}
 
 export function fetchFavouritedStatusesRequest() {
   return {
     type: FAVOURITED_STATUSES_FETCH_REQUEST,
     skipLoading: true,
   };
-};
+}
 
 export function fetchFavouritedStatusesSuccess(statuses, next) {
   return {
@@ -41,7 +41,7 @@ export function fetchFavouritedStatusesSuccess(statuses, next) {
     next,
     skipLoading: true,
   };
-};
+}
 
 export function fetchFavouritedStatusesFail(error) {
   return {
@@ -49,7 +49,7 @@ export function fetchFavouritedStatusesFail(error) {
     error,
     skipLoading: true,
   };
-};
+}
 
 export function expandFavouritedStatuses() {
   return (dispatch, getState) => {
@@ -69,13 +69,13 @@ export function expandFavouritedStatuses() {
       dispatch(expandFavouritedStatusesFail(error));
     });
   };
-};
+}
 
 export function expandFavouritedStatusesRequest() {
   return {
     type: FAVOURITED_STATUSES_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandFavouritedStatusesSuccess(statuses, next) {
   return {
@@ -83,11 +83,11 @@ export function expandFavouritedStatusesSuccess(statuses, next) {
     statuses,
     next,
   };
-};
+}
 
 export function expandFavouritedStatusesFail(error) {
   return {
     type: FAVOURITED_STATUSES_EXPAND_FAIL,
     error,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/height_cache.js b/app/javascript/flavours/glitch/actions/height_cache.js
index 4c752993f..a8645410c 100644
--- a/app/javascript/flavours/glitch/actions/height_cache.js
+++ b/app/javascript/flavours/glitch/actions/height_cache.js
@@ -8,10 +8,10 @@ export function setHeight (key, id, height) {
     id,
     height,
   };
-};
+}
 
 export function clearHeight () {
   return {
     type: HEIGHT_CACHE_CLEAR,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/interactions.js b/app/javascript/flavours/glitch/actions/interactions.js
index 225ee7eb2..c7b552a65 100644
--- a/app/javascript/flavours/glitch/actions/interactions.js
+++ b/app/javascript/flavours/glitch/actions/interactions.js
@@ -54,7 +54,7 @@ export function reblog(status, visibility) {
       dispatch(reblogFail(status, error));
     });
   };
-};
+}
 
 export function unreblog(status) {
   return (dispatch, getState) => {
@@ -67,21 +67,21 @@ export function unreblog(status) {
       dispatch(unreblogFail(status, error));
     });
   };
-};
+}
 
 export function reblogRequest(status) {
   return {
     type: REBLOG_REQUEST,
     status: status,
   };
-};
+}
 
 export function reblogSuccess(status) {
   return {
     type: REBLOG_SUCCESS,
     status: status,
   };
-};
+}
 
 export function reblogFail(status, error) {
   return {
@@ -89,21 +89,21 @@ export function reblogFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function unreblogRequest(status) {
   return {
     type: UNREBLOG_REQUEST,
     status: status,
   };
-};
+}
 
 export function unreblogSuccess(status) {
   return {
     type: UNREBLOG_SUCCESS,
     status: status,
   };
-};
+}
 
 export function unreblogFail(status, error) {
   return {
@@ -111,7 +111,7 @@ export function unreblogFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function favourite(status) {
   return function (dispatch, getState) {
@@ -124,7 +124,7 @@ export function favourite(status) {
       dispatch(favouriteFail(status, error));
     });
   };
-};
+}
 
 export function unfavourite(status) {
   return (dispatch, getState) => {
@@ -137,21 +137,21 @@ export function unfavourite(status) {
       dispatch(unfavouriteFail(status, error));
     });
   };
-};
+}
 
 export function favouriteRequest(status) {
   return {
     type: FAVOURITE_REQUEST,
     status: status,
   };
-};
+}
 
 export function favouriteSuccess(status) {
   return {
     type: FAVOURITE_SUCCESS,
     status: status,
   };
-};
+}
 
 export function favouriteFail(status, error) {
   return {
@@ -159,21 +159,21 @@ export function favouriteFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function unfavouriteRequest(status) {
   return {
     type: UNFAVOURITE_REQUEST,
     status: status,
   };
-};
+}
 
 export function unfavouriteSuccess(status) {
   return {
     type: UNFAVOURITE_SUCCESS,
     status: status,
   };
-};
+}
 
 export function unfavouriteFail(status, error) {
   return {
@@ -181,7 +181,7 @@ export function unfavouriteFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function bookmark(status) {
   return function (dispatch, getState) {
@@ -194,7 +194,7 @@ export function bookmark(status) {
       dispatch(bookmarkFail(status, error));
     });
   };
-};
+}
 
 export function unbookmark(status) {
   return (dispatch, getState) => {
@@ -207,21 +207,21 @@ export function unbookmark(status) {
       dispatch(unbookmarkFail(status, error));
     });
   };
-};
+}
 
 export function bookmarkRequest(status) {
   return {
     type: BOOKMARK_REQUEST,
     status: status,
   };
-};
+}
 
 export function bookmarkSuccess(status) {
   return {
     type: BOOKMARK_SUCCESS,
     status: status,
   };
-};
+}
 
 export function bookmarkFail(status, error) {
   return {
@@ -229,21 +229,21 @@ export function bookmarkFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function unbookmarkRequest(status) {
   return {
     type: UNBOOKMARK_REQUEST,
     status: status,
   };
-};
+}
 
 export function unbookmarkSuccess(status) {
   return {
     type: UNBOOKMARK_SUCCESS,
     status: status,
   };
-};
+}
 
 export function unbookmarkFail(status, error) {
   return {
@@ -251,7 +251,7 @@ export function unbookmarkFail(status, error) {
     status: status,
     error: error,
   };
-};
+}
 
 export function fetchReblogs(id) {
   return (dispatch, getState) => {
@@ -264,14 +264,14 @@ export function fetchReblogs(id) {
       dispatch(fetchReblogsFail(id, error));
     });
   };
-};
+}
 
 export function fetchReblogsRequest(id) {
   return {
     type: REBLOGS_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchReblogsSuccess(id, accounts) {
   return {
@@ -279,14 +279,14 @@ export function fetchReblogsSuccess(id, accounts) {
     id,
     accounts,
   };
-};
+}
 
 export function fetchReblogsFail(id, error) {
   return {
     type: REBLOGS_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function fetchFavourites(id) {
   return (dispatch, getState) => {
@@ -299,14 +299,14 @@ export function fetchFavourites(id) {
       dispatch(fetchFavouritesFail(id, error));
     });
   };
-};
+}
 
 export function fetchFavouritesRequest(id) {
   return {
     type: FAVOURITES_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchFavouritesSuccess(id, accounts) {
   return {
@@ -314,14 +314,14 @@ export function fetchFavouritesSuccess(id, accounts) {
     id,
     accounts,
   };
-};
+}
 
 export function fetchFavouritesFail(id, error) {
   return {
     type: FAVOURITES_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function pin(status) {
   return (dispatch, getState) => {
@@ -334,21 +334,21 @@ export function pin(status) {
       dispatch(pinFail(status, error));
     });
   };
-};
+}
 
 export function pinRequest(status) {
   return {
     type: PIN_REQUEST,
     status,
   };
-};
+}
 
 export function pinSuccess(status) {
   return {
     type: PIN_SUCCESS,
     status,
   };
-};
+}
 
 export function pinFail(status, error) {
   return {
@@ -356,7 +356,7 @@ export function pinFail(status, error) {
     status,
     error,
   };
-};
+}
 
 export function unpin (status) {
   return (dispatch, getState) => {
@@ -369,21 +369,21 @@ export function unpin (status) {
       dispatch(unpinFail(status, error));
     });
   };
-};
+}
 
 export function unpinRequest(status) {
   return {
     type: UNPIN_REQUEST,
     status,
   };
-};
+}
 
 export function unpinSuccess(status) {
   return {
     type: UNPIN_SUCCESS,
     status,
   };
-};
+}
 
 export function unpinFail(status, error) {
   return {
@@ -391,4 +391,4 @@ export function unpinFail(status, error) {
     status,
     error,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/local_settings.js b/app/javascript/flavours/glitch/actions/local_settings.js
index a4a928611..adf7fd2ab 100644
--- a/app/javascript/flavours/glitch/actions/local_settings.js
+++ b/app/javascript/flavours/glitch/actions/local_settings.js
@@ -33,14 +33,14 @@ export function checkDeprecatedLocalSettings() {
       }));
     }
   };
-};
+}
 
 export function clearDeprecatedLocalSettings() {
   return (dispatch) => {
     dispatch(deleteLocalSetting(['content_warnings', 'auto_unfold']));
     dispatch(deleteLocalSetting(['swipe_to_change_columns']));
   };
-};
+}
 
 export function changeLocalSetting(key, value) {
   return dispatch => {
@@ -52,7 +52,7 @@ export function changeLocalSetting(key, value) {
 
     dispatch(saveLocalSettings());
   };
-};
+}
 
 export function deleteLocalSetting(key) {
   return dispatch => {
@@ -63,7 +63,7 @@ export function deleteLocalSetting(key) {
 
     dispatch(saveLocalSettings());
   };
-};
+}
 
 //  __TODO :__
 //  Right now `saveLocalSettings()` doesn't keep track of which user
@@ -74,4 +74,4 @@ export function saveLocalSettings() {
     const localSettings = getState().get('local_settings').toJS();
     localStorage.setItem('mastodon-settings', JSON.stringify(localSettings));
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/markers.js b/app/javascript/flavours/glitch/actions/markers.js
index 3b6a76bc4..f82675342 100644
--- a/app/javascript/flavours/glitch/actions/markers.js
+++ b/app/javascript/flavours/glitch/actions/markers.js
@@ -55,7 +55,7 @@ export const synchronouslySubmitMarkers = () => (dispatch, getState) => {
     client.open('POST', '/api/v1/markers', false);
     client.setRequestHeader('Content-Type', 'application/json');
     client.setRequestHeader('Authorization', `Bearer ${accessToken}`);
-    client.SUBMIT(JSON.stringify(params));
+    client.send(JSON.stringify(params));
   } catch (e) {
     // Do not make the BeforeUnload handler error out
   }
@@ -101,7 +101,7 @@ export function submitMarkersSuccess({ home, notifications }) {
     home: (home || {}).last_read_id,
     notifications: (notifications || {}).last_read_id,
   };
-};
+}
 
 export function submitMarkers(params = {}) {
   const result = (dispatch, getState) => debouncedSubmitMarkers(dispatch, getState);
@@ -111,7 +111,7 @@ export function submitMarkers(params = {}) {
   }
 
   return result;
-};
+}
 
 export const fetchMarkers = () => (dispatch, getState) => {
   const params = { timeline: ['notifications'] };
@@ -130,7 +130,7 @@ export function fetchMarkersRequest() {
     type: MARKERS_FETCH_REQUEST,
     skipLoading: true,
   };
-};
+}
 
 export function fetchMarkersSuccess(markers) {
   return {
@@ -138,7 +138,7 @@ export function fetchMarkersSuccess(markers) {
     markers,
     skipLoading: true,
   };
-};
+}
 
 export function fetchMarkersFail(error) {
   return {
@@ -147,4 +147,4 @@ export function fetchMarkersFail(error) {
     skipLoading: true,
     skipAlert: true,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/modal.js b/app/javascript/flavours/glitch/actions/modal.js
index 3e576fab8..ef2ae0e4c 100644
--- a/app/javascript/flavours/glitch/actions/modal.js
+++ b/app/javascript/flavours/glitch/actions/modal.js
@@ -7,7 +7,7 @@ export function openModal(type, props) {
     modalType: type,
     modalProps: props,
   };
-};
+}
 
 export function closeModal(type, options = { ignoreFocus: false }) {
   return {
@@ -15,4 +15,4 @@ export function closeModal(type, options = { ignoreFocus: false }) {
     modalType: type,
     ignoreFocus: options.ignoreFocus,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/mutes.js b/app/javascript/flavours/glitch/actions/mutes.js
index 1ccf9592f..aa47d1464 100644
--- a/app/javascript/flavours/glitch/actions/mutes.js
+++ b/app/javascript/flavours/glitch/actions/mutes.js
@@ -26,13 +26,13 @@ export function fetchMutes() {
       dispatch(fetchRelationships(response.data.map(item => item.id)));
     }).catch(error => dispatch(fetchMutesFail(error)));
   };
-};
+}
 
 export function fetchMutesRequest() {
   return {
     type: MUTES_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchMutesSuccess(accounts, next) {
   return {
@@ -40,14 +40,14 @@ export function fetchMutesSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function fetchMutesFail(error) {
   return {
     type: MUTES_FETCH_FAIL,
     error,
   };
-};
+}
 
 export function expandMutes() {
   return (dispatch, getState) => {
@@ -66,13 +66,13 @@ export function expandMutes() {
       dispatch(fetchRelationships(response.data.map(item => item.id)));
     }).catch(error => dispatch(expandMutesFail(error)));
   };
-};
+}
 
 export function expandMutesRequest() {
   return {
     type: MUTES_EXPAND_REQUEST,
   };
-};
+}
 
 export function expandMutesSuccess(accounts, next) {
   return {
@@ -80,14 +80,14 @@ export function expandMutesSuccess(accounts, next) {
     accounts,
     next,
   };
-};
+}
 
 export function expandMutesFail(error) {
   return {
     type: MUTES_EXPAND_FAIL,
     error,
   };
-};
+}
 
 export function initMuteModal(account) {
   return dispatch => {
diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js
index 158a5b7e4..989bc4144 100644
--- a/app/javascript/flavours/glitch/actions/notifications.js
+++ b/app/javascript/flavours/glitch/actions/notifications.js
@@ -129,7 +129,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
       });
     }
   };
-};
+}
 
 const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
 
@@ -209,14 +209,14 @@ export function expandNotifications({ maxId, forceLoad } = {}, done = noOp) {
       done();
     });
   };
-};
+}
 
 export function expandNotificationsRequest(isLoadingMore) {
   return {
     type: NOTIFICATIONS_EXPAND_REQUEST,
     skipLoading: !isLoadingMore,
   };
-};
+}
 
 export function expandNotificationsSuccess(notifications, next, isLoadingMore, isLoadingRecent, usePendingItems) {
   return {
@@ -227,7 +227,7 @@ export function expandNotificationsSuccess(notifications, next, isLoadingMore, i
     usePendingItems,
     skipLoading: !isLoadingMore,
   };
-};
+}
 
 export function expandNotificationsFail(error, isLoadingMore) {
   return {
@@ -236,7 +236,7 @@ export function expandNotificationsFail(error, isLoadingMore) {
     skipLoading: !isLoadingMore,
     skipAlert: !isLoadingMore || error.name === 'AbortError',
   };
-};
+}
 
 export function clearNotifications() {
   return (dispatch, getState) => {
@@ -246,14 +246,14 @@ export function clearNotifications() {
 
     api(getState).post('/api/v1/notifications/clear');
   };
-};
+}
 
 export function scrollTopNotifications(top) {
   return {
     type: NOTIFICATIONS_SCROLL_TOP,
     top,
   };
-};
+}
 
 export function deleteMarkedNotifications() {
   return (dispatch, getState) => {
@@ -277,33 +277,33 @@ export function deleteMarkedNotifications() {
       dispatch(deleteMarkedNotificationsFail(error));
     });
   };
-};
+}
 
 export function enterNotificationClearingMode(yes) {
   return {
     type: NOTIFICATIONS_ENTER_CLEARING_MODE,
     yes: yes,
   };
-};
+}
 
 export function markAllNotifications(yes) {
   return {
     type: NOTIFICATIONS_MARK_ALL_FOR_DELETE,
     yes: yes, // true, false or null. null = invert
   };
-};
+}
 
 export function deleteMarkedNotificationsRequest() {
   return {
     type: NOTIFICATIONS_DELETE_MARKED_REQUEST,
   };
-};
+}
 
 export function deleteMarkedNotificationsFail() {
   return {
     type: NOTIFICATIONS_DELETE_MARKED_FAIL,
   };
-};
+}
 
 export function markNotificationForDelete(id, yes) {
   return {
@@ -311,32 +311,32 @@ export function markNotificationForDelete(id, yes) {
     id: id,
     yes: yes,
   };
-};
+}
 
 export function deleteMarkedNotificationsSuccess() {
   return {
     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,
   };
-};
+}
 
 export function setFilter (filterType) {
   return dispatch => {
@@ -348,13 +348,13 @@ export function setFilter (filterType) {
     dispatch(expandNotifications({ forceLoad: true }));
     dispatch(saveSettings());
   };
-};
+}
 
 export function markNotificationsAsRead() {
   return {
     type: NOTIFICATIONS_MARK_AS_READ,
   };
-};
+}
 
 // Browser support
 export function setupBrowserNotifications() {
@@ -379,7 +379,7 @@ export function requestBrowserPermission(callback = noOp) {
       callback(permission);
     });
   };
-};
+}
 
 export function setBrowserSupport (value) {
   return {
diff --git a/app/javascript/flavours/glitch/actions/onboarding.js b/app/javascript/flavours/glitch/actions/onboarding.js
index a161c50ef..5038b7eb6 100644
--- a/app/javascript/flavours/glitch/actions/onboarding.js
+++ b/app/javascript/flavours/glitch/actions/onboarding.js
@@ -11,4 +11,4 @@ export function showOnboardingOnce() {
       dispatch(saveSettings());
     }
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/pin_statuses.js b/app/javascript/flavours/glitch/actions/pin_statuses.js
index 0926978ac..d8c0a1373 100644
--- a/app/javascript/flavours/glitch/actions/pin_statuses.js
+++ b/app/javascript/flavours/glitch/actions/pin_statuses.js
@@ -18,13 +18,13 @@ export function fetchPinnedStatuses() {
       dispatch(fetchPinnedStatusesFail(error));
     });
   };
-};
+}
 
 export function fetchPinnedStatusesRequest() {
   return {
     type: PINNED_STATUSES_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchPinnedStatusesSuccess(statuses, next) {
   return {
@@ -32,11 +32,11 @@ export function fetchPinnedStatusesSuccess(statuses, next) {
     statuses,
     next,
   };
-};
+}
 
 export function fetchPinnedStatusesFail(error) {
   return {
     type: PINNED_STATUSES_FETCH_FAIL,
     error,
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/push_notifications/registerer.js b/app/javascript/flavours/glitch/actions/push_notifications/registerer.js
index 762fe260c..bc5634233 100644
--- a/app/javascript/flavours/glitch/actions/push_notifications/registerer.js
+++ b/app/javascript/flavours/glitch/actions/push_notifications/registerer.js
@@ -6,7 +6,7 @@ import { setBrowserSupport, setSubscription, clearSubscription } from './setter'
 const urlBase64ToUint8Array = (base64String) => {
   const padding = '='.repeat((4 - base64String.length % 4) % 4);
   const base64 = (base64String + padding)
-    .replace(/\-/g, '+')
+    .replace(/-/g, '+')
     .replace(/_/g, '/');
 
   const rawData = window.atob(base64);
diff --git a/app/javascript/flavours/glitch/actions/search.js b/app/javascript/flavours/glitch/actions/search.js
index f21c0058b..0012808e5 100644
--- a/app/javascript/flavours/glitch/actions/search.js
+++ b/app/javascript/flavours/glitch/actions/search.js
@@ -19,13 +19,13 @@ export function changeSearch(value) {
     type: SEARCH_CHANGE,
     value,
   };
-};
+}
 
 export function clearSearch() {
   return {
     type: SEARCH_CLEAR,
   };
-};
+}
 
 export function submitSearch() {
   return (dispatch, getState) => {
@@ -60,13 +60,13 @@ export function submitSearch() {
       dispatch(fetchSearchFail(error));
     });
   };
-};
+}
 
 export function fetchSearchRequest() {
   return {
     type: SEARCH_FETCH_REQUEST,
   };
-};
+}
 
 export function fetchSearchSuccess(results, searchTerm) {
   return {
@@ -74,14 +74,14 @@ export function fetchSearchSuccess(results, searchTerm) {
     results,
     searchTerm,
   };
-};
+}
 
 export function fetchSearchFail(error) {
   return {
     type: SEARCH_FETCH_FAIL,
     error,
   };
-};
+}
 
 export const expandSearch = type => (dispatch, getState) => {
   const value  = getState().getIn(['search', 'value']);
diff --git a/app/javascript/flavours/glitch/actions/server.js b/app/javascript/flavours/glitch/actions/server.js
index 31d4aea10..091af0f0f 100644
--- a/app/javascript/flavours/glitch/actions/server.js
+++ b/app/javascript/flavours/glitch/actions/server.js
@@ -5,6 +5,10 @@ export const SERVER_FETCH_REQUEST = 'Server_FETCH_REQUEST';
 export const SERVER_FETCH_SUCCESS = 'Server_FETCH_SUCCESS';
 export const SERVER_FETCH_FAIL    = 'Server_FETCH_FAIL';
 
+export const SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST = 'SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST';
+export const SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS = 'SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS';
+export const SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL    = 'SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL';
+
 export const EXTENDED_DESCRIPTION_REQUEST = 'EXTENDED_DESCRIPTION_REQUEST';
 export const EXTENDED_DESCRIPTION_SUCCESS = 'EXTENDED_DESCRIPTION_SUCCESS';
 export const EXTENDED_DESCRIPTION_FAIL    = 'EXTENDED_DESCRIPTION_FAIL';
@@ -37,6 +41,29 @@ const fetchServerFail = error => ({
   error,
 });
 
+export const fetchServerTranslationLanguages = () => (dispatch, getState) => {
+  dispatch(fetchServerTranslationLanguagesRequest());
+
+  api(getState)
+    .get('/api/v1/instance/translation_languages').then(({ data }) => {
+      dispatch(fetchServerTranslationLanguagesSuccess(data));
+    }).catch(err => dispatch(fetchServerTranslationLanguagesFail(err)));
+};
+
+const fetchServerTranslationLanguagesRequest = () => ({
+  type: SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST,
+});
+
+const fetchServerTranslationLanguagesSuccess = translationLanguages => ({
+  type: SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS,
+  translationLanguages,
+});
+
+const fetchServerTranslationLanguagesFail = error => ({
+  type: SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL,
+  error,
+});
+
 export const fetchExtendedDescription = () => (dispatch, getState) => {
   dispatch(fetchExtendedDescriptionRequest());
 
diff --git a/app/javascript/flavours/glitch/actions/settings.js b/app/javascript/flavours/glitch/actions/settings.js
index 5634a11ef..60f0abf95 100644
--- a/app/javascript/flavours/glitch/actions/settings.js
+++ b/app/javascript/flavours/glitch/actions/settings.js
@@ -15,7 +15,7 @@ export function changeSetting(path, value) {
 
     dispatch(saveSettings());
   };
-};
+}
 
 const debouncedSave = debounce((dispatch, getState) => {
   if (getState().getIn(['settings', 'saved'])) {
@@ -31,4 +31,4 @@ const debouncedSave = debounce((dispatch, getState) => {
 
 export function saveSettings() {
   return (dispatch, getState) => debouncedSave(dispatch, getState);
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/statuses.js b/app/javascript/flavours/glitch/actions/statuses.js
index efb4cc33b..487cd6988 100644
--- a/app/javascript/flavours/glitch/actions/statuses.js
+++ b/app/javascript/flavours/glitch/actions/statuses.js
@@ -45,7 +45,7 @@ export function fetchStatusRequest(id, skipLoading) {
     id,
     skipLoading,
   };
-};
+}
 
 export function fetchStatus(id, forceFetch = false) {
   return (dispatch, getState) => {
@@ -66,14 +66,14 @@ export function fetchStatus(id, forceFetch = false) {
       dispatch(fetchStatusFail(id, error, skipLoading));
     });
   };
-};
+}
 
 export function fetchStatusSuccess(skipLoading) {
   return {
     type: STATUS_FETCH_SUCCESS,
     skipLoading,
   };
-};
+}
 
 export function fetchStatusFail(id, error, skipLoading) {
   return {
@@ -83,7 +83,7 @@ export function fetchStatusFail(id, error, skipLoading) {
     skipLoading,
     skipAlert: true,
   };
-};
+}
 
 export function redraft(status, raw_text, content_type) {
   return {
@@ -92,7 +92,7 @@ export function redraft(status, raw_text, content_type) {
     raw_text,
     content_type,
   };
-};
+}
 
 export const editStatus = (id, routerHistory) => (dispatch, getState) => {
   let status = getState().getIn(['statuses', id]);
@@ -148,21 +148,21 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
       dispatch(deleteStatusFail(id, error));
     });
   };
-};
+}
 
 export function deleteStatusRequest(id) {
   return {
     type: STATUS_DELETE_REQUEST,
     id: id,
   };
-};
+}
 
 export function deleteStatusSuccess(id) {
   return {
     type: STATUS_DELETE_SUCCESS,
     id: id,
   };
-};
+}
 
 export function deleteStatusFail(id, error) {
   return {
@@ -170,7 +170,7 @@ export function deleteStatusFail(id, error) {
     id: id,
     error: error,
   };
-};
+}
 
 export const updateStatus = status => dispatch =>
   dispatch(importFetchedStatus(status));
@@ -191,14 +191,14 @@ export function fetchContext(id) {
       dispatch(fetchContextFail(id, error));
     });
   };
-};
+}
 
 export function fetchContextRequest(id) {
   return {
     type: CONTEXT_FETCH_REQUEST,
     id,
   };
-};
+}
 
 export function fetchContextSuccess(id, ancestors, descendants) {
   return {
@@ -208,7 +208,7 @@ export function fetchContextSuccess(id, ancestors, descendants) {
     descendants,
     statuses: ancestors.concat(descendants),
   };
-};
+}
 
 export function fetchContextFail(id, error) {
   return {
@@ -217,7 +217,7 @@ export function fetchContextFail(id, error) {
     error,
     skipAlert: true,
   };
-};
+}
 
 export function muteStatus(id) {
   return (dispatch, getState) => {
@@ -229,21 +229,21 @@ export function muteStatus(id) {
       dispatch(muteStatusFail(id, error));
     });
   };
-};
+}
 
 export function muteStatusRequest(id) {
   return {
     type: STATUS_MUTE_REQUEST,
     id,
   };
-};
+}
 
 export function muteStatusSuccess(id) {
   return {
     type: STATUS_MUTE_SUCCESS,
     id,
   };
-};
+}
 
 export function muteStatusFail(id, error) {
   return {
@@ -251,7 +251,7 @@ export function muteStatusFail(id, error) {
     id,
     error,
   };
-};
+}
 
 export function unmuteStatus(id) {
   return (dispatch, getState) => {
@@ -263,21 +263,21 @@ export function unmuteStatus(id) {
       dispatch(unmuteStatusFail(id, error));
     });
   };
-};
+}
 
 export function unmuteStatusRequest(id) {
   return {
     type: STATUS_UNMUTE_REQUEST,
     id,
   };
-};
+}
 
 export function unmuteStatusSuccess(id) {
   return {
     type: STATUS_UNMUTE_SUCCESS,
     id,
   };
-};
+}
 
 export function unmuteStatusFail(id, error) {
   return {
@@ -285,7 +285,7 @@ export function unmuteStatusFail(id, error) {
     id,
     error,
   };
-};
+}
 
 export function hideStatus(ids) {
   if (!Array.isArray(ids)) {
@@ -296,7 +296,7 @@ export function hideStatus(ids) {
     type: STATUS_HIDE,
     ids,
   };
-};
+}
 
 export function revealStatus(ids) {
   if (!Array.isArray(ids)) {
@@ -307,7 +307,7 @@ export function revealStatus(ids) {
     type: STATUS_REVEAL,
     ids,
   };
-};
+}
 
 export function toggleStatusCollapse(id, isCollapsed) {
   return {
@@ -315,7 +315,7 @@ export function toggleStatusCollapse(id, isCollapsed) {
     id,
     isCollapsed,
   };
-};
+}
 
 export const translateStatus = id => (dispatch, getState) => {
   dispatch(translateStatusRequest(id));
diff --git a/app/javascript/flavours/glitch/actions/store.js b/app/javascript/flavours/glitch/actions/store.js
index 9dbc0b214..137b68e22 100644
--- a/app/javascript/flavours/glitch/actions/store.js
+++ b/app/javascript/flavours/glitch/actions/store.js
@@ -18,7 +18,7 @@ const applyMigrations = (state) => {
       if (state.getIn(['settings', 'notifications', 'showUnread']) !== false) {
         state.setIn(['settings', 'notifications', 'showUnread'], state.getIn(['local_settings', 'notifications', 'show_unread']));
       }
-      state.removeIn(['local_settings', 'notifications', 'show_unread'])
+      state.removeIn(['local_settings', 'notifications', 'show_unread']);
     }
   });
 };
@@ -36,4 +36,4 @@ export function hydrateStore(rawState) {
     dispatch(importFetchedAccounts(Object.values(rawState.accounts)));
     dispatch(saveSettings());
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/actions/suggestions.js b/app/javascript/flavours/glitch/actions/suggestions.js
index 1f1116e75..9e8cd1ea4 100644
--- a/app/javascript/flavours/glitch/actions/suggestions.js
+++ b/app/javascript/flavours/glitch/actions/suggestions.js
@@ -21,14 +21,14 @@ export function fetchSuggestions(withRelationships = false) {
       }
     }).catch(error => dispatch(fetchSuggestionsFail(error)));
   };
-};
+}
 
 export function fetchSuggestionsRequest() {
   return {
     type: SUGGESTIONS_FETCH_REQUEST,
     skipLoading: true,
   };
-};
+}
 
 export function fetchSuggestionsSuccess(suggestions) {
   return {
@@ -36,7 +36,7 @@ export function fetchSuggestionsSuccess(suggestions) {
     suggestions,
     skipLoading: true,
   };
-};
+}
 
 export function fetchSuggestionsFail(error) {
   return {
@@ -45,7 +45,7 @@ export function fetchSuggestionsFail(error) {
     skipLoading: true,
     skipAlert: true,
   };
-};
+}
 
 export const dismissSuggestion = accountId => (dispatch, getState) => {
   dispatch({
diff --git a/app/javascript/flavours/glitch/actions/tags.js b/app/javascript/flavours/glitch/actions/tags.js
index 37e79d4cb..dda8c924b 100644
--- a/app/javascript/flavours/glitch/actions/tags.js
+++ b/app/javascript/flavours/glitch/actions/tags.js
@@ -1,9 +1,17 @@
-import api from '../api';
+import api, { getLinks } from '../api';
 
 export const HASHTAG_FETCH_REQUEST = 'HASHTAG_FETCH_REQUEST';
 export const HASHTAG_FETCH_SUCCESS = 'HASHTAG_FETCH_SUCCESS';
 export const HASHTAG_FETCH_FAIL    = 'HASHTAG_FETCH_FAIL';
 
+export const FOLLOWED_HASHTAGS_FETCH_REQUEST = 'FOLLOWED_HASHTAGS_FETCH_REQUEST';
+export const FOLLOWED_HASHTAGS_FETCH_SUCCESS = 'FOLLOWED_HASHTAGS_FETCH_SUCCESS';
+export const FOLLOWED_HASHTAGS_FETCH_FAIL    = 'FOLLOWED_HASHTAGS_FETCH_FAIL';
+
+export const FOLLOWED_HASHTAGS_EXPAND_REQUEST = 'FOLLOWED_HASHTAGS_EXPAND_REQUEST';
+export const FOLLOWED_HASHTAGS_EXPAND_SUCCESS = 'FOLLOWED_HASHTAGS_EXPAND_SUCCESS';
+export const FOLLOWED_HASHTAGS_EXPAND_FAIL    = 'FOLLOWED_HASHTAGS_EXPAND_FAIL';
+
 export const HASHTAG_FOLLOW_REQUEST = 'HASHTAG_FOLLOW_REQUEST';
 export const HASHTAG_FOLLOW_SUCCESS = 'HASHTAG_FOLLOW_SUCCESS';
 export const HASHTAG_FOLLOW_FAIL    = 'HASHTAG_FOLLOW_FAIL';
@@ -37,6 +45,78 @@ export const fetchHashtagFail = error => ({
   error,
 });
 
+export const fetchFollowedHashtags = () => (dispatch, getState) => {
+  dispatch(fetchFollowedHashtagsRequest());
+
+  api(getState).get('/api/v1/followed_tags').then(response => {
+    const next = getLinks(response).refs.find(link => link.rel === 'next');
+    dispatch(fetchFollowedHashtagsSuccess(response.data, next ? next.uri : null));
+  }).catch(err => {
+    dispatch(fetchFollowedHashtagsFail(err));
+  });
+};
+
+export function fetchFollowedHashtagsRequest() {
+  return {
+    type: FOLLOWED_HASHTAGS_FETCH_REQUEST,
+  };
+}
+
+export function fetchFollowedHashtagsSuccess(followed_tags, next) {
+  return {
+    type: FOLLOWED_HASHTAGS_FETCH_SUCCESS,
+    followed_tags,
+    next,
+  };
+}
+
+export function fetchFollowedHashtagsFail(error) {
+  return {
+    type: FOLLOWED_HASHTAGS_FETCH_FAIL,
+    error,
+  };
+}
+
+export function expandFollowedHashtags() {
+  return (dispatch, getState) => {
+    const url = getState().getIn(['followed_tags', 'next']);
+
+    if (url === null) {
+      return;
+    }
+
+    dispatch(expandFollowedHashtagsRequest());
+
+    api(getState).get(url).then(response => {
+      const next = getLinks(response).refs.find(link => link.rel === 'next');
+      dispatch(expandFollowedHashtagsSuccess(response.data, next ? next.uri : null));
+    }).catch(error => {
+      dispatch(expandFollowedHashtagsFail(error));
+    });
+  };
+}
+
+export function expandFollowedHashtagsRequest() {
+  return {
+    type: FOLLOWED_HASHTAGS_EXPAND_REQUEST,
+  };
+}
+
+export function expandFollowedHashtagsSuccess(followed_tags, next) {
+  return {
+    type: FOLLOWED_HASHTAGS_EXPAND_SUCCESS,
+    followed_tags,
+    next,
+  };
+}
+
+export function expandFollowedHashtagsFail(error) {
+  return {
+    type: FOLLOWED_HASHTAGS_EXPAND_FAIL,
+    error,
+  };
+}
+
 export const followHashtag = name => (dispatch, getState) => {
   dispatch(followHashtagRequest(name));
 
diff --git a/app/javascript/flavours/glitch/actions/timelines.js b/app/javascript/flavours/glitch/actions/timelines.js
index a1c4dd43a..eb817daf9 100644
--- a/app/javascript/flavours/glitch/actions/timelines.js
+++ b/app/javascript/flavours/glitch/actions/timelines.js
@@ -55,14 +55,14 @@ export function updateTimeline(timeline, status, accept) {
       timeline,
       status,
       usePendingItems: preferPendingItems,
-      filtered
+      filtered,
     });
 
     if (timeline === 'home') {
       dispatch(submitMarkers());
     }
   };
-};
+}
 
 export function deleteFromTimelines(id) {
   return (dispatch, getState) => {
@@ -78,13 +78,13 @@ export function deleteFromTimelines(id) {
       reblogOf,
     });
   };
-};
+}
 
 export function clearTimeline(timeline) {
   return (dispatch) => {
     dispatch({ type: TIMELINE_CLEAR, timeline });
   };
-};
+}
 
 const noOp = () => {};
 
@@ -134,7 +134,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
       done();
     });
   };
-};
+}
 
 export function fillTimelineGaps(timelineId, path, params = {}, done = noOp) {
   return (dispatch, getState) => {
@@ -181,7 +181,7 @@ export function expandTimelineRequest(timeline, isLoadingMore) {
     timeline,
     skipLoading: !isLoadingMore,
   };
-};
+}
 
 export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore, usePendingItems) {
   return {
@@ -194,7 +194,7 @@ export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadi
     usePendingItems,
     skipLoading: !isLoadingMore,
   };
-};
+}
 
 export function expandTimelineFail(timeline, error, isLoadingMore) {
   return {
@@ -204,7 +204,7 @@ export function expandTimelineFail(timeline, error, isLoadingMore) {
     skipLoading: !isLoadingMore,
     skipNotFound: timeline.startsWith('account:'),
   };
-};
+}
 
 export function scrollTopTimeline(timeline, top) {
   return {
@@ -212,7 +212,7 @@ export function scrollTopTimeline(timeline, top) {
     timeline,
     top,
   };
-};
+}
 
 export function connectTimeline(timeline) {
   return {
@@ -220,7 +220,7 @@ export function connectTimeline(timeline) {
     timeline,
     usePendingItems: preferPendingItems,
   };
-};
+}
 
 export const disconnectTimeline = timeline => ({
   type: TIMELINE_DISCONNECT,
diff --git a/app/javascript/flavours/glitch/base_polyfills.js b/app/javascript/flavours/glitch/base_polyfills.js
index 12096d902..91bc5d6dc 100644
--- a/app/javascript/flavours/glitch/base_polyfills.js
+++ b/app/javascript/flavours/glitch/base_polyfills.js
@@ -1,17 +1,11 @@
 import 'intl';
 import 'intl/locale-data/jsonp/en';
 import 'es6-symbol/implement';
-import includes from 'array-includes';
 import assign from 'object-assign';
 import values from 'object.values';
-import isNaN from 'is-nan';
 import { decode as decodeBase64 } from './utils/base64';
 import promiseFinally from 'promise.prototype.finally';
 
-if (!Array.prototype.includes) {
-  includes.shim();
-}
-
 if (!Object.assign) {
   Object.assign = assign;
 }
@@ -20,10 +14,6 @@ if (!Object.values) {
   values.shim();
 }
 
-if (!Number.isNaN) {
-  Number.isNaN = isNaN;
-}
-
 promiseFinally.shim();
 
 if (!HTMLCanvasElement.prototype.toBlob) {
diff --git a/app/javascript/flavours/glitch/compare_id.js b/app/javascript/flavours/glitch/compare_id.js
index 66cf51c4b..d2bd74f44 100644
--- a/app/javascript/flavours/glitch/compare_id.js
+++ b/app/javascript/flavours/glitch/compare_id.js
@@ -8,4 +8,4 @@ export default function compareId (id1, id2) {
   } else {
     return id1.length > id2.length ? 1 : -1;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/components/account.js b/app/javascript/flavours/glitch/components/account.jsx
index 8e810ce5f..7b66d5a6e 100644
--- a/app/javascript/flavours/glitch/components/account.js
+++ b/app/javascript/flavours/glitch/components/account.jsx
@@ -23,7 +23,6 @@ const messages = defineMessages({
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
 });
 
-export default @injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -48,27 +47,27 @@ class Account extends ImmutablePureComponent {
 
   handleFollow = () => {
     this.props.onFollow(this.props.account);
-  }
+  };
 
   handleBlock = () => {
     this.props.onBlock(this.props.account);
-  }
+  };
 
   handleMute = () => {
     this.props.onMute(this.props.account);
-  }
+  };
 
   handleMuteNotifications = () => {
     this.props.onMuteNotifications(this.props.account, true);
-  }
+  };
 
   handleUnmuteNotifications = () => {
     this.props.onMuteNotifications(this.props.account, false);
-  }
+  };
 
   handleAction = () => {
     this.props.onActionClick(this.props.account);
-  }
+  };
 
   render () {
     const {
@@ -184,3 +183,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Account);
diff --git a/app/javascript/flavours/glitch/components/admin/Counter.js b/app/javascript/flavours/glitch/components/admin/Counter.jsx
index 5b6a19f8d..5b6a19f8d 100644
--- a/app/javascript/flavours/glitch/components/admin/Counter.js
+++ b/app/javascript/flavours/glitch/components/admin/Counter.jsx
diff --git a/app/javascript/flavours/glitch/components/admin/Dimension.js b/app/javascript/flavours/glitch/components/admin/Dimension.jsx
index 3dac8c6c2..3dac8c6c2 100644
--- a/app/javascript/flavours/glitch/components/admin/Dimension.js
+++ b/app/javascript/flavours/glitch/components/admin/Dimension.jsx
diff --git a/app/javascript/flavours/glitch/components/admin/ReportReasonSelector.js b/app/javascript/flavours/glitch/components/admin/ReportReasonSelector.jsx
index 771dbb452..ecefe7a84 100644
--- a/app/javascript/flavours/glitch/components/admin/ReportReasonSelector.js
+++ b/app/javascript/flavours/glitch/components/admin/ReportReasonSelector.jsx
@@ -33,7 +33,7 @@ class Category extends React.PureComponent {
     const { id, text, disabled, selected, children } = this.props;
 
     return (
-      <div tabIndex='0' role='button' className={classNames('report-reason-selector__category', { selected, disabled })} onClick={this.handleClick}>
+      <div tabIndex={0} role='button' className={classNames('report-reason-selector__category', { selected, disabled })} onClick={this.handleClick}>
         {selected && <input type='hidden' name='report[category]' value={id} />}
 
         <div className='report-reason-selector__category__label'>
@@ -74,7 +74,7 @@ class Rule extends React.PureComponent {
     const { id, text, disabled, selected } = this.props;
 
     return (
-      <div tabIndex='0' role='button' className={classNames('report-reason-selector__rule', { selected, disabled })} onClick={this.handleClick}>
+      <div tabIndex={0} role='button' className={classNames('report-reason-selector__rule', { selected, disabled })} onClick={this.handleClick}>
         <span className={classNames('poll__input', { checkbox: true, active: selected, disabled })} />
         {selected && <input type='hidden' name='report[rule_ids][]' value={id} />}
         {text}
@@ -84,7 +84,6 @@ class Rule extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class ReportReasonSelector extends React.PureComponent {
 
   static propTypes = {
@@ -157,3 +156,5 @@ class ReportReasonSelector extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ReportReasonSelector);
diff --git a/app/javascript/flavours/glitch/components/admin/Retention.js b/app/javascript/flavours/glitch/components/admin/Retention.jsx
index 9cc39040b..e1ba3f6c9 100644
--- a/app/javascript/flavours/glitch/components/admin/Retention.js
+++ b/app/javascript/flavours/glitch/components/admin/Retention.jsx
@@ -137,7 +137,7 @@ export default class Retention extends React.PureComponent {
       break;
     default:
       title = <FormattedMessage id='admin.dashboard.monthly_retention' defaultMessage='User retention rate by month after sign-up' />;
-    };
+    }
 
     return (
       <div className='retention'>
diff --git a/app/javascript/flavours/glitch/components/admin/Trends.js b/app/javascript/flavours/glitch/components/admin/Trends.jsx
index 4c17b69a0..774bf36e6 100644
--- a/app/javascript/flavours/glitch/components/admin/Trends.js
+++ b/app/javascript/flavours/glitch/components/admin/Trends.jsx
@@ -50,7 +50,7 @@ export default class Trends extends React.PureComponent {
             <Hashtag
               key={hashtag.name}
               name={hashtag.name}
-              href={`/admin/tags/${hashtag.id}`}
+              href={hashtag.id === undefined ? undefined : `/admin/tags/${hashtag.id}`}
               people={hashtag.history[0].accounts * 1 + hashtag.history[1].accounts * 1}
               uses={hashtag.history[0].uses * 1 + hashtag.history[1].uses * 1}
               history={hashtag.history.reverse().map(day => day.uses)}
diff --git a/app/javascript/flavours/glitch/components/animated_number.js b/app/javascript/flavours/glitch/components/animated_number.jsx
index 9431c96f7..dd21d97f0 100644
--- a/app/javascript/flavours/glitch/components/animated_number.js
+++ b/app/javascript/flavours/glitch/components/animated_number.jsx
@@ -38,13 +38,13 @@ export default class AnimatedNumber extends React.PureComponent {
     const { direction } = this.state;
 
     return { y: -1 * direction };
-  }
+  };
 
   willLeave = () => {
     const { direction } = this.state;
 
     return { y: spring(1 * direction, { damping: 35, stiffness: 400 }) };
-  }
+  };
 
   render () {
     const { value, obfuscate } = this.props;
diff --git a/app/javascript/flavours/glitch/components/attachment_list.js b/app/javascript/flavours/glitch/components/attachment_list.jsx
index 68b80b19f..68b80b19f 100644
--- a/app/javascript/flavours/glitch/components/attachment_list.js
+++ b/app/javascript/flavours/glitch/components/attachment_list.jsx
diff --git a/app/javascript/flavours/glitch/components/autosuggest_emoji.js b/app/javascript/flavours/glitch/components/autosuggest_emoji.jsx
index 83fafbd10..83fafbd10 100644
--- a/app/javascript/flavours/glitch/components/autosuggest_emoji.js
+++ b/app/javascript/flavours/glitch/components/autosuggest_emoji.jsx
diff --git a/app/javascript/flavours/glitch/components/autosuggest_hashtag.js b/app/javascript/flavours/glitch/components/autosuggest_hashtag.jsx
index d787ed07a..d787ed07a 100644
--- a/app/javascript/flavours/glitch/components/autosuggest_hashtag.js
+++ b/app/javascript/flavours/glitch/components/autosuggest_hashtag.jsx
diff --git a/app/javascript/flavours/glitch/components/autosuggest_input.js b/app/javascript/flavours/glitch/components/autosuggest_input.jsx
index b40a2ff35..ea9fd0828 100644
--- a/app/javascript/flavours/glitch/components/autosuggest_input.js
+++ b/app/javascript/flavours/glitch/components/autosuggest_input.jsx
@@ -50,6 +50,8 @@ export default class AutosuggestInput extends ImmutablePureComponent {
     id: PropTypes.string,
     searchTokens: PropTypes.arrayOf(PropTypes.string),
     maxLength: PropTypes.number,
+    lang: PropTypes.string,
+    spellCheck: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -77,7 +79,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
     }
 
     this.props.onChange(e);
-  }
+  };
 
   onKeyDown = (e) => {
     const { suggestions, disabled } = this.props;
@@ -135,22 +137,22 @@ export default class AutosuggestInput extends ImmutablePureComponent {
     }
 
     this.props.onKeyDown(e);
-  }
+  };
 
   onBlur = () => {
     this.setState({ suggestionsHidden: true, focused: false });
-  }
+  };
 
   onFocus = () => {
     this.setState({ focused: true });
-  }
+  };
 
   onSuggestionClick = (e) => {
     const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
     e.preventDefault();
     this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
     this.input.focus();
-  }
+  };
 
   componentWillReceiveProps (nextProps) {
     if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
@@ -160,7 +162,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
 
   setInput = (c) => {
     this.input = c;
-  }
+  };
 
   renderSuggestion = (suggestion, i) => {
     const { selectedSuggestion } = this.state;
@@ -178,14 +180,14 @@ export default class AutosuggestInput extends ImmutablePureComponent {
     }
 
     return (
-      <div role='button' tabIndex='0' key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
+      <div role='button' tabIndex={0} key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
         {inner}
       </div>
     );
-  }
+  };
 
   render () {
-    const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength } = this.props;
+    const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength, lang, spellCheck } = this.props;
     const { suggestionsHidden } = this.state;
 
     return (
@@ -210,6 +212,8 @@ export default class AutosuggestInput extends ImmutablePureComponent {
             id={id}
             className={className}
             maxLength={maxLength}
+            lang={lang}
+            spellCheck={spellCheck}
           />
         </label>
 
diff --git a/app/javascript/flavours/glitch/components/autosuggest_textarea.js b/app/javascript/flavours/glitch/components/autosuggest_textarea.jsx
index 967c593af..a016e44b7 100644
--- a/app/javascript/flavours/glitch/components/autosuggest_textarea.js
+++ b/app/javascript/flavours/glitch/components/autosuggest_textarea.jsx
@@ -48,6 +48,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
     onKeyDown: PropTypes.func,
     onPaste: PropTypes.func.isRequired,
     autoFocus: PropTypes.bool,
+    lang: PropTypes.string,
   };
 
   static defaultProps = {
@@ -74,7 +75,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
     }
 
     this.props.onChange(e);
-  }
+  };
 
   onKeyDown = (e) => {
     const { suggestions, disabled } = this.props;
@@ -132,25 +133,25 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
     }
 
     this.props.onKeyDown(e);
-  }
+  };
 
   onBlur = () => {
     this.setState({ suggestionsHidden: true, focused: false });
-  }
+  };
 
   onFocus = (e) => {
     this.setState({ focused: true });
     if (this.props.onFocus) {
       this.props.onFocus(e);
     }
-  }
+  };
 
   onSuggestionClick = (e) => {
     const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
     e.preventDefault();
     this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
     this.textarea.focus();
-  }
+  };
 
   componentWillReceiveProps (nextProps) {
     if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
@@ -160,14 +161,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
 
   setTextarea = (c) => {
     this.textarea = c;
-  }
+  };
 
   onPaste = (e) => {
     if (e.clipboardData && e.clipboardData.files.length === 1) {
       this.props.onPaste(e.clipboardData.files);
       e.preventDefault();
     }
-  }
+  };
 
   renderSuggestion = (suggestion, i) => {
     const { selectedSuggestion } = this.state;
@@ -185,14 +186,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
     }
 
     return (
-      <div role='button' tabIndex='0' key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
+      <div role='button' tabIndex={0} key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
         {inner}
       </div>
     );
-  }
+  };
 
   render () {
-    const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props;
+    const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, lang, children } = this.props;
     const { suggestionsHidden } = this.state;
 
     return [
@@ -216,6 +217,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
               onPaste={this.onPaste}
               dir='auto'
               aria-autocomplete='list'
+              lang={lang}
             />
           </label>
         </div>
diff --git a/app/javascript/flavours/glitch/components/avatar.js b/app/javascript/flavours/glitch/components/avatar.jsx
index 38fd99af5..f30b33e70 100644
--- a/app/javascript/flavours/glitch/components/avatar.js
+++ b/app/javascript/flavours/glitch/components/avatar.jsx
@@ -28,12 +28,12 @@ export default class Avatar extends React.PureComponent {
   handleMouseEnter = () => {
     if (this.props.animate) return;
     this.setState({ hovering: true });
-  }
+  };
 
   handleMouseLeave = () => {
     if (this.props.animate) return;
     this.setState({ hovering: false });
-  }
+  };
 
   render () {
     const {
diff --git a/app/javascript/flavours/glitch/components/avatar_composite.js b/app/javascript/flavours/glitch/components/avatar_composite.jsx
index c0ce7761d..c0ce7761d 100644
--- a/app/javascript/flavours/glitch/components/avatar_composite.js
+++ b/app/javascript/flavours/glitch/components/avatar_composite.jsx
diff --git a/app/javascript/flavours/glitch/components/avatar_overlay.js b/app/javascript/flavours/glitch/components/avatar_overlay.jsx
index 01dec587a..01dec587a 100644
--- a/app/javascript/flavours/glitch/components/avatar_overlay.js
+++ b/app/javascript/flavours/glitch/components/avatar_overlay.jsx
diff --git a/app/javascript/flavours/glitch/components/blurhash.js b/app/javascript/flavours/glitch/components/blurhash.jsx
index 2af5cfc56..2af5cfc56 100644
--- a/app/javascript/flavours/glitch/components/blurhash.js
+++ b/app/javascript/flavours/glitch/components/blurhash.jsx
diff --git a/app/javascript/flavours/glitch/components/button.js b/app/javascript/flavours/glitch/components/button.jsx
index b1815c3e1..40b8f5a15 100644
--- a/app/javascript/flavours/glitch/components/button.js
+++ b/app/javascript/flavours/glitch/components/button.jsx
@@ -19,11 +19,11 @@ export default class Button extends React.PureComponent {
     if (!this.props.disabled) {
       this.props.onClick(e);
     }
-  }
+  };
 
   setRef = (c) => {
     this.node = c;
-  }
+  };
 
   focus() {
     this.node.focus();
diff --git a/app/javascript/flavours/glitch/components/check.js b/app/javascript/flavours/glitch/components/check.jsx
index ee2ef1595..ee2ef1595 100644
--- a/app/javascript/flavours/glitch/components/check.js
+++ b/app/javascript/flavours/glitch/components/check.jsx
diff --git a/app/javascript/flavours/glitch/components/column.js b/app/javascript/flavours/glitch/components/column.jsx
index cf0e6d5e4..47293ef18 100644
--- a/app/javascript/flavours/glitch/components/column.js
+++ b/app/javascript/flavours/glitch/components/column.jsx
@@ -29,11 +29,11 @@ export default class Column extends React.PureComponent {
     }
 
     this._interruptScrollAnimation();
-  }
+  };
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   componentDidMount () {
     if (this.props.bindToDocument) {
diff --git a/app/javascript/flavours/glitch/components/column_back_button.js b/app/javascript/flavours/glitch/components/column_back_button.jsx
index 05688f867..e9e2615cb 100644
--- a/app/javascript/flavours/glitch/components/column_back_button.js
+++ b/app/javascript/flavours/glitch/components/column_back_button.jsx
@@ -26,7 +26,7 @@ export default class ColumnBackButton extends React.PureComponent {
     } else {
       this.context.router.history.push('/');
     }
-  }
+  };
 
   render () {
     const { multiColumn } = this.props;
diff --git a/app/javascript/flavours/glitch/components/column_back_button_slim.js b/app/javascript/flavours/glitch/components/column_back_button_slim.jsx
index faa0c23a8..4df045b5f 100644
--- a/app/javascript/flavours/glitch/components/column_back_button_slim.js
+++ b/app/javascript/flavours/glitch/components/column_back_button_slim.jsx
@@ -21,12 +21,12 @@ export default class ColumnBackButtonSlim extends React.PureComponent {
     } else {
       this.context.router.history.push('/');
     }
-  }
+  };
 
   render () {
     return (
       <div className='column-back-button--slim'>
-        <div role='button' tabIndex='0' onClick={this.handleClick} className='column-back-button column-back-button--slim-button'>
+        <div role='button' tabIndex={0} onClick={this.handleClick} className='column-back-button column-back-button--slim-button'>
           <Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
           <FormattedMessage id='column_back_button.label' defaultMessage='Back' />
         </div>
diff --git a/app/javascript/flavours/glitch/components/column_header.js b/app/javascript/flavours/glitch/components/column_header.jsx
index 0f89b3a97..6fbe2955d 100644
--- a/app/javascript/flavours/glitch/components/column_header.js
+++ b/app/javascript/flavours/glitch/components/column_header.jsx
@@ -12,7 +12,6 @@ const messages = defineMessages({
   moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
 });
 
-export default @injectIntl
 class ColumnHeader extends React.PureComponent {
 
   static contextTypes = {
@@ -55,39 +54,39 @@ class ColumnHeader extends React.PureComponent {
     } else {
       this.context.router.history.push('/');
     }
-  }
+  };
 
   handleToggleClick = (e) => {
     e.stopPropagation();
     this.setState({ collapsed: !this.state.collapsed, animating: true });
-  }
+  };
 
   handleTitleClick = () => {
     this.props.onClick?.();
-  }
+  };
 
   handleMoveLeft = () => {
     this.props.onMove(-1);
-  }
+  };
 
   handleMoveRight = () => {
     this.props.onMove(1);
-  }
+  };
 
   handleBackClick = (event) => {
     this.historyBack(event.shiftKey);
-  }
+  };
 
   handleTransitionEnd = () => {
     this.setState({ animating: false });
-  }
+  };
 
   handlePin = () => {
     if (!this.props.pinned) {
       this.historyBack();
     }
     this.props.onPin();
-  }
+  };
 
   render () {
     const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues } = this.props;
@@ -218,3 +217,5 @@ class ColumnHeader extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnHeader);
diff --git a/app/javascript/flavours/glitch/components/common_counter.js b/app/javascript/flavours/glitch/components/common_counter.jsx
index dd9b62de9..dd9b62de9 100644
--- a/app/javascript/flavours/glitch/components/common_counter.js
+++ b/app/javascript/flavours/glitch/components/common_counter.jsx
diff --git a/app/javascript/flavours/glitch/components/dismissable_banner.js b/app/javascript/flavours/glitch/components/dismissable_banner.jsx
index ff52a619d..9b3faf6f2 100644
--- a/app/javascript/flavours/glitch/components/dismissable_banner.js
+++ b/app/javascript/flavours/glitch/components/dismissable_banner.jsx
@@ -8,7 +8,6 @@ const messages = defineMessages({
   dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
 });
 
-export default @injectIntl
 class DismissableBanner extends React.PureComponent {
 
   static propTypes = {
@@ -24,7 +23,7 @@ class DismissableBanner extends React.PureComponent {
   handleDismiss = () => {
     const { id } = this.props;
     this.setState({ visible: false }, () => bannerSettings.set(id, true));
-  }
+  };
 
   render () {
     const { visible } = this.state;
@@ -49,3 +48,5 @@ class DismissableBanner extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(DismissableBanner);
diff --git a/app/javascript/flavours/glitch/components/display_name.js b/app/javascript/flavours/glitch/components/display_name.jsx
index 1c2297578..19f63ec60 100644
--- a/app/javascript/flavours/glitch/components/display_name.js
+++ b/app/javascript/flavours/glitch/components/display_name.jsx
@@ -27,7 +27,7 @@ export default class DisplayName extends React.PureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -40,7 +40,7 @@ export default class DisplayName extends React.PureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   render() {
     const { account, className, inline, localDomain, others, onAccountClick } = this.props;
@@ -74,7 +74,7 @@ export default class DisplayName extends React.PureComponent {
       )).reduce((prev, cur) => [prev, ', ', cur]);
 
       if (others.size - 2 > 0) {
-       displayName.push(` +${others.size - 2}`);
+        displayName.push(` +${others.size - 2}`);
       }
 
       suffix = (
diff --git a/app/javascript/mastodon/components/domain.js b/app/javascript/flavours/glitch/components/domain.jsx
index 697065d87..85ebdbde9 100644
--- a/app/javascript/mastodon/components/domain.js
+++ b/app/javascript/flavours/glitch/components/domain.jsx
@@ -8,7 +8,6 @@ const messages = defineMessages({
   unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
 });
 
-export default @injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -19,7 +18,7 @@ class Account extends ImmutablePureComponent {
 
   handleDomainUnblock = () => {
     this.props.onUnblockDomain(this.props.domain);
-  }
+  };
 
   render () {
     const { domain, intl } = this.props;
@@ -40,3 +39,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Account);
diff --git a/app/javascript/flavours/glitch/components/dropdown_menu.js b/app/javascript/flavours/glitch/components/dropdown_menu.jsx
index 7c70f750f..7fb75b59e 100644
--- a/app/javascript/flavours/glitch/components/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/components/dropdown_menu.jsx
@@ -36,7 +36,7 @@ class DropdownMenu extends React.PureComponent {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
     }
-  }
+  };
 
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
@@ -56,11 +56,11 @@ class DropdownMenu extends React.PureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   setFocusRef = c => {
     this.focusedItem = c;
-  }
+  };
 
   handleKeyDown = e => {
     const items = Array.from(this.node.querySelectorAll('a, button'));
@@ -97,18 +97,18 @@ class DropdownMenu extends React.PureComponent {
       e.preventDefault();
       e.stopPropagation();
     }
-  }
+  };
 
   handleItemKeyPress = e => {
     if (e.key === 'Enter' || e.key === ' ') {
       this.handleClick(e);
     }
-  }
+  };
 
   handleClick = e => {
     const { onItemClick } = this.props;
     onItemClick(e);
-  }
+  };
 
   renderItem = (option, i) => {
     if (option === null) {
@@ -119,12 +119,12 @@ class DropdownMenu extends React.PureComponent {
 
     return (
       <li className='dropdown-menu__item' key={`${text}-${i}`}>
-        <a href={href} target={target} data-method={method} rel='noopener noreferrer' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}>
+        <a href={href} target={target} data-method={method} rel='noopener noreferrer' role='button' tabIndex={0} ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}>
           {text}
         </a>
       </li>
     );
-  }
+  };
 
   render () {
     const { items, scrollable, renderHeader, loading } = this.props;
@@ -194,7 +194,7 @@ export default class Dropdown extends React.PureComponent {
     } else {
       this.props.onOpen(this.state.id, this.handleItemClick, type !== 'click');
     }
-  }
+  };
 
   handleClose = () => {
     if (this.activeElement) {
@@ -202,13 +202,13 @@ export default class Dropdown extends React.PureComponent {
       this.activeElement = null;
     }
     this.props.onClose(this.state.id);
-  }
+  };
 
   handleMouseDown = () => {
     if (!this.state.open) {
       this.activeElement = document.activeElement;
     }
-  }
+  };
 
   handleButtonKeyDown = (e) => {
     switch(e.key) {
@@ -217,7 +217,7 @@ export default class Dropdown extends React.PureComponent {
       this.handleMouseDown();
       break;
     }
-  }
+  };
 
   handleKeyPress = (e) => {
     switch(e.key) {
@@ -228,7 +228,7 @@ export default class Dropdown extends React.PureComponent {
       e.preventDefault();
       break;
     }
-  }
+  };
 
   handleItemClick = e => {
     const { onItemClick } = this.props;
@@ -247,25 +247,25 @@ export default class Dropdown extends React.PureComponent {
       e.preventDefault();
       this.context.router.history.push(item.to);
     }
-  }
+  };
 
   setTargetRef = c => {
     this.target = c;
-  }
+  };
 
   findTarget = () => {
     return this.target;
-  }
+  };
 
   componentWillUnmount = () => {
     if (this.state.id === this.props.openDropdownId) {
       this.handleClose();
     }
-  }
+  };
 
   close = () => {
     this.handleClose();
-  }
+  };
 
   render () {
     const {
diff --git a/app/javascript/flavours/glitch/components/edited_timestamp/index.js b/app/javascript/flavours/glitch/components/edited_timestamp/index.jsx
index 9648133af..6d73fa68c 100644
--- a/app/javascript/flavours/glitch/components/edited_timestamp/index.js
+++ b/app/javascript/flavours/glitch/components/edited_timestamp/index.jsx
@@ -16,8 +16,6 @@ const mapDispatchToProps = (dispatch, { statusId }) => ({
 
 });
 
-export default @connect(null, mapDispatchToProps)
-@injectIntl
 class EditedTimestamp extends React.PureComponent {
 
   static propTypes = {
@@ -36,7 +34,7 @@ class EditedTimestamp extends React.PureComponent {
     return (
       <FormattedMessage id='status.edited_x_times' defaultMessage='Edited {count, plural, one {{count} time} other {{count} times}}' values={{ count: items.size - 1 }} />
     );
-  }
+  };
 
   renderItem = (item, index, { onClick, onKeyPress }) => {
     const formattedDate = <RelativeTimestamp timestamp={item.get('created_at')} short={false} />;
@@ -53,7 +51,7 @@ class EditedTimestamp extends React.PureComponent {
         <button data-index={index} onClick={onClick} onKeyPress={onKeyPress}>{label}</button>
       </li>
     );
-  }
+  };
 
   render () {
     const { timestamp, intl, statusId } = this.props;
@@ -68,3 +66,5 @@ class EditedTimestamp extends React.PureComponent {
   }
 
 }
+
+export default connect(null, mapDispatchToProps)(injectIntl(EditedTimestamp));
diff --git a/app/javascript/flavours/glitch/components/error_boundary.js b/app/javascript/flavours/glitch/components/error_boundary.jsx
index e0ca3e2b0..234a53417 100644
--- a/app/javascript/flavours/glitch/components/error_boundary.js
+++ b/app/javascript/flavours/glitch/components/error_boundary.jsx
@@ -18,7 +18,7 @@ export default class ErrorBoundary extends React.PureComponent {
     stackTrace: undefined,
     mappedStackTrace: undefined,
     componentStack: undefined,
-  }
+  };
 
   componentDidCatch(error, info) {
     this.setState({
@@ -72,7 +72,7 @@ export default class ErrorBoundary extends React.PureComponent {
     }
 
     return (
-      <div tabIndex='-1'>
+      <div tabIndex={-1}>
         <div className='error-boundary'>
           <h1><FormattedMessage id='web_app_crash.title' defaultMessage="We're sorry, but something went wrong with the Mastodon app." /></h1>
           <p>
diff --git a/app/javascript/flavours/glitch/components/gifv.js b/app/javascript/flavours/glitch/components/gifv.jsx
index b775e5200..1ce7e7c29 100644
--- a/app/javascript/flavours/glitch/components/gifv.js
+++ b/app/javascript/flavours/glitch/components/gifv.jsx
@@ -6,6 +6,7 @@ export default class GIFV extends React.PureComponent {
   static propTypes = {
     src: PropTypes.string.isRequired,
     alt: PropTypes.string,
+    lang: PropTypes.string,
     width: PropTypes.number,
     height: PropTypes.number,
     onClick: PropTypes.func,
@@ -17,7 +18,7 @@ export default class GIFV extends React.PureComponent {
 
   handleLoadedData = () => {
     this.setState({ loading: false });
-  }
+  };
 
   componentWillReceiveProps (nextProps) {
     if (nextProps.src !== this.props.src) {
@@ -32,10 +33,10 @@ export default class GIFV extends React.PureComponent {
       e.stopPropagation();
       onClick();
     }
-  }
+  };
 
   render () {
-    const { src, width, height, alt } = this.props;
+    const { src, width, height, alt, lang } = this.props;
     const { loading } = this.state;
 
     return (
@@ -45,9 +46,10 @@ export default class GIFV extends React.PureComponent {
             width={width}
             height={height}
             role='button'
-            tabIndex='0'
+            tabIndex={0}
             aria-label={alt}
             title={alt}
+            lang={lang}
             onClick={this.handleClick}
           />
         )}
@@ -55,9 +57,10 @@ export default class GIFV extends React.PureComponent {
         <video
           src={src}
           role='button'
-          tabIndex='0'
+          tabIndex={0}
           aria-label={alt}
           title={alt}
+          lang={lang}
           muted
           loop
           autoPlay
diff --git a/app/javascript/flavours/glitch/components/hashtag.js b/app/javascript/flavours/glitch/components/hashtag.jsx
index 422b9a8fa..422b9a8fa 100644
--- a/app/javascript/flavours/glitch/components/hashtag.js
+++ b/app/javascript/flavours/glitch/components/hashtag.jsx
diff --git a/app/javascript/flavours/glitch/components/icon.js b/app/javascript/flavours/glitch/components/icon.jsx
index d8a17722f..d8a17722f 100644
--- a/app/javascript/flavours/glitch/components/icon.js
+++ b/app/javascript/flavours/glitch/components/icon.jsx
diff --git a/app/javascript/flavours/glitch/components/icon_button.js b/app/javascript/flavours/glitch/components/icon_button.jsx
index 2485f0f48..10d7926be 100644
--- a/app/javascript/flavours/glitch/components/icon_button.js
+++ b/app/javascript/flavours/glitch/components/icon_button.jsx
@@ -46,7 +46,7 @@ export default class IconButton extends React.PureComponent {
   state = {
     activate: false,
     deactivate: false,
-  }
+  };
 
   componentWillReceiveProps (nextProps) {
     if (!nextProps.animate) return;
@@ -64,25 +64,25 @@ export default class IconButton extends React.PureComponent {
     if (!this.props.disabled) {
       this.props.onClick(e);
     }
-  }
+  };
 
   handleKeyPress = (e) => {
     if (this.props.onKeyPress && !this.props.disabled) {
       this.props.onKeyPress(e);
     }
-  }
+  };
 
   handleMouseDown = (e) => {
     if (!this.props.disabled && this.props.onMouseDown) {
       this.props.onMouseDown(e);
     }
-  }
+  };
 
   handleKeyDown = (e) => {
     if (!this.props.disabled && this.props.onKeyDown) {
       this.props.onKeyDown(e);
     }
-  }
+  };
 
   render () {
     // Hack required for some icons which have an overriden size
diff --git a/app/javascript/flavours/glitch/components/icon_with_badge.js b/app/javascript/flavours/glitch/components/icon_with_badge.jsx
index a42ba4589..a42ba4589 100644
--- a/app/javascript/flavours/glitch/components/icon_with_badge.js
+++ b/app/javascript/flavours/glitch/components/icon_with_badge.jsx
diff --git a/app/javascript/flavours/glitch/components/image.js b/app/javascript/flavours/glitch/components/image.jsx
index 6e81ddf08..6e81ddf08 100644
--- a/app/javascript/flavours/glitch/components/image.js
+++ b/app/javascript/flavours/glitch/components/image.jsx
diff --git a/app/javascript/flavours/glitch/components/inline_account.js b/app/javascript/flavours/glitch/components/inline_account.jsx
index 2ef1f52cc..c04618d66 100644
--- a/app/javascript/flavours/glitch/components/inline_account.js
+++ b/app/javascript/flavours/glitch/components/inline_account.jsx
@@ -14,7 +14,6 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-export default @connect(makeMapStateToProps)
 class InlineAccount extends React.PureComponent {
 
   static propTypes = {
@@ -32,3 +31,5 @@ class InlineAccount extends React.PureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(InlineAccount);
diff --git a/app/javascript/flavours/glitch/components/intersection_observer_article.js b/app/javascript/flavours/glitch/components/intersection_observer_article.jsx
index b28e44e4c..6c00e557d 100644
--- a/app/javascript/flavours/glitch/components/intersection_observer_article.js
+++ b/app/javascript/flavours/glitch/components/intersection_observer_article.jsx
@@ -21,7 +21,7 @@ export default class IntersectionObserverArticle extends React.Component {
 
   state = {
     isHidden: false, // set to true in requestIdleCallback to trigger un-render
-  }
+  };
 
   shouldComponentUpdate (nextProps, nextState) {
     const isUnrendered = !this.state.isIntersecting && (this.state.isHidden || this.props.cachedHeight);
@@ -63,7 +63,7 @@ export default class IntersectionObserverArticle extends React.Component {
 
     scheduleIdleTask(this.calculateHeight);
     this.setState(this.updateStateAfterIntersection);
-  }
+  };
 
   updateStateAfterIntersection = (prevState) => {
     if (prevState.isIntersecting !== false && !this.entry.isIntersecting) {
@@ -73,7 +73,7 @@ export default class IntersectionObserverArticle extends React.Component {
       isIntersecting: this.entry.isIntersecting,
       isHidden: false,
     };
-  }
+  };
 
   calculateHeight = () => {
     const { onHeightChange, saveHeightKey, id } = this.props;
@@ -84,7 +84,7 @@ export default class IntersectionObserverArticle extends React.Component {
     if (onHeightChange && saveHeightKey) {
       onHeightChange(saveHeightKey, id, this.height);
     }
-  }
+  };
 
   hideIfNotIntersecting = () => {
     if (!this.componentMounted) {
@@ -96,11 +96,11 @@ export default class IntersectionObserverArticle extends React.Component {
     // this is to save DOM nodes and avoid using up too much memory.
     // See: https://github.com/mastodon/mastodon/issues/2900
     this.setState((prevState) => ({ isHidden: !prevState.isIntersecting }));
-  }
+  };
 
   handleRef = (node) => {
     this.node = node;
-  }
+  };
 
   render () {
     const { children, id, index, listLength, cachedHeight } = this.props;
@@ -120,9 +120,10 @@ export default class IntersectionObserverArticle extends React.Component {
         aria-posinset={index + 1}
         aria-setsize={listLength}
         data-id={id}
-        tabIndex='0'
-        style={style}>
-          {children && React.cloneElement(children, { hidden: !isIntersecting && (isHidden || !!cachedHeight) })}
+        tabIndex={0}
+        style={style}
+      >
+        {children && React.cloneElement(children, { hidden: !isIntersecting && (isHidden || !!cachedHeight) })}
       </article>
     );
   }
diff --git a/app/javascript/flavours/glitch/components/link.js b/app/javascript/flavours/glitch/components/link.jsx
index bbec121a8..bbec121a8 100644
--- a/app/javascript/flavours/glitch/components/link.js
+++ b/app/javascript/flavours/glitch/components/link.jsx
diff --git a/app/javascript/flavours/glitch/components/load_gap.js b/app/javascript/flavours/glitch/components/load_gap.jsx
index fe3f60a58..e70365d9e 100644
--- a/app/javascript/flavours/glitch/components/load_gap.js
+++ b/app/javascript/flavours/glitch/components/load_gap.jsx
@@ -7,7 +7,6 @@ const messages = defineMessages({
   load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
 });
 
-export default @injectIntl
 class LoadGap extends React.PureComponent {
 
   static propTypes = {
@@ -19,7 +18,7 @@ class LoadGap extends React.PureComponent {
 
   handleClick = () => {
     this.props.onClick(this.props.maxId);
-  }
+  };
 
   render () {
     const { disabled, intl } = this.props;
@@ -32,3 +31,5 @@ class LoadGap extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(LoadGap);
diff --git a/app/javascript/flavours/glitch/components/load_more.js b/app/javascript/flavours/glitch/components/load_more.jsx
index 389c3e1e1..ab9428e35 100644
--- a/app/javascript/flavours/glitch/components/load_more.js
+++ b/app/javascript/flavours/glitch/components/load_more.jsx
@@ -8,11 +8,11 @@ export default class LoadMore extends React.PureComponent {
     onClick: PropTypes.func,
     disabled: PropTypes.bool,
     visible: PropTypes.bool,
-  }
+  };
 
   static defaultProps = {
     visible: true,
-  }
+  };
 
   render() {
     const { disabled, visible } = this.props;
diff --git a/app/javascript/flavours/glitch/components/load_pending.js b/app/javascript/flavours/glitch/components/load_pending.jsx
index 7e2702403..a75259146 100644
--- a/app/javascript/flavours/glitch/components/load_pending.js
+++ b/app/javascript/flavours/glitch/components/load_pending.jsx
@@ -7,7 +7,7 @@ export default class LoadPending extends React.PureComponent {
   static propTypes = {
     onClick: PropTypes.func,
     count: PropTypes.number,
-  }
+  };
 
   render() {
     const { count } = this.props;
diff --git a/app/javascript/flavours/glitch/components/loading_indicator.js b/app/javascript/flavours/glitch/components/loading_indicator.jsx
index 59f721c50..59f721c50 100644
--- a/app/javascript/flavours/glitch/components/loading_indicator.js
+++ b/app/javascript/flavours/glitch/components/loading_indicator.jsx
diff --git a/app/javascript/flavours/glitch/components/logo.js b/app/javascript/flavours/glitch/components/logo.jsx
index ee5c22496..ee5c22496 100644
--- a/app/javascript/flavours/glitch/components/logo.js
+++ b/app/javascript/flavours/glitch/components/logo.jsx
diff --git a/app/javascript/flavours/glitch/components/media_attachments.js b/app/javascript/flavours/glitch/components/media_attachments.jsx
index a517fcf30..b11d3526f 100644
--- a/app/javascript/flavours/glitch/components/media_attachments.js
+++ b/app/javascript/flavours/glitch/components/media_attachments.jsx
@@ -10,6 +10,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
 
   static propTypes = {
     status: ImmutablePropTypes.map.isRequired,
+    lang: PropTypes.string,
     height: PropTypes.number,
     width: PropTypes.number,
     revealed: PropTypes.bool,
@@ -30,7 +31,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
     return (
       <div className='media-gallery' style={{ height, width }} />
     );
-  }
+  };
 
   renderLoadingVideoPlayer = () => {
     const { height, width } = this.props;
@@ -38,7 +39,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
     return (
       <div className='video-player' style={{ height, width }} />
     );
-  }
+  };
 
   renderLoadingAudioPlayer = () => {
     const { height, width } = this.props;
@@ -46,10 +47,10 @@ export default class MediaAttachments extends ImmutablePureComponent {
     return (
       <div className='audio-player' style={{ height, width }} />
     );
-  }
+  };
 
   render () {
-    const { status, width, height, revealed } = this.props;
+    const { status, lang, width, height, revealed } = this.props;
     const mediaAttachments = status.get('media_attachments');
 
     if (mediaAttachments.size === 0) {
@@ -65,6 +66,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
             <Component
               src={audio.get('url')}
               alt={audio.get('description')}
+              lang={lang || status.get('language')}
               width={width}
               height={height}
               poster={audio.get('preview_url') || status.getIn(['account', 'avatar_static'])}
@@ -88,6 +90,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
               blurhash={video.get('blurhash')}
               src={video.get('url')}
               alt={video.get('description')}
+              lang={lang || status.get('language')}
               width={width}
               height={height}
               inline
@@ -104,6 +107,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
           {Component => (
             <Component
               media={mediaAttachments}
+              lang={lang || status.get('language')}
               sensitive={status.get('sensitive')}
               defaultWidth={width}
               revealed={revealed}
diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.jsx
index 23e279589..b38f732f1 100644
--- a/app/javascript/flavours/glitch/components/media_gallery.js
+++ b/app/javascript/flavours/glitch/components/media_gallery.jsx
@@ -36,6 +36,7 @@ class Item extends React.PureComponent {
 
   static propTypes = {
     attachment: ImmutablePropTypes.map.isRequired,
+    lang: PropTypes.string,
     standalone: PropTypes.bool,
     index: PropTypes.number.isRequired,
     size: PropTypes.number.isRequired,
@@ -60,14 +61,14 @@ class Item extends React.PureComponent {
     if (this.hoverToPlay()) {
       e.target.play();
     }
-  }
+  };
 
   handleMouseLeave = (e) => {
     if (this.hoverToPlay()) {
       e.target.pause();
       e.target.currentTime = 0;
     }
-  }
+  };
 
   getAutoPlay() {
     return this.props.autoplay || autoPlayGif;
@@ -91,14 +92,14 @@ class Item extends React.PureComponent {
     }
 
     e.stopPropagation();
-  }
+  };
 
   handleImageLoad = () => {
     this.setState({ loaded: true });
-  }
+  };
 
   render () {
-    const { attachment, index, size, standalone, letterbox, displayWidth, visible } = this.props;
+    const { attachment, lang, index, size, standalone, letterbox, displayWidth, visible } = this.props;
 
     let width  = 50;
     let height = 100;
@@ -154,7 +155,7 @@ class Item extends React.PureComponent {
     if (attachment.get('type') === 'unknown') {
       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}%` }}>
-          <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={attachment.get('description')} target='_blank' rel='noopener noreferrer'>
+          <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={attachment.get('description')} lang={lang} target='_blank' rel='noopener noreferrer'>
             <Blurhash
               hash={attachment.get('blurhash')}
               className='media-gallery__preview'
@@ -195,6 +196,7 @@ class Item extends React.PureComponent {
             sizes={sizes}
             alt={attachment.get('description')}
             title={attachment.get('description')}
+            lang={lang}
             style={{ objectPosition: letterbox ? null : `${x}% ${y}%` }}
             onLoad={this.handleImageLoad}
           />
@@ -209,6 +211,7 @@ class Item extends React.PureComponent {
             className={`media-gallery__item-gifv-thumbnail${letterbox ? ' letterbox' : ''}`}
             aria-label={attachment.get('description')}
             title={attachment.get('description')}
+            lang={lang}
             role='application'
             src={attachment.get('url')}
             onClick={this.handleClick}
@@ -241,7 +244,6 @@ class Item extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class MediaGallery extends React.PureComponent {
 
   static propTypes = {
@@ -251,6 +253,7 @@ class MediaGallery extends React.PureComponent {
     fullwidth: PropTypes.bool,
     hidden: PropTypes.bool,
     media: ImmutablePropTypes.list.isRequired,
+    lang: PropTypes.string,
     size: PropTypes.object,
     onOpenMedia: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
@@ -307,11 +310,11 @@ class MediaGallery extends React.PureComponent {
     } else {
       this.setState({ visible: !this.state.visible });
     }
-  }
+  };
 
   handleClick = (index) => {
     this.props.onOpenMedia(this.props.media, index);
-  }
+  };
 
   handleRef = (node) => {
     this.node = node;
@@ -319,11 +322,11 @@ class MediaGallery extends React.PureComponent {
     if (this.node) {
       this._setDimensions();
     }
-  }
+  };
 
   _setDimensions () {
     const width = this.node.offsetWidth;
- 
+
     if (width && width != this.state.width) {
       // offsetWidth triggers a layout, so only calculate when we need to
       if (this.props.cacheWidth) {
@@ -342,7 +345,7 @@ class MediaGallery extends React.PureComponent {
   }
 
   render () {
-    const { media, intl, sensitive, letterbox, fullwidth, defaultWidth, autoplay } = this.props;
+    const { media, lang, intl, sensitive, letterbox, fullwidth, defaultWidth, autoplay } = this.props;
     const { visible } = this.state;
     const size     = media.take(4).size;
     const uncached = media.every(attachment => attachment.get('type') === 'unknown');
@@ -360,13 +363,13 @@ class MediaGallery extends React.PureComponent {
     } else if (width) {
       style.height = width / (16/9);
     } else {
-      return (<div className={computedClass} ref={this.handleRef}></div>);
+      return (<div className={computedClass} ref={this.handleRef} />);
     }
 
     if (this.isStandaloneEligible()) {
-      children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
+      children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} lang={lang} displayWidth={width} visible={visible} />;
     } else {
-      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} size={size} letterbox={letterbox} displayWidth={width} visible={visible || uncached} />);
+      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} lang={lang} size={size} letterbox={letterbox} displayWidth={width} visible={visible || uncached} />);
     }
 
     if (uncached) {
@@ -402,3 +405,5 @@ class MediaGallery extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(MediaGallery);
diff --git a/app/javascript/flavours/glitch/components/missing_indicator.js b/app/javascript/flavours/glitch/components/missing_indicator.jsx
index 08e39c236..08e39c236 100644
--- a/app/javascript/flavours/glitch/components/missing_indicator.js
+++ b/app/javascript/flavours/glitch/components/missing_indicator.jsx
diff --git a/app/javascript/flavours/glitch/components/modal_root.js b/app/javascript/flavours/glitch/components/modal_root.jsx
index 056277447..5a5563e87 100644
--- a/app/javascript/flavours/glitch/components/modal_root.js
+++ b/app/javascript/flavours/glitch/components/modal_root.jsx
@@ -5,6 +5,7 @@ import { createBrowserHistory } from 'history';
 import { multiply } from 'color-blend';
 
 export default class ModalRoot extends React.PureComponent {
+
   static contextTypes = {
     router: PropTypes.object,
   };
@@ -28,7 +29,7 @@ export default class ModalRoot extends React.PureComponent {
          && !!this.props.children && !this.props.noEsc) {
       this.props.onClose();
     }
-  }
+  };
 
   handleKeyDown = (e) => {
     if (e.key === 'Tab') {
@@ -49,7 +50,7 @@ export default class ModalRoot extends React.PureComponent {
         e.preventDefault();
       }
     }
-  }
+  };
 
   componentDidMount () {
     window.addEventListener('keyup', this.handleKeyUp, false);
@@ -125,11 +126,11 @@ export default class ModalRoot extends React.PureComponent {
 
   getSiblings = () => {
     return Array(...this.node.parentElement.childNodes).filter(node => node !== this.node);
-  }
+  };
 
   setRef = ref => {
     this.node = ref;
-  }
+  };
 
   render () {
     const { children, onClose } = this.props;
diff --git a/app/javascript/flavours/glitch/components/navigation_portal.js b/app/javascript/flavours/glitch/components/navigation_portal.jsx
index 90afa1da0..9e8494179 100644
--- a/app/javascript/flavours/glitch/components/navigation_portal.js
+++ b/app/javascript/flavours/glitch/components/navigation_portal.jsx
@@ -15,7 +15,6 @@ const DefaultNavigation = () => (
   </>
 );
 
-export default @withRouter
 class NavigationPortal extends React.PureComponent {
 
   render () {
@@ -33,3 +32,5 @@ class NavigationPortal extends React.PureComponent {
   }
 
 }
+
+export default withRouter(NavigationPortal);
diff --git a/app/javascript/flavours/glitch/components/not_signed_in_indicator.js b/app/javascript/flavours/glitch/components/not_signed_in_indicator.jsx
index b440c6be2..b440c6be2 100644
--- a/app/javascript/flavours/glitch/components/not_signed_in_indicator.js
+++ b/app/javascript/flavours/glitch/components/not_signed_in_indicator.jsx
diff --git a/app/javascript/flavours/glitch/components/notification_purge_buttons.js b/app/javascript/flavours/glitch/components/notification_purge_buttons.jsx
index 3c7d67109..1d807bc23 100644
--- a/app/javascript/flavours/glitch/components/notification_purge_buttons.js
+++ b/app/javascript/flavours/glitch/components/notification_purge_buttons.jsx
@@ -19,7 +19,6 @@ const messages = defineMessages({
   btnApply : { id: 'notification_purge.btn_apply', defaultMessage: 'Clear\nselected' },
 });
 
-export default @injectIntl
 class NotificationPurgeButtons extends ImmutablePureComponent {
 
   static propTypes = {
@@ -57,3 +56,5 @@ class NotificationPurgeButtons extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(NotificationPurgeButtons);
diff --git a/app/javascript/flavours/glitch/components/permalink.js b/app/javascript/flavours/glitch/components/permalink.jsx
index 718b02115..b09b17eeb 100644
--- a/app/javascript/flavours/glitch/components/permalink.js
+++ b/app/javascript/flavours/glitch/components/permalink.jsx
@@ -24,12 +24,12 @@ export default class Permalink extends React.PureComponent {
 
       if (this.context.router) {
         e.preventDefault();
-        let state = {...this.context.router.history.location.state};
+        let state = { ...this.context.router.history.location.state };
         state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
         this.context.router.history.push(this.props.to, state);
       }
     }
-  }
+  };
 
   render () {
     const {
diff --git a/app/javascript/flavours/glitch/components/picture_in_picture_placeholder.js b/app/javascript/flavours/glitch/components/picture_in_picture_placeholder.jsx
index 01dce0a38..9d1b7f55a 100644
--- a/app/javascript/flavours/glitch/components/picture_in_picture_placeholder.js
+++ b/app/javascript/flavours/glitch/components/picture_in_picture_placeholder.jsx
@@ -6,7 +6,6 @@ import { connect } from 'react-redux';
 import { debounce } from 'lodash';
 import { FormattedMessage } from 'react-intl';
 
-export default @connect()
 class PictureInPicturePlaceholder extends React.PureComponent {
 
   static propTypes = {
@@ -22,7 +21,7 @@ class PictureInPicturePlaceholder extends React.PureComponent {
   handleClick = () => {
     const { dispatch } = this.props;
     dispatch(removePictureInPicture());
-  }
+  };
 
   setRef = c => {
     this.node = c;
@@ -30,7 +29,7 @@ class PictureInPicturePlaceholder extends React.PureComponent {
     if (this.node) {
       this._setDimensions();
     }
-  }
+  };
 
   _setDimensions () {
     const width  = this.node.offsetWidth;
@@ -59,7 +58,7 @@ class PictureInPicturePlaceholder extends React.PureComponent {
     const { height } = this.state;
 
     return (
-      <div ref={this.setRef} className='picture-in-picture-placeholder' style={{ height }} role='button' tabIndex='0' onClick={this.handleClick}>
+      <div ref={this.setRef} className='picture-in-picture-placeholder' style={{ height }} role='button' tabIndex={0} onClick={this.handleClick}>
         <Icon id='window-restore' />
         <FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' />
       </div>
@@ -67,3 +66,5 @@ class PictureInPicturePlaceholder extends React.PureComponent {
   }
 
 }
+
+export default connect()(PictureInPicturePlaceholder);
diff --git a/app/javascript/flavours/glitch/components/poll.js b/app/javascript/flavours/glitch/components/poll.jsx
index da65cd241..fb37612d9 100644
--- a/app/javascript/flavours/glitch/components/poll.js
+++ b/app/javascript/flavours/glitch/components/poll.jsx
@@ -31,7 +31,6 @@ const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
   return obj;
 }, {});
 
-export default @injectIntl
 class Poll extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -40,6 +39,7 @@ class Poll extends ImmutablePureComponent {
 
   static propTypes = {
     poll: ImmutablePropTypes.map,
+    lang: PropTypes.string,
     intl: PropTypes.object.isRequired,
     disabled: PropTypes.bool,
     refresh: PropTypes.func,
@@ -95,7 +95,7 @@ class Poll extends ImmutablePureComponent {
       tmp[value] = true;
       this.setState({ selected: tmp });
     }
-  }
+  };
 
   handleOptionChange = ({ target: { value } }) => {
     this._toggleOption(value);
@@ -107,7 +107,7 @@ class Poll extends ImmutablePureComponent {
       e.stopPropagation();
       e.preventDefault();
     }
-  }
+  };
 
   handleVote = () => {
     if (this.props.disabled) {
@@ -126,7 +126,7 @@ class Poll extends ImmutablePureComponent {
   };
 
   renderOption (option, optionIndex, showResults) {
-    const { poll, disabled, intl } = this.props;
+    const { poll, lang, disabled, intl } = this.props;
     const pollVotesCount  = poll.get('voters_count') || poll.get('votes_count');
     const percent         = pollVotesCount === 0 ? 0 : (option.get('votes_count') / pollVotesCount) * 100;
     const leading         = poll.get('options').filterNot(other => other.get('title') === option.get('title')).every(other => option.get('votes_count') >= other.get('votes_count'));
@@ -154,11 +154,12 @@ class Poll extends ImmutablePureComponent {
           {!showResults && (
             <span
               className={classNames('poll__input', { checkbox: poll.get('multiple'), active })}
-              tabIndex='0'
+              tabIndex={0}
               role={poll.get('multiple') ? 'checkbox' : 'radio'}
               onKeyPress={this.handleOptionKeyPress}
               aria-checked={active}
               aria-label={option.get('title')}
+              lang={lang}
               data-index={optionIndex}
             />
           )}
@@ -175,6 +176,7 @@ class Poll extends ImmutablePureComponent {
 
           <span
             className='poll__option__text translate'
+            lang={lang}
             dangerouslySetInnerHTML={{ __html: titleEmojified }}
           />
 
@@ -231,3 +233,5 @@ class Poll extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Poll);
diff --git a/app/javascript/flavours/glitch/components/radio_button.js b/app/javascript/flavours/glitch/components/radio_button.jsx
index 0496fa286..0496fa286 100644
--- a/app/javascript/flavours/glitch/components/radio_button.js
+++ b/app/javascript/flavours/glitch/components/radio_button.jsx
diff --git a/app/javascript/flavours/glitch/components/regeneration_indicator.js b/app/javascript/flavours/glitch/components/regeneration_indicator.jsx
index 68ce09df9..68ce09df9 100644
--- a/app/javascript/flavours/glitch/components/regeneration_indicator.js
+++ b/app/javascript/flavours/glitch/components/regeneration_indicator.jsx
diff --git a/app/javascript/mastodon/components/relative_timestamp.js b/app/javascript/flavours/glitch/components/relative_timestamp.jsx
index 512480339..e6c3e0880 100644
--- a/app/javascript/mastodon/components/relative_timestamp.js
+++ b/app/javascript/flavours/glitch/components/relative_timestamp.jsx
@@ -121,7 +121,6 @@ const timeRemainingString = (intl, date, now, timeGiven = true) => {
   return relativeTime;
 };
 
-export default @injectIntl
 class RelativeTimestamp extends React.Component {
 
   static propTypes = {
@@ -197,3 +196,5 @@ class RelativeTimestamp extends React.Component {
   }
 
 }
+
+export default injectIntl(RelativeTimestamp);
diff --git a/app/javascript/flavours/glitch/components/scrollable_list.js b/app/javascript/flavours/glitch/components/scrollable_list.jsx
index 8eb2b66d4..fc7dc989d 100644
--- a/app/javascript/flavours/glitch/components/scrollable_list.js
+++ b/app/javascript/flavours/glitch/components/scrollable_list.jsx
@@ -20,7 +20,6 @@ const mapStateToProps = (state, { scrollKey }) => {
   };
 };
 
-export default @connect(mapStateToProps, null, null, { forwardRef: true })
 class ScrollableList extends PureComponent {
 
   static contextTypes = {
@@ -137,7 +136,7 @@ class ScrollableList extends PureComponent {
     }
     this.mouseMovedRecently = false;
     this.scrollToTopOnMouseIdle = false;
-  }
+  };
 
   componentDidMount () {
     this.attachScrollListener();
@@ -154,29 +153,29 @@ class ScrollableList extends PureComponent {
     } else {
       return null;
     }
-  }
+  };
 
   getScrollTop = () => {
     return this.props.bindToDocument ? document.scrollingElement.scrollTop : this.node.scrollTop;
-  }
+  };
 
   getScrollHeight = () => {
     return this.props.bindToDocument ? document.scrollingElement.scrollHeight : this.node.scrollHeight;
-  }
+  };
 
   getClientHeight = () => {
     return this.props.bindToDocument ? document.scrollingElement.clientHeight : this.node.clientHeight;
-  }
+  };
 
   updateScrollBottom = (snapshot) => {
     const newScrollTop = this.getScrollHeight() - snapshot;
 
     this.setScrollTop(newScrollTop);
-  }
+  };
 
   cacheMediaWidth = (width) => {
     if (width && this.state.cachedMediaWidth != width) this.setState({ cachedMediaWidth: width });
-  }
+  };
 
   getSnapshotBeforeUpdate (prevProps, prevState) {
     const someItemInserted = React.Children.count(prevProps.children) > 0 &&
@@ -208,7 +207,7 @@ class ScrollableList extends PureComponent {
 
   onFullScreenChange = () => {
     this.setState({ fullscreen: isFullscreen() });
-  }
+  };
 
   attachIntersectionObserver () {
     this.intersectionObserverWrapper.connect({
@@ -256,12 +255,12 @@ class ScrollableList extends PureComponent {
 
   setRef = (c) => {
     this.node = c;
-  }
+  };
 
   handleLoadMore = e => {
     e.preventDefault();
     this.props.onLoadMore();
-  }
+  };
 
   handleLoadPending = e => {
     e.preventDefault();
@@ -273,7 +272,7 @@ class ScrollableList extends PureComponent {
     this.clearMouseIdleTimer();
     this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY);
     this.mouseMovedRecently = true;
-  }
+  };
 
   render () {
     const { children, scrollKey, trackScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props;
@@ -352,3 +351,5 @@ class ScrollableList extends PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(ScrollableList);
diff --git a/app/javascript/flavours/glitch/components/server_banner.js b/app/javascript/flavours/glitch/components/server_banner.jsx
index 36e0ff238..ba84064a8 100644
--- a/app/javascript/flavours/glitch/components/server_banner.js
+++ b/app/javascript/flavours/glitch/components/server_banner.jsx
@@ -18,8 +18,6 @@ const mapStateToProps = state => ({
   server: state.getIn(['server', 'server']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class ServerBanner extends React.PureComponent {
 
   static propTypes = {
@@ -91,3 +89,5 @@ class ServerBanner extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(ServerBanner));
diff --git a/app/javascript/flavours/glitch/components/setting_text.js b/app/javascript/flavours/glitch/components/setting_text.jsx
index 2c1b70bc3..3a21a0601 100644
--- a/app/javascript/flavours/glitch/components/setting_text.js
+++ b/app/javascript/flavours/glitch/components/setting_text.jsx
@@ -13,7 +13,7 @@ export default class SettingText extends React.PureComponent {
 
   handleChange = (e) => {
     this.props.onChange(this.props.settingPath, e.target.value);
-  }
+  };
 
   render () {
     const { settings, settingPath, label } = this.props;
diff --git a/app/javascript/flavours/glitch/components/short_number.js b/app/javascript/flavours/glitch/components/short_number.jsx
index 535c17727..535c17727 100644
--- a/app/javascript/flavours/glitch/components/short_number.js
+++ b/app/javascript/flavours/glitch/components/short_number.jsx
diff --git a/app/javascript/flavours/glitch/components/skeleton.js b/app/javascript/flavours/glitch/components/skeleton.jsx
index 6a17ffb26..6a17ffb26 100644
--- a/app/javascript/flavours/glitch/components/skeleton.js
+++ b/app/javascript/flavours/glitch/components/skeleton.jsx
diff --git a/app/javascript/flavours/glitch/components/spoilers.js b/app/javascript/flavours/glitch/components/spoilers.jsx
index 8527403c1..d1d875807 100644
--- a/app/javascript/flavours/glitch/components/spoilers.js
+++ b/app/javascript/flavours/glitch/components/spoilers.jsx
@@ -4,6 +4,7 @@ import { FormattedMessage } from 'react-intl';
 
 export default
 class Spoilers extends React.PureComponent {
+
   static propTypes = {
     spoilerText: PropTypes.string,
     children: PropTypes.node,
@@ -11,40 +12,41 @@ class Spoilers extends React.PureComponent {
 
   state = {
     hidden: true,
-  }
+  };
 
   handleSpoilerClick = () => {
     this.setState({ hidden: !this.state.hidden });
-  }
+  };
 
   render () {
     const { spoilerText, children } = this.props;
     const { hidden } = this.state;
 
-      const toggleText = hidden ?
-        <FormattedMessage
-          id='status.show_more'
-          defaultMessage='Show more'
-          key='0'
-        /> :
-        <FormattedMessage
-          id='status.show_less'
-          defaultMessage='Show less'
-          key='0'
-        />;
+    const toggleText = hidden ?
+      (<FormattedMessage
+        id='status.show_more'
+        defaultMessage='Show more'
+        key='0'
+      />) :
+      (<FormattedMessage
+        id='status.show_less'
+        defaultMessage='Show less'
+        key='0'
+      />);
 
     return ([
       <p className='spoiler__text'>
         {spoilerText}
         {' '}
-        <button tabIndex='0' className='status__content__spoiler-link' onClick={this.handleSpoilerClick}>
+        <button tabIndex={0} className='status__content__spoiler-link' onClick={this.handleSpoilerClick}>
           {toggleText}
         </button>
       </p>,
       <div className={`status__content__spoiler ${!hidden ? 'status__content__spoiler--visible' : ''}`}>
         {children}
-      </div>
+      </div>,
     ]);
   }
+
 }
 
diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.jsx
index 409ec0adc..fbb610823 100644
--- a/app/javascript/flavours/glitch/components/status.js
+++ b/app/javascript/flavours/glitch/components/status.jsx
@@ -54,9 +54,8 @@ export const defaultMediaVisibility = (status, settings) => {
   }
 
   return (displayMedia !== 'hide_all' && !status.get('sensitive') || displayMedia === 'show_all');
-}
+};
 
-export default @injectIntl
 class Status extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -117,7 +116,7 @@ class Status extends ImmutablePureComponent {
     revealBehindCW: undefined,
     showCard: false,
     forceFilter: undefined,
-  }
+  };
 
   // Avoid checking props that are functions (and whose equality will always
   // evaluate to false. See react-immutable-pure-component for usage.
@@ -132,14 +131,14 @@ class Status extends ImmutablePureComponent {
     'expanded',
     'unread',
     'pictureInPicture',
-  ]
+  ];
 
   updateOnStates = [
     'isExpanded',
     'isCollapsed',
     'showMedia',
     'forceFilter',
-  ]
+  ];
 
   //  If our settings have changed to disable collapsed statuses, then we
   //  need to make sure that we uncollapse every one. We do that by watching
@@ -224,7 +223,7 @@ class Status extends ImmutablePureComponent {
   //   -  The user has decided to collapse all notifications ('muted'
   //      statuses).
   //   -  The user has decided to collapse long statuses and the status is
-  //      over 400px (without media, or 650px with).
+  //      over the user set value (default 400 without media, or 610px with).
   //   -  The status is a reply and the user has decided to collapse all
   //      replies.
   //   -  The status contains media and the user has decided to collapse all
@@ -251,10 +250,15 @@ class Status extends ImmutablePureComponent {
     // as it could cause surprising changes when receiving notifications
     if (settings.getIn(['content_warnings', 'shared_state']) && status.get('spoiler_text').length && !status.get('hidden')) return;
 
+    let autoCollapseHeight = parseInt(autoCollapseSettings.get('height'));
+    if (status.get('media_attachments').size && !muted) {
+      autoCollapseHeight += 210;
+    }
+
     if (collapse ||
       autoCollapseSettings.get('all') ||
       (autoCollapseSettings.get('notifications') && muted) ||
-      (autoCollapseSettings.get('lengthy') && node.clientHeight > ((status.get('media_attachments').size && !muted) ? 650 : 400)) ||
+      (autoCollapseSettings.get('lengthy') && node.clientHeight > autoCollapseHeight) ||
       (autoCollapseSettings.get('reblogs') && prepend === 'reblogged_by') ||
       (autoCollapseSettings.get('replies') && status.get('in_reply_to_id', null) !== null) ||
       (autoCollapseSettings.get('media') && !(status.get('spoiler_text').length) && status.get('media_attachments').size > 0)
@@ -297,7 +301,9 @@ class Status extends ImmutablePureComponent {
     if (this.node && this.props.getScrollPosition) {
       const position = this.props.getScrollPosition();
       if (position !== null && this.node.offsetTop < position.top) {
-         requestAnimationFrame(() => { this.props.updateScrollBottom(position.height - position.top); });
+        requestAnimationFrame(() => {
+          this.props.updateScrollBottom(position.height - position.top);
+        });
       }
     }
   }
@@ -316,7 +322,7 @@ class Status extends ImmutablePureComponent {
     } else {
       this.setState({ isCollapsed: false });
     }
-  }
+  };
 
   setExpansion = (value) => {
     if (this.props.settings.getIn(['content_warnings', 'shared_state']) && this.props.status.get('hidden') === value) {
@@ -327,7 +333,7 @@ class Status extends ImmutablePureComponent {
     if (value) {
       this.setCollapsed(false);
     }
-  }
+  };
 
   //  `parseClick()` takes a click event and responds appropriately.
   //  If our status is collapsed, then clicking on it should uncollapse it.
@@ -356,17 +362,17 @@ class Status extends ImmutablePureComponent {
             status.getIn(['reblog', 'id'], status.get('id'))
           }`;
         }
-        let state = {...router.history.location.state};
+        let state = { ...router.history.location.state };
         state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
         router.history.push(destination, state);
       }
       e.preventDefault();
     }
-  }
+  };
 
   handleToggleMediaVisibility = () => {
     this.setState({ showMedia: !this.state.showMedia });
-  }
+  };
 
   handleExpandedToggle = () => {
     if (this.props.settings.getIn(['content_warnings', 'shared_state'])) {
@@ -379,11 +385,11 @@ class Status extends ImmutablePureComponent {
   handleOpenVideo = (options) => {
     const { status } = this.props;
     this.props.onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), options);
-  }
+  };
 
   handleOpenMedia = (media, index) => {
     this.props.onOpenMedia(this.props.status.get('id'), media, index);
-  }
+  };
 
   handleHotkeyOpenMedia = e => {
     const { status, onOpenMedia, onOpenVideo } = this.props;
@@ -398,84 +404,84 @@ class Status extends ImmutablePureComponent {
         onOpenMedia(statusId, status.get('media_attachments'), 0);
       }
     }
-  }
+  };
 
   handleDeployPictureInPicture = (type, mediaProps) => {
     const { deployPictureInPicture, status } = this.props;
 
     deployPictureInPicture(status, type, mediaProps);
-  }
+  };
 
   handleHotkeyReply = e => {
     e.preventDefault();
     this.props.onReply(this.props.status, this.context.router.history);
-  }
+  };
 
   handleHotkeyFavourite = (e) => {
     this.props.onFavourite(this.props.status, e);
-  }
+  };
 
   handleHotkeyBoost = e => {
     this.props.onReblog(this.props.status, e);
-  }
+  };
 
   handleHotkeyBookmark = e => {
     this.props.onBookmark(this.props.status, e);
-  }
+  };
 
   handleHotkeyMention = e => {
     e.preventDefault();
     this.props.onMention(this.props.status.get('account'), this.context.router.history);
-  }
+  };
 
   handleHotkeyOpen = () => {
-    let state = {...this.context.router.history.location.state};
+    let state = { ...this.context.router.history.location.state };
     state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
     const status = this.props.status;
     this.context.router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`, state);
-  }
+  };
 
   handleHotkeyOpenProfile = () => {
-    let state = {...this.context.router.history.location.state};
+    let state = { ...this.context.router.history.location.state };
     state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
     this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`, state);
-  }
+  };
 
   handleHotkeyMoveUp = e => {
     this.props.onMoveUp(this.props.containerId || this.props.id, e.target.getAttribute('data-featured'));
-  }
+  };
 
   handleHotkeyMoveDown = e => {
     this.props.onMoveDown(this.props.containerId || this.props.id, e.target.getAttribute('data-featured'));
-  }
+  };
 
   handleHotkeyCollapse = e => {
     if (!this.props.settings.getIn(['collapsed', 'enabled']))
       return;
 
     this.setCollapsed(!this.state.isCollapsed);
-  }
+  };
 
   handleHotkeyToggleSensitive = () => {
     this.handleToggleMediaVisibility();
-  }
+  };
 
   handleUnfilterClick = e => {
     this.setState({ forceFilter: false });
     e.preventDefault();
-  }
+  };
 
   handleFilterClick = () => {
     this.setState({ forceFilter: true });
-  }
+  };
 
   handleRef = c => {
     this.node = c;
-  }
+  };
 
   handleTranslate = () => {
     this.props.onTranslate(this.props.status);
-  }
+  };
 
   renderLoadingMediaGallery () {
     return <div className='media-gallery' style={{ height: '110px' }} />;
@@ -558,7 +564,7 @@ class Status extends ImmutablePureComponent {
     if (hidden) {
       return (
         <HotKeys handlers={handlers}>
-          <div ref={this.handleRef} className='status focusable' tabIndex='0'>
+          <div ref={this.handleRef} className='status focusable' tabIndex={0}>
             <span>{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}</span>
             <span>{status.get('content')}</span>
           </div>
@@ -575,7 +581,7 @@ class Status extends ImmutablePureComponent {
 
       return (
         <HotKeys handlers={minHandlers}>
-          <div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0' ref={this.handleRef}>
+          <div className='status__wrapper status__wrapper--filtered focusable' tabIndex={0} ref={this.handleRef}>
             <FormattedMessage id='status.filtered' defaultMessage='Filtered' />: {matchedFilters.join(', ')}.
             {' '}
             <button className='status__wrapper--filtered__button' onClick={this.handleUnfilterClick}>
@@ -623,6 +629,7 @@ class Status extends ImmutablePureComponent {
               <Component
                 src={attachment.get('url')}
                 alt={attachment.get('description')}
+                lang={status.get('language')}
                 poster={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
                 backgroundColor={attachment.getIn(['meta', 'colors', 'background'])}
                 foregroundColor={attachment.getIn(['meta', 'colors', 'foreground'])}
@@ -652,6 +659,7 @@ class Status extends ImmutablePureComponent {
               blurhash={attachment.get('blurhash')}
               src={attachment.get('url')}
               alt={attachment.get('description')}
+              lang={status.get('language')}
               inline
               sensitive={status.get('sensitive')}
               letterbox={settings.getIn(['media', 'letterbox'])}
@@ -673,6 +681,7 @@ class Status extends ImmutablePureComponent {
             {Component => (
               <Component
                 media={attachments}
+                lang={status.get('language')}
                 sensitive={status.get('sensitive')}
                 letterbox={settings.getIn(['media', 'letterbox'])}
                 fullwidth={settings.getIn(['media', 'fullwidth'])}
@@ -707,7 +716,7 @@ class Status extends ImmutablePureComponent {
     }
 
     if (status.get('poll')) {
-      contentMedia.push(<PollContainer pollId={status.get('poll')} />);
+      contentMedia.push(<PollContainer pollId={status.get('poll')} lang={status.get('language')} />);
       contentMediaIcons.push('tasks');
     }
 
@@ -760,7 +769,7 @@ class Status extends ImmutablePureComponent {
           style={isCollapsed && background ? { backgroundImage: `url(${background})` } : null}
           {...selectorAttribs}
           ref={handleRef}
-          tabIndex='0'
+          tabIndex={0}
           data-featured={featured ? 'true' : null}
           aria-label={textForScreenReader(intl, status, rebloggedByText, !status.get('hidden'))}
         >
@@ -820,3 +829,5 @@ class Status extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Status);
diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.jsx
index baf61000a..091d0b24b 100644
--- a/app/javascript/flavours/glitch/components/status_action_bar.js
+++ b/app/javascript/flavours/glitch/components/status_action_bar.jsx
@@ -46,7 +46,6 @@ const messages = defineMessages({
   openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
 });
 
-export default @injectIntl
 class StatusActionBar extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -86,7 +85,7 @@ class StatusActionBar extends ImmutablePureComponent {
     'showReplyCount',
     'withCounters',
     'withDismiss',
-  ]
+  ];
 
   handleReplyClick = () => {
     const { signedIn } = this.context.identity;
@@ -96,14 +95,14 @@ class StatusActionBar extends ImmutablePureComponent {
     } else {
       this.props.onInteractionModal('reply', this.props.status);
     }
-  }
+  };
 
   handleShareClick = () => {
     navigator.share({
       text: this.props.status.get('search_index'),
       url: this.props.status.get('url'),
     });
-  }
+  };
 
   handleFavouriteClick = (e) => {
     const { signedIn } = this.context.identity;
@@ -113,7 +112,7 @@ class StatusActionBar extends ImmutablePureComponent {
     } else {
       this.props.onInteractionModal('favourite', this.props.status);
     }
-  }
+  };
 
   handleReblogClick = e => {
     const { signedIn } = this.context.identity;
@@ -123,78 +122,78 @@ class StatusActionBar extends ImmutablePureComponent {
     } else {
       this.props.onInteractionModal('reblog', this.props.status);
     }
-  }
+  };
 
   handleBookmarkClick = (e) => {
     this.props.onBookmark(this.props.status, e);
-  }
+  };
 
   handleDeleteClick = () => {
     this.props.onDelete(this.props.status, this.context.router.history);
-  }
+  };
 
   handleRedraftClick = () => {
     this.props.onDelete(this.props.status, this.context.router.history, true);
-  }
+  };
 
   handleEditClick = () => {
     this.props.onEdit(this.props.status, this.context.router.history);
-  }
+  };
 
   handlePinClick = () => {
     this.props.onPin(this.props.status);
-  }
+  };
 
   handleMentionClick = () => {
     this.props.onMention(this.props.status.get('account'), this.context.router.history);
-  }
+  };
 
   handleDirectClick = () => {
     this.props.onDirect(this.props.status.get('account'), this.context.router.history);
-  }
+  };
 
   handleMuteClick = () => {
     this.props.onMute(this.props.status.get('account'));
-  }
+  };
 
   handleBlockClick = () => {
     this.props.onBlock(this.props.status);
-  }
+  };
 
   handleOpen = () => {
-    let state = {...this.context.router.history.location.state};
+    let state = { ...this.context.router.history.location.state };
     if (state.mastodonModalKey) {
       this.context.router.history.replace(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}`, { mastodonBackSteps: (state.mastodonBackSteps || 0) + 1 });
     } else {
       state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
       this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}`, state);
     }
-  }
+  };
 
   handleEmbed = () => {
     this.props.onEmbed(this.props.status);
-  }
+  };
 
   handleReport = () => {
     this.props.onReport(this.props.status);
-  }
+  };
 
   handleConversationMuteClick = () => {
     this.props.onMuteConversation(this.props.status);
-  }
+  };
 
   handleCopy = () => {
     const url = this.props.status.get('url');
     navigator.clipboard.writeText(url);
-  }
+  };
 
   handleHideClick = () => {
     this.props.onFilter();
-  }
+  };
 
   handleFilterClick = () => {
     this.props.onAddFilter(this.props.status);
-  }
+  };
 
   render () {
     const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
@@ -339,3 +338,5 @@ class StatusActionBar extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(StatusActionBar);
diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.jsx
index c59f42220..e66df588a 100644
--- a/app/javascript/flavours/glitch/components/status_content.js
+++ b/app/javascript/flavours/glitch/components/status_content.jsx
@@ -3,16 +3,17 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import { FormattedMessage, injectIntl } from 'react-intl';
 import Permalink from './permalink';
+import { connect } from 'react-redux';
 import classnames from 'classnames';
 import Icon from 'flavours/glitch/components/icon';
-import { autoPlayGif, languages as preloadedLanguages, translationEnabled } from 'flavours/glitch/initial_state';
+import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state';
 import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
 
 const textMatchesTarget = (text, origin, host) => {
   return (text === origin || text === host
           || text.startsWith(origin + '/') || text.startsWith(host + '/')
           || 'www.' + text === host || ('www.' + text).startsWith(host + '/'));
-}
+};
 
 const isLinkMisleading = (link) => {
   let linkTextParts = [];
@@ -99,7 +100,10 @@ class TranslateButton extends React.PureComponent {
 
 }
 
-export default @injectIntl
+const mapStateToProps = state => ({
+  languages: state.getIn(['server', 'translationLanguages', 'items']),
+});
+
 class StatusContent extends React.PureComponent {
 
   static contextTypes = {
@@ -120,6 +124,7 @@ class StatusContent extends React.PureComponent {
     onUpdate: PropTypes.func,
     tagLinks: PropTypes.bool,
     rewriteMentions: PropTypes.string,
+    languages: ImmutablePropTypes.map,
     intl: PropTypes.object,
   };
 
@@ -168,8 +173,8 @@ class StatusContent extends React.PureComponent {
         link.setAttribute('title', link.href);
         link.classList.add('unhandled-link');
 
-      link.setAttribute('target', '_blank');
-      link.setAttribute('rel', 'noopener nofollow noreferrer');
+        link.setAttribute('target', '_blank');
+        link.setAttribute('rel', 'noopener nofollow noreferrer');
 
         try {
           if (tagLinks && isLinkMisleading(link)) {
@@ -210,7 +215,7 @@ class StatusContent extends React.PureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -223,7 +228,7 @@ class StatusContent extends React.PureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   componentDidMount () {
     this._updateStatusLinks();
@@ -238,13 +243,13 @@ class StatusContent extends React.PureComponent {
     if (this.props.collapsed) {
       if (this.props.parseClick) this.props.parseClick(e);
     }
-  }
+  };
 
   onMentionClick = (mention, e) => {
     if (this.props.parseClick) {
       this.props.parseClick(e, `/@${mention.get('acct')}`);
     }
-  }
+  };
 
   onHashtagClick = (hashtag, e) => {
     hashtag = hashtag.replace(/^#/, '');
@@ -252,11 +257,11 @@ class StatusContent extends React.PureComponent {
     if (this.props.parseClick) {
       this.props.parseClick(e, `/tags/${hashtag}`);
     }
-  }
+  };
 
   handleMouseDown = (e) => {
     this.startXY = [e.clientX, e.clientY];
-  }
+  };
 
   handleMouseUp = (e) => {
     const { parseClick, disabled } = this.props;
@@ -281,7 +286,7 @@ class StatusContent extends React.PureComponent {
     }
 
     this.startXY = null;
-  }
+  };
 
   handleSpoilerClick = (e) => {
     e.preventDefault();
@@ -291,15 +296,15 @@ class StatusContent extends React.PureComponent {
     } else {
       this.setState({ hidden: !this.state.hidden });
     }
-  }
+  };
 
   handleTranslate = () => {
     this.props.onTranslate();
-  }
+  };
 
   setContentsRef = (c) => {
     this.contentsNode = c;
-  }
+  };
 
   render () {
     const {
@@ -315,7 +320,9 @@ class StatusContent extends React.PureComponent {
     } = this.props;
 
     const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
-    const renderTranslate = translationEnabled && this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && status.get('language') !== null && intl.locale !== status.get('language');
+    const contentLocale = intl.locale.replace(/[_-].*/, '');
+    const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
+    const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && targetLanguages?.includes(contentLocale);
 
     const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') };
     const spoilerContent = { __html: status.get('spoilerHtml') };
@@ -380,7 +387,7 @@ class StatusContent extends React.PureComponent {
       }
 
       return (
-        <div className={classNames} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
+        <div className={classNames} tabIndex={0} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
           <p
             style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}
           >
@@ -417,14 +424,14 @@ class StatusContent extends React.PureComponent {
           className={classNames}
           onMouseDown={this.handleMouseDown}
           onMouseUp={this.handleMouseUp}
-          tabIndex='0'
+          tabIndex={0}
         >
           <div
             ref={this.setContentsRef}
             key={`contents-${tagLinks}-${rewriteMentions}`}
             dangerouslySetInnerHTML={content}
             className='status__content__text translate'
-            tabIndex='0'
+            tabIndex={0}
             onMouseEnter={this.handleMouseEnter}
             onMouseLeave={this.handleMouseLeave}
             lang={lang}
@@ -438,14 +445,14 @@ class StatusContent extends React.PureComponent {
       return (
         <div
           className='status__content'
-          tabIndex='0'
+          tabIndex={0}
         >
           <div
             ref={this.setContentsRef}
             key={`contents-${tagLinks}`}
             className='status__content__text translate'
             dangerouslySetInnerHTML={content}
-            tabIndex='0'
+            tabIndex={0}
             onMouseEnter={this.handleMouseEnter}
             onMouseLeave={this.handleMouseLeave}
             lang={lang}
@@ -459,3 +466,5 @@ class StatusContent extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(StatusContent));
diff --git a/app/javascript/flavours/glitch/components/status_header.js b/app/javascript/flavours/glitch/components/status_header.jsx
index 990dea536..21d8b4212 100644
--- a/app/javascript/flavours/glitch/components/status_header.js
+++ b/app/javascript/flavours/glitch/components/status_header.jsx
@@ -21,12 +21,12 @@ export default class StatusHeader extends React.PureComponent {
   handleClick = (acct, e) => {
     const { parseClick } = this.props;
     parseClick(e, `/@${acct}`);
-  }
+  };
 
   handleAccountClick = (e) => {
     const { status } = this.props;
     this.handleClick(status.getIn(['account', 'acct']), e);
-  }
+  };
 
   //  Rendering.
   render () {
diff --git a/app/javascript/flavours/glitch/components/status_icons.js b/app/javascript/flavours/glitch/components/status_icons.jsx
index 71ffb2e56..3baff2206 100644
--- a/app/javascript/flavours/glitch/components/status_icons.js
+++ b/app/javascript/flavours/glitch/components/status_icons.jsx
@@ -40,7 +40,6 @@ LanguageIcon.propTypes = {
   language: PropTypes.string.isRequired,
 };
 
-export default @injectIntl
 class StatusIcons extends React.PureComponent {
 
   static propTypes = {
@@ -60,22 +59,22 @@ class StatusIcons extends React.PureComponent {
       setCollapsed(!collapsed);
       e.preventDefault();
     }
-  }
+  };
 
   mediaIconTitleText (mediaIcon) {
     const { intl } = this.props;
 
     switch (mediaIcon) {
-      case 'link':
-        return intl.formatMessage(messages.previewCard);
-      case 'picture-o':
-        return intl.formatMessage(messages.pictures);
-      case 'tasks':
-        return intl.formatMessage(messages.poll);
-      case 'video-camera':
-        return intl.formatMessage(messages.video);
-      case 'music':
-        return intl.formatMessage(messages.audio);
+    case 'link':
+      return intl.formatMessage(messages.previewCard);
+    case 'picture-o':
+      return intl.formatMessage(messages.pictures);
+    case 'tasks':
+      return intl.formatMessage(messages.poll);
+    case 'video-camera':
+      return intl.formatMessage(messages.video);
+    case 'music':
+      return intl.formatMessage(messages.audio);
     }
   }
 
@@ -143,3 +142,5 @@ class StatusIcons extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(StatusIcons);
diff --git a/app/javascript/flavours/glitch/components/status_list.js b/app/javascript/flavours/glitch/components/status_list.jsx
index 0d843a27d..a9c06f693 100644
--- a/app/javascript/flavours/glitch/components/status_list.js
+++ b/app/javascript/flavours/glitch/components/status_list.jsx
@@ -35,7 +35,7 @@ export default class StatusList extends ImmutablePureComponent {
 
   getFeaturedStatusCount = () => {
     return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0;
-  }
+  };
 
   getCurrentStatusIndex = (id, featured) => {
     if (featured) {
@@ -43,21 +43,21 @@ export default class StatusList extends ImmutablePureComponent {
     } else {
       return this.props.statusIds.indexOf(id) + this.getFeaturedStatusCount();
     }
-  }
+  };
 
   handleMoveUp = (id, featured) => {
     const elementIndex = this.getCurrentStatusIndex(id, featured) - 1;
     this._selectChild(elementIndex, true);
-  }
+  };
 
   handleMoveDown = (id, featured) => {
     const elementIndex = this.getCurrentStatusIndex(id, featured) + 1;
     this._selectChild(elementIndex, false);
-  }
+  };
 
   handleLoadOlder = debounce(() => {
     this.props.onLoadMore(this.props.statusIds.size > 0 ? this.props.statusIds.last() : undefined);
-  }, 300, { leading: true })
+  }, 300, { leading: true });
 
   _selectChild (index, align_top) {
     const container = this.node.node;
@@ -75,7 +75,7 @@ export default class StatusList extends ImmutablePureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   render () {
     const { statusIds, featuredStatusIds, onLoadMore, timelineId, ...other }  = this.props;
diff --git a/app/javascript/flavours/glitch/components/status_prepend.js b/app/javascript/flavours/glitch/components/status_prepend.jsx
index f82533062..8c4343b04 100644
--- a/app/javascript/flavours/glitch/components/status_prepend.js
+++ b/app/javascript/flavours/glitch/components/status_prepend.jsx
@@ -18,7 +18,7 @@ export default class StatusPrepend extends React.PureComponent {
   handleClick = (e) => {
     const { account, parseClick } = this.props;
     parseClick(e, `/@${account.get('acct')}`);
-  }
+  };
 
   Message = () => {
     const { type, account } = this.props;
@@ -98,7 +98,7 @@ export default class StatusPrepend extends React.PureComponent {
       );
     }
     return null;
-  }
+  };
 
   render () {
     const { Message } = this;
@@ -126,7 +126,7 @@ export default class StatusPrepend extends React.PureComponent {
     case 'update':
       iconId = 'pencil';
       break;
-    };
+    }
 
     return !type ? null : (
       <aside className={type === 'reblogged_by' || type === 'featured' ? 'status__prepend' : 'notification__message'}>
diff --git a/app/javascript/flavours/glitch/components/status_visibility_icon.js b/app/javascript/flavours/glitch/components/status_visibility_icon.jsx
index 07d56c7a8..fcedfbfd6 100644
--- a/app/javascript/flavours/glitch/components/status_visibility_icon.js
+++ b/app/javascript/flavours/glitch/components/status_visibility_icon.jsx
@@ -12,7 +12,6 @@ const messages = defineMessages({
   direct: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
 });
 
-export default @injectIntl
 class VisibilityIcon extends ImmutablePureComponent {
 
   static propTypes = {
@@ -49,3 +48,5 @@ class VisibilityIcon extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(VisibilityIcon);
diff --git a/app/javascript/flavours/glitch/components/timeline_hint.js b/app/javascript/flavours/glitch/components/timeline_hint.jsx
index fb55a62cc..fb55a62cc 100644
--- a/app/javascript/flavours/glitch/components/timeline_hint.js
+++ b/app/javascript/flavours/glitch/components/timeline_hint.jsx
diff --git a/app/javascript/flavours/glitch/containers/account_container.js b/app/javascript/flavours/glitch/containers/account_container.jsx
index 5b57d730f..5b57d730f 100644
--- a/app/javascript/flavours/glitch/containers/account_container.js
+++ b/app/javascript/flavours/glitch/containers/account_container.jsx
diff --git a/app/javascript/flavours/glitch/containers/admin_component.js b/app/javascript/flavours/glitch/containers/admin_component.jsx
index 64dabac8b..64dabac8b 100644
--- a/app/javascript/flavours/glitch/containers/admin_component.js
+++ b/app/javascript/flavours/glitch/containers/admin_component.jsx
diff --git a/app/javascript/flavours/glitch/containers/compose_container.js b/app/javascript/flavours/glitch/containers/compose_container.jsx
index 1e49b89a0..1e49b89a0 100644
--- a/app/javascript/flavours/glitch/containers/compose_container.js
+++ b/app/javascript/flavours/glitch/containers/compose_container.jsx
diff --git a/app/javascript/flavours/glitch/containers/domain_container.js b/app/javascript/flavours/glitch/containers/domain_container.jsx
index e92e102ab..e92e102ab 100644
--- a/app/javascript/flavours/glitch/containers/domain_container.js
+++ b/app/javascript/flavours/glitch/containers/domain_container.jsx
diff --git a/app/javascript/flavours/glitch/containers/mastodon.js b/app/javascript/flavours/glitch/containers/mastodon.jsx
index dd7623a81..dd7623a81 100644
--- a/app/javascript/flavours/glitch/containers/mastodon.js
+++ b/app/javascript/flavours/glitch/containers/mastodon.jsx
diff --git a/app/javascript/flavours/glitch/containers/media_container.js b/app/javascript/flavours/glitch/containers/media_container.jsx
index f1e8534aa..f245e2f0a 100644
--- a/app/javascript/flavours/glitch/containers/media_container.js
+++ b/app/javascript/flavours/glitch/containers/media_container.jsx
@@ -39,7 +39,7 @@ export default class MediaContainer extends PureComponent {
     document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
 
     this.setState({ media, index });
-  }
+  };
 
   handleOpenVideo = (options) => {
     const { components } = this.props;
@@ -50,11 +50,11 @@ export default class MediaContainer extends PureComponent {
     document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
 
     this.setState({ media: mediaList, options });
-  }
+  };
 
   handleCloseMedia = () => {
     document.body.classList.remove('with-modals--active');
-    document.documentElement.style.marginRight = 0;
+    document.documentElement.style.marginRight = '0';
 
     this.setState({
       media: null,
@@ -63,11 +63,11 @@ export default class MediaContainer extends PureComponent {
       backgroundColor: null,
       options: null,
     });
-  }
+  };
 
   setBackgroundColor = color => {
     this.setState({ backgroundColor: color });
-  }
+  };
 
   render () {
     const { locale, components } = this.props;
diff --git a/app/javascript/flavours/glitch/containers/status_container.js b/app/javascript/flavours/glitch/containers/status_container.js
index 645919ebe..9873725e4 100644
--- a/app/javascript/flavours/glitch/containers/status_container.js
+++ b/app/javascript/flavours/glitch/containers/status_container.js
@@ -52,6 +52,8 @@ const messages = defineMessages({
   redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? You will lose all replies, boosts and favourites to it.' },
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
+  editConfirm: { id: 'confirmations.edit.confirm', defaultMessage: 'Edit' },
+  editMessage: { id: 'confirmations.edit.message', defaultMessage: 'Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
   unfilterConfirm: { id: 'confirmations.unfilter.confirm', defaultMessage: 'Show' },
   author: { id: 'confirmations.unfilter.author', defaultMessage: 'Author' },
   matchingFilters: { id: 'confirmations.unfilter.filters', defaultMessage: 'Matching {count, plural, one {filter} other {filters}}' },
@@ -183,7 +185,18 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
   },
 
   onEdit (status, history) {
-    dispatch(editStatus(status.get('id'), history));
+    dispatch((_, getState) => {
+      let state = getState();
+      if (state.getIn(['compose', 'text']).trim().length !== 0) {
+        dispatch(openModal('CONFIRM', {
+          message: intl.formatMessage(messages.editMessage),
+          confirm: intl.formatMessage(messages.editConfirm),
+          onConfirm: () => dispatch(editStatus(status.get('id'), history)),
+        }));
+      } else {
+        dispatch(editStatus(status.get('id'), history));
+      }
+    });
   },
 
   onTranslate (status) {
diff --git a/app/javascript/flavours/glitch/extra_polyfills.js b/app/javascript/flavours/glitch/extra_polyfills.js
index 6e8004f07..e6c69de8b 100644
--- a/app/javascript/flavours/glitch/extra_polyfills.js
+++ b/app/javascript/flavours/glitch/extra_polyfills.js
@@ -1,3 +1,2 @@
 import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
-import 'intersection-observer';
 import 'requestidlecallback';
diff --git a/app/javascript/flavours/glitch/features/about/index.js b/app/javascript/flavours/glitch/features/about/index.jsx
index 57f295ea9..67d5afbdd 100644
--- a/app/javascript/flavours/glitch/features/about/index.js
+++ b/app/javascript/flavours/glitch/features/about/index.jsx
@@ -59,7 +59,7 @@ class Section extends React.PureComponent {
     const { collapsed } = this.state;
 
     this.setState({ collapsed: !collapsed }, () => onOpen && onOpen());
-  }
+  };
 
   render () {
     const { title, children } = this.props;
@@ -67,7 +67,7 @@ class Section extends React.PureComponent {
 
     return (
       <div className={classNames('about__section', { active: !collapsed })}>
-        <div className='about__section__title' role='button' tabIndex='0' onClick={this.handleClick}>
+        <div className='about__section__title' role='button' tabIndex={0} onClick={this.handleClick}>
           <Icon id={collapsed ? 'chevron-right' : 'chevron-down'} fixedWidth /> {title}
         </div>
 
@@ -80,8 +80,6 @@ class Section extends React.PureComponent {
 
 }
 
-export default @connect(mapStateToProps)
-@injectIntl
 class About extends React.PureComponent {
 
   static propTypes = {
@@ -106,7 +104,7 @@ class About extends React.PureComponent {
   handleDomainBlocksOpen = () => {
     const { dispatch } = this.props;
     dispatch(fetchDomainBlocks());
-  }
+  };
 
   render () {
     const { multiColumn, intl, server, extendedDescription, domainBlocks } = this.props;
@@ -218,3 +216,5 @@ class About extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(About));
diff --git a/app/javascript/flavours/glitch/features/account/components/account_note.js b/app/javascript/flavours/glitch/features/account/components/account_note.jsx
index 6e5ed7703..8ab00f6d4 100644
--- a/app/javascript/flavours/glitch/features/account/components/account_note.js
+++ b/app/javascript/flavours/glitch/features/account/components/account_note.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   placeholder: { id: 'account_note.glitch_placeholder', defaultMessage: 'No comment provided' },
 });
 
-export default @injectIntl
 class Header extends ImmutablePureComponent {
 
   static propTypes = {
@@ -41,7 +40,7 @@ class Header extends ImmutablePureComponent {
     } else if (e.keyCode === 27) {
       this.props.onCancelAccountNote();
     }
-  }
+  };
 
   render () {
     const { account, accountNote, isEditing, isSubmitting, intl } = this.props;
@@ -54,11 +53,11 @@ class Header extends ImmutablePureComponent {
     if (isEditing) {
       action_buttons = (
         <div className='account__header__account-note__buttons'>
-          <button className='icon-button' tabIndex='0' onClick={this.props.onCancelAccountNote} disabled={isSubmitting}>
+          <button className='icon-button' tabIndex={0} onClick={this.props.onCancelAccountNote} disabled={isSubmitting}>
             <Icon id='times' size={15} /> <FormattedMessage id='account_note.cancel' defaultMessage='Cancel' />
           </button>
           <div className='flex-spacer' />
-          <button className='icon-button' tabIndex='0' onClick={this.props.onSaveAccountNote} disabled={isSubmitting}>
+          <button className='icon-button' tabIndex={0} onClick={this.props.onSaveAccountNote} disabled={isSubmitting}>
             <Icon id='check' size={15} /> <FormattedMessage id='account_note.save' defaultMessage='Save' />
           </button>
         </div>
@@ -66,7 +65,7 @@ class Header extends ImmutablePureComponent {
     } else {
       action_buttons = (
         <div className='account__header__account-note__buttons'>
-          <button className='icon-button' tabIndex='0' onClick={this.props.onEditAccountNote} disabled={isSubmitting}>
+          <button className='icon-button' tabIndex={0} onClick={this.props.onEditAccountNote} disabled={isSubmitting}>
             <Icon id='pencil' size={15} /> <FormattedMessage id='account_note.edit' defaultMessage='Edit' />
           </button>
         </div>
@@ -102,3 +101,5 @@ class Header extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Header);
diff --git a/app/javascript/flavours/glitch/features/account/components/action_bar.js b/app/javascript/flavours/glitch/features/account/components/action_bar.jsx
index ce0584124..e32bc0141 100644
--- a/app/javascript/flavours/glitch/features/account/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/account/components/action_bar.jsx
@@ -8,7 +8,6 @@ import { me, isStaff } from 'flavours/glitch/initial_state';
 import { profileLink, accountAdminLink } from 'flavours/glitch/utils/backend_links';
 import Icon from 'flavours/glitch/components/icon';
 
-export default @injectIntl
 class ActionBar extends React.PureComponent {
 
   static propTypes = {
@@ -21,7 +20,7 @@ class ActionBar extends React.PureComponent {
       return false;
     }
     return !location.pathname.match(/\/(followers|following)\/?$/);
-  }
+  };
 
   render () {
     const { account, intl } = this.props;
@@ -32,7 +31,7 @@ class ActionBar extends React.PureComponent {
           <div className='account__disclaimer'>
             <Icon id='info-circle' fixedWidth /> <FormattedMessage
               id='account.suspended_disclaimer_full'
-              defaultMessage="This user has been suspended by a moderator."
+              defaultMessage='This user has been suspended by a moderator.'
             />
           </div>
         </div>
@@ -83,3 +82,5 @@ class ActionBar extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ActionBar);
diff --git a/app/javascript/flavours/glitch/features/account/components/featured_tags.js b/app/javascript/flavours/glitch/features/account/components/featured_tags.jsx
index d646b08b2..42e0a8d2f 100644
--- a/app/javascript/flavours/glitch/features/account/components/featured_tags.js
+++ b/app/javascript/flavours/glitch/features/account/components/featured_tags.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   empty: { id: 'account.featured_tags.last_status_never', defaultMessage: 'No posts' },
 });
 
-export default @injectIntl
 class FeaturedTags extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -51,3 +50,5 @@ class FeaturedTags extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(FeaturedTags);
diff --git a/app/javascript/flavours/glitch/features/account/components/follow_request_note.js b/app/javascript/flavours/glitch/features/account/components/follow_request_note.jsx
index 73c1737a6..73c1737a6 100644
--- a/app/javascript/flavours/glitch/features/account/components/follow_request_note.js
+++ b/app/javascript/flavours/glitch/features/account/components/follow_request_note.jsx
diff --git a/app/javascript/flavours/glitch/features/account/components/header.js b/app/javascript/flavours/glitch/features/account/components/header.jsx
index ec4a192bc..6f918abcf 100644
--- a/app/javascript/flavours/glitch/features/account/components/header.js
+++ b/app/javascript/flavours/glitch/features/account/components/header.jsx
@@ -45,6 +45,7 @@ const messages = defineMessages({
   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
+  followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
   blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
   domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
   mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
@@ -75,7 +76,6 @@ const dateFormatOptions = {
   minute: '2-digit',
 };
 
-export default @injectIntl
 class Header extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -108,7 +108,7 @@ class Header extends ImmutablePureComponent {
 
   openEditProfile = () => {
     window.open(profileLink, '_blank');
-  }
+  };
 
   handleMouseEnter = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -121,7 +121,7 @@ class Header extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -134,14 +134,14 @@ class Header extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   handleAvatarClick = e => {
     if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
       e.preventDefault();
       this.props.onOpenAvatar();
     }
-  }
+  };
 
   handleShare = () => {
     const { account } = this.props;
@@ -152,7 +152,7 @@ class Header extends ImmutablePureComponent {
     }).catch((e) => {
       if (e.name !== 'AbortError') console.error(e);
     });
-  }
+  };
 
   render () {
     const { account, hidden, intl, domain } = this.props;
@@ -176,8 +176,7 @@ class Header extends ImmutablePureComponent {
 
     if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
       info.push(<span className='relationship-tag'><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span>);
-    }
-    else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
+    } else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
       info.push(<span className='relationship-tag'><FormattedMessage id='account.blocked' defaultMessage='Blocked' /></span>);
     }
 
@@ -188,7 +187,7 @@ class Header extends ImmutablePureComponent {
     }
 
     if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) {
-      bellBtn = <IconButton icon='bell-o' size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
+      bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
     }
 
     if (me !== account.get('id')) {
@@ -245,6 +244,7 @@ class Header extends ImmutablePureComponent {
       menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
       menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
       menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
+      menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
       menu.push(null);
       menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
       menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
@@ -319,6 +319,11 @@ class Header extends ImmutablePureComponent {
       badge = null;
     }
 
+    let role = null;
+    if (account.getIn(['roles', 0])) {
+      role = (<div key='role' className={`account-role user-role-${account.getIn(['roles', 0, 'id'])}`}>{account.getIn(['roles', 0, 'name'])}</div>);
+    }
+
     return (
       <div className={classNames('account__header', { inactive: !!account.get('moved') })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
         {!(suspended || hidden || account.get('moved')) && account.getIn(['relationship', 'requested_by']) && <FollowRequestNoteContainer account={account} />}
@@ -335,6 +340,7 @@ class Header extends ImmutablePureComponent {
           <div className='account__header__tabs'>
             <a className='avatar' href={account.get('avatar')} rel='noopener noreferrer' target='_blank' onClick={this.handleAvatarClick}>
               <Avatar account={suspended || hidden ? undefined : account} size={90} />
+              {role}
             </a>
 
             {!suspended && (
@@ -370,7 +376,7 @@ class Header extends ImmutablePureComponent {
                     {fields.map((pair, i) => (
                       <dl key={i}>
                         <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
-   
+
                         <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) })}><Icon id='check' className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} className='translate' />
                         </dd>
@@ -396,3 +402,5 @@ class Header extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Header);
diff --git a/app/javascript/flavours/glitch/features/account/components/profile_column_header.js b/app/javascript/flavours/glitch/features/account/components/profile_column_header.jsx
index 17c08e375..62a459fff 100644
--- a/app/javascript/flavours/glitch/features/account/components/profile_column_header.js
+++ b/app/javascript/flavours/glitch/features/account/components/profile_column_header.jsx
@@ -7,7 +7,6 @@ const messages = defineMessages({
   profile: { id: 'column_header.profile', defaultMessage: 'Profile' },
 });
 
-export default @injectIntl
 class ProfileColumnHeader extends React.PureComponent {
 
   static propTypes = {
@@ -31,3 +30,5 @@ class ProfileColumnHeader extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ProfileColumnHeader);
diff --git a/app/javascript/flavours/glitch/features/account/navigation.js b/app/javascript/flavours/glitch/features/account/navigation.jsx
index edae38ce5..b8b8e54de 100644
--- a/app/javascript/flavours/glitch/features/account/navigation.js
+++ b/app/javascript/flavours/glitch/features/account/navigation.jsx
@@ -19,7 +19,6 @@ const mapStateToProps = (state, { match: { params: { acct } } }) => {
   };
 };
 
-export default @connect(mapStateToProps)
 class AccountNavigation extends React.PureComponent {
 
   static propTypes = {
@@ -50,3 +49,5 @@ class AccountNavigation extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AccountNavigation);
diff --git a/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js b/app/javascript/flavours/glitch/features/account_gallery/components/media_item.jsx
index f20ee685e..5fd84996b 100644
--- a/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js
+++ b/app/javascript/flavours/glitch/features/account_gallery/components/media_item.jsx
@@ -22,20 +22,20 @@ export default class MediaItem extends ImmutablePureComponent {
 
   handleImageLoad = () => {
     this.setState({ loaded: true });
-  }
+  };
 
   handleMouseEnter = e => {
     if (this.hoverToPlay()) {
       e.target.play();
     }
-  }
+  };
 
   handleMouseLeave = e => {
     if (this.hoverToPlay()) {
       e.target.pause();
       e.target.currentTime = 0;
     }
-  }
+  };
 
   hoverToPlay () {
     return !autoPlayGif && ['gifv', 'video'].indexOf(this.props.attachment.get('type')) !== -1;
@@ -51,7 +51,7 @@ export default class MediaItem extends ImmutablePureComponent {
         this.setState({ visible: true });
       }
     }
-  }
+  };
 
   render () {
     const { attachment, displayWidth } = this.props;
@@ -76,6 +76,7 @@ export default class MediaItem extends ImmutablePureComponent {
           <img
             src={attachment.get('preview_url') || attachment.getIn(['account', 'avatar_static'])}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             onLoad={this.handleImageLoad}
           />
         );
@@ -95,6 +96,7 @@ export default class MediaItem extends ImmutablePureComponent {
           <img
             src={attachment.get('preview_url')}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             style={{ objectPosition: `${x}% ${y}%` }}
             onLoad={this.handleImageLoad}
           />
@@ -105,6 +107,7 @@ export default class MediaItem extends ImmutablePureComponent {
             className='media-gallery__item-gifv-thumbnail'
             aria-label={attachment.get('description')}
             title={attachment.get('description')}
+            lang={status.get('language')}
             role='application'
             src={attachment.get('url')}
             onMouseEnter={this.handleMouseEnter}
diff --git a/app/javascript/flavours/glitch/features/account_gallery/index.js b/app/javascript/flavours/glitch/features/account_gallery/index.jsx
index 638224bc0..6914bcba7 100644
--- a/app/javascript/flavours/glitch/features/account_gallery/index.js
+++ b/app/javascript/flavours/glitch/features/account_gallery/index.jsx
@@ -45,7 +45,7 @@ class LoadMoreMedia extends ImmutablePureComponent {
 
   handleLoadMore = () => {
     this.props.onLoadMore(this.props.maxId);
-  }
+  };
 
   render () {
     return (
@@ -58,7 +58,6 @@ class LoadMoreMedia extends ImmutablePureComponent {
 
 }
 
-export default @connect(mapStateToProps)
 class AccountGallery extends ImmutablePureComponent {
 
   static propTypes = {
@@ -109,13 +108,13 @@ class AccountGallery extends ImmutablePureComponent {
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   handleScrollToBottom = () => {
     if (this.props.hasMore) {
       this.handleLoadMore(this.props.attachments.size > 0 ? this.props.attachments.last().getIn(['status', 'id']) : undefined);
     }
-  }
+  };
 
   handleScroll = e => {
     const { scrollTop, scrollHeight, clientHeight } = e.target;
@@ -124,7 +123,7 @@ class AccountGallery extends ImmutablePureComponent {
     if (150 > offset && !this.props.isLoading) {
       this.handleScrollToBottom();
     }
-  }
+  };
 
   handleLoadMore = maxId => {
     this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, { maxId }));
@@ -133,11 +132,11 @@ class AccountGallery extends ImmutablePureComponent {
   handleLoadOlder = e => {
     e.preventDefault();
     this.handleScrollToBottom();
-  }
+  };
 
   setColumnRef = c => {
     this.column = c;
-  }
+  };
 
   handleOpenMedia = attachment => {
     const { dispatch } = this.props;
@@ -153,13 +152,13 @@ class AccountGallery extends ImmutablePureComponent {
 
       dispatch(openModal('MEDIA', { media, index, statusId }));
     }
-  }
+  };
 
   handleRef = c => {
     if (c) {
       this.setState({ width: c.offsetWidth });
     }
-  }
+  };
 
   render () {
     const { attachments, isLoading, hasMore, isAccount, multiColumn, suspended } = this.props;
@@ -223,3 +222,5 @@ class AccountGallery extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AccountGallery);
diff --git a/app/javascript/flavours/glitch/features/account_timeline/components/header.js b/app/javascript/flavours/glitch/features/account_timeline/components/header.jsx
index 90c4c9d51..eec065b43 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/components/header.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/components/header.jsx
@@ -37,35 +37,35 @@ export default class Header extends ImmutablePureComponent {
 
   handleFollow = () => {
     this.props.onFollow(this.props.account);
-  }
+  };
 
   handleBlock = () => {
     this.props.onBlock(this.props.account);
-  }
+  };
 
   handleMention = () => {
     this.props.onMention(this.props.account, this.context.router.history);
-  }
+  };
 
   handleDirect = () => {
     this.props.onDirect(this.props.account, this.context.router.history);
-  }
+  };
 
   handleReport = () => {
     this.props.onReport(this.props.account);
-  }
+  };
 
   handleReblogToggle = () => {
     this.props.onReblogToggle(this.props.account);
-  }
+  };
 
   handleNotifyToggle = () => {
     this.props.onNotifyToggle(this.props.account);
-  }
+  };
 
   handleMute = () => {
     this.props.onMute(this.props.account);
-  }
+  };
 
   handleBlockDomain = () => {
     const domain = this.props.account.get('acct').split('@')[1];
@@ -73,7 +73,7 @@ export default class Header extends ImmutablePureComponent {
     if (!domain) return;
 
     this.props.onBlockDomain(domain);
-  }
+  };
 
   handleUnblockDomain = () => {
     const domain = this.props.account.get('acct').split('@')[1];
@@ -81,31 +81,31 @@ export default class Header extends ImmutablePureComponent {
     if (!domain) return;
 
     this.props.onUnblockDomain(domain);
-  }
+  };
 
   handleEndorseToggle = () => {
     this.props.onEndorseToggle(this.props.account);
-  }
+  };
 
   handleAddToList = () => {
     this.props.onAddToList(this.props.account);
-  }
+  };
 
   handleEditAccountNote = () => {
     this.props.onEditAccountNote(this.props.account);
-  }
+  };
 
   handleChangeLanguages = () => {
     this.props.onChangeLanguages(this.props.account);
-  }
+  };
 
   handleInteractionModal = () => {
     this.props.onInteractionModal(this.props.account);
-  }
+  };
 
   handleOpenAvatar = () => {
     this.props.onOpenAvatar(this.props.account);
-  }
+  };
 
   render () {
     const { account, hidden, hideTabs } = this.props;
diff --git a/app/javascript/flavours/glitch/features/account_timeline/components/limited_account_hint.js b/app/javascript/flavours/glitch/features/account_timeline/components/limited_account_hint.jsx
index ca0e60672..c622b7607 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/components/limited_account_hint.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/components/limited_account_hint.jsx
@@ -14,13 +14,12 @@ const mapDispatchToProps = (dispatch, { accountId }) => ({
 
 });
 
-export default @connect(() => {}, mapDispatchToProps)
 class LimitedAccountHint extends React.PureComponent {
 
   static propTypes = {
     accountId: PropTypes.string.isRequired,
     reveal: PropTypes.func,
-  }
+  };
 
   render () {
     const { reveal } = this.props;
@@ -34,3 +33,5 @@ class LimitedAccountHint extends React.PureComponent {
   }
 
 }
+
+export default connect(() => {}, mapDispatchToProps)(LimitedAccountHint);
diff --git a/app/javascript/flavours/glitch/features/account_timeline/components/moved_note.js b/app/javascript/flavours/glitch/features/account_timeline/components/moved_note.jsx
index 308407e94..40bdc4034 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/components/moved_note.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/components/moved_note.jsx
@@ -21,13 +21,13 @@ export default class MovedNote extends ImmutablePureComponent {
   handleAccountClick = e => {
     if (e.button === 0) {
       e.preventDefault();
-      let state = {...this.context.router.history.location.state};
+      let state = { ...this.context.router.history.location.state };
       state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
       this.context.router.history.push(`/@${this.props.to.get('acct')}`, state);
     }
 
     e.stopPropagation();
-  }
+  };
 
   render () {
     const { from, to } = this.props;
diff --git a/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js b/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.jsx
index 25bcd0119..3ec47cf2f 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/containers/header_container.jsx
@@ -12,7 +12,7 @@ import {
 } from 'flavours/glitch/actions/accounts';
 import {
   mentionCompose,
-  directCompose
+  directCompose,
 } from 'flavours/glitch/actions/compose';
 import { initMuteModal } from 'flavours/glitch/actions/mutes';
 import { initBlockModal } from 'flavours/glitch/actions/blocks';
diff --git a/app/javascript/flavours/glitch/features/account_timeline/index.js b/app/javascript/flavours/glitch/features/account_timeline/index.jsx
index b79082f00..38361b1ca 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/index.jsx
@@ -62,7 +62,6 @@ RemoteHint.propTypes = {
   url: PropTypes.string.isRequired,
 };
 
-export default @connect(mapStateToProps)
 class AccountTimeline extends ImmutablePureComponent {
 
   static propTypes = {
@@ -140,15 +139,15 @@ class AccountTimeline extends ImmutablePureComponent {
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   handleLoadMore = maxId => {
     this.props.dispatch(expandAccountTimeline(this.props.accountId, { maxId, withReplies: this.props.withReplies, tagged: this.props.params.tagged }));
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   render () {
     const { accountId, statusIds, featuredStatusIds, isLoading, hasMore, suspended, isAccount, hidden, multiColumn, remote, remoteUrl } = this.props;
@@ -207,3 +206,5 @@ class AccountTimeline extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AccountTimeline);
diff --git a/app/javascript/flavours/glitch/features/audio/index.js b/app/javascript/flavours/glitch/features/audio/index.jsx
index 014a0a213..556a74ac4 100644
--- a/app/javascript/flavours/glitch/features/audio/index.js
+++ b/app/javascript/flavours/glitch/features/audio/index.jsx
@@ -1,12 +1,10 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
-import { formatTime } from 'flavours/glitch/features/video';
+import { formatTime, getPointerPosition, fileNameFromURL } from 'flavours/glitch/features/video';
 import Icon from 'flavours/glitch/components/icon';
 import classNames from 'classnames';
-import { throttle } from 'lodash';
-import { getPointerPosition, fileNameFromURL } from 'flavours/glitch/features/video';
-import { debounce } from 'lodash';
+import { throttle, debounce } from 'lodash';
 import Visualizer from './visualizer';
 import { displayMedia, useBlurhash } from 'flavours/glitch/initial_state';
 import Blurhash from 'flavours/glitch/components/blurhash';
@@ -24,12 +22,12 @@ const messages = defineMessages({
 const TICK_SIZE = 10;
 const PADDING   = 180;
 
-export default @injectIntl
 class Audio extends React.PureComponent {
 
   static propTypes = {
     src: PropTypes.string.isRequired,
     alt: PropTypes.string,
+    lang: PropTypes.string,
     poster: PropTypes.string,
     duration: PropTypes.number,
     width: PropTypes.number,
@@ -59,7 +57,7 @@ class Audio extends React.PureComponent {
     duration: null,
     paused: true,
     muted: false,
-    volume: 0.5,
+    volume: 1,
     dragging: false,
     revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
   };
@@ -75,13 +73,13 @@ class Audio extends React.PureComponent {
     if (this.player) {
       this._setDimensions();
     }
-  }
+  };
 
   _pack() {
     return {
       src: this.props.src,
-      volume: this.audio.volume,
-      muted: this.audio.muted,
+      volume: this.state.volume,
+      muted: this.state.muted,
       currentTime: this.audio.currentTime,
       poster: this.props.poster,
       backgroundColor: this.props.backgroundColor,
@@ -107,26 +105,27 @@ class Audio extends React.PureComponent {
 
   setSeekRef = c => {
     this.seek = c;
-  }
+  };
 
   setVolumeRef = c => {
     this.volume = c;
-  }
+  };
 
   setAudioRef = c => {
     this.audio = c;
 
     if (this.audio) {
-      this.setState({ volume: this.audio.volume, muted: this.audio.muted });
+      this.audio.volume = 1;
+      this.audio.muted = false;
     }
-  }
+  };
 
   setCanvasRef = c => {
     this.canvas = c;
 
     this.visualizer.setCanvas(c);
-  }
- 
+  };
+
   componentDidMount () {
     window.addEventListener('scroll', this.handleScroll);
     window.addEventListener('resize', this.handleResize, { passive: true });
@@ -168,7 +167,7 @@ class Audio extends React.PureComponent {
     } else {
       this.setState({ paused: true }, () => this.audio.pause());
     }
-  }
+  };
 
   handleResize = debounce(() => {
     if (this.player) {
@@ -186,7 +185,7 @@ class Audio extends React.PureComponent {
     }
 
     this._renderCanvas();
-  }
+  };
 
   handlePause = () => {
     this.setState({ paused: true });
@@ -194,7 +193,7 @@ class Audio extends React.PureComponent {
     if (this.audioContext) {
       this.audioContext.suspend();
     }
-  }
+  };
 
   handleProgress = () => {
     const lastTimeRange = this.audio.buffered.length - 1;
@@ -202,15 +201,17 @@ class Audio extends React.PureComponent {
     if (lastTimeRange > -1) {
       this.setState({ buffer: Math.ceil(this.audio.buffered.end(lastTimeRange) / this.audio.duration * 100) });
     }
-  }
+  };
 
   toggleMute = () => {
     const muted = !this.state.muted;
 
     this.setState({ muted }, () => {
-      this.audio.muted = muted;
+      if (this.gainNode) {
+        this.gainNode.gain.value = muted ? 0 : this.state.volume;
+      }
     });
-  }
+  };
 
   toggleReveal = () => {
     if (this.props.onToggleVisibility) {
@@ -218,7 +219,7 @@ class Audio extends React.PureComponent {
     } else {
       this.setState({ revealed: !this.state.revealed });
     }
-  }
+  };
 
   handleVolumeMouseDown = e => {
     document.addEventListener('mousemove', this.handleMouseVolSlide, true);
@@ -230,14 +231,14 @@ class Audio extends React.PureComponent {
 
     e.preventDefault();
     e.stopPropagation();
-  }
+  };
 
   handleVolumeMouseUp = () => {
     document.removeEventListener('mousemove', this.handleMouseVolSlide, true);
     document.removeEventListener('mouseup', this.handleVolumeMouseUp, true);
     document.removeEventListener('touchmove', this.handleMouseVolSlide, true);
     document.removeEventListener('touchend', this.handleVolumeMouseUp, true);
-  }
+  };
 
   handleMouseDown = e => {
     document.addEventListener('mousemove', this.handleMouseMove, true);
@@ -251,7 +252,7 @@ class Audio extends React.PureComponent {
 
     e.preventDefault();
     e.stopPropagation();
-  }
+  };
 
   handleMouseUp = () => {
     document.removeEventListener('mousemove', this.handleMouseMove, true);
@@ -261,7 +262,7 @@ class Audio extends React.PureComponent {
 
     this.setState({ dragging: false });
     this.audio.play();
-  }
+  };
 
   handleMouseMove = throttle(e => {
     const { x } = getPointerPosition(this.seek, e);
@@ -279,14 +280,16 @@ class Audio extends React.PureComponent {
       currentTime: this.audio.currentTime,
       duration: this.audio.duration,
     });
-  }
+  };
 
   handleMouseVolSlide = throttle(e => {
     const { x } = getPointerPosition(this.volume, e);
 
     if(!isNaN(x)) {
       this.setState({ volume: x }, () => {
-        this.audio.volume = x;
+        if (this.gainNode) {
+          this.gainNode.gain.value = this.state.muted ? 0 : x;
+        }
       });
     }
   }, 15);
@@ -312,41 +315,38 @@ class Audio extends React.PureComponent {
 
   handleMouseEnter = () => {
     this.setState({ hovered: true });
-  }
+  };
 
   handleMouseLeave = () => {
     this.setState({ hovered: false });
-  }
+  };
 
   handleLoadedData = () => {
-    const { autoPlay, currentTime, volume, muted } = this.props;
+    const { autoPlay, currentTime } = this.props;
 
     if (currentTime) {
       this.audio.currentTime = currentTime;
     }
 
-    if (volume !== undefined) {
-      this.audio.volume = volume;
-    }
-
-    if (muted !== undefined) {
-      this.audio.muted = muted;
-    }
-
     if (autoPlay) {
       this.togglePlay();
     }
-  }
+  };
 
   _initAudioContext () {
     const AudioContext = window.AudioContext || window.webkitAudioContext;
     const context      = new AudioContext();
     const source       = context.createMediaElementSource(this.audio);
+    const gainNode     = context.createGain();
+
+    gainNode.gain.value = this.state.muted ? 0 : this.state.volume;
 
     this.visualizer.setAudioContext(context, source);
-    source.connect(context.destination);
+    source.connect(gainNode);
+    gainNode.connect(context.destination);
 
     this.audioContext = context;
+    this.gainNode = gainNode;
   }
 
   handleDownload = () => {
@@ -365,7 +365,7 @@ class Audio extends React.PureComponent {
     }).catch(err => {
       console.error(err);
     });
-  }
+  };
 
   _renderCanvas () {
     requestAnimationFrame(() => {
@@ -436,7 +436,7 @@ class Audio extends React.PureComponent {
       e.stopPropagation();
       this.togglePlay();
     }
-  }
+  };
 
   handleKeyDown = e => {
     switch(e.key) {
@@ -461,10 +461,10 @@ class Audio extends React.PureComponent {
       this.seekBy(10);
       break;
     }
-  }
+  };
 
   render () {
-    const { src, intl, alt, editable, autoPlay, sensitive, blurhash } = this.props;
+    const { src, intl, alt, lang, editable, autoPlay, sensitive, blurhash } = this.props;
     const { paused, muted, volume, currentTime, duration, buffer, dragging, revealed } = this.state;
     const progress = Math.min((currentTime / duration) * 100, 100);
 
@@ -476,7 +476,7 @@ class Audio extends React.PureComponent {
     }
 
     return (
-      <div className={classNames('audio-player', { editable, inactive: !revealed })} ref={this.setPlayerRef} style={{ backgroundColor: this._getBackgroundColor(), color: this._getForegroundColor(), width: '100%', height: this.props.fullscreen ? '100%' : (this.state.height || this.props.height) }} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} tabIndex='0' onKeyDown={this.handleKeyDown}>
+      <div className={classNames('audio-player', { editable, inactive: !revealed })} ref={this.setPlayerRef} style={{ backgroundColor: this._getBackgroundColor(), color: this._getForegroundColor(), width: '100%', height: this.props.fullscreen ? '100%' : (this.state.height || this.props.height) }} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} tabIndex={0} onKeyDown={this.handleKeyDown}>
 
         <Blurhash
           hash={blurhash}
@@ -499,7 +499,7 @@ class Audio extends React.PureComponent {
 
         <canvas
           role='button'
-          tabIndex='0'
+          tabIndex={0}
           className='audio-player__canvas'
           width={this.state.width}
           height={this.state.height}
@@ -509,6 +509,7 @@ class Audio extends React.PureComponent {
           onKeyDown={this.handleAudioKeyDown}
           title={alt}
           aria-label={alt}
+          lang={lang}
         />
 
         <div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
@@ -531,7 +532,7 @@ class Audio extends React.PureComponent {
 
           <span
             className={classNames('video-player__seek__handle', { active: dragging })}
-            tabIndex='0'
+            tabIndex={0}
             style={{ left: `${progress}%`, backgroundColor: this._getAccentColor() }}
             onKeyDown={this.handleAudioKeyDown}
           />
@@ -548,7 +549,7 @@ class Audio extends React.PureComponent {
 
                 <span
                   className='video-player__volume__handle'
-                  tabIndex='0'
+                  tabIndex={0}
                   style={{ left: `${volume * 100}%`, backgroundColor: this._getAccentColor() }}
                 />
               </div>
@@ -573,3 +574,5 @@ class Audio extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(Audio);
diff --git a/app/javascript/flavours/glitch/features/blocks/index.js b/app/javascript/flavours/glitch/features/blocks/index.jsx
index 4461bd14d..461dac2ec 100644
--- a/app/javascript/flavours/glitch/features/blocks/index.js
+++ b/app/javascript/flavours/glitch/features/blocks/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['user_lists', 'blocks', 'isLoading'], true),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Blocks extends ImmutablePureComponent {
 
   static propTypes = {
@@ -77,3 +75,5 @@ class Blocks extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Blocks));
diff --git a/app/javascript/flavours/glitch/features/bookmarked_statuses/index.js b/app/javascript/flavours/glitch/features/bookmarked_statuses/index.jsx
index 8978ac5fc..90d8fd0ef 100644
--- a/app/javascript/flavours/glitch/features/bookmarked_statuses/index.js
+++ b/app/javascript/flavours/glitch/features/bookmarked_statuses/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'bookmarks', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Bookmarks extends ImmutablePureComponent {
 
   static propTypes = {
@@ -48,24 +46,24 @@ class Bookmarks extends ImmutablePureComponent {
     } else {
       dispatch(addColumn('BOOKMARKS', {}));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = debounce(() => {
     this.props.dispatch(expandBookmarkedStatuses());
-  }, 300, { leading: true })
+  }, 300, { leading: true });
 
   render () {
     const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
@@ -106,3 +104,5 @@ class Bookmarks extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Bookmarks));
diff --git a/app/javascript/flavours/glitch/features/closed_registrations_modal/index.js b/app/javascript/flavours/glitch/features/closed_registrations_modal/index.jsx
index cb91636cb..1f17ea9cf 100644
--- a/app/javascript/flavours/glitch/features/closed_registrations_modal/index.js
+++ b/app/javascript/flavours/glitch/features/closed_registrations_modal/index.jsx
@@ -9,7 +9,6 @@ const mapStateToProps = state => ({
   message: state.getIn(['server', 'server', 'registrations', 'message']),
 });
 
-export default @connect(mapStateToProps)
 class ClosedRegistrationsModal extends ImmutablePureComponent {
 
   componentDidMount () {
@@ -72,4 +71,6 @@ class ClosedRegistrationsModal extends ImmutablePureComponent {
     );
   }
 
-};
+}
+
+export default connect(mapStateToProps)(ClosedRegistrationsModal);
diff --git a/app/javascript/flavours/glitch/features/community_timeline/components/column_settings.js b/app/javascript/flavours/glitch/features/community_timeline/components/column_settings.jsx
index 69a4699ac..0ea874e95 100644
--- a/app/javascript/flavours/glitch/features/community_timeline/components/column_settings.js
+++ b/app/javascript/flavours/glitch/features/community_timeline/components/column_settings.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   settings: { id: 'home.settings', defaultMessage: 'Column settings' },
 });
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -39,3 +38,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/flavours/glitch/features/community_timeline/containers/column_settings_container.js b/app/javascript/flavours/glitch/features/community_timeline/containers/column_settings_container.js
index b892f08ad..eac1c4bba 100644
--- a/app/javascript/flavours/glitch/features/community_timeline/containers/column_settings_container.js
+++ b/app/javascript/flavours/glitch/features/community_timeline/containers/column_settings_container.js
@@ -12,7 +12,7 @@ const mapStateToProps = (state, { columnId }) => {
     settings: (uuid && index >= 0) ? columns.get(index).get('params') : state.getIn(['settings', 'community']),
   };
 };
- 
+
 const mapDispatchToProps = (dispatch, { columnId }) => {
   return {
     onChange (key, checked) {
diff --git a/app/javascript/flavours/glitch/features/community_timeline/index.js b/app/javascript/flavours/glitch/features/community_timeline/index.jsx
index 67bf54875..8f3e10fe9 100644
--- a/app/javascript/flavours/glitch/features/community_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/community_timeline/index.jsx
@@ -32,8 +32,6 @@ const mapStateToProps = (state, { columnId }) => {
   };
 };
 
-export default @connect(mapStateToProps)
-@injectIntl
 class CommunityTimeline extends React.PureComponent {
 
   static defaultProps = {
@@ -63,16 +61,16 @@ class CommunityTimeline extends React.PureComponent {
     } else {
       dispatch(addColumn('COMMUNITY', { other: { onlyMedia } }));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   componentDidMount () {
     const { dispatch, onlyMedia } = this.props;
@@ -112,13 +110,13 @@ class CommunityTimeline extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = maxId => {
     const { dispatch, onlyMedia } = this.props;
 
     dispatch(expandCommunityTimeline({ maxId, onlyMedia }));
-  }
+  };
 
   render () {
     const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
@@ -162,3 +160,5 @@ class CommunityTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(CommunityTimeline));
diff --git a/app/javascript/flavours/glitch/features/compose/components/action_bar.js b/app/javascript/flavours/glitch/features/compose/components/action_bar.jsx
index 267c0ba69..af1f02efc 100644
--- a/app/javascript/flavours/glitch/features/compose/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/compose/components/action_bar.jsx
@@ -12,6 +12,7 @@ const messages = defineMessages({
   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
+  followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
   blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
   domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
   mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
@@ -20,7 +21,6 @@ const messages = defineMessages({
   bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
 });
 
-export default @injectIntl
 class ActionBar extends React.PureComponent {
 
   static propTypes = {
@@ -31,7 +31,7 @@ class ActionBar extends React.PureComponent {
 
   handleLogout = () => {
     this.props.onLogout();
-  }
+  };
 
   render () {
     const { intl } = this.props;
@@ -46,6 +46,7 @@ class ActionBar extends React.PureComponent {
     menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
     menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' });
     menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
+    menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
     menu.push(null);
     menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
     menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
@@ -64,3 +65,5 @@ class ActionBar extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ActionBar);
diff --git a/app/javascript/flavours/glitch/features/compose/components/autosuggest_account.js b/app/javascript/flavours/glitch/features/compose/components/autosuggest_account.jsx
index fb9bb5035..fb9bb5035 100644
--- a/app/javascript/flavours/glitch/features/compose/components/autosuggest_account.js
+++ b/app/javascript/flavours/glitch/features/compose/components/autosuggest_account.jsx
diff --git a/app/javascript/flavours/glitch/features/compose/components/character_counter.js b/app/javascript/flavours/glitch/features/compose/components/character_counter.jsx
index 0ecfc9141..0ecfc9141 100644
--- a/app/javascript/flavours/glitch/features/compose/components/character_counter.js
+++ b/app/javascript/flavours/glitch/features/compose/components/character_counter.jsx
diff --git a/app/javascript/flavours/glitch/features/compose/components/compose_form.js b/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx
index 0462c7c4b..973a17a1a 100644
--- a/app/javascript/flavours/glitch/features/compose/components/compose_form.js
+++ b/app/javascript/flavours/glitch/features/compose/components/compose_form.jsx
@@ -21,14 +21,17 @@ import { length } from 'stringz';
 
 const messages = defineMessages({
   placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' },
-  missingDescriptionMessage: {  id: 'confirmations.missing_media_description.message',
-                                defaultMessage: 'At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.' },
-  missingDescriptionConfirm: {  id: 'confirmations.missing_media_description.confirm',
-                                defaultMessage: 'Send anyway' },
+  missingDescriptionMessage: {
+    id: 'confirmations.missing_media_description.message',
+    defaultMessage: 'At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.',
+  },
+  missingDescriptionConfirm: {
+    id: 'confirmations.missing_media_description.confirm',
+    defaultMessage: 'Send anyway',
+  },
   spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
 });
 
-export default @injectIntl
 class ComposeForm extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -61,6 +64,7 @@ class ComposeForm extends ImmutablePureComponent {
     anyMedia: PropTypes.bool,
     isInReply: PropTypes.bool,
     singleColumn: PropTypes.bool,
+    lang: PropTypes.string,
 
     advancedOptions: ImmutablePropTypes.map,
     layout: PropTypes.string,
@@ -82,22 +86,22 @@ class ComposeForm extends ImmutablePureComponent {
 
   handleChange = (e) => {
     this.props.onChange(e.target.value);
-  }
+  };
 
   getFulltextForCharacterCounting = () => {
     return [
       this.props.spoiler? this.props.spoilerText: '',
       countableText(this.props.text),
-      this.props.advancedOptions && this.props.advancedOptions.get('do_not_federate') ? ' 👁️' : ''
+      this.props.advancedOptions && this.props.advancedOptions.get('do_not_federate') ? ' 👁️' : '',
     ].join('');
-  }
+  };
 
   canSubmit = () => {
     const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
     const fulltext = this.getFulltextForCharacterCounting();
 
     return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > maxChars || (!fulltext.trim().length && !anyMedia));
-  }
+  };
 
   handleSubmit = (overriddenVisibility = null) => {
     const {
@@ -128,7 +132,7 @@ class ComposeForm extends ImmutablePureComponent {
       }
       onSubmit(this.context.router ? this.context.router.history : null);
     }
-  }
+  };
 
   //  Changes the text value of the spoiler.
   handleChangeSpoiler = ({ target: { value } }) => {
@@ -136,7 +140,7 @@ class ComposeForm extends ImmutablePureComponent {
     if (onChangeSpoilerText) {
       onChangeSpoilerText(value);
     }
-  }
+  };
 
   setRef = c => {
     this.composeForm = c;
@@ -149,7 +153,7 @@ class ComposeForm extends ImmutablePureComponent {
     if (onPickEmoji) {
       onPickEmoji(selectionStart, data);
     }
-  }
+  };
 
   //  Handles the secondary submit button.
   handleSecondarySubmit = () => {
@@ -157,16 +161,16 @@ class ComposeForm extends ImmutablePureComponent {
       sideArm,
     } = this.props;
     this.handleSubmit(sideArm === 'none' ? null : sideArm);
-  }
+  };
 
   //  Selects a suggestion from the autofill.
   onSuggestionSelected = (tokenStart, token, value) => {
     this.props.onSuggestionSelected(tokenStart, token, value, ['text']);
-  }
+  };
 
   onSpoilerSuggestionSelected = (tokenStart, token, value) => {
     this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']);
-  }
+  };
 
   handleKeyDown = (e) => {
     if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
@@ -176,21 +180,21 @@ class ComposeForm extends ImmutablePureComponent {
     if (e.keyCode == 13 && e.altKey) {
       this.handleSecondarySubmit();
     }
-  }
+  };
 
   //  Sets a reference to the textarea.
   setAutosuggestTextarea = (textareaComponent) => {
     if (textareaComponent) {
       this.textarea = textareaComponent.textarea;
     }
-  }
+  };
 
   //  Sets a reference to the CW field.
   handleRefSpoilerText = (spoilerComponent) => {
     if (spoilerComponent) {
       this.spoilerText = spoilerComponent.input;
     }
-  }
+  };
 
   handleFocus = () => {
     if (this.composeForm && !this.props.singleColumn) {
@@ -199,7 +203,7 @@ class ComposeForm extends ImmutablePureComponent {
         this.composeForm.scrollIntoView();
       }
     }
-  }
+  };
 
   componentDidMount () {
     this._updateFocusAndSelection({ });
@@ -216,7 +220,7 @@ class ComposeForm extends ImmutablePureComponent {
   //      - Replying to more than one user, selects any usernames past
   //        the first; this provides a convenient shortcut to drop
   //        everyone else from the conversation.
-   _updateFocusAndSelection = (prevProps) => {
+  _updateFocusAndSelection = (prevProps) => {
     const {
       textarea,
       spoilerText,
@@ -270,7 +274,7 @@ class ComposeForm extends ImmutablePureComponent {
         }
       }
     }
-  }
+  };
 
 
   render () {
@@ -325,7 +329,9 @@ class ComposeForm extends ImmutablePureComponent {
             searchTokens={[':']}
             id='glitch.composer.spoiler.input'
             className='spoiler-input__input'
+            lang={this.props.lang}
             autoFocus={false}
+            spellCheck
           />
         </div>
 
@@ -343,6 +349,7 @@ class ComposeForm extends ImmutablePureComponent {
           onSuggestionSelected={this.onSuggestionSelected}
           onPaste={onPaste}
           autoFocus={!showSearch && !isMobile(window.innerWidth, layout)}
+          lang={this.props.lang}
         >
           <EmojiPickerDropdown onPickEmoji={handleEmojiPick} />
           <TextareaIcons advancedOptions={advancedOptions} />
@@ -381,3 +388,5 @@ class ComposeForm extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(ComposeForm);
diff --git a/app/javascript/flavours/glitch/features/compose/components/dropdown.js b/app/javascript/flavours/glitch/features/compose/components/dropdown.jsx
index d98b311d9..fe4ab36f5 100644
--- a/app/javascript/flavours/glitch/features/compose/components/dropdown.js
+++ b/app/javascript/flavours/glitch/features/compose/components/dropdown.jsx
@@ -64,7 +64,7 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
       }
       this.setState({ open: !this.state.open, openedViaKeyboard: type !== 'click' });
     }
-  }
+  };
 
   handleKeyDown = (e) => {
     switch (e.key) {
@@ -72,13 +72,13 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
       this.handleClose();
       break;
     }
-  }
+  };
 
   handleMouseDown = () => {
     if (!this.state.open) {
       this.activeElement = document.activeElement;
     }
-  }
+  };
 
   handleButtonKeyDown = (e) => {
     switch(e.key) {
@@ -87,7 +87,7 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
       this.handleMouseDown();
       break;
     }
-  }
+  };
 
   handleKeyPress = (e) => {
     switch(e.key) {
@@ -98,14 +98,14 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
       e.preventDefault();
       break;
     }
-  }
+  };
 
   handleClose = () => {
     if (this.state.open && this.activeElement) {
       this.activeElement.focus({ preventScroll: true });
     }
     this.setState({ open: false });
-  }
+  };
 
   handleItemClick = (e) => {
     const {
@@ -151,22 +151,22 @@ export default class ComposerOptionsDropdown extends React.PureComponent {
           ...rest,
           active: value && name === value,
           name,
-        })
+        }),
       ),
     };
-  }
+  };
 
   setTargetRef = c => {
     this.target = c;
-  }
+  };
 
   findTarget = () => {
     return this.target;
-  }
+  };
 
   handleOverlayEnter = (state) => {
     this.setState({ placement: state.placement });
-  }
+  };
 
   //  Rendering.
   render () {
diff --git a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.jsx
index c4895dfd0..1ccccad31 100644
--- a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.jsx
@@ -44,12 +44,12 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
     }
-  }
+  };
 
   //  Stores our node in `this.node`.
   setRef = (node) => {
     this.node = node;
-  }
+  };
 
   //  On mounting, we add our listeners.
   componentDidMount () {
@@ -84,7 +84,7 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
       onClose();
     }
     onChange(name);
-  }
+  };
 
   // Handle changes differently whether the dropdown is a list of options or actions
   handleChange = (name) => {
@@ -93,7 +93,7 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
     } else {
       this.setState({ value: name });
     }
-  }
+  };
 
   handleKeyDown = (e) => {
     const index = Number(e.currentTarget.getAttribute('data-index'));
@@ -135,11 +135,11 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
       e.preventDefault();
       e.stopPropagation();
     }
-  }
+  };
 
   setFocusRef = c => {
     this.focusedItem = c;
-  }
+  };
 
   renderItem = (item, i) => {
     const { name, icon, meta, text } = item;
@@ -169,7 +169,7 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
         onClick={this.handleClick}
         onKeyDown={this.handleKeyDown}
         role='option'
-        tabIndex='0'
+        tabIndex={0}
         key={name}
         data-index={i}
         ref={active ? this.setFocusRef : null}
@@ -177,7 +177,7 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
         {contents}
       </div>
     );
-  }
+  };
 
   //  Rendering.
   render () {
diff --git a/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.js b/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.jsx
index 38c735551..1b8991f00 100644
--- a/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.js
+++ b/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.jsx
@@ -58,7 +58,7 @@ class ModifierPickerMenu extends React.PureComponent {
 
   handleClick = e => {
     this.props.onSelect(e.currentTarget.getAttribute('data-index') * 1);
-  }
+  };
 
   componentWillReceiveProps (nextProps) {
     if (nextProps.active) {
@@ -76,7 +76,7 @@ class ModifierPickerMenu extends React.PureComponent {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
     }
-  }
+  };
 
   attachListeners () {
     document.addEventListener('click', this.handleDocumentClick, false);
@@ -90,7 +90,7 @@ class ModifierPickerMenu extends React.PureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   render () {
     const { active } = this.props;
@@ -125,12 +125,12 @@ class ModifierPicker extends React.PureComponent {
     } else {
       this.props.onOpen();
     }
-  }
+  };
 
   handleSelect = modifier => {
     this.props.onChange(modifier);
     this.props.onClose();
-  }
+  };
 
   render () {
     const { active, modifier } = this.props;
@@ -145,8 +145,7 @@ class ModifierPicker extends React.PureComponent {
 
 }
 
-@injectIntl
-class EmojiPickerMenu extends React.PureComponent {
+class EmojiPickerMenuImpl extends React.PureComponent {
 
   static propTypes = {
     custom_emojis: ImmutablePropTypes.list,
@@ -175,7 +174,7 @@ class EmojiPickerMenu extends React.PureComponent {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
     }
-  }
+  };
 
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
@@ -199,7 +198,7 @@ class EmojiPickerMenu extends React.PureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   getI18n = () => {
     const { intl } = this.props;
@@ -220,7 +219,7 @@ class EmojiPickerMenu extends React.PureComponent {
         custom: intl.formatMessage(messages.custom),
       },
     };
-  }
+  };
 
   handleClick = (emoji, event) => {
     if (!emoji.native) {
@@ -230,19 +229,19 @@ class EmojiPickerMenu extends React.PureComponent {
       this.props.onClose();
     }
     this.props.onPick(emoji);
-  }
+  };
 
   handleModifierOpen = () => {
     this.setState({ modifierOpen: true });
-  }
+  };
 
   handleModifierClose = () => {
     this.setState({ modifierOpen: false });
-  }
+  };
 
   handleModifierChange = modifier => {
     this.props.onSkinTone(modifier);
-  }
+  };
 
   render () {
     const { loading, style, intl, custom_emojis, skinTone, frequentlyUsedEmojis } = this.props;
@@ -307,7 +306,8 @@ class EmojiPickerMenu extends React.PureComponent {
 
 }
 
-export default @injectIntl
+const EmojiPickerMenu = injectIntl(EmojiPickerMenuImpl);
+
 class EmojiPickerDropdown extends React.PureComponent {
 
   static propTypes = {
@@ -327,7 +327,7 @@ class EmojiPickerDropdown extends React.PureComponent {
 
   setRef = (c) => {
     this.dropdown = c;
-  }
+  };
 
   onShowDropdown = () => {
     this.setState({ active: true });
@@ -344,11 +344,11 @@ class EmojiPickerDropdown extends React.PureComponent {
         this.setState({ loading: false, active: false });
       });
     }
-  }
+  };
 
   onHideDropdown = () => {
     this.setState({ active: false });
-  }
+  };
 
   onToggle = (e) => {
     if (!this.state.loading && (!e.key || e.key === 'Enter')) {
@@ -358,21 +358,21 @@ class EmojiPickerDropdown extends React.PureComponent {
         this.onShowDropdown(e);
       }
     }
-  }
+  };
 
   handleKeyDown = e => {
     if (e.key === 'Escape') {
       this.onHideDropdown();
     }
-  }
+  };
 
   setTargetRef = c => {
     this.target = c;
-  }
+  };
 
   findTarget = () => {
     return this.target;
-  }
+  };
 
   render () {
     const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props;
@@ -411,3 +411,5 @@ class EmojiPickerDropdown extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(EmojiPickerDropdown);
diff --git a/app/javascript/flavours/glitch/features/compose/components/header.js b/app/javascript/flavours/glitch/features/compose/components/header.jsx
index 7ecb573ab..764fcec5e 100644
--- a/app/javascript/flavours/glitch/features/compose/components/header.js
+++ b/app/javascript/flavours/glitch/features/compose/components/header.jsx
@@ -45,8 +45,8 @@ const messages = defineMessages({
   },
 });
 
-export default @injectIntl
 class Header extends ImmutablePureComponent {
+
   static propTypes = {
     columns: ImmutablePropTypes.list,
     unreadNotifications: PropTypes.number,
@@ -63,7 +63,7 @@ class Header extends ImmutablePureComponent {
     this.props.onLogout();
 
     return false;
-  }
+  };
 
   render () {
     const { intl, columns, unreadNotifications, showNotificationsBadge, onSettingsClick } = this.props;
@@ -71,8 +71,8 @@ class Header extends ImmutablePureComponent {
     //  Only renders the component if the column isn't being shown.
     const renderForColumn = conditionalRender.bind(null,
       columnId => !columns || !columns.some(
-        column => column.get('id') === columnId
-      )
+        column => column.get('id') === columnId,
+      ),
     );
 
     //  The result.
@@ -125,10 +125,13 @@ class Header extends ImmutablePureComponent {
         <a
           aria-label={intl.formatMessage(messages.logout)}
           onClick={this.handleLogoutClick}
-          href={ signOutLink }
+          href={signOutLink}
           title={intl.formatMessage(messages.logout)}
         ><Icon id='sign-out' /></a>
       </nav>
     );
-  };
+  }
+
 }
+
+export default injectIntl(Header);
diff --git a/app/javascript/flavours/glitch/features/compose/components/language_dropdown.js b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx
index 3a1fa0226..05614de01 100644
--- a/app/javascript/flavours/glitch/features/compose/components/language_dropdown.js
+++ b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx
@@ -40,7 +40,7 @@ class LanguageDropdownMenu extends React.PureComponent {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
     }
-  }
+  };
 
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
@@ -63,15 +63,15 @@ class LanguageDropdownMenu extends React.PureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   setListRef = c => {
     this.listNode = c;
-  }
+  };
 
   handleSearchChange = ({ target }) => {
     this.setState({ searchValue: target.value });
-  }
+  };
 
   search () {
     const { languages, value, frequentlyUsedLanguages } = this.props;
@@ -122,7 +122,7 @@ class LanguageDropdownMenu extends React.PureComponent {
 
     this.props.onClose();
     this.props.onChange(value);
-  }
+  };
 
   handleKeyDown = e => {
     const { onClose } = this.props;
@@ -163,7 +163,7 @@ class LanguageDropdownMenu extends React.PureComponent {
       e.preventDefault();
       e.stopPropagation();
     }
-  }
+  };
 
   handleSearchKeyDown = e => {
     const { onChange, onClose } = this.props;
@@ -199,21 +199,21 @@ class LanguageDropdownMenu extends React.PureComponent {
 
       break;
     }
-  }
+  };
 
   handleClear = () => {
     this.setState({ searchValue: '' });
-  }
+  };
 
   renderItem = lang => {
     const { value } = this.props;
 
     return (
-      <div key={lang[0]} role='option' tabIndex='0' data-index={lang[0]} className={classNames('language-dropdown__dropdown__results__item', { active: lang[0] === value })} aria-selected={lang[0] === value} onClick={this.handleClick} onKeyDown={this.handleKeyDown}>
-        <span className='language-dropdown__dropdown__results__item__native-name'>{lang[2]}</span> <span className='language-dropdown__dropdown__results__item__common-name'>({lang[1]})</span>
+      <div key={lang[0]} role='option' tabIndex={0} data-index={lang[0]} className={classNames('language-dropdown__dropdown__results__item', { active: lang[0] === value })} aria-selected={lang[0] === value} onClick={this.handleClick} onKeyDown={this.handleKeyDown}>
+        <span className='language-dropdown__dropdown__results__item__native-name' lang={lang[0]}>{lang[2]}</span> <span className='language-dropdown__dropdown__results__item__common-name'>({lang[1]})</span>
       </div>
     );
-  }
+  };
 
   render () {
     const { intl } = this.props;
@@ -237,7 +237,6 @@ class LanguageDropdownMenu extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class LanguageDropdown extends React.PureComponent {
 
   static propTypes = {
@@ -259,7 +258,7 @@ class LanguageDropdown extends React.PureComponent {
     }
 
     this.setState({ open: !this.state.open });
-  }
+  };
 
   handleClose = () => {
     const { value, onClose } = this.props;
@@ -270,24 +269,24 @@ class LanguageDropdown extends React.PureComponent {
 
     this.setState({ open: false });
     onClose(value);
-  }
+  };
 
   handleChange = value => {
     const { onChange } = this.props;
     onChange(value);
-  }
+  };
 
   setTargetRef = c => {
     this.target = c;
-  }
+  };
 
   findTarget = () => {
     return this.target;
-  }
+  };
 
   handleOverlayEnter = (state) => {
     this.setState({ placement: state.placement });
-  }
+  };
 
   render () {
     const { value, intl, frequentlyUsedLanguages } = this.props;
@@ -325,3 +324,5 @@ class LanguageDropdown extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(LanguageDropdown);
diff --git a/app/javascript/flavours/glitch/features/compose/components/navigation_bar.js b/app/javascript/flavours/glitch/features/compose/components/navigation_bar.jsx
index 1a68f1e12..1a68f1e12 100644
--- a/app/javascript/flavours/glitch/features/compose/components/navigation_bar.js
+++ b/app/javascript/flavours/glitch/features/compose/components/navigation_bar.jsx
diff --git a/app/javascript/flavours/glitch/features/compose/components/options.js b/app/javascript/flavours/glitch/features/compose/components/options.jsx
index b5276c371..19ead2f21 100644
--- a/app/javascript/flavours/glitch/features/compose/components/options.js
+++ b/app/javascript/flavours/glitch/features/compose/components/options.jsx
@@ -83,8 +83,11 @@ const messages = defineMessages({
   },
 });
 
-@connect((state, { name }) => ({ checked: state.getIn(['compose', 'advanced_options', name]) }))
-class ToggleOption extends ImmutablePureComponent {
+const mapStateToProps = (state, { name }) => ({
+  checked: state.getIn(['compose', 'advanced_options', name]),
+});
+
+class ToggleOptionImpl extends ImmutablePureComponent {
 
   static propTypes = {
     name: PropTypes.string.isRequired,
@@ -113,7 +116,8 @@ class ToggleOption extends ImmutablePureComponent {
 
 }
 
-export default @injectIntl
+const ToggleOption = connect(mapStateToProps)(ToggleOptionImpl);
+
 class ComposerOptions extends ImmutablePureComponent {
 
   static propTypes = {
@@ -144,7 +148,7 @@ class ComposerOptions extends ImmutablePureComponent {
     if (files.length && onUpload) {
       onUpload(files);
     }
-  }
+  };
 
   //  Handles attachment clicks.
   handleClickAttach = (name) => {
@@ -164,12 +168,12 @@ class ComposerOptions extends ImmutablePureComponent {
       }
       return;
     }
-  }
+  };
 
   //  Handles a ref to the file input.
   handleRefFileElement = (fileElement) => {
     this.fileElement = fileElement;
-  }
+  };
 
   renderToggleItemContents = (item) => {
     const { onChangeAdvancedOption } = this.props;
@@ -315,3 +319,5 @@ class ComposerOptions extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(ComposerOptions);
diff --git a/app/javascript/flavours/glitch/features/compose/components/poll_form.js b/app/javascript/flavours/glitch/features/compose/components/poll_form.jsx
index afb5da097..cbd53c4d5 100644
--- a/app/javascript/flavours/glitch/features/compose/components/poll_form.js
+++ b/app/javascript/flavours/glitch/features/compose/components/poll_form.jsx
@@ -21,11 +21,11 @@ const messages = defineMessages({
   days: { id: 'intervals.full.days', defaultMessage: '{number, plural, one {# day} other {# days}}' },
 });
 
-@injectIntl
-class Option extends React.PureComponent {
+class OptionIntl extends React.PureComponent {
 
   static propTypes = {
     title: PropTypes.string.isRequired,
+    lang: PropTypes.string,
     index: PropTypes.number.isRequired,
     isPollMultiple: PropTypes.bool,
     autoFocus: PropTypes.bool,
@@ -48,18 +48,18 @@ class Option extends React.PureComponent {
 
   onSuggestionsClearRequested = () => {
     this.props.onClearSuggestions();
-  }
+  };
 
   onSuggestionsFetchRequested = (token) => {
     this.props.onFetchSuggestions(token);
-  }
+  };
 
   onSuggestionSelected = (tokenStart, token, value) => {
     this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
-  }
+  };
 
   render () {
-    const { isPollMultiple, title, index, autoFocus, intl } = this.props;
+    const { isPollMultiple, title, lang, index, autoFocus, intl } = this.props;
 
     return (
       <li>
@@ -70,6 +70,8 @@ class Option extends React.PureComponent {
             placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })}
             maxLength={pollLimits.max_option_chars}
             value={title}
+            lang={lang}
+            spellCheck
             onChange={this.handleOptionTitleChange}
             suggestions={this.props.suggestions}
             onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
@@ -89,12 +91,13 @@ class Option extends React.PureComponent {
 
 }
 
-export default
-@injectIntl
+const Option = injectIntl(OptionIntl);
+
 class PollForm extends ImmutablePureComponent {
 
   static propTypes = {
     options: ImmutablePropTypes.list,
+    lang: PropTypes.string,
     expiresIn: PropTypes.number,
     isMultiple: PropTypes.bool,
     onChangeOption: PropTypes.func.isRequired,
@@ -121,7 +124,7 @@ class PollForm extends ImmutablePureComponent {
   };
 
   render () {
-    const { options, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl, ...other } = this.props;
+    const { options, lang, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl, ...other } = this.props;
 
     if (!options) {
       return null;
@@ -132,7 +135,7 @@ class PollForm extends ImmutablePureComponent {
     return (
       <div className='compose-form__poll-wrapper'>
         <ul>
-          {options.map((title, i) => <Option title={title} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} autoFocus={i === autoFocusIndex} {...other} />)}
+          {options.map((title, i) => <Option title={title} lang={lang} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} autoFocus={i === autoFocusIndex} {...other} />)}
           {options.size < pollLimits.max_options && (
             <label className='poll__text editable'>
               <span className={classNames('poll__input')} style={{ opacity: 0 }} />
@@ -164,3 +167,5 @@ class PollForm extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(PollForm);
diff --git a/app/javascript/flavours/glitch/features/compose/components/privacy_dropdown.js b/app/javascript/flavours/glitch/features/compose/components/privacy_dropdown.jsx
index 02cf72289..4bfbb5b8c 100644
--- a/app/javascript/flavours/glitch/features/compose/components/privacy_dropdown.js
+++ b/app/javascript/flavours/glitch/features/compose/components/privacy_dropdown.jsx
@@ -16,7 +16,6 @@ const messages = defineMessages({
   change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
 });
 
-export default @injectIntl
 class PrivacyDropdown extends React.PureComponent {
 
   static propTypes = {
@@ -86,3 +85,5 @@ class PrivacyDropdown extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(PrivacyDropdown);
diff --git a/app/javascript/flavours/glitch/features/compose/components/publisher.js b/app/javascript/flavours/glitch/features/compose/components/publisher.jsx
index 9d53b7ee3..3128303c6 100644
--- a/app/javascript/flavours/glitch/features/compose/components/publisher.js
+++ b/app/javascript/flavours/glitch/features/compose/components/publisher.jsx
@@ -26,7 +26,6 @@ const messages = defineMessages({
   saveChanges: { id: 'compose_form.save_changes', defaultMessage: 'Save changes' },
 });
 
-export default @injectIntl
 class Publisher extends ImmutablePureComponent {
 
   static propTypes = {
@@ -94,5 +93,8 @@ class Publisher extends ImmutablePureComponent {
         </div>
       </div>
     );
-  };
+  }
+
 }
+
+export default injectIntl(Publisher);
diff --git a/app/javascript/flavours/glitch/features/compose/components/reply_indicator.js b/app/javascript/flavours/glitch/features/compose/components/reply_indicator.jsx
index 7ad9e2b64..179d85ac3 100644
--- a/app/javascript/flavours/glitch/features/compose/components/reply_indicator.js
+++ b/app/javascript/flavours/glitch/features/compose/components/reply_indicator.jsx
@@ -19,7 +19,6 @@ const messages = defineMessages({
 });
 
 
-export default @injectIntl
 class ReplyIndicator extends ImmutablePureComponent {
 
   static propTypes = {
@@ -33,7 +32,7 @@ class ReplyIndicator extends ImmutablePureComponent {
     if (onCancel) {
       onCancel();
     }
-  }
+  };
 
   //  Rendering.
   render () {
@@ -80,3 +79,5 @@ class ReplyIndicator extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(ReplyIndicator);
diff --git a/app/javascript/flavours/glitch/features/compose/components/search.js b/app/javascript/flavours/glitch/features/compose/components/search.jsx
index e5874de75..cb6afca8d 100644
--- a/app/javascript/flavours/glitch/features/compose/components/search.js
+++ b/app/javascript/flavours/glitch/features/compose/components/search.jsx
@@ -45,7 +45,6 @@ class SearchPopout extends React.PureComponent {
 }
 
 //  The component.
-export default @injectIntl
 class Search extends React.PureComponent {
 
   static contextTypes = {
@@ -71,14 +70,14 @@ class Search extends React.PureComponent {
 
   setRef = c => {
     this.searchForm = c;
-  }
+  };
 
   handleChange = (e) => {
     const { onChange } = this.props;
     if (onChange) {
       onChange(e.target.value);
     }
-  }
+  };
 
   handleClear = (e) => {
     const {
@@ -90,11 +89,11 @@ class Search extends React.PureComponent {
     if (onClear && (submitted || value && value.length)) {
       onClear();
     }
-  }
+  };
 
   handleBlur = () => {
     this.setState({ expanded: false });
-  }
+  };
 
   handleFocus = () => {
     this.setState({ expanded: true });
@@ -106,7 +105,7 @@ class Search extends React.PureComponent {
         this.searchForm.scrollIntoView();
       }
     }
-  }
+  };
 
   handleKeyUp = (e) => {
     const { onSubmit } = this.props;
@@ -121,11 +120,11 @@ class Search extends React.PureComponent {
     case 'Escape':
       focusRoot();
     }
-  }
+  };
 
   findTarget = () => {
     return this.searchForm;
-  }
+  };
 
   render () {
     const { intl, value, submitted } = this.props;
@@ -148,7 +147,7 @@ class Search extends React.PureComponent {
           onBlur={this.handleBlur}
         />
 
-        <div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
+        <div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
           <Icon id='search' className={hasValue ? '' : 'active'} />
           <Icon id='times-circle' className={hasValue ? 'active' : ''} />
         </div>
@@ -166,3 +165,5 @@ class Search extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(Search);
diff --git a/app/javascript/flavours/glitch/features/compose/components/search_results.js b/app/javascript/flavours/glitch/features/compose/components/search_results.jsx
index c2178702c..bf009d13a 100644
--- a/app/javascript/flavours/glitch/features/compose/components/search_results.js
+++ b/app/javascript/flavours/glitch/features/compose/components/search_results.jsx
@@ -14,7 +14,6 @@ const messages = defineMessages({
   dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
 });
 
-export default @injectIntl
 class SearchResults extends ImmutablePureComponent {
 
   static propTypes = {
@@ -103,7 +102,7 @@ class SearchResults extends ImmutablePureComponent {
         <section className='search-results__section'>
           <h5><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></h5>
 
-          {results.get('statuses').map(statusId => <StatusContainer id={statusId} key={statusId}/>)}
+          {results.get('statuses').map(statusId => <StatusContainer id={statusId} key={statusId} />)}
 
           {results.get('statuses').size >= 5 && <LoadMore visible onClick={this.handleLoadMoreStatuses} />}
         </section>
@@ -136,5 +135,8 @@ class SearchResults extends ImmutablePureComponent {
         {hashtags}
       </div>
     );
-  };
+  }
+
 }
+
+export default injectIntl(SearchResults);
diff --git a/app/javascript/flavours/glitch/features/compose/components/text_icon_button.js b/app/javascript/flavours/glitch/features/compose/components/text_icon_button.jsx
index a35bd4ff5..a35bd4ff5 100644
--- a/app/javascript/flavours/glitch/features/compose/components/text_icon_button.js
+++ b/app/javascript/flavours/glitch/features/compose/components/text_icon_button.jsx
diff --git a/app/javascript/flavours/glitch/features/compose/components/textarea_icons.js b/app/javascript/flavours/glitch/features/compose/components/textarea_icons.jsx
index 25c2443b1..73281fc74 100644
--- a/app/javascript/flavours/glitch/features/compose/components/textarea_icons.js
+++ b/app/javascript/flavours/glitch/features/compose/components/textarea_icons.jsx
@@ -27,7 +27,6 @@ const iconMap = [
   ['threaded_mode', 'comments', messages.threadedMode],
 ];
 
-export default @injectIntl
 class TextareaIcons extends ImmutablePureComponent {
 
   static propTypes = {
@@ -51,9 +50,12 @@ class TextareaIcons extends ImmutablePureComponent {
                 id={icon}
               />
             </span>
-          ) : null
+          ) : null,
         ) : null}
       </div>
     );
   }
+
 }
+
+export default injectIntl(TextareaIcons);
diff --git a/app/javascript/flavours/glitch/features/compose/components/upload.js b/app/javascript/flavours/glitch/features/compose/components/upload.jsx
index 6528bbc84..7d5784561 100644
--- a/app/javascript/flavours/glitch/features/compose/components/upload.js
+++ b/app/javascript/flavours/glitch/features/compose/components/upload.jsx
@@ -23,33 +23,38 @@ export default class Upload extends ImmutablePureComponent {
   handleUndoClick = e => {
     e.stopPropagation();
     this.props.onUndo(this.props.media.get('id'));
-  }
+  };
 
   handleFocalPointClick = e => {
     e.stopPropagation();
     this.props.onOpenFocalPoint(this.props.media.get('id'));
-  }
+  };
 
   render () {
     const { media } = this.props;
+
+    if (!media) {
+      return null;
+    }
+
     const focusX = media.getIn(['meta', 'focus', 'x']);
     const focusY = media.getIn(['meta', 'focus', 'y']);
     const x = ((focusX /  2) + .5) * 100;
     const y = ((focusY / -2) + .5) * 100;
 
     return (
-      <div className='compose-form__upload' tabIndex='0' role='button'>
-        <Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12, }) }}>
+      <div className='compose-form__upload' tabIndex={0} role='button'>
+        <Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
           {({ scale }) => (
             <div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}>
               <div className='compose-form__upload__actions'>
-                <button className='icon-button' onClick={this.handleUndoClick}><Icon id='times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button>
-                {!!media.get('unattached') && (<button className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>)}
+                <button type='button' className='icon-button' onClick={this.handleUndoClick}><Icon id='times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button>
+                <button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>
               </div>
 
-              {(media.get('description') || '').length === 0 && !!media.get('unattached') && (
+              {(media.get('description') || '').length === 0 && (
                 <div className='compose-form__upload__warning'>
-                  <button className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button>
+                  <button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button>
                 </div>
               )}
             </div>
diff --git a/app/javascript/flavours/glitch/features/compose/components/upload_form.js b/app/javascript/flavours/glitch/features/compose/components/upload_form.jsx
index 7ebbac963..f2e7fe7a2 100644
--- a/app/javascript/flavours/glitch/features/compose/components/upload_form.js
+++ b/app/javascript/flavours/glitch/features/compose/components/upload_form.jsx
@@ -6,6 +6,7 @@ import UploadContainer from '../containers/upload_container';
 import SensitiveButtonContainer from '../containers/sensitive_button_container';
 
 export default class UploadForm extends ImmutablePureComponent {
+
   static propTypes = {
     mediaIds: ImmutablePropTypes.list.isRequired,
   };
diff --git a/app/javascript/flavours/glitch/features/compose/components/upload_progress.js b/app/javascript/flavours/glitch/features/compose/components/upload_progress.jsx
index 39ac31053..39ac31053 100644
--- a/app/javascript/flavours/glitch/features/compose/components/upload_progress.js
+++ b/app/javascript/flavours/glitch/features/compose/components/upload_progress.jsx
diff --git a/app/javascript/flavours/glitch/features/compose/components/warning.js b/app/javascript/flavours/glitch/features/compose/components/warning.jsx
index 803b7f86a..803b7f86a 100644
--- a/app/javascript/flavours/glitch/features/compose/components/warning.js
+++ b/app/javascript/flavours/glitch/features/compose/components/warning.jsx
diff --git a/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js b/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js
index d12c98c01..ddcdb367a 100644
--- a/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js
+++ b/app/javascript/flavours/glitch/features/compose/containers/compose_form_container.js
@@ -21,12 +21,18 @@ import { changeLocalSetting } from 'flavours/glitch/actions/local_settings';
 import { privacyPreference } from 'flavours/glitch/utils/privacy_preference';
 
 const messages = defineMessages({
-  missingDescriptionMessage: {  id: 'confirmations.missing_media_description.message',
-                                defaultMessage: 'At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.' },
-  missingDescriptionConfirm: {  id: 'confirmations.missing_media_description.confirm',
-                                defaultMessage: 'Send anyway' },
-  missingDescriptionEdit:    {  id: 'confirmations.missing_media_description.edit',
-                                defaultMessage: 'Edit media' },
+  missingDescriptionMessage: {
+    id: 'confirmations.missing_media_description.message',
+    defaultMessage: 'At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.',
+  },
+  missingDescriptionConfirm: {
+    id: 'confirmations.missing_media_description.confirm',
+    defaultMessage: 'Send anyway',
+  },
+  missingDescriptionEdit: {
+    id: 'confirmations.missing_media_description.edit',
+    defaultMessage: 'Edit media',
+  },
 });
 
 //  State mapping.
@@ -38,12 +44,12 @@ function mapStateToProps (state) {
   const sideArmRestrictedPrivacy = replyPrivacy ? privacyPreference(replyPrivacy, sideArmBasePrivacy) : null;
   let sideArmPrivacy = null;
   switch (state.getIn(['local_settings', 'side_arm_reply_mode'])) {
-    case 'copy':
-      sideArmPrivacy = replyPrivacy;
-      break;
-    case 'restrict':
-      sideArmPrivacy = sideArmRestrictedPrivacy;
-      break;
+  case 'copy':
+    sideArmPrivacy = replyPrivacy;
+    break;
+  case 'restrict':
+    sideArmPrivacy = sideArmRestrictedPrivacy;
+    break;
   }
   sideArmPrivacy = sideArmPrivacy || sideArmBasePrivacy;
   return {
@@ -70,8 +76,9 @@ function mapStateToProps (state) {
     mediaDescriptionConfirmation: state.getIn(['local_settings', 'confirm_missing_media_description']),
     preselectOnReply: state.getIn(['local_settings', 'preselect_on_reply']),
     isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
+    lang: state.getIn(['compose', 'language']),
   };
-};
+}
 
 //  Dispatch mapping.
 const mapDispatchToProps = (dispatch, { intl }) => ({
@@ -123,7 +130,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
       onConfirm: () => {
         if (overriddenVisibility) {
           dispatch(changeComposeVisibility(overriddenVisibility));
-        };
+        }
         dispatch(submitCompose(routerHistory));
       },
       secondary: intl.formatMessage(messages.missingDescriptionEdit),
diff --git a/app/javascript/flavours/glitch/features/compose/containers/options_container.js b/app/javascript/flavours/glitch/features/compose/containers/options_container.js
index 5de9f5419..19a90ac8b 100644
--- a/app/javascript/flavours/glitch/features/compose/containers/options_container.js
+++ b/app/javascript/flavours/glitch/features/compose/containers/options_container.js
@@ -23,7 +23,7 @@ function mapStateToProps (state) {
     showContentTypeChoice: state.getIn(['local_settings', 'show_content_type_choice']),
     contentType: state.getIn(['compose', 'content_type']),
   };
-};
+}
 
 const mapDispatchToProps = (dispatch) => ({
 
diff --git a/app/javascript/flavours/glitch/features/compose/containers/poll_form_container.js b/app/javascript/flavours/glitch/features/compose/containers/poll_form_container.js
index e87e58771..14038b3e8 100644
--- a/app/javascript/flavours/glitch/features/compose/containers/poll_form_container.js
+++ b/app/javascript/flavours/glitch/features/compose/containers/poll_form_container.js
@@ -1,7 +1,10 @@
 import { connect } from 'react-redux';
 import PollForm from '../components/poll_form';
-import { addPollOption, removePollOption, changePollOption, changePollSettings } from 'flavours/glitch/actions/compose';
 import {
+  addPollOption,
+  removePollOption,
+  changePollOption,
+  changePollSettings,
   clearComposeSuggestions,
   fetchComposeSuggestions,
   selectComposeSuggestion,
@@ -10,6 +13,7 @@ import {
 const mapStateToProps = state => ({
   suggestions: state.getIn(['compose', 'suggestions']),
   options: state.getIn(['compose', 'poll', 'options']),
+  lang: state.getIn(['compose', 'language']),
   expiresIn: state.getIn(['compose', 'poll', 'expires_in']),
   isMultiple: state.getIn(['compose', 'poll', 'multiple']),
 });
diff --git a/app/javascript/flavours/glitch/features/compose/containers/sensitive_button_container.js b/app/javascript/flavours/glitch/features/compose/containers/sensitive_button_container.jsx
index 9c23d3f47..9c23d3f47 100644
--- a/app/javascript/flavours/glitch/features/compose/containers/sensitive_button_container.js
+++ b/app/javascript/flavours/glitch/features/compose/containers/sensitive_button_container.jsx
diff --git a/app/javascript/flavours/glitch/features/compose/containers/upload_container.js b/app/javascript/flavours/glitch/features/compose/containers/upload_container.js
index f3ca4ce7b..2189c870b 100644
--- a/app/javascript/flavours/glitch/features/compose/containers/upload_container.js
+++ b/app/javascript/flavours/glitch/features/compose/containers/upload_container.js
@@ -1,7 +1,6 @@
 import { connect } from 'react-redux';
 import Upload from '../components/upload';
-import { undoUploadCompose, initMediaEditModal } from 'flavours/glitch/actions/compose';
-import { submitCompose } from 'flavours/glitch/actions/compose';
+import { undoUploadCompose, initMediaEditModal, submitCompose } from 'flavours/glitch/actions/compose';
 
 const mapStateToProps = (state, { id }) => ({
   media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
diff --git a/app/javascript/flavours/glitch/features/compose/containers/warning_container.js b/app/javascript/flavours/glitch/features/compose/containers/warning_container.jsx
index b2ed40b82..5b48c45e4 100644
--- a/app/javascript/flavours/glitch/features/compose/containers/warning_container.js
+++ b/app/javascript/flavours/glitch/features/compose/containers/warning_container.jsx
@@ -8,7 +8,7 @@ import { profileLink, termsLink } from 'flavours/glitch/utils/backend_links';
 
 const buildHashtagRE = () => {
   try {
-    const HASHTAG_SEPARATORS = "_\\u00b7\\u200c";
+    const HASHTAG_SEPARATORS = '_\\u00b7\\u200c';
     const ALPHA = '\\p{L}\\p{M}';
     const WORD = '\\p{L}\\p{M}\\p{N}\\p{Pc}';
     return new RegExp(
@@ -22,10 +22,10 @@ const buildHashtagRE = () => {
       '[' + WORD + '_]*' +
       '[' + ALPHA + ']' +
       '[' + WORD + '_]*' +
-      '))', 'iu'
+      '))', 'iu',
     );
   } catch {
-    return /(?:^|[^\/\)\w])#(\w*[a-zA-Z·]\w*)/i;
+    return /(?:^|[^/)\w])#(\w*[a-zA-Z·]\w*)/i;
   }
 };
 
diff --git a/app/javascript/flavours/glitch/features/compose/index.js b/app/javascript/flavours/glitch/features/compose/index.jsx
index 8ca378672..5547a1210 100644
--- a/app/javascript/flavours/glitch/features/compose/index.js
+++ b/app/javascript/flavours/glitch/features/compose/index.jsx
@@ -4,7 +4,7 @@ import NavigationContainer from './containers/navigation_container';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { connect } from 'react-redux';
-import { mountCompose, unmountCompose } from 'flavours/glitch/actions/compose';
+import { mountCompose, unmountCompose, cycleElefriendCompose } from 'flavours/glitch/actions/compose';
 import { injectIntl, defineMessages } from 'react-intl';
 import classNames from 'classnames';
 import SearchContainer from './containers/search_container';
@@ -12,7 +12,6 @@ import Motion from '../ui/util/optional_motion';
 import spring from 'react-motion/lib/spring';
 import SearchResultsContainer from './containers/search_results_container';
 import { me, mascot } from 'flavours/glitch/initial_state';
-import { cycleElefriendCompose } from 'flavours/glitch/actions/compose';
 import HeaderContainer from './containers/header_container';
 import Column from 'flavours/glitch/components/column';
 import { Helmet } from 'react-helmet';
@@ -40,9 +39,8 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
   },
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class Compose extends React.PureComponent {
+
   static propTypes = {
     multiColumn: PropTypes.bool,
     showSearch: PropTypes.bool,
@@ -114,3 +112,5 @@ class Compose extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(Compose));
diff --git a/app/javascript/flavours/glitch/features/compose/util/counter.js b/app/javascript/flavours/glitch/features/compose/util/counter.js
index 7aa9e87b1..ec2431096 100644
--- a/app/javascript/flavours/glitch/features/compose/util/counter.js
+++ b/app/javascript/flavours/glitch/features/compose/util/counter.js
@@ -5,5 +5,5 @@ const urlPlaceholder = '$2xxxxxxxxxxxxxxxxxxxxxxx';
 export function countableText(inputText) {
   return inputText
     .replace(urlRegex, urlPlaceholder)
-    .replace(/(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/ig, '$1@$3');
-};
+    .replace(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, '$1@$3');
+}
diff --git a/app/javascript/flavours/glitch/features/direct_timeline/components/column_settings.js b/app/javascript/flavours/glitch/features/direct_timeline/components/column_settings.jsx
index 18c3c7e21..79e98ec6f 100644
--- a/app/javascript/flavours/glitch/features/direct_timeline/components/column_settings.js
+++ b/app/javascript/flavours/glitch/features/direct_timeline/components/column_settings.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   settings: { id: 'home.settings', defaultMessage: 'Column settings' },
 });
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -40,3 +39,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js b/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.jsx
index 00d9fdcd0..63a331086 100644
--- a/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.js
+++ b/app/javascript/flavours/glitch/features/direct_timeline/components/conversation.jsx
@@ -24,7 +24,6 @@ const messages = defineMessages({
   unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
 });
 
-export default @injectIntl
 class Conversation extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -60,12 +59,12 @@ class Conversation extends ImmutablePureComponent {
         }
         destination = `/statuses/${lastStatus.get('id')}`;
       }
-      let state = {...router.history.location.state};
+      let state = { ...router.history.location.state };
       state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
       router.history.push(destination, state);
       e.preventDefault();
     }
-  }
+  };
 
   handleMouseEnter = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -78,7 +77,7 @@ class Conversation extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -91,7 +90,7 @@ class Conversation extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   handleClick = () => {
     if (!this.context.router) {
@@ -105,31 +104,31 @@ class Conversation extends ImmutablePureComponent {
     }
 
     this.context.router.history.push(`/@${lastStatus.getIn(['account', 'acct'])}/${lastStatus.get('id')}`);
-  }
+  };
 
   handleMarkAsRead = () => {
     this.props.markRead();
-  }
+  };
 
   handleReply = () => {
     this.props.reply(this.props.lastStatus, this.context.router.history);
-  }
+  };
 
   handleDelete = () => {
     this.props.delete();
-  }
+  };
 
   handleHotkeyMoveUp = () => {
     this.props.onMoveUp(this.props.conversationId);
-  }
+  };
 
   handleHotkeyMoveDown = () => {
     this.props.onMoveDown(this.props.conversationId);
-  }
+  };
 
   handleConversationMute = () => {
     this.props.onMute(this.props.lastStatus);
-  }
+  };
 
   handleShowMore = () => {
     this.props.onToggleHidden(this.props.lastStatus);
@@ -141,7 +140,7 @@ class Conversation extends ImmutablePureComponent {
 
   setExpansion = value => {
     this.setState({ isExpanded: value });
-  }
+  };
 
   render () {
     const { accounts, lastStatus, unread, scrollKey, intl } = this.props;
@@ -183,7 +182,7 @@ class Conversation extends ImmutablePureComponent {
 
     return (
       <HotKeys handlers={handlers}>
-        <div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex='0'>
+        <div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex={0}>
           <div className='conversation__avatar' onClick={this.handleClick} role='presentation'>
             <AvatarComposite accounts={accounts} size={48} />
           </div>
@@ -230,3 +229,5 @@ class Conversation extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Conversation);
diff --git a/app/javascript/flavours/glitch/features/direct_timeline/components/conversations_list.js b/app/javascript/flavours/glitch/features/direct_timeline/components/conversations_list.jsx
index c2aff1b57..2bfe6fbf1 100644
--- a/app/javascript/flavours/glitch/features/direct_timeline/components/conversations_list.js
+++ b/app/javascript/flavours/glitch/features/direct_timeline/components/conversations_list.jsx
@@ -16,17 +16,17 @@ export default class ConversationsList extends ImmutablePureComponent {
     onLoadMore: PropTypes.func,
   };
 
-  getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id)
+  getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id);
 
   handleMoveUp = id => {
     const elementIndex = this.getCurrentIndex(id) - 1;
     this._selectChild(elementIndex, true);
-  }
+  };
 
   handleMoveDown = id => {
     const elementIndex = this.getCurrentIndex(id) + 1;
     this._selectChild(elementIndex, false);
-  }
+  };
 
   _selectChild (index, align_top) {
     const container = this.node.node;
@@ -44,7 +44,7 @@ export default class ConversationsList extends ImmutablePureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   handleLoadOlder = debounce(() => {
     const last = this.props.conversations.last();
@@ -52,13 +52,13 @@ export default class ConversationsList extends ImmutablePureComponent {
     if (last && last.get('last_status')) {
       this.props.onLoadMore(last.get('last_status'));
     }
-  }, 300, { leading: true })
+  }, 300, { leading: true });
 
   render () {
-    const { conversations, onLoadMore, ...other } = this.props;
+    const { conversations, isLoading, onLoadMore, ...other } = this.props;
 
     return (
-      <ScrollableList {...other} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
+      <ScrollableList {...other} isLoading={isLoading} showLoading={isLoading && conversations.isEmpty()} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
         {conversations.map(item => (
           <ConversationContainer
             key={item.get('id')}
diff --git a/app/javascript/flavours/glitch/features/direct_timeline/index.js b/app/javascript/flavours/glitch/features/direct_timeline/index.jsx
index d55c63c2b..2fe3f2568 100644
--- a/app/javascript/flavours/glitch/features/direct_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/direct_timeline/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = state => ({
   conversationsMode: state.getIn(['settings', 'direct', 'conversations']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class DirectTimeline extends React.PureComponent {
 
   static propTypes = {
@@ -43,16 +41,16 @@ class DirectTimeline extends React.PureComponent {
     } else {
       dispatch(addColumn('DIRECT', {}));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   componentDidMount () {
     const { dispatch, conversationsMode } = this.props;
@@ -89,15 +87,15 @@ class DirectTimeline extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMoreTimeline = maxId => {
     this.props.dispatch(expandDirectTimeline({ maxId }));
-  }
+  };
 
   handleLoadMoreConversations = maxId => {
     this.props.dispatch(expandConversations({ maxId }));
-  }
+  };
 
   render () {
     const { intl, hasUnread, columnId, multiColumn, conversationsMode } = this.props;
@@ -110,8 +108,10 @@ class DirectTimeline extends React.PureComponent {
           trackScroll={!pinned}
           scrollKey={`direct_timeline-${columnId}`}
           timelineId='direct'
+          bindToDocument={!multiColumn}
           onLoadMore={this.handleLoadMore}
           prepend={<div className='follow_requests-unlocked_explanation'><span><FormattedMessage id='compose_form.encryption_warning' defaultMessage='Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.' /> <a href='/terms' target='_blank'><FormattedMessage id='compose_form.direct_message_warning_learn_more' defaultMessage='Learn more' /></a></span></div>}
+          alwaysPrepend
           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." />}
         />
       );
@@ -121,8 +121,10 @@ class DirectTimeline extends React.PureComponent {
           trackScroll={!pinned}
           scrollKey={`direct_timeline-${columnId}`}
           timelineId='direct'
+          bindToDocument={!multiColumn}
           onLoadMore={this.handleLoadMoreTimeline}
           prepend={<div className='follow_requests-unlocked_explanation'><span><FormattedMessage id='compose_form.encryption_warning' defaultMessage='Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.' /> <a href='/terms' target='_blank'><FormattedMessage id='compose_form.direct_message_warning_learn_more' defaultMessage='Learn more' /></a></span></div>}
+          alwaysPrepend
           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." />}
         />
       );
@@ -154,3 +156,5 @@ class DirectTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(DirectTimeline));
diff --git a/app/javascript/flavours/glitch/features/directory/components/account_card.js b/app/javascript/flavours/glitch/features/directory/components/account_card.jsx
index ccc3dd3d2..663710b06 100644
--- a/app/javascript/flavours/glitch/features/directory/components/account_card.js
+++ b/app/javascript/flavours/glitch/features/directory/components/account_card.jsx
@@ -93,9 +93,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
 
 });
 
-export default
-@injectIntl
-@connect(makeMapStateToProps, mapDispatchToProps)
 class AccountCard extends ImmutablePureComponent {
 
   static propTypes = {
@@ -118,7 +115,7 @@ class AccountCard extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -131,7 +128,7 @@ class AccountCard extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   handleFollow = () => {
     this.props.onFollow(this.props.account);
@@ -143,11 +140,11 @@ class AccountCard extends ImmutablePureComponent {
 
   handleMute = () => {
     this.props.onMute(this.props.account);
-  }
+  };
 
   handleEditProfile = () => {
     window.open('/settings/profile', '_blank');
-  }
+  };
 
   handleDismiss = (e) => {
     const { account, onDismiss } = this.props;
@@ -155,7 +152,7 @@ class AccountCard extends ImmutablePureComponent {
 
     e.preventDefault();
     e.stopPropagation();
-  }
+  };
 
   render() {
     const { account, intl } = this.props;
@@ -246,3 +243,5 @@ class AccountCard extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(AccountCard));
diff --git a/app/javascript/flavours/glitch/features/directory/index.js b/app/javascript/flavours/glitch/features/directory/index.jsx
index 94bcd578c..4278a4e71 100644
--- a/app/javascript/flavours/glitch/features/directory/index.js
+++ b/app/javascript/flavours/glitch/features/directory/index.jsx
@@ -29,8 +29,6 @@ const mapStateToProps = state => ({
   domain: state.getIn(['meta', 'domain']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Directory extends React.PureComponent {
 
   static contextTypes = {
@@ -64,7 +62,7 @@ class Directory extends React.PureComponent {
     } else {
       dispatch(addColumn('DIRECTORY', this.getParams(this.props, this.state)));
     }
-  }
+  };
 
   getParams = (props, state) => ({
     order: state.order === null ? (props.params.order || 'active') : state.order,
@@ -74,11 +72,11 @@ class Directory extends React.PureComponent {
   handleMove = dir => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   componentDidMount () {
     const { dispatch } = this.props;
@@ -97,7 +95,7 @@ class Directory extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleChangeOrder = e => {
     const { dispatch, columnId } = this.props;
@@ -107,7 +105,7 @@ class Directory extends React.PureComponent {
     } else {
       this.setState({ order: e.target.value });
     }
-  }
+  };
 
   handleChangeLocal = e => {
     const { dispatch, columnId } = this.props;
@@ -117,12 +115,12 @@ class Directory extends React.PureComponent {
     } else {
       this.setState({ local: e.target.value === '1' });
     }
-  }
+  };
 
   handleLoadMore = () => {
     const { dispatch } = this.props;
     dispatch(expandDirectory(this.getParams(this.props, this.state)));
-  }
+  };
 
   render () {
     const { isLoading, accountIds, intl, columnId, multiColumn, domain } = this.props;
@@ -176,3 +174,5 @@ class Directory extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Directory));
diff --git a/app/javascript/flavours/glitch/features/domain_blocks/index.js b/app/javascript/flavours/glitch/features/domain_blocks/index.jsx
index cb0b55c63..1ab7c3663 100644
--- a/app/javascript/flavours/glitch/features/domain_blocks/index.js
+++ b/app/javascript/flavours/glitch/features/domain_blocks/index.jsx
@@ -23,8 +23,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['domain_lists', 'blocks', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Blocks extends ImmutablePureComponent {
 
   static propTypes = {
@@ -81,3 +79,5 @@ class Blocks extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Blocks));
diff --git a/app/javascript/flavours/glitch/features/emoji/emoji.js b/app/javascript/flavours/glitch/features/emoji/emoji.js
index 4f33200b6..24c5814c4 100644
--- a/app/javascript/flavours/glitch/features/emoji/emoji.js
+++ b/app/javascript/flavours/glitch/features/emoji/emoji.js
@@ -50,7 +50,7 @@ const emojifyTextNode = (node, customEmojis) => {
         if (shortname in customEmojis) {
           const filename = autoPlayGif ? customEmojis[shortname].url : customEmojis[shortname].static_url;
           replacement = document.createElement('img');
-          replacement.setAttribute('draggable', false);
+          replacement.setAttribute('draggable', 'false');
           replacement.setAttribute('class', 'emojione custom-emoji');
           replacement.setAttribute('alt', shortname);
           replacement.setAttribute('title', shortname);
@@ -65,7 +65,7 @@ const emojifyTextNode = (node, customEmojis) => {
       const { filename, shortCode } = unicodeMapping[match];
       const title = shortCode ? `:${shortCode}:` : '';
       replacement = document.createElement('img');
-      replacement.setAttribute('draggable', false);
+      replacement.setAttribute('draggable', 'false');
       replacement.setAttribute('class', 'emojione');
       replacement.setAttribute('alt', match);
       replacement.setAttribute('title', title);
diff --git a/app/javascript/flavours/glitch/features/emoji/emoji_utils.js b/app/javascript/flavours/glitch/features/emoji/emoji_utils.js
index dbf725c1f..be793526d 100644
--- a/app/javascript/flavours/glitch/features/emoji/emoji_utils.js
+++ b/app/javascript/flavours/glitch/features/emoji/emoji_utils.js
@@ -73,7 +73,7 @@ const stringFromCodePoint = _String.fromCodePoint || function () {
 
 const _JSON = JSON;
 
-const COLONS_REGEX = /^(?:\:([^\:]+)\:)(?:\:skin-tone-(\d)\:)?$/;
+const COLONS_REGEX = /^(?::([^:]+):)(?::skin-tone-(\d):)?$/;
 const SKINS = [
   '1F3FA', '1F3FB', '1F3FC',
   '1F3FD', '1F3FE', '1F3FF',
@@ -135,19 +135,19 @@ function getData(emoji, skin, set) {
       }
     }
 
-    if (data.short_names.hasOwnProperty(emoji)) {
+    if (Object.prototype.hasOwnProperty.call(data.short_names, emoji)) {
       emoji = data.short_names[emoji];
     }
 
-    if (data.emojis.hasOwnProperty(emoji)) {
+    if (Object.prototype.hasOwnProperty.call(data.emojis, emoji)) {
       emojiData = data.emojis[emoji];
     }
   } else if (emoji.id) {
-    if (data.short_names.hasOwnProperty(emoji.id)) {
+    if (Object.prototype.hasOwnProperty.call(data.short_names, emoji.id)) {
       emoji.id = data.short_names[emoji.id];
     }
 
-    if (data.emojis.hasOwnProperty(emoji.id)) {
+    if (Object.prototype.hasOwnProperty.call(data.emojis, emoji.id)) {
       emojiData = data.emojis[emoji.id];
       skin = skin || emoji.skin;
     }
@@ -216,7 +216,7 @@ function deepMerge(a, b) {
     let originalValue = a[key],
       value = originalValue;
 
-    if (b.hasOwnProperty(key)) {
+    if (Object.prototype.hasOwnProperty.call(b, key)) {
       value = b[key];
     }
 
diff --git a/app/javascript/flavours/glitch/features/explore/components/story.js b/app/javascript/flavours/glitch/features/explore/components/story.jsx
index 8270d3ccb..8270d3ccb 100644
--- a/app/javascript/flavours/glitch/features/explore/components/story.js
+++ b/app/javascript/flavours/glitch/features/explore/components/story.jsx
diff --git a/app/javascript/flavours/glitch/features/explore/index.js b/app/javascript/flavours/glitch/features/explore/index.jsx
index da0dc7f7c..3587de1db 100644
--- a/app/javascript/flavours/glitch/features/explore/index.js
+++ b/app/javascript/flavours/glitch/features/explore/index.jsx
@@ -24,8 +24,6 @@ const mapStateToProps = state => ({
   isSearching: state.getIn(['search', 'submitted']) || !showTrends,
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Explore extends React.PureComponent {
 
   static contextTypes = {
@@ -41,11 +39,11 @@ class Explore extends React.PureComponent {
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   render() {
     const { intl, multiColumn, isSearching } = this.props;
@@ -90,7 +88,9 @@ class Explore extends React.PureComponent {
                 <Route path='/explore/tags' component={Tags} />
                 <Route path='/explore/links' component={Links} />
                 <Route path='/explore/suggestions' component={Suggestions} />
-                <Route exact path={['/explore', '/explore/posts', '/search']} component={Statuses} componentParams={{ multiColumn }} />
+                <Route exact path={['/explore', '/explore/posts', '/search']}>
+                  <Statuses multiColumn={multiColumn} />
+                </Route>
               </Switch>
 
               <Helmet>
@@ -105,3 +105,5 @@ class Explore extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Explore));
diff --git a/app/javascript/flavours/glitch/features/explore/links.js b/app/javascript/flavours/glitch/features/explore/links.jsx
index 092f86b29..425934c4a 100644
--- a/app/javascript/flavours/glitch/features/explore/links.js
+++ b/app/javascript/flavours/glitch/features/explore/links.jsx
@@ -13,7 +13,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['trends', 'links', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Links extends React.PureComponent {
 
   static propTypes = {
@@ -68,3 +67,5 @@ class Links extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Links);
diff --git a/app/javascript/flavours/glitch/features/explore/results.js b/app/javascript/flavours/glitch/features/explore/results.jsx
index 892980d95..0d6c0e8f1 100644
--- a/app/javascript/flavours/glitch/features/explore/results.js
+++ b/app/javascript/flavours/glitch/features/explore/results.jsx
@@ -42,8 +42,6 @@ const renderStatuses = (results, onLoadMore) => appendLoadMore('statuses', resul
   <Status key={`status-${item}`} id={item} />
 )), onLoadMore);
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Results extends React.PureComponent {
 
   static propTypes = {
@@ -124,3 +122,5 @@ class Results extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Results));
diff --git a/app/javascript/flavours/glitch/features/explore/statuses.js b/app/javascript/flavours/glitch/features/explore/statuses.jsx
index 0a5c9de23..381c50c5d 100644
--- a/app/javascript/flavours/glitch/features/explore/statuses.js
+++ b/app/javascript/flavours/glitch/features/explore/statuses.jsx
@@ -14,7 +14,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'trending', 'next']),
 });
 
-export default @connect(mapStateToProps)
 class Statuses extends React.PureComponent {
 
   static propTypes = {
@@ -33,7 +32,7 @@ class Statuses extends React.PureComponent {
   handleLoadMore = debounce(() => {
     const { dispatch } = this.props;
     dispatch(expandTrendingStatuses());
-  }, 300, { leading: true })
+  }, 300, { leading: true });
 
   render () {
     const { isLoading, hasMore, statusIds, multiColumn } = this.props;
@@ -62,3 +61,5 @@ class Statuses extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Statuses);
diff --git a/app/javascript/flavours/glitch/features/explore/suggestions.js b/app/javascript/flavours/glitch/features/explore/suggestions.jsx
index 52e5ce62b..e1b84098a 100644
--- a/app/javascript/flavours/glitch/features/explore/suggestions.js
+++ b/app/javascript/flavours/glitch/features/explore/suggestions.jsx
@@ -12,7 +12,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['suggestions', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Suggestions extends React.PureComponent {
 
   static propTypes = {
@@ -29,7 +28,7 @@ class Suggestions extends React.PureComponent {
   handleDismiss = (accountId) => {
     const { dispatch } = this.props;
     dispatch(dismissSuggestion(accountId));
-  }
+  };
 
   render () {
     const { isLoading, suggestions } = this.props;
@@ -54,3 +53,5 @@ class Suggestions extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Suggestions);
diff --git a/app/javascript/flavours/glitch/features/explore/tags.js b/app/javascript/flavours/glitch/features/explore/tags.jsx
index 938036b64..e0fdd1d91 100644
--- a/app/javascript/flavours/glitch/features/explore/tags.js
+++ b/app/javascript/flavours/glitch/features/explore/tags.jsx
@@ -13,7 +13,6 @@ const mapStateToProps = state => ({
   isLoadingHashtags: state.getIn(['trends', 'tags', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Tags extends React.PureComponent {
 
   static propTypes = {
@@ -60,3 +59,5 @@ class Tags extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Tags);
diff --git a/app/javascript/flavours/glitch/features/favourited_statuses/index.js b/app/javascript/flavours/glitch/features/favourited_statuses/index.jsx
index a03e1a4eb..60d281f97 100644
--- a/app/javascript/flavours/glitch/features/favourited_statuses/index.js
+++ b/app/javascript/flavours/glitch/features/favourited_statuses/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Favourites extends ImmutablePureComponent {
 
   static propTypes = {
@@ -48,24 +46,24 @@ class Favourites extends ImmutablePureComponent {
     } else {
       dispatch(addColumn('FAVOURITES', {}));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = debounce(() => {
     this.props.dispatch(expandFavouritedStatuses());
-  }, 300, { leading: true })
+  }, 300, { leading: true });
 
   render () {
     const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
@@ -106,3 +104,5 @@ class Favourites extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Favourites));
diff --git a/app/javascript/flavours/glitch/features/favourites/index.js b/app/javascript/flavours/glitch/features/favourites/index.jsx
index 47c3279c4..21ce7fcc7 100644
--- a/app/javascript/flavours/glitch/features/favourites/index.js
+++ b/app/javascript/flavours/glitch/features/favourites/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Favourites extends ImmutablePureComponent {
 
   static propTypes = {
@@ -48,15 +46,15 @@ class Favourites extends ImmutablePureComponent {
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleRefresh = () => {
     this.props.dispatch(fetchFavourites(this.props.params.statusId));
-  }
+  };
 
   render () {
     const { intl, accountIds, multiColumn } = this.props;
@@ -101,3 +99,5 @@ class Favourites extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Favourites));
diff --git a/app/javascript/flavours/glitch/features/filters/added_to_filter.js b/app/javascript/flavours/glitch/features/filters/added_to_filter.jsx
index becb170cd..2f3f98c81 100644
--- a/app/javascript/flavours/glitch/features/filters/added_to_filter.js
+++ b/app/javascript/flavours/glitch/features/filters/added_to_filter.jsx
@@ -10,7 +10,6 @@ const mapStateToProps = (state, { filterId }) => ({
   filter: state.getIn(['filters', filterId]),
 });
 
-export default @connect(mapStateToProps)
 class AddedToFilter extends React.PureComponent {
 
   static propTypes = {
@@ -100,3 +99,5 @@ class AddedToFilter extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AddedToFilter);
diff --git a/app/javascript/flavours/glitch/features/filters/select_filter.js b/app/javascript/flavours/glitch/features/filters/select_filter.jsx
index 5391766c9..a33892f83 100644
--- a/app/javascript/flavours/glitch/features/filters/select_filter.js
+++ b/app/javascript/flavours/glitch/features/filters/select_filter.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = (state, { contextType }) => ({
   ]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class SelectFilter extends React.PureComponent {
 
   static propTypes = {
@@ -67,15 +65,15 @@ class SelectFilter extends React.PureComponent {
     }
 
     return (
-      <div key={filter[0]} role='button' tabIndex='0' data-index={filter[0]} className='language-dropdown__dropdown__results__item' onClick={this.handleItemClick} onKeyDown={this.handleKeyDown}>
+      <div key={filter[0]} role='button' tabIndex={0} data-index={filter[0]} className='language-dropdown__dropdown__results__item' onClick={this.handleItemClick} onKeyDown={this.handleKeyDown}>
         <span className='language-dropdown__dropdown__results__item__native-name'>{filter[1]}</span> {warning}
       </div>
     );
-  }
+  };
 
   renderCreateNew (name) {
     return (
-      <div key='add-new-filter' role='button' tabIndex='0' className='language-dropdown__dropdown__results__item' onClick={this.handleNewFilterClick} onKeyDown={this.handleKeyDown}>
+      <div key='add-new-filter' role='button' tabIndex={0} className='language-dropdown__dropdown__results__item' onClick={this.handleNewFilterClick} onKeyDown={this.handleKeyDown}>
         <Icon id='plus' fixedWidth /> <FormattedMessage id='filter_modal.select_filter.prompt_new' defaultMessage='New category: {name}' values={{ name }} />
       </div>
     );
@@ -83,11 +81,11 @@ class SelectFilter extends React.PureComponent {
 
   handleSearchChange = ({ target }) => {
     this.setState({ searchValue: target.value });
-  }
+  };
 
   setListRef = c => {
     this.listNode = c;
-  }
+  };
 
   handleKeyDown = e => {
     const index = Array.from(this.listNode.childNodes).findIndex(node => node === e.currentTarget);
@@ -125,7 +123,7 @@ class SelectFilter extends React.PureComponent {
       e.preventDefault();
       e.stopPropagation();
     }
-  }
+  };
 
   handleSearchKeyDown = e => {
     let element = null;
@@ -143,11 +141,11 @@ class SelectFilter extends React.PureComponent {
 
       break;
     }
-  }
+  };
 
   handleClear = () => {
     this.setState({ searchValue: '' });
-  }
+  };
 
   handleItemClick = e => {
     const value = e.currentTarget.getAttribute('data-index');
@@ -155,7 +153,7 @@ class SelectFilter extends React.PureComponent {
     e.preventDefault();
 
     this.props.onSelectFilter(value);
-  }
+  };
 
   handleNewFilterClick = e => {
     e.preventDefault();
@@ -190,3 +188,5 @@ class SelectFilter extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(SelectFilter));
diff --git a/app/javascript/flavours/glitch/features/follow_recommendations/components/account.js b/app/javascript/flavours/glitch/features/follow_recommendations/components/account.jsx
index 2c668da3e..e56af7364 100644
--- a/app/javascript/flavours/glitch/features/follow_recommendations/components/account.js
+++ b/app/javascript/flavours/glitch/features/follow_recommendations/components/account.jsx
@@ -27,13 +27,11 @@ const makeMapStateToProps = () => {
 };
 
 const getFirstSentence = str => {
-  const arr = str.split(/(([\.\?!]+\s)|[.。?!\n•])/);
+  const arr = str.split(/(([.?!]+\s)|[.。?!\n•])/);
 
   return arr[0];
 };
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -50,7 +48,7 @@ class Account extends ImmutablePureComponent {
     } else {
       dispatch(followAccount(account.get('id')));
     }
-  }
+  };
 
   render () {
     const { account, intl } = this.props;
@@ -83,3 +81,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(Account));
diff --git a/app/javascript/flavours/glitch/features/follow_recommendations/index.js b/app/javascript/flavours/glitch/features/follow_recommendations/index.jsx
index d9d962b7c..70f2191f1 100644
--- a/app/javascript/flavours/glitch/features/follow_recommendations/index.js
+++ b/app/javascript/flavours/glitch/features/follow_recommendations/index.jsx
@@ -19,7 +19,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['suggestions', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class FollowRecommendations extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -69,7 +68,7 @@ class FollowRecommendations extends ImmutablePureComponent {
     }));
 
     router.history.push('/home');
-  }
+  };
 
   render () {
     const { suggestions, isLoading } = this.props;
@@ -114,3 +113,5 @@ class FollowRecommendations extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(FollowRecommendations);
diff --git a/app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.js b/app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.jsx
index cbe7a1032..af8a534fa 100644
--- a/app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.js
+++ b/app/javascript/flavours/glitch/features/follow_requests/components/account_authorize.jsx
@@ -13,7 +13,6 @@ const messages = defineMessages({
   reject: { id: 'follow_request.reject', defaultMessage: 'Reject' },
 });
 
-export default @injectIntl
 class AccountAuthorize extends ImmutablePureComponent {
 
   static propTypes = {
@@ -47,3 +46,5 @@ class AccountAuthorize extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(AccountAuthorize);
diff --git a/app/javascript/flavours/glitch/features/follow_requests/index.js b/app/javascript/flavours/glitch/features/follow_requests/index.jsx
index 7b35e3ec9..a9a35f54b 100644
--- a/app/javascript/flavours/glitch/features/follow_requests/index.js
+++ b/app/javascript/flavours/glitch/features/follow_requests/index.jsx
@@ -3,7 +3,6 @@ import { connect } from 'react-redux';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { debounce } from 'lodash';
-import LoadingIndicator from 'flavours/glitch/components/loading_indicator';
 import Column from 'flavours/glitch/features/ui/components/column';
 import ColumnBackButtonSlim from 'flavours/glitch/components/column_back_button_slim';
 import AccountAuthorizeContainer from './containers/account_authorize_container';
@@ -26,8 +25,6 @@ const mapStateToProps = state => ({
   domain: state.getIn(['meta', 'domain']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class FollowRequests extends ImmutablePureComponent {
 
   static propTypes = {
@@ -53,16 +50,8 @@ class FollowRequests extends ImmutablePureComponent {
   render () {
     const { intl, accountIds, hasMore, multiColumn, locked, domain, isLoading } = this.props;
 
-    if (!accountIds) {
-      return (
-        <Column name='follow-requests'>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
     const emptyMessage = <FormattedMessage id='empty_column.follow_requests' defaultMessage="You don't have any follow requests yet. When you receive one, it will show up here." />;
-    const unlockedPrependMessage = locked ? null : (
+    const unlockedPrependMessage = !locked && accountIds.size > 0 && (
       <div className='follow_requests-unlocked_explanation'>
         <FormattedMessage
           id='follow_requests.unlocked_explanation'
@@ -81,6 +70,7 @@ class FollowRequests extends ImmutablePureComponent {
           onLoadMore={this.handleLoadMore}
           hasMore={hasMore}
           isLoading={isLoading}
+          showLoading={isLoading && accountIds.size === 0}
           emptyMessage={emptyMessage}
           bindToDocument={!multiColumn}
           prepend={unlockedPrependMessage}
@@ -98,3 +88,5 @@ class FollowRequests extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(FollowRequests));
diff --git a/app/javascript/flavours/glitch/features/followed_tags/index.jsx b/app/javascript/flavours/glitch/features/followed_tags/index.jsx
new file mode 100644
index 000000000..a5abb151f
--- /dev/null
+++ b/app/javascript/flavours/glitch/features/followed_tags/index.jsx
@@ -0,0 +1,89 @@
+import { debounce } from 'lodash';
+import PropTypes from 'prop-types';
+import React from 'react';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
+import { connect } from 'react-redux';
+import ColumnHeader from 'flavours/glitch/components/column_header';
+import ScrollableList from 'flavours/glitch/components/scrollable_list';
+import Column from 'flavours/glitch/features/ui/components/column';
+import { Helmet } from 'react-helmet';
+import Hashtag from 'flavours/glitch/components/hashtag';
+import { expandFollowedHashtags, fetchFollowedHashtags } from 'flavours/glitch/actions/tags';
+
+const messages = defineMessages({
+  heading: { id: 'followed_tags', defaultMessage: 'Followed hashtags' },
+});
+
+const mapStateToProps = state => ({
+  hashtags: state.getIn(['followed_tags', 'items']),
+  isLoading: state.getIn(['followed_tags', 'isLoading'], true),
+  hasMore: !!state.getIn(['followed_tags', 'next']),
+});
+
+class FollowedTags extends ImmutablePureComponent {
+
+  static propTypes = {
+    params: PropTypes.object.isRequired,
+    dispatch: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+    hashtags: ImmutablePropTypes.list,
+    isLoading: PropTypes.bool,
+    hasMore: PropTypes.bool,
+    multiColumn: PropTypes.bool,
+  };
+
+  componentDidMount() {
+    this.props.dispatch(fetchFollowedHashtags());
+  }
+
+  handleLoadMore = debounce(() => {
+    this.props.dispatch(expandFollowedHashtags());
+  }, 300, { leading: true });
+
+  render () {
+    const { intl, hashtags, isLoading, hasMore, multiColumn } = this.props;
+
+    const emptyMessage = <FormattedMessage id='empty_column.followed_tags' defaultMessage='You have not followed any hashtags yet. When you do, they will show up here.' />;
+
+    return (
+      <Column bindToDocument={!multiColumn}>
+        <ColumnHeader
+          icon='hashtag'
+          title={intl.formatMessage(messages.heading)}
+          showBackButton
+          multiColumn={multiColumn}
+        />
+
+        <ScrollableList
+          scrollKey='followed_tags'
+          emptyMessage={emptyMessage}
+          hasMore={hasMore}
+          isLoading={isLoading}
+          onLoadMore={this.handleLoadMore}
+          bindToDocument={!multiColumn}
+        >
+          {hashtags.map((hashtag) => (
+            <Hashtag
+              key={hashtag.get('name')}
+              name={hashtag.get('name')}
+              to={`/tags/${hashtag.get('name')}`}
+              withGraph={false}
+              // Taken from ImmutableHashtag. Should maybe refactor ImmutableHashtag to accept more options?
+              people={hashtag.getIn(['history', 0, 'accounts']) * 1 + hashtag.getIn(['history', 1, 'accounts']) * 1}
+              history={hashtag.get('history').reverse().map((day) => day.get('uses')).toArray()}
+            />
+          ))}
+        </ScrollableList>
+
+        <Helmet>
+          <meta name='robots' content='noindex' />
+        </Helmet>
+      </Column>
+    );
+  }
+
+}
+
+export default connect(mapStateToProps)(injectIntl(FollowedTags));
diff --git a/app/javascript/flavours/glitch/features/followers/index.js b/app/javascript/flavours/glitch/features/followers/index.jsx
index 7122c1905..2565772d1 100644
--- a/app/javascript/flavours/glitch/features/followers/index.js
+++ b/app/javascript/flavours/glitch/features/followers/index.jsx
@@ -53,7 +53,6 @@ RemoteHint.propTypes = {
   url: PropTypes.string.isRequired,
 };
 
-export default @connect(mapStateToProps)
 class Followers extends ImmutablePureComponent {
 
   static propTypes = {
@@ -107,11 +106,11 @@ class Followers extends ImmutablePureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   render () {
     const { accountId, accountIds, hasMore, isAccount, multiColumn, isLoading, suspended, hidden, remote, remoteUrl } = this.props;
@@ -172,3 +171,5 @@ class Followers extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Followers);
diff --git a/app/javascript/flavours/glitch/features/following/index.js b/app/javascript/flavours/glitch/features/following/index.jsx
index 4ad670105..2c05e3310 100644
--- a/app/javascript/flavours/glitch/features/following/index.js
+++ b/app/javascript/flavours/glitch/features/following/index.jsx
@@ -53,7 +53,6 @@ RemoteHint.propTypes = {
   url: PropTypes.string.isRequired,
 };
 
-export default @connect(mapStateToProps)
 class Following extends ImmutablePureComponent {
 
   static propTypes = {
@@ -107,11 +106,11 @@ class Following extends ImmutablePureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   render () {
     const { accountId, accountIds, hasMore, isAccount, multiColumn, isLoading, suspended, hidden, remote, remoteUrl } = this.props;
@@ -172,3 +171,5 @@ class Following extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Following);
diff --git a/app/javascript/flavours/glitch/features/generic_not_found/index.js b/app/javascript/flavours/glitch/features/generic_not_found/index.jsx
index 4412adaed..4412adaed 100644
--- a/app/javascript/flavours/glitch/features/generic_not_found/index.js
+++ b/app/javascript/flavours/glitch/features/generic_not_found/index.jsx
diff --git a/app/javascript/flavours/glitch/features/getting_started/components/announcements.js b/app/javascript/flavours/glitch/features/getting_started/components/announcements.jsx
index 93f3c9428..29288076b 100644
--- a/app/javascript/flavours/glitch/features/getting_started/components/announcements.js
+++ b/app/javascript/flavours/glitch/features/getting_started/components/announcements.jsx
@@ -6,9 +6,8 @@ import PropTypes from 'prop-types';
 import IconButton from 'flavours/glitch/components/icon_button';
 import Icon from 'flavours/glitch/components/icon';
 import { defineMessages, injectIntl, FormattedMessage, FormattedDate } from 'react-intl';
-import { autoPlayGif, reduceMotion, disableSwiping } from 'flavours/glitch/initial_state';
+import { autoPlayGif, reduceMotion, disableSwiping, mascot } from 'flavours/glitch/initial_state';
 import elephantUIPlane from 'mastodon/../images/elephant_ui_plane.svg';
-import { mascot } from 'flavours/glitch/initial_state';
 import unicodeMapping from 'flavours/glitch/features/emoji/emoji_unicode_mapping_light';
 import classNames from 'classnames';
 import EmojiPickerDropdown from 'flavours/glitch/features/compose/containers/emoji_picker_dropdown_container';
@@ -35,7 +34,7 @@ class Content extends ImmutablePureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   componentDidMount () {
     this._updateLinks();
@@ -89,7 +88,7 @@ class Content extends ImmutablePureComponent {
       e.preventDefault();
       this.context.router.history.push(`/@${mention.get('acct')}`);
     }
-  }
+  };
 
   onHashtagClick = (hashtag, e) => {
     hashtag = hashtag.replace(/^#/, '');
@@ -98,14 +97,14 @@ class Content extends ImmutablePureComponent {
       e.preventDefault();
       this.context.router.history.push(`/tags/${hashtag}`);
     }
-  }
+  };
 
   onStatusClick = (status, e) => {
     if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
       e.preventDefault();
       this.context.router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
     }
-  }
+  };
 
   handleMouseEnter = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -118,7 +117,7 @@ class Content extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -131,7 +130,7 @@ class Content extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   render () {
     const { announcement } = this.props;
@@ -216,11 +215,11 @@ class Reaction extends ImmutablePureComponent {
     } else {
       addReaction(announcementId, reaction.get('name'));
     }
-  }
+  };
 
-  handleMouseEnter = () => this.setState({ hovered: true })
+  handleMouseEnter = () => this.setState({ hovered: true });
 
-  handleMouseLeave = () => this.setState({ hovered: false })
+  handleMouseLeave = () => this.setState({ hovered: false });
 
   render () {
     const { reaction } = this.props;
@@ -254,7 +253,7 @@ class ReactionsBar extends ImmutablePureComponent {
   handleEmojiPick = data => {
     const { addReaction, announcementId } = this.props;
     addReaction(announcementId, data.native.replace(/:/g, ''));
-  }
+  };
 
   willEnter () {
     return { scale: reduceMotion ? 1 : 0 };
@@ -356,7 +355,6 @@ class Announcement extends ImmutablePureComponent {
 
 }
 
-export default @injectIntl
 class Announcements extends ImmutablePureComponent {
 
   static propTypes = {
@@ -397,15 +395,15 @@ class Announcements extends ImmutablePureComponent {
 
   handleChangeIndex = index => {
     this.setState({ index: index % this.props.announcements.size });
-  }
+  };
 
   handleNextClick = () => {
     this.setState({ index: (this.state.index + 1) % this.props.announcements.size });
-  }
+  };
 
   handlePrevClick = () => {
     this.setState({ index: (this.props.announcements.size + this.state.index - 1) % this.props.announcements.size });
-  }
+  };
 
   render () {
     const { announcements, intl } = this.props;
@@ -420,7 +418,7 @@ class Announcements extends ImmutablePureComponent {
         <img className='announcements__mastodon' alt='' draggable='false' src={mascot || elephantUIPlane} />
 
         <div className='announcements__container'>
-          <ReactSwipeableViews animateHeight={!reduceMotion} adjustHeight={reduceMotion} index={index} onChangeIndex={this.handleChangeIndex}>
+          <ReactSwipeableViews animateHeight animateTransitions={!reduceMotion} index={index} onChangeIndex={this.handleChangeIndex}>
             {announcements.map((announcement, idx) => (
               <Announcement
                 key={announcement.get('id')}
@@ -448,3 +446,5 @@ class Announcements extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Announcements);
diff --git a/app/javascript/flavours/glitch/features/getting_started/components/trends.js b/app/javascript/flavours/glitch/features/getting_started/components/trends.jsx
index d45934d6e..d45934d6e 100644
--- a/app/javascript/flavours/glitch/features/getting_started/components/trends.js
+++ b/app/javascript/flavours/glitch/features/getting_started/components/trends.jsx
diff --git a/app/javascript/flavours/glitch/features/getting_started/index.js b/app/javascript/flavours/glitch/features/getting_started/index.jsx
index f9d79013b..4064a5451 100644
--- a/app/javascript/flavours/glitch/features/getting_started/index.js
+++ b/app/javascript/flavours/glitch/features/getting_started/index.jsx
@@ -79,9 +79,7 @@ const badgeDisplay = (number, limit) => {
 
 const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
 
- export default @connect(makeMapStateToProps, mapDispatchToProps)
- @injectIntl
- class GettingStarted extends ImmutablePureComponent {
+class GettingStarted extends ImmutablePureComponent {
 
   static contextTypes = {
     router: PropTypes.object.isRequired,
@@ -164,7 +162,7 @@ const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
         <div key='9'>
           <ColumnLink key='lists' icon='bars' text={intl.formatMessage(messages.lists)} to='/lists' />
           {lists.filter(list => !columns.find(item => item.get('id') === 'LIST' && item.getIn(['params', 'id']) === list.get('id'))).map(list =>
-            <ColumnLink key={`list-${list.get('id')}`} to={`/lists/${list.get('id')}`} icon='list-ul' text={list.get('title')} />
+            <ColumnLink key={`list-${list.get('id')}`} to={`/lists/${list.get('id')}`} icon='list-ul' text={list.get('title')} />,
           )}
         </div>,
       ]);
@@ -202,3 +200,5 @@ const NAVIGATION_PANEL_BREAKPOINT = 600 + (285 * 2) + (10 * 2);
   }
 
 }
+
+export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted));
diff --git a/app/javascript/flavours/glitch/features/getting_started_misc/index.js b/app/javascript/flavours/glitch/features/getting_started_misc/index.jsx
index de354d6b1..fb4ec2fce 100644
--- a/app/javascript/flavours/glitch/features/getting_started_misc/index.js
+++ b/app/javascript/flavours/glitch/features/getting_started_misc/index.jsx
@@ -16,17 +16,18 @@ const messages = defineMessages({
   blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
   domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
   mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
-  info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' },
   show_me_around: { id: 'getting_started.onboarding', defaultMessage: 'Show me around' },
   pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned posts' },
-  info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' },
   keyboard_shortcuts: { id: 'navigation_bar.keyboard_shortcuts', defaultMessage: 'Keyboard shortcuts' },
   featured_users: { id: 'navigation_bar.featured_users', defaultMessage: 'Featured users' },
 });
 
-export default @connect()
-@injectIntl
-class gettingStartedMisc extends ImmutablePureComponent {
+class GettingStartedMisc extends ImmutablePureComponent {
+
+  static contextTypes = {
+    router: PropTypes.object.isRequired,
+    identity: PropTypes.object,
+  };
 
   static propTypes = {
     intl: PropTypes.object.isRequired,
@@ -35,16 +36,15 @@ class gettingStartedMisc extends ImmutablePureComponent {
 
   openOnboardingModal = (e) => {
     this.props.dispatch(openModal('ONBOARDING'));
-  }
+  };
 
   openFeaturedAccountsModal = (e) => {
     this.props.dispatch(openModal('PINNED_ACCOUNTS_EDITOR'));
-  }
+  };
 
   render () {
     const { intl } = this.props;
-
-    let i = 1;
+    const { signedIn } = this.context.identity;
 
     return (
       <Column icon='ellipsis-h' heading={intl.formatMessage(messages.heading)}>
@@ -52,18 +52,19 @@ class gettingStartedMisc extends ImmutablePureComponent {
 
         <div className='scrollable'>
           <ColumnSubheading text={intl.formatMessage(messages.subheading)} />
-          <ColumnLink key='{i++}' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />
-          <ColumnLink key='{i++}' icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />
-          <ColumnLink key='{i++}' icon='users' text={intl.formatMessage(messages.featured_users)} onClick={this.openFeaturedAccountsModal} />
-          <ColumnLink key='{i++}' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
-          <ColumnLink key='{i++}' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
-          <ColumnLink key='{i++}' icon='minus-circle' text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' />
-          <ColumnLink key='{i++}' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' />
-          <ColumnLink key='{i++}' icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />
-          <ColumnLink key='{i++}' icon='hand-o-right' text={intl.formatMessage(messages.show_me_around)} onClick={this.openOnboardingModal} />
+          {signedIn && (<ColumnLink key='favourites' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />)}
+          {signedIn && (<ColumnLink key='pinned' icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />)}
+          {signedIn && (<ColumnLink key='featured_users' icon='users' text={intl.formatMessage(messages.featured_users)} onClick={this.openFeaturedAccountsModal} />)}
+          {signedIn && (<ColumnLink key='mutes' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />)}
+          {signedIn && (<ColumnLink key='blocks' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />)}
+          {signedIn && (<ColumnLink key='domain_blocks' icon='minus-circle' text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' />)}
+          <ColumnLink key='shortcuts' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' />
+          {signedIn && (<ColumnLink key='onboarding' icon='hand-o-right' text={intl.formatMessage(messages.show_me_around)} onClick={this.openOnboardingModal} />)}
         </div>
       </Column>
     );
   }
 
 }
+
+export default connect()(injectIntl(GettingStartedMisc));
diff --git a/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.js b/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.jsx
index ac7863ed3..f140f2d01 100644
--- a/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.js
+++ b/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.jsx
@@ -12,7 +12,6 @@ const messages = defineMessages({
   noOptions: { id: 'hashtag.column_settings.select.no_options_message', defaultMessage: 'No suggestions found' },
 });
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -131,3 +130,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/flavours/glitch/features/hashtag_timeline/index.js b/app/javascript/flavours/glitch/features/hashtag_timeline/index.jsx
index 219dc0ec6..fe5afa240 100644
--- a/app/javascript/flavours/glitch/features/hashtag_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/hashtag_timeline/index.jsx
@@ -26,8 +26,6 @@ const mapStateToProps = (state, props) => ({
   tag: state.getIn(['tags', props.params.id]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class HashtagTimeline extends React.PureComponent {
 
   disconnects = [];
@@ -54,7 +52,7 @@ class HashtagTimeline extends React.PureComponent {
     } else {
       dispatch(addColumn('HASHTAG', { id: this.props.params.id }));
     }
-  }
+  };
 
   title = () => {
     const { id } = this.props.params;
@@ -73,7 +71,7 @@ class HashtagTimeline extends React.PureComponent {
     }
 
     return title;
-  }
+  };
 
   additionalFor = (mode) => {
     const { tags } = this.props.params;
@@ -83,16 +81,16 @@ class HashtagTimeline extends React.PureComponent {
     } else {
       return '';
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   _subscribe (dispatch, id, tags = {}, local) {
     const { signedIn } = this.context.identity;
@@ -157,14 +155,14 @@ class HashtagTimeline extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = maxId => {
     const { dispatch, params } = this.props;
     const { id, tags, local }  = params;
 
     dispatch(expandHashtagTimeline(id, { maxId, tags, local }));
-  }
+  };
 
   handleFollow = () => {
     const { dispatch, params, tag } = this.props;
@@ -180,7 +178,7 @@ class HashtagTimeline extends React.PureComponent {
     } else {
       dispatch(followHashtag(id));
     }
-  }
+  };
 
   render () {
     const { hasUnread, columnId, multiColumn, tag, intl } = this.props;
@@ -193,8 +191,12 @@ class HashtagTimeline extends React.PureComponent {
     if (tag) {
       const following = tag.get('following');
 
+      const classes = classNames('column-header__button', {
+        active: following,
+      });
+
       followButton = (
-        <button className={classNames('column-header__button')} onClick={this.handleFollow} disabled={!signedIn} active={following} title={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-label={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)}>
+        <button className={classes} onClick={this.handleFollow} disabled={!signedIn} title={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-label={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)}>
           <Icon id={following ? 'user-times' : 'user-plus'} fixedWidth className='column-header__icon' />
         </button>
       );
@@ -235,3 +237,5 @@ class HashtagTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(HashtagTimeline));
diff --git a/app/javascript/flavours/glitch/features/home_timeline/components/column_settings.js b/app/javascript/flavours/glitch/features/home_timeline/components/column_settings.jsx
index df615db65..1eeeaa378 100644
--- a/app/javascript/flavours/glitch/features/home_timeline/components/column_settings.js
+++ b/app/javascript/flavours/glitch/features/home_timeline/components/column_settings.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   settings: { id: 'home.settings', defaultMessage: 'Column settings' },
 });
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -48,3 +47,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/flavours/glitch/features/home_timeline/index.js b/app/javascript/flavours/glitch/features/home_timeline/index.jsx
index 5ed108ad2..71619394b 100644
--- a/app/javascript/flavours/glitch/features/home_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/home_timeline/index.jsx
@@ -31,8 +31,6 @@ const mapStateToProps = state => ({
   regex: state.getIn(['settings', 'home', 'regex', 'body']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class HomeTimeline extends React.PureComponent {
 
   static contextTypes = {
@@ -60,24 +58,24 @@ class HomeTimeline extends React.PureComponent {
     } else {
       dispatch(addColumn('HOME', {}));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = maxId => {
     this.props.dispatch(expandHomeTimeline({ maxId }));
-  }
+  };
 
   componentDidMount () {
     setTimeout(() => this.props.dispatch(fetchAnnouncements()), 700);
@@ -116,7 +114,7 @@ class HomeTimeline extends React.PureComponent {
   handleToggleAnnouncementsClick = (e) => {
     e.stopPropagation();
     this.props.dispatch(toggleShowAnnouncements());
-  }
+  };
 
   render () {
     const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
@@ -176,3 +174,5 @@ class HomeTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(HomeTimeline));
diff --git a/app/javascript/flavours/glitch/features/interaction_modal/index.js b/app/javascript/flavours/glitch/features/interaction_modal/index.jsx
index b71c041c9..20e4959e6 100644
--- a/app/javascript/flavours/glitch/features/interaction_modal/index.js
+++ b/app/javascript/flavours/glitch/features/interaction_modal/index.jsx
@@ -30,14 +30,14 @@ class Copypaste extends React.PureComponent {
 
   setRef = c => {
     this.input = c;
-  }
+  };
 
   handleInputClick = () => {
     this.setState({ copied: false });
     this.input.focus();
     this.input.select();
     this.input.setSelectionRange(0, this.input.value.length);
-  }
+  };
 
   handleButtonClick = () => {
     const { value } = this.props;
@@ -45,7 +45,7 @@ class Copypaste extends React.PureComponent {
     this.input.blur();
     this.setState({ copied: true });
     this.timeout = setTimeout(() => this.setState({ copied: false }), 700);
-  }
+  };
 
   componentWillUnmount () {
     if (this.timeout) clearTimeout(this.timeout);
@@ -74,7 +74,6 @@ class Copypaste extends React.PureComponent {
 
 }
 
-export default @connect(mapStateToProps, mapDispatchToProps)
 class InteractionModal extends React.PureComponent {
 
   static propTypes = {
@@ -86,7 +85,7 @@ class InteractionModal extends React.PureComponent {
 
   handleSignupClick = () => {
     this.props.onSignupClick();
-  }
+  };
 
   render () {
     const { url, type, displayNameHtml } = this.props;
@@ -159,3 +158,5 @@ class InteractionModal extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(InteractionModal);
diff --git a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.jsx
index 2bc0116d4..7160e7efb 100644
--- a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js
+++ b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.jsx
@@ -15,8 +15,6 @@ const mapStateToProps = state => ({
   collapseEnabled: state.getIn(['local_settings', 'collapsed', 'enabled']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class KeyboardShortcuts extends ImmutablePureComponent {
 
   static propTypes = {
@@ -147,3 +145,5 @@ class KeyboardShortcuts extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(KeyboardShortcuts));
diff --git a/app/javascript/flavours/glitch/features/list_adder/components/account.js b/app/javascript/flavours/glitch/features/list_adder/components/account.jsx
index 1369aac07..034ed0edc 100644
--- a/app/javascript/flavours/glitch/features/list_adder/components/account.js
+++ b/app/javascript/flavours/glitch/features/list_adder/components/account.jsx
@@ -18,8 +18,6 @@ const makeMapStateToProps = () => {
 };
 
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -41,3 +39,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(Account));
diff --git a/app/javascript/flavours/glitch/features/list_adder/components/list.js b/app/javascript/flavours/glitch/features/list_adder/components/list.jsx
index 4666ca47b..1957bbe42 100644
--- a/app/javascript/flavours/glitch/features/list_adder/components/list.js
+++ b/app/javascript/flavours/glitch/features/list_adder/components/list.jsx
@@ -13,7 +13,7 @@ const messages = defineMessages({
   add: { id: 'lists.account.add', defaultMessage: 'Add to list' },
 });
 
-const MapStateToProps = (state, { listId, added }) => ({
+const mapStateToProps = (state, { listId, added }) => ({
   list: state.get('lists').get(listId),
   added: typeof added === 'undefined' ? state.getIn(['listAdder', 'lists', 'items']).includes(listId) : added,
 });
@@ -23,8 +23,6 @@ const mapDispatchToProps = (dispatch, { listId }) => ({
   onAdd: () => dispatch(addToListAdder(listId)),
 });
 
-export default @connect(MapStateToProps, mapDispatchToProps)
-@injectIntl
 class List extends ImmutablePureComponent {
 
   static propTypes = {
@@ -67,3 +65,5 @@ class List extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(List));
diff --git a/app/javascript/flavours/glitch/features/list_adder/index.js b/app/javascript/flavours/glitch/features/list_adder/index.jsx
index cb8a15e8c..45d5589f9 100644
--- a/app/javascript/flavours/glitch/features/list_adder/index.js
+++ b/app/javascript/flavours/glitch/features/list_adder/index.jsx
@@ -28,8 +28,6 @@ const mapDispatchToProps = dispatch => ({
   onReset: () => dispatch(resetListAdder()),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class ListAdder extends ImmutablePureComponent {
 
   static propTypes = {
@@ -71,3 +69,5 @@ class ListAdder extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ListAdder));
diff --git a/app/javascript/flavours/glitch/features/list_editor/components/account.js b/app/javascript/flavours/glitch/features/list_editor/components/account.jsx
index 71a8b7673..71a8b7673 100644
--- a/app/javascript/flavours/glitch/features/list_editor/components/account.js
+++ b/app/javascript/flavours/glitch/features/list_editor/components/account.jsx
diff --git a/app/javascript/flavours/glitch/features/list_editor/components/edit_list_form.js b/app/javascript/flavours/glitch/features/list_editor/components/edit_list_form.jsx
index a8cab2762..b4886ef0e 100644
--- a/app/javascript/flavours/glitch/features/list_editor/components/edit_list_form.js
+++ b/app/javascript/flavours/glitch/features/list_editor/components/edit_list_form.jsx
@@ -19,8 +19,6 @@ const mapDispatchToProps = dispatch => ({
   onSubmit: () => dispatch(submitListEditor(false)),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class ListForm extends React.PureComponent {
 
   static propTypes = {
@@ -33,16 +31,16 @@ class ListForm extends React.PureComponent {
 
   handleChange = e => {
     this.props.onChange(e.target.value);
-  }
+  };
 
   handleSubmit = e => {
     e.preventDefault();
     this.props.onSubmit();
-  }
+  };
 
   handleClick = () => {
     this.props.onSubmit();
-  }
+  };
 
   render () {
     const { value, disabled, intl } = this.props;
@@ -68,3 +66,5 @@ class ListForm extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ListForm));
diff --git a/app/javascript/flavours/glitch/features/list_editor/components/search.js b/app/javascript/flavours/glitch/features/list_editor/components/search.jsx
index 192643f77..3b66bc325 100644
--- a/app/javascript/flavours/glitch/features/list_editor/components/search.js
+++ b/app/javascript/flavours/glitch/features/list_editor/components/search.jsx
@@ -20,17 +20,17 @@ export default class Search extends React.PureComponent {
 
   handleChange = e => {
     this.props.onChange(e.target.value);
-  }
+  };
 
   handleKeyUp = e => {
     if (e.keyCode === 13) {
       this.props.onSubmit(this.props.value);
     }
-  }
+  };
 
   handleClear = () => {
     this.props.onClear();
-  }
+  };
 
   render () {
     const { value, intl } = this.props;
@@ -51,7 +51,7 @@ export default class Search extends React.PureComponent {
           />
         </label>
 
-        <div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
+        <div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
           <Icon id='search' className={classNames({ active: !hasValue })} />
           <Icon id='times-circle' aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
         </div>
diff --git a/app/javascript/flavours/glitch/features/list_editor/index.js b/app/javascript/flavours/glitch/features/list_editor/index.jsx
index c2ca07053..44951d1c6 100644
--- a/app/javascript/flavours/glitch/features/list_editor/index.js
+++ b/app/javascript/flavours/glitch/features/list_editor/index.jsx
@@ -22,8 +22,6 @@ const mapDispatchToProps = dispatch => ({
   onReset: () => dispatch(resetListEditor()),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class ListEditor extends ImmutablePureComponent {
 
   static propTypes = {
@@ -62,7 +60,7 @@ class ListEditor extends ImmutablePureComponent {
             {accountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} added />)}
           </div>
 
-          {showSearch && <div role='button' tabIndex='-1' className='drawer__backdrop' onClick={onClear} />}
+          {showSearch && <div role='button' tabIndex={-1} className='drawer__backdrop' onClick={onClear} />}
 
           <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
             {({ x }) =>
@@ -77,3 +75,5 @@ class ListEditor extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ListEditor));
diff --git a/app/javascript/flavours/glitch/features/list_timeline/index.js b/app/javascript/flavours/glitch/features/list_timeline/index.jsx
index a94c05c56..a32383b13 100644
--- a/app/javascript/flavours/glitch/features/list_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/list_timeline/index.jsx
@@ -31,8 +31,6 @@ const mapStateToProps = (state, props) => ({
   hasUnread: state.getIn(['timelines', `list:${props.params.id}`, 'unread']) > 0,
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class ListTimeline extends React.PureComponent {
 
   static contextTypes = {
@@ -58,16 +56,16 @@ class ListTimeline extends React.PureComponent {
       dispatch(addColumn('LIST', { id: this.props.params.id }));
       this.context.router.history.push('/');
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   componentDidMount () {
     const { dispatch } = this.props;
@@ -105,16 +103,16 @@ class ListTimeline extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = maxId => {
     const { id } = this.props.params;
     this.props.dispatch(expandListTimeline(id, { maxId }));
-  }
+  };
 
   handleEditClick = () => {
     this.props.dispatch(openModal('LIST_EDITOR', { listId: this.props.params.id }));
-  }
+  };
 
   handleDeleteClick = () => {
     const { dispatch, columnId, intl } = this.props;
@@ -126,20 +124,20 @@ class ListTimeline extends React.PureComponent {
       onConfirm: () => {
         dispatch(deleteList(id));
 
-        if (!!columnId) {
+        if (columnId) {
           dispatch(removeColumn(columnId));
         } else {
           this.context.router.history.push('/lists');
         }
       },
     }));
-  }
+  };
 
   handleRepliesPolicyChange = ({ target }) => {
     const { dispatch, list } = this.props;
     const { id } = this.props.params;
     this.props.dispatch(updateList(id, undefined, false, target.value));
-  }
+  };
 
   render () {
     const { hasUnread, columnId, multiColumn, list, intl } = this.props;
@@ -179,11 +177,11 @@ class ListTimeline extends React.PureComponent {
           multiColumn={multiColumn}
         >
           <div className='column-settings__row column-header__links'>
-            <button className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.handleEditClick}>
+            <button className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}>
               <Icon id='pencil' /> <FormattedMessage id='lists.edit' defaultMessage='Edit list' />
             </button>
 
-            <button className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.handleDeleteClick}>
+            <button className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
               <Icon id='trash' /> <FormattedMessage id='lists.delete' defaultMessage='Delete list' />
             </button>
           </div>
@@ -222,3 +220,5 @@ class ListTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(ListTimeline));
diff --git a/app/javascript/flavours/glitch/features/lists/components/new_list_form.js b/app/javascript/flavours/glitch/features/lists/components/new_list_form.jsx
index cc78d30b7..be94ff559 100644
--- a/app/javascript/flavours/glitch/features/lists/components/new_list_form.js
+++ b/app/javascript/flavours/glitch/features/lists/components/new_list_form.jsx
@@ -20,8 +20,6 @@ const mapDispatchToProps = dispatch => ({
   onSubmit: () => dispatch(submitListEditor(true)),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class NewListForm extends React.PureComponent {
 
   static propTypes = {
@@ -34,16 +32,16 @@ class NewListForm extends React.PureComponent {
 
   handleChange = e => {
     this.props.onChange(e.target.value);
-  }
+  };
 
   handleSubmit = e => {
     e.preventDefault();
     this.props.onSubmit();
-  }
+  };
 
   handleClick = () => {
     this.props.onSubmit();
-  }
+  };
 
   render () {
     const { value, disabled, intl } = this.props;
@@ -76,3 +74,5 @@ class NewListForm extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(NewListForm));
diff --git a/app/javascript/flavours/glitch/features/lists/index.js b/app/javascript/flavours/glitch/features/lists/index.jsx
index 8773be5e6..dce0dcd8f 100644
--- a/app/javascript/flavours/glitch/features/lists/index.js
+++ b/app/javascript/flavours/glitch/features/lists/index.jsx
@@ -32,8 +32,6 @@ const mapStateToProps = state => ({
   lists: getOrderedLists(state),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Lists extends ImmutablePureComponent {
 
   static propTypes = {
@@ -87,3 +85,5 @@ class Lists extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Lists));
diff --git a/app/javascript/flavours/glitch/features/local_settings/index.js b/app/javascript/flavours/glitch/features/local_settings/index.jsx
index 4e4605ea9..4e4605ea9 100644
--- a/app/javascript/flavours/glitch/features/local_settings/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/index.jsx
diff --git a/app/javascript/flavours/glitch/features/local_settings/navigation/index.js b/app/javascript/flavours/glitch/features/local_settings/navigation/index.jsx
index 98dda182f..fe08e5d7b 100644
--- a/app/javascript/flavours/glitch/features/local_settings/navigation/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/navigation/index.jsx
@@ -19,7 +19,6 @@ const messages = defineMessages({
   close: { id: 'settings.close', defaultMessage: 'Close' },
 });
 
-export default @injectIntl
 class LocalSettingsNavigation extends React.PureComponent {
 
   static propTypes = {
@@ -72,7 +71,7 @@ class LocalSettingsNavigation extends React.PureComponent {
         />
         <LocalSettingsNavigationItem
           active={index === 5}
-          href={ preferencesLink }
+          href={preferencesLink}
           index={5}
           icon='cog'
           title={intl.formatMessage(messages.preferences)}
@@ -90,3 +89,5 @@ class LocalSettingsNavigation extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(LocalSettingsNavigation);
diff --git a/app/javascript/flavours/glitch/features/local_settings/navigation/item/index.js b/app/javascript/flavours/glitch/features/local_settings/navigation/item/index.jsx
index 739c5ebae..9ac6d9b73 100644
--- a/app/javascript/flavours/glitch/features/local_settings/navigation/item/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/navigation/item/index.jsx
@@ -26,7 +26,7 @@ export default class LocalSettingsPage extends React.PureComponent {
       onNavigate(index);
       e.preventDefault();
     }
-  }
+  };
 
   render () {
     const { handleClick } = this;
@@ -60,7 +60,7 @@ export default class LocalSettingsPage extends React.PureComponent {
       <a
         onClick={handleClick}
         role='button'
-        tabIndex='0'
+        tabIndex={0}
         className={finalClassName}
         title={title}
         aria-label={title}
diff --git a/app/javascript/flavours/glitch/features/local_settings/page/deprecated_item/index.js b/app/javascript/flavours/glitch/features/local_settings/page/deprecated_item/index.jsx
index 362bd97c0..362bd97c0 100644
--- a/app/javascript/flavours/glitch/features/local_settings/page/deprecated_item/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/page/deprecated_item/index.jsx
diff --git a/app/javascript/flavours/glitch/features/local_settings/page/index.js b/app/javascript/flavours/glitch/features/local_settings/page/index.jsx
index d01eec811..83b0c7960 100644
--- a/app/javascript/flavours/glitch/features/local_settings/page/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/page/index.jsx
@@ -5,7 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
 
 //  Our imports
-import { expandSpoilers, disableSwiping } from 'flavours/glitch/initial_state';
+import { expandSpoilers } from 'flavours/glitch/initial_state';
 import { preferenceLink } from 'flavours/glitch/utils/backend_links';
 import LocalSettingsPageItem from './item';
 import DeprecatedLocalSettingsPageItem from './deprecated_item';
@@ -31,7 +31,6 @@ const messages = defineMessages({
   pop_in_right: { id: 'settings.pop_in_right', defaultMessage:  'Right' },
 });
 
-export default @injectIntl
 class LocalSettingsPage extends React.PureComponent {
 
   static propTypes = {
@@ -60,7 +59,7 @@ class LocalSettingsPage extends React.PureComponent {
           onChange={onChange}
         >
           <FormattedMessage id='settings.hicolor_privacy_icons' defaultMessage='High color privacy icons' />
-          <span className='hint'><FormattedMessage id='settings.hicolor_privacy_icons.hint' defaultMessage="Display privacy icons in bright and easily distinguishable colors" /></span>
+          <span className='hint'><FormattedMessage id='settings.hicolor_privacy_icons.hint' defaultMessage='Display privacy icons in bright and easily distinguishable colors' /></span>
         </LocalSettingsPageItem>
         <LocalSettingsPageItem
           settings={settings}
@@ -77,7 +76,7 @@ class LocalSettingsPage extends React.PureComponent {
           onChange={onChange}
         >
           <FormattedMessage id='settings.tag_misleading_links' defaultMessage='Tag misleading links' />
-          <span className='hint'><FormattedMessage id='settings.tag_misleading_links.hint' defaultMessage="Add a visual indication with the link target host to every link not mentioning it explicitly" /></span>
+          <span className='hint'><FormattedMessage id='settings.tag_misleading_links.hint' defaultMessage='Add a visual indication with the link target host to every link not mentioning it explicitly' /></span>
         </LocalSettingsPageItem>
         <LocalSettingsPageItem
           settings={settings}
@@ -100,7 +99,7 @@ class LocalSettingsPage extends React.PureComponent {
             id='mastodon-settings--notifications-tab_badge'
             onChange={onChange}
           >
-            <FormattedMessage id='settings.notifications.tab_badge' defaultMessage="Unread notifications badge" />
+            <FormattedMessage id='settings.notifications.tab_badge' defaultMessage='Unread notifications badge' />
             <span className='hint'><FormattedMessage id='settings.notifications.tab_badge.hint' defaultMessage="Display a badge for unread notifications in the column icons when the notifications column isn't open" /></span>
           </LocalSettingsPageItem>
           <LocalSettingsPageItem
@@ -110,7 +109,7 @@ class LocalSettingsPage extends React.PureComponent {
             onChange={onChange}
           >
             <FormattedMessage id='settings.notifications.favicon_badge' defaultMessage='Unread notifications favicon badge' />
-            <span className='hint'><FormattedMessage id='settings.notifications.favicon_badge.hint' defaultMessage="Add a badge for unread notifications to the favicon" /></span>
+            <span className='hint'><FormattedMessage id='settings.notifications.favicon_badge.hint' defaultMessage='Add a badge for unread notifications to the favicon' /></span>
           </LocalSettingsPageItem>
         </section>
 
@@ -306,7 +305,7 @@ class LocalSettingsPage extends React.PureComponent {
                         defaultMessage='user preferences'
                       />
                     </a>
-                  )
+                  ),
                 }}
               />
             </span>
@@ -406,6 +405,18 @@ class LocalSettingsPage extends React.PureComponent {
           >
             <FormattedMessage id='settings.auto_collapse_media' defaultMessage='Toots with media' />
           </LocalSettingsPageItem>
+          <LocalSettingsPageItem
+            settings={settings}
+            item={['collapsed', 'auto', 'height']}
+            id='mastodon-settings--collapsed-auto-height'
+            placeholder='400'
+            onChange={onChange}
+            dependsOn={[['collapsed', 'enabled']]}
+            dependsOnNot={[['collapsed', 'auto', 'all']]}
+            inputProps={{ type: 'number', min: '200', max: '999' }}
+          >
+            <FormattedMessage id='settings.auto_collapse_height' defaultMessage='Height (in pixels) for a toot to be considered lengthy' />
+          </LocalSettingsPageItem>
         </section>
         <section>
           <h2><FormattedMessage id='settings.image_backgrounds' defaultMessage='Image backgrounds' /></h2>
@@ -501,3 +512,5 @@ class LocalSettingsPage extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(LocalSettingsPage);
diff --git a/app/javascript/flavours/glitch/features/local_settings/page/item/index.js b/app/javascript/flavours/glitch/features/local_settings/page/item/index.jsx
index 6b24e4143..41c0676a2 100644
--- a/app/javascript/flavours/glitch/features/local_settings/page/item/index.js
+++ b/app/javascript/flavours/glitch/features/local_settings/page/item/index.jsx
@@ -14,6 +14,7 @@ export default class LocalSettingsPageItem extends React.PureComponent {
     id: PropTypes.string.isRequired,
     item: PropTypes.array.isRequired,
     onChange: PropTypes.func.isRequired,
+    inputProps: PropTypes.object,
     options: PropTypes.arrayOf(PropTypes.shape({
       value: PropTypes.string.isRequired,
       message: PropTypes.string.isRequired,
@@ -30,11 +31,11 @@ export default class LocalSettingsPageItem extends React.PureComponent {
     if (options && options.length > 0) onChange(item, target.value);
     else if (placeholder) onChange(item, target.value);
     else onChange(item, target.checked);
-  }
+  };
 
   render () {
     const { handleChange } = this;
-    const { settings, item, id, options, children, dependsOn, dependsOnNot, placeholder, disabled } = this.props;
+    const { settings, item, id, inputProps, options, children, dependsOn, dependsOnNot, placeholder, disabled } = this.props;
     let enabled = !disabled;
 
     if (dependsOn) {
@@ -54,14 +55,17 @@ export default class LocalSettingsPageItem extends React.PureComponent {
         let optionId = `${id}--${opt.value}`;
         return (
           <label htmlFor={optionId}>
-            <input type='radio'
+            <input
+              type='radio'
               name={id}
               id={optionId}
+              key={optionId}
               value={opt.value}
               onBlur={handleChange}
               onChange={handleChange}
-              checked={ currentValue === opt.value }
+              checked={currentValue === opt.value}
               disabled={!enabled}
+              {...inputProps}
             />
             {opt.message}
             {opt.hint && <span className='hint'>{opt.hint}</span>}
@@ -89,6 +93,7 @@ export default class LocalSettingsPageItem extends React.PureComponent {
                 placeholder={placeholder}
                 onChange={handleChange}
                 disabled={!enabled}
+	        {...inputProps}
               />
             </p>
           </label>
@@ -103,6 +108,7 @@ export default class LocalSettingsPageItem extends React.PureComponent {
             checked={settings.getIn(item)}
             onChange={handleChange}
             disabled={!enabled}
+            {...inputProps}
           />
           {children}
         </label>
diff --git a/app/javascript/flavours/glitch/features/mutes/index.js b/app/javascript/flavours/glitch/features/mutes/index.jsx
index 8da106e47..b699fdb27 100644
--- a/app/javascript/flavours/glitch/features/mutes/index.js
+++ b/app/javascript/flavours/glitch/features/mutes/index.jsx
@@ -23,8 +23,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['user_lists', 'mutes', 'isLoading'], true),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Mutes extends ImmutablePureComponent {
 
   static propTypes = {
@@ -82,3 +80,5 @@ class Mutes extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Mutes));
diff --git a/app/javascript/flavours/glitch/features/notifications/components/admin_report.js b/app/javascript/flavours/glitch/features/notifications/components/admin_report.jsx
index 4662bd953..9b55fe4e9 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/admin_report.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/admin_report.jsx
@@ -32,28 +32,28 @@ export default class AdminReport extends ImmutablePureComponent {
   handleMoveUp = () => {
     const { notification, onMoveUp } = this.props;
     onMoveUp(notification.get('id'));
-  }
+  };
 
   handleMoveDown = () => {
     const { notification, onMoveDown } = this.props;
     onMoveDown(notification.get('id'));
-  }
+  };
 
   handleOpen = () => {
     this.handleOpenProfile();
-  }
+  };
 
   handleOpenProfile = () => {
     const { notification } = this.props;
     this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`);
-  }
+  };
 
   handleMention = e => {
     e.preventDefault();
 
     const { notification, onMention } = this.props;
     onMention(notification.get('account'), this.context.router.history);
-  }
+  };
 
   getHandlers () {
     return {
@@ -91,7 +91,7 @@ export default class AdminReport extends ImmutablePureComponent {
 
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-admin-report focusable', { unread })} tabIndex='0'>
+        <div className={classNames('notification notification-admin-report focusable', { unread })} tabIndex={0}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon id='flag' fixedWidth />
diff --git a/app/javascript/flavours/glitch/features/notifications/components/admin_signup.js b/app/javascript/flavours/glitch/features/notifications/components/admin_signup.jsx
index 355ebef94..d982108e9 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/admin_signup.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/admin_signup.jsx
@@ -26,28 +26,28 @@ export default class NotificationFollow extends ImmutablePureComponent {
   handleMoveUp = () => {
     const { notification, onMoveUp } = this.props;
     onMoveUp(notification.get('id'));
-  }
+  };
 
   handleMoveDown = () => {
     const { notification, onMoveDown } = this.props;
     onMoveDown(notification.get('id'));
-  }
+  };
 
   handleOpen = () => {
     this.handleOpenProfile();
-  }
+  };
 
   handleOpenProfile = () => {
     const { notification } = this.props;
     this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`);
-  }
+  };
 
   handleMention = e => {
     e.preventDefault();
 
     const { notification, onMention } = this.props;
     onMention(notification.get('account'), this.context.router.history);
-  }
+  };
 
   getHandlers () {
     return {
@@ -78,7 +78,7 @@ export default class NotificationFollow extends ImmutablePureComponent {
     //  Renders.
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-admin-sign-up focusable', { unread })} tabIndex='0'>
+        <div className={classNames('notification notification-admin-sign-up focusable', { unread })} tabIndex={0}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon fixedWidth id='user-plus' />
diff --git a/app/javascript/flavours/glitch/features/notifications/components/clear_column_button.js b/app/javascript/flavours/glitch/features/notifications/components/clear_column_button.jsx
index ee77cfb8e..cd150314b 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/clear_column_button.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/clear_column_button.jsx
@@ -11,7 +11,7 @@ export default class ClearColumnButton extends React.Component {
 
   render () {
     return (
-      <button className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.props.onClick}><Icon id='eraser' /> <FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' /></button>
+      <button className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.props.onClick}><Icon id='eraser' /> <FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' /></button>
     );
   }
 
diff --git a/app/javascript/flavours/glitch/features/notifications/components/column_settings.js b/app/javascript/flavours/glitch/features/notifications/components/column_settings.jsx
index 64fd98bd9..1c04218ba 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/column_settings.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/column_settings.jsx
@@ -27,7 +27,7 @@ export default class ColumnSettings extends React.PureComponent {
 
   onPushChange = (path, checked) => {
     this.props.onChange(['push', ...path], checked);
-  }
+  };
 
   render () {
     const { settings, pushSettings, onChange, onClear, alertsEnabled, browserSupport, browserPermission, onRequestNotificationPermission } = this.props;
diff --git a/app/javascript/flavours/glitch/features/notifications/components/filter_bar.js b/app/javascript/flavours/glitch/features/notifications/components/filter_bar.jsx
index c1de0f90e..7f36fb813 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/filter_bar.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/filter_bar.jsx
@@ -12,7 +12,6 @@ const tooltips = defineMessages({
   statuses: { id: 'notifications.filter.statuses', defaultMessage: 'Updates from people you follow' },
 });
 
-export default @injectIntl
 class FilterBar extends React.PureComponent {
 
   static propTypes = {
@@ -108,3 +107,5 @@ class FilterBar extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(FilterBar);
diff --git a/app/javascript/flavours/glitch/features/notifications/components/follow.js b/app/javascript/flavours/glitch/features/notifications/components/follow.jsx
index b8fad19d0..e9ef70911 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/follow.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/follow.jsx
@@ -26,28 +26,28 @@ export default class NotificationFollow extends ImmutablePureComponent {
   handleMoveUp = () => {
     const { notification, onMoveUp } = this.props;
     onMoveUp(notification.get('id'));
-  }
+  };
 
   handleMoveDown = () => {
     const { notification, onMoveDown } = this.props;
     onMoveDown(notification.get('id'));
-  }
+  };
 
   handleOpen = () => {
     this.handleOpenProfile();
-  }
+  };
 
   handleOpenProfile = () => {
     const { notification } = this.props;
     this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`);
-  }
+  };
 
   handleMention = e => {
     e.preventDefault();
 
     const { notification, onMention } = this.props;
     onMention(notification.get('account'), this.context.router.history);
-  }
+  };
 
   getHandlers () {
     return {
@@ -78,7 +78,7 @@ export default class NotificationFollow extends ImmutablePureComponent {
     //  Renders.
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-follow focusable', { unread })} tabIndex='0'>
+        <div className={classNames('notification notification-follow focusable', { unread })} tabIndex={0}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon fixedWidth id='user-plus' />
diff --git a/app/javascript/flavours/glitch/features/notifications/components/follow_request.js b/app/javascript/flavours/glitch/features/notifications/components/follow_request.jsx
index 69b92a06f..2b985bc08 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/follow_request.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/follow_request.jsx
@@ -17,7 +17,6 @@ const messages = defineMessages({
   reject: { id: 'follow_request.reject', defaultMessage: 'Reject' },
 });
 
-export default @injectIntl
 class FollowRequest extends ImmutablePureComponent {
 
   static propTypes = {
@@ -32,28 +31,28 @@ class FollowRequest extends ImmutablePureComponent {
   handleMoveUp = () => {
     const { notification, onMoveUp } = this.props;
     onMoveUp(notification.get('id'));
-  }
+  };
 
   handleMoveDown = () => {
     const { notification, onMoveDown } = this.props;
     onMoveDown(notification.get('id'));
-  }
+  };
 
   handleOpen = () => {
     this.handleOpenProfile();
-  }
+  };
 
   handleOpenProfile = () => {
     const { notification } = this.props;
     this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`);
-  }
+  };
 
   handleMention = e => {
     e.preventDefault();
 
     const { notification, onMention } = this.props;
     onMention(notification.get('account'), this.context.router.history);
-  }
+  };
 
   getHandlers () {
     return {
@@ -96,7 +95,7 @@ class FollowRequest extends ImmutablePureComponent {
 
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-follow-request focusable', { unread })} tabIndex='0'>
+        <div className={classNames('notification notification-follow-request focusable', { unread })} tabIndex={0}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon id='user' fixedWidth />
@@ -130,3 +129,5 @@ class FollowRequest extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(FollowRequest);
diff --git a/app/javascript/flavours/glitch/features/notifications/components/grant_permission_button.js b/app/javascript/flavours/glitch/features/notifications/components/grant_permission_button.jsx
index 798e4c787..5b2db48fd 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/grant_permission_button.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/grant_permission_button.jsx
@@ -10,7 +10,7 @@ export default class GrantPermissionButton extends React.PureComponent {
 
   render () {
     return (
-      <button className='text-btn column-header__permission-btn' tabIndex='0' onClick={this.props.onClick}>
+      <button className='text-btn column-header__permission-btn' tabIndex={0} onClick={this.props.onClick}>
         <FormattedMessage id='notifications.grant_permission' defaultMessage='Grant permission.' />
       </button>
     );
diff --git a/app/javascript/flavours/glitch/features/notifications/components/notification.js b/app/javascript/flavours/glitch/features/notifications/components/notification.jsx
index d676a4207..d1aea1b21 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/notification.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/notification.jsx
@@ -124,6 +124,7 @@ export default class Notification extends ImmutablePureComponent {
           onMoveDown={onMoveDown}
           onMoveUp={onMoveUp}
           onMention={onMention}
+          contextType='notifications'
           getScrollPosition={getScrollPosition}
           updateScrollBottom={updateScrollBottom}
           cachedMediaWidth={this.props.cachedMediaWidth}
@@ -146,6 +147,7 @@ export default class Notification extends ImmutablePureComponent {
           onMoveDown={onMoveDown}
           onMoveUp={onMoveUp}
           onMention={onMention}
+          contextType='notifications'
           getScrollPosition={getScrollPosition}
           updateScrollBottom={updateScrollBottom}
           cachedMediaWidth={this.props.cachedMediaWidth}
@@ -168,6 +170,7 @@ export default class Notification extends ImmutablePureComponent {
           onMoveDown={onMoveDown}
           onMoveUp={onMoveUp}
           onMention={onMention}
+          contextType='notifications'
           getScrollPosition={getScrollPosition}
           updateScrollBottom={updateScrollBottom}
           cachedMediaWidth={this.props.cachedMediaWidth}
@@ -190,6 +193,7 @@ export default class Notification extends ImmutablePureComponent {
           onMoveDown={onMoveDown}
           onMoveUp={onMoveUp}
           onMention={onMention}
+          contextType='notifications'
           getScrollPosition={getScrollPosition}
           updateScrollBottom={updateScrollBottom}
           cachedMediaWidth={this.props.cachedMediaWidth}
@@ -212,6 +216,7 @@ export default class Notification extends ImmutablePureComponent {
           onMoveDown={onMoveDown}
           onMoveUp={onMoveUp}
           onMention={onMention}
+          contextType='notifications'
           getScrollPosition={getScrollPosition}
           updateScrollBottom={updateScrollBottom}
           cachedMediaWidth={this.props.cachedMediaWidth}
diff --git a/app/javascript/flavours/glitch/features/notifications/components/notifications_permission_banner.js b/app/javascript/flavours/glitch/features/notifications/components/notifications_permission_banner.jsx
index dd163225e..5a12191a5 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/notifications_permission_banner.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/notifications_permission_banner.jsx
@@ -12,8 +12,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @connect()
-@injectIntl
 class NotificationsPermissionBanner extends React.PureComponent {
 
   static propTypes = {
@@ -23,11 +21,11 @@ class NotificationsPermissionBanner extends React.PureComponent {
 
   handleClick = () => {
     this.props.dispatch(requestBrowserPermission());
-  }
+  };
 
   handleClose = () => {
     this.props.dispatch(changeSetting(['notifications', 'dismissPermissionBanner'], true));
-  }
+  };
 
   render () {
     const { intl } = this.props;
@@ -46,3 +44,5 @@ class NotificationsPermissionBanner extends React.PureComponent {
   }
 
 }
+
+export default connect()(injectIntl(NotificationsPermissionBanner));
diff --git a/app/javascript/flavours/glitch/features/notifications/components/overlay.js b/app/javascript/flavours/glitch/features/notifications/components/overlay.jsx
index f3ccafc06..554a7a668 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/overlay.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/overlay.jsx
@@ -15,7 +15,6 @@ const messages = defineMessages({
   markForDeletion: { id: 'notification.markForDeletion', defaultMessage: 'Mark for deletion' },
 });
 
-export default @injectIntl
 class NotificationOverlay extends ImmutablePureComponent {
 
   static propTypes = {
@@ -29,7 +28,7 @@ class NotificationOverlay extends ImmutablePureComponent {
     const mark = !this.props.notification.get('markedForDelete');
     const id = this.props.notification.get('id');
     this.props.onMarkForDelete(id, mark);
-  }
+  };
 
   render () {
     const { notification, show, intl } = this.props;
@@ -56,3 +55,5 @@ class NotificationOverlay extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(NotificationOverlay);
diff --git a/app/javascript/flavours/glitch/features/notifications/components/pill_bar_button.js b/app/javascript/flavours/glitch/features/notifications/components/pill_bar_button.jsx
index 223b7f75f..2f0b48ef9 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/pill_bar_button.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/pill_bar_button.jsx
@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
-import classNames from 'classnames'
+import classNames from 'classnames';
 
 export default class PillBarButton extends React.PureComponent {
 
@@ -12,12 +12,12 @@ export default class PillBarButton extends React.PureComponent {
     label: PropTypes.node.isRequired,
     onChange: PropTypes.func.isRequired,
     disabled: PropTypes.bool,
-  }
+  };
 
   onChange = () => {
     const { settings, settingPath } = this.props;
     this.props.onChange(settingPath, !settings.getIn(settingPath));
-  }
+  };
 
   render () {
     const { prefix, settings, settingPath, label, disabled } = this.props;
diff --git a/app/javascript/flavours/glitch/features/notifications/components/report.js b/app/javascript/flavours/glitch/features/notifications/components/report.jsx
index 46a307250..9110735a1 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/report.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/report.jsx
@@ -13,7 +13,6 @@ const messages = defineMessages({
   violation: { id: 'report_notification.categories.violation', defaultMessage: 'Rule violation' },
 });
 
-export default @injectIntl
 class Report extends ImmutablePureComponent {
 
   static propTypes = {
@@ -60,3 +59,5 @@ class Report extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Report);
diff --git a/app/javascript/flavours/glitch/features/notifications/components/setting_toggle.js b/app/javascript/flavours/glitch/features/notifications/components/setting_toggle.jsx
index e472f7c4f..dc7b89b7f 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/setting_toggle.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/setting_toggle.jsx
@@ -14,11 +14,11 @@ export default class SettingToggle extends React.PureComponent {
     onChange: PropTypes.func.isRequired,
     defaultValue: PropTypes.bool,
     disabled: PropTypes.bool,
-  }
+  };
 
   onChange = ({ target }) => {
     this.props.onChange(this.props.settingPath, target.checked);
-  }
+  };
 
   render () {
     const { prefix, settings, settingPath, label, meta, defaultValue, disabled } = this.props;
diff --git a/app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js b/app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js
index c2564f44e..27c2f96fe 100644
--- a/app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js
+++ b/app/javascript/flavours/glitch/features/notifications/containers/column_settings_container.js
@@ -2,8 +2,7 @@ import { connect } from 'react-redux';
 import { defineMessages, injectIntl } from 'react-intl';
 import ColumnSettings from '../components/column_settings';
 import { changeSetting } from 'flavours/glitch/actions/settings';
-import { setFilter } from 'flavours/glitch/actions/notifications';
-import { clearNotifications, requestBrowserPermission } from 'flavours/glitch/actions/notifications';
+import { setFilter, clearNotifications, requestBrowserPermission } from 'flavours/glitch/actions/notifications';
 import { changeAlerts as changePushNotifications } from 'flavours/glitch/actions/push_notifications';
 import { openModal } from 'flavours/glitch/actions/modal';
 import { showAlert } from 'flavours/glitch/actions/alerts';
diff --git a/app/javascript/flavours/glitch/features/notifications/index.js b/app/javascript/flavours/glitch/features/notifications/index.jsx
index fc42a4de4..2de073077 100644
--- a/app/javascript/flavours/glitch/features/notifications/index.js
+++ b/app/javascript/flavours/glitch/features/notifications/index.jsx
@@ -92,8 +92,6 @@ const mapDispatchToProps = dispatch => ({
   dispatch,
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class Notifications extends React.PureComponent {
 
   static contextTypes = {
@@ -158,30 +156,30 @@ class Notifications extends React.PureComponent {
     } else {
       dispatch(addColumn('NOTIFICATIONS', {}));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setColumnRef = c => {
     this.column = c;
-  }
+  };
 
   handleMoveUp = id => {
     const elementIndex = this.props.notifications.findIndex(item => item !== null && item.get('id') === id) - 1;
     this._selectChild(elementIndex, true);
-  }
+  };
 
   handleMoveDown = id => {
     const elementIndex = this.props.notifications.findIndex(item => item !== null && item.get('id') === id) + 1;
     this._selectChild(elementIndex, false);
-  }
+  };
 
   _selectChild (index, align_top) {
     const container = this.column.node;
@@ -213,16 +211,16 @@ class Notifications extends React.PureComponent {
 
   handleTransitionEndNCD = () => {
     this.setState({ animatingNCD: false });
-  }
+  };
 
   onEnterCleaningMode = () => {
     this.setState({ animatingNCD: true });
     this.props.onEnterCleaningMode(!this.props.notifCleaningActive);
-  }
+  };
 
   handleMarkAsRead = () => {
     this.props.onMarkAsRead();
-  }
+  };
 
   render () {
     const { intl, notifications, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props;
@@ -380,3 +378,5 @@ class Notifications extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(Notifications));
diff --git a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx
index f05a763e0..51fe023d3 100644
--- a/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.js
+++ b/app/javascript/flavours/glitch/features/picture_in_picture/components/footer.jsx
@@ -38,8 +38,6 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class Footer extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -114,7 +112,7 @@ class Footer extends ImmutablePureComponent {
   _performReblog = (privacy) => {
     const { dispatch, status } = this.props;
     dispatch(reblog(status, privacy));
-  }
+  };
 
   handleReblogClick = e => {
     const { dispatch, status } = this.props;
@@ -151,7 +149,7 @@ class Footer extends ImmutablePureComponent {
     }
 
     router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
-  }
+  };
 
   render () {
     const { status, intl, showReplyCount, withOpenButton } = this.props;
@@ -215,3 +213,5 @@ class Footer extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(Footer));
diff --git a/app/javascript/flavours/glitch/features/picture_in_picture/components/header.js b/app/javascript/flavours/glitch/features/picture_in_picture/components/header.jsx
index 26f2da374..b9b90f1d8 100644
--- a/app/javascript/flavours/glitch/features/picture_in_picture/components/header.js
+++ b/app/javascript/flavours/glitch/features/picture_in_picture/components/header.jsx
@@ -17,8 +17,6 @@ const mapStateToProps = (state, { accountId }) => ({
   account: state.getIn(['accounts', accountId]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Header extends ImmutablePureComponent {
 
   static propTypes = {
@@ -45,3 +43,5 @@ class Header extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Header));
diff --git a/app/javascript/flavours/glitch/features/picture_in_picture/index.js b/app/javascript/flavours/glitch/features/picture_in_picture/index.jsx
index 3e6a20faa..e6fb64ff9 100644
--- a/app/javascript/flavours/glitch/features/picture_in_picture/index.js
+++ b/app/javascript/flavours/glitch/features/picture_in_picture/index.jsx
@@ -13,7 +13,6 @@ const mapStateToProps = state => ({
   left: state.getIn(['local_settings', 'media', 'pop_in_position']) === 'left',
 });
 
-export default @connect(mapStateToProps)
 class PictureInPicture extends React.Component {
 
   static propTypes = {
@@ -35,7 +34,7 @@ class PictureInPicture extends React.Component {
   handleClose = () => {
     const { dispatch } = this.props;
     dispatch(removePictureInPicture());
-  }
+  };
 
   render () {
     const { type, src, currentTime, accountId, statusId, left } = this.props;
@@ -86,3 +85,5 @@ class PictureInPicture extends React.Component {
   }
 
 }
+
+export default connect(mapStateToProps)(PictureInPicture);
diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js
index 5a1efce0a..db586ecf7 100644
--- a/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js
+++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/containers/search_container.js
@@ -4,7 +4,7 @@ import { injectIntl } from 'react-intl';
 import {
   fetchPinnedAccountsSuggestions,
   clearPinnedAccountsSuggestions,
-  changePinnedAccountsSuggestions
+  changePinnedAccountsSuggestions,
 } from '../../../actions/accounts';
 import Search from 'flavours/glitch/features/list_editor/components/search';
 
diff --git a/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js b/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.jsx
index 43ae0ec2f..834de652f 100644
--- a/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.js
+++ b/app/javascript/flavours/glitch/features/pinned_accounts_editor/index.jsx
@@ -21,8 +21,6 @@ const mapDispatchToProps = dispatch => ({
   onReset: () => dispatch(resetPinnedAccountsEditor()),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class PinnedAccountsEditor extends ImmutablePureComponent {
 
   static propTypes = {
@@ -61,7 +59,7 @@ class PinnedAccountsEditor extends ImmutablePureComponent {
             {accountIds.map(accountId => <AccountContainer key={accountId} accountId={accountId} added />)}
           </div>
 
-          {showSearch && <div role='button' tabIndex='-1' className='drawer__backdrop' onClick={onClear} />}
+          {showSearch && <div role='button' tabIndex={-1} className='drawer__backdrop' onClick={onClear} />}
 
           <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
             {({ x }) =>
@@ -76,3 +74,5 @@ class PinnedAccountsEditor extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(PinnedAccountsEditor));
diff --git a/app/javascript/flavours/glitch/features/pinned_statuses/index.js b/app/javascript/flavours/glitch/features/pinned_statuses/index.jsx
index eeeab46ab..41be2f7f3 100644
--- a/app/javascript/flavours/glitch/features/pinned_statuses/index.js
+++ b/app/javascript/flavours/glitch/features/pinned_statuses/index.jsx
@@ -19,8 +19,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'pins', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class PinnedStatuses extends ImmutablePureComponent {
 
   static propTypes = {
@@ -37,11 +35,11 @@ class PinnedStatuses extends ImmutablePureComponent {
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   render () {
     const { intl, statusIds, hasMore, multiColumn } = this.props;
@@ -63,3 +61,5 @@ class PinnedStatuses extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(PinnedStatuses));
diff --git a/app/javascript/flavours/glitch/features/privacy_policy/index.js b/app/javascript/flavours/glitch/features/privacy_policy/index.jsx
index 4618d9e32..a43befa73 100644
--- a/app/javascript/flavours/glitch/features/privacy_policy/index.js
+++ b/app/javascript/flavours/glitch/features/privacy_policy/index.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   title: { id: 'privacy_policy.title', defaultMessage: 'Privacy Policy' },
 });
 
-export default @injectIntl
 class PrivacyPolicy extends React.PureComponent {
 
   static propTypes = {
@@ -59,3 +58,5 @@ class PrivacyPolicy extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(PrivacyPolicy);
diff --git a/app/javascript/flavours/glitch/features/public_timeline/components/column_settings.js b/app/javascript/flavours/glitch/features/public_timeline/components/column_settings.jsx
index cfe821cfc..a44d5c784 100644
--- a/app/javascript/flavours/glitch/features/public_timeline/components/column_settings.js
+++ b/app/javascript/flavours/glitch/features/public_timeline/components/column_settings.jsx
@@ -9,7 +9,6 @@ const messages = defineMessages({
   filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
 });
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -40,3 +39,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/flavours/glitch/features/public_timeline/containers/column_settings_container.js b/app/javascript/flavours/glitch/features/public_timeline/containers/column_settings_container.js
index 5091bfb90..97b756658 100644
--- a/app/javascript/flavours/glitch/features/public_timeline/containers/column_settings_container.js
+++ b/app/javascript/flavours/glitch/features/public_timeline/containers/column_settings_container.js
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
 import ColumnSettings from '../components/column_settings';
 import { changeSetting } from 'flavours/glitch/actions/settings';
 import { changeColumnParams } from 'flavours/glitch/actions/columns';
- 
+
 const mapStateToProps = (state, { columnId }) => {
   const uuid = columnId;
   const columns = state.getIn(['settings', 'columns']);
diff --git a/app/javascript/flavours/glitch/features/public_timeline/index.js b/app/javascript/flavours/glitch/features/public_timeline/index.jsx
index a61a47de1..737e5723f 100644
--- a/app/javascript/flavours/glitch/features/public_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/public_timeline/index.jsx
@@ -35,8 +35,6 @@ const mapStateToProps = (state, { columnId }) => {
   };
 };
 
-export default @connect(mapStateToProps)
-@injectIntl
 class PublicTimeline extends React.PureComponent {
 
   static defaultProps = {
@@ -68,16 +66,16 @@ class PublicTimeline extends React.PureComponent {
     } else {
       dispatch(addColumn(onlyRemote ? 'REMOTE' : 'PUBLIC', { other: { onlyMedia, onlyRemote, allowLocalOnly } }));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   componentDidMount () {
     const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props;
@@ -116,13 +114,13 @@ class PublicTimeline extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = maxId => {
     const { dispatch, onlyMedia, onlyRemote, allowLocalOnly } = this.props;
 
     dispatch(expandPublicTimeline({ maxId, onlyMedia, onlyRemote, allowLocalOnly }));
-  }
+  };
 
   render () {
     const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote, allowLocalOnly } = this.props;
@@ -166,3 +164,5 @@ class PublicTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(PublicTimeline));
diff --git a/app/javascript/flavours/glitch/features/reblogs/index.js b/app/javascript/flavours/glitch/features/reblogs/index.jsx
index b097ff9d7..34fe24d3f 100644
--- a/app/javascript/flavours/glitch/features/reblogs/index.js
+++ b/app/javascript/flavours/glitch/features/reblogs/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Reblogs extends ImmutablePureComponent {
 
   static propTypes = {
@@ -48,15 +46,15 @@ class Reblogs extends ImmutablePureComponent {
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleRefresh = () => {
     this.props.dispatch(fetchReblogs(this.props.params.statusId));
-  }
+  };
 
   render () {
     const { intl, accountIds, multiColumn } = this.props;
@@ -102,3 +100,5 @@ class Reblogs extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Reblogs));
diff --git a/app/javascript/flavours/glitch/features/report/category.js b/app/javascript/flavours/glitch/features/report/category.jsx
index 55c43577b..43e311f3d 100644
--- a/app/javascript/flavours/glitch/features/report/category.js
+++ b/app/javascript/flavours/glitch/features/report/category.jsx
@@ -24,8 +24,6 @@ const mapStateToProps = state => ({
   rules: state.getIn(['server', 'server', 'rules'], ImmutableList()),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Category extends React.PureComponent {
 
   static propTypes = {
@@ -102,3 +100,5 @@ class Category extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Category));
diff --git a/app/javascript/flavours/glitch/features/report/comment.js b/app/javascript/flavours/glitch/features/report/comment.jsx
index ec261afcb..afcb7afa4 100644
--- a/app/javascript/flavours/glitch/features/report/comment.js
+++ b/app/javascript/flavours/glitch/features/report/comment.jsx
@@ -8,7 +8,6 @@ const messages = defineMessages({
   placeholder: { id: 'report.placeholder', defaultMessage: 'Type or paste additional comments' },
 });
 
-export default @injectIntl
 class Comment extends React.PureComponent {
 
   static propTypes = {
@@ -81,3 +80,5 @@ class Comment extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(Comment);
diff --git a/app/javascript/flavours/glitch/features/report/components/option.js b/app/javascript/flavours/glitch/features/report/components/option.jsx
index 7e94f0654..7827a6b3b 100644
--- a/app/javascript/flavours/glitch/features/report/components/option.js
+++ b/app/javascript/flavours/glitch/features/report/components/option.jsx
@@ -24,12 +24,12 @@ export default class Option extends React.PureComponent {
       e.preventDefault();
       onToggle(value, !checked);
     }
-  }
+  };
 
   handleChange = e => {
     const { value, onToggle } = this.props;
     onToggle(value, e.target.checked);
-  }
+  };
 
   render () {
     const { name, value, checked, label, labelComponent, description, multiple } = this.props;
@@ -40,7 +40,7 @@ export default class Option extends React.PureComponent {
 
         <span
           className={classNames('poll__input', { active: checked, checkbox: multiple })}
-          tabIndex='0'
+          tabIndex={0}
           role='radio'
           onKeyPress={this.handleKeyPress}
           aria-checked={checked}
diff --git a/app/javascript/flavours/glitch/features/report/components/status_check_box.js b/app/javascript/flavours/glitch/features/report/components/status_check_box.jsx
index 2231fc0ce..2231fc0ce 100644
--- a/app/javascript/flavours/glitch/features/report/components/status_check_box.js
+++ b/app/javascript/flavours/glitch/features/report/components/status_check_box.jsx
diff --git a/app/javascript/flavours/glitch/features/report/rules.js b/app/javascript/flavours/glitch/features/report/rules.jsx
index efcdf1fcf..72ba75b48 100644
--- a/app/javascript/flavours/glitch/features/report/rules.js
+++ b/app/javascript/flavours/glitch/features/report/rules.jsx
@@ -10,7 +10,6 @@ const mapStateToProps = state => ({
   rules: state.getIn(['server', 'server', 'rules']),
 });
 
-export default @connect(mapStateToProps)
 class Rules extends React.PureComponent {
 
   static propTypes = {
@@ -62,3 +61,5 @@ class Rules extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Rules);
diff --git a/app/javascript/flavours/glitch/features/report/statuses.js b/app/javascript/flavours/glitch/features/report/statuses.jsx
index 47d5ee863..a687917ce 100644
--- a/app/javascript/flavours/glitch/features/report/statuses.js
+++ b/app/javascript/flavours/glitch/features/report/statuses.jsx
@@ -13,7 +13,6 @@ const mapStateToProps = (state, { accountId }) => ({
   isLoading: state.getIn(['timelines', `account:${accountId}:with_replies`, 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Statuses extends React.PureComponent {
 
   static propTypes = {
@@ -59,3 +58,5 @@ class Statuses extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Statuses);
diff --git a/app/javascript/flavours/glitch/features/report/thanks.js b/app/javascript/flavours/glitch/features/report/thanks.jsx
index 454979f9f..30c88e2f2 100644
--- a/app/javascript/flavours/glitch/features/report/thanks.js
+++ b/app/javascript/flavours/glitch/features/report/thanks.jsx
@@ -12,7 +12,6 @@ import {
 
 const mapStateToProps = () => ({});
 
-export default @connect(mapStateToProps)
 class Thanks extends React.PureComponent {
 
   static propTypes = {
@@ -82,3 +81,5 @@ class Thanks extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Thanks);
diff --git a/app/javascript/flavours/glitch/features/standalone/compose/index.js b/app/javascript/flavours/glitch/features/standalone/compose/index.jsx
index b33c21cb5..c53442435 100644
--- a/app/javascript/flavours/glitch/features/standalone/compose/index.js
+++ b/app/javascript/flavours/glitch/features/standalone/compose/index.jsx
@@ -9,7 +9,7 @@ export default class Compose extends React.PureComponent {
   render () {
     return (
       <div>
-        <ComposeFormContainer />
+        <ComposeFormContainer autoFocus />
         <NotificationsContainer />
         <ModalContainer />
         <LoadingBarContainer className='loading-bar' />
diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.js b/app/javascript/flavours/glitch/features/status/components/action_bar.jsx
index 73913dd49..d5ab730d6 100644
--- a/app/javascript/flavours/glitch/features/status/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/status/components/action_bar.jsx
@@ -39,7 +39,6 @@ const messages = defineMessages({
   openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
 });
 
-export default @injectIntl
 class ActionBar extends React.PureComponent {
 
   static contextTypes = {
@@ -68,75 +67,75 @@ class ActionBar extends React.PureComponent {
 
   handleReplyClick = () => {
     this.props.onReply(this.props.status);
-  }
+  };
 
   handleReblogClick = (e) => {
     this.props.onReblog(this.props.status, e);
-  }
+  };
 
   handleFavouriteClick = (e) => {
     this.props.onFavourite(this.props.status, e);
-  }
+  };
 
   handleBookmarkClick = (e) => {
     this.props.onBookmark(this.props.status, e);
-  }
+  };
 
   handleDeleteClick = () => {
     this.props.onDelete(this.props.status, this.context.router.history);
-  }
+  };
 
   handleRedraftClick = () => {
     this.props.onDelete(this.props.status, this.context.router.history, true);
-  }
+  };
 
   handleEditClick = () => {
     this.props.onEdit(this.props.status, this.context.router.history);
-  }
+  };
 
   handleDirectClick = () => {
     this.props.onDirect(this.props.status.get('account'), this.context.router.history);
-  }
+  };
 
   handleMentionClick = () => {
     this.props.onMention(this.props.status.get('account'), this.context.router.history);
-  }
+  };
 
   handleMuteClick = () => {
     this.props.onMute(this.props.status.get('account'));
-  }
+  };
 
   handleConversationMuteClick = () => {
     this.props.onMuteConversation(this.props.status);
-  }
+  };
 
   handleBlockClick = () => {
     this.props.onBlock(this.props.status);
-  }
+  };
 
   handleReport = () => {
     this.props.onReport(this.props.status);
-  }
+  };
 
   handlePinClick = () => {
     this.props.onPin(this.props.status);
-  }
+  };
 
   handleShare = () => {
     navigator.share({
       text: this.props.status.get('search_index'),
       url: this.props.status.get('url'),
     });
-  }
+  };
 
   handleEmbed = () => {
     this.props.onEmbed(this.props.status);
-  }
+  };
 
   handleCopy = () => {
     const url = this.props.status.get('url');
     navigator.clipboard.writeText(url);
-  }
+  };
 
   render () {
     const { status, intl } = this.props;
@@ -228,3 +227,5 @@ class ActionBar extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ActionBar);
diff --git a/app/javascript/flavours/glitch/features/status/components/card.js b/app/javascript/flavours/glitch/features/status/components/card.jsx
index 2d2e49eb8..359dbbc20 100644
--- a/app/javascript/flavours/glitch/features/status/components/card.js
+++ b/app/javascript/flavours/glitch/features/status/components/card.jsx
@@ -138,7 +138,7 @@ export default class Card extends React.PureComponent {
     } else {
       this.setState({ embedded: true });
     }
-  }
+  };
 
   setRef = c => {
     this.node = c;
@@ -146,17 +146,17 @@ export default class Card extends React.PureComponent {
     if (this.node) {
       this._setDimensions();
     }
-  }
+  };
 
   handleImageLoad = () => {
     this.setState({ previewLoaded: true });
-  }
+  };
 
   handleReveal = e => {
     e.preventDefault();
     e.stopPropagation();
     this.setState({ revealed: true });
-  }
+  };
 
   renderVideo () {
     const { card }  = this.props;
@@ -188,11 +188,12 @@ export default class Card extends React.PureComponent {
     const interactive = card.get('type') !== 'link';
     const className   = classnames('status-card', { horizontal, compact, interactive });
     const title       = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener noreferrer' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
+    const language    = card.get('language') || '';
     const ratio       = card.get('width') / card.get('height');
     const height      = (compact && !embedded) ? (width / (16 / 9)) : (width / ratio);
 
     const description = (
-      <div className='status-card__content'>
+      <div className='status-card__content' lang={language}>
         {title}
         {!(horizontal || compact) && <p className='status-card__description'>{trim(card.get('description') || '', maxDescription)}</p>}
         <span className='status-card__host'>{provider}</span>
diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.jsx
index 907fc3f1c..cfe6c965e 100644
--- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js
+++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.jsx
@@ -21,7 +21,6 @@ import AnimatedNumber from 'flavours/glitch/components/animated_number';
 import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
 import EditedTimestamp from 'flavours/glitch/components/edited_timestamp';
 
-export default @injectIntl
 class DetailedStatus extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -56,28 +55,28 @@ class DetailedStatus extends ImmutablePureComponent {
   handleAccountClick = (e) => {
     if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey) && this.context.router) {
       e.preventDefault();
-      let state = {...this.context.router.history.location.state};
+      let state = { ...this.context.router.history.location.state };
       state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
       this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`, state);
     }
 
     e.stopPropagation();
-  }
+  };
 
   parseClick = (e, destination) => {
     if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey) && this.context.router) {
       e.preventDefault();
-      let state = {...this.context.router.history.location.state};
+      let state = { ...this.context.router.history.location.state };
       state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
       this.context.router.history.push(destination, state);
     }
 
     e.stopPropagation();
-  }
+  };
 
   handleOpenVideo = (options) => {
     this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), options);
-  }
+  };
 
   _measureHeight (heightJustChanged) {
     if (this.props.measureHeight && this.node) {
@@ -92,7 +91,7 @@ class DetailedStatus extends ImmutablePureComponent {
   setRef = c => {
     this.node = c;
     this._measureHeight();
-  }
+  };
 
   componentDidUpdate (prevProps, prevState) {
     this._measureHeight(prevState.height !== this.state.height);
@@ -100,7 +99,7 @@ class DetailedStatus extends ImmutablePureComponent {
 
   handleChildUpdate = () => {
     this._measureHeight();
-  }
+  };
 
   handleModalLink = e => {
     e.preventDefault();
@@ -114,12 +113,12 @@ class DetailedStatus extends ImmutablePureComponent {
     }
 
     window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
-  }
+  };
 
   handleTranslate = () => {
     const { onTranslate, status } = this.props;
     onTranslate(status);
-  }
+  };
 
   render () {
     const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
@@ -169,6 +168,7 @@ class DetailedStatus extends ImmutablePureComponent {
           <Audio
             src={attachment.get('url')}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             duration={attachment.getIn(['meta', 'original', 'duration'], 0)}
             poster={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
             backgroundColor={attachment.getIn(['meta', 'colors', 'background'])}
@@ -191,6 +191,7 @@ class DetailedStatus extends ImmutablePureComponent {
             blurhash={attachment.get('blurhash')}
             src={attachment.get('url')}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             inline
             sensitive={status.get('sensitive')}
             letterbox={settings.getIn(['media', 'letterbox'])}
@@ -209,6 +210,7 @@ class DetailedStatus extends ImmutablePureComponent {
             standalone
             sensitive={status.get('sensitive')}
             media={status.get('media_attachments')}
+            lang={status.get('language')}
             letterbox={settings.getIn(['media', 'letterbox'])}
             fullwidth={settings.getIn(['media', 'fullwidth'])}
             hidden={!expanded}
@@ -225,7 +227,7 @@ class DetailedStatus extends ImmutablePureComponent {
     }
 
     if (status.get('poll')) {
-      contentMedia.push(<PollContainer pollId={status.get('poll')} />);
+      contentMedia.push(<PollContainer pollId={status.get('poll')} lang={status.get('language')} />);
       contentMediaIcons.push('tasks');
     }
 
@@ -333,3 +335,5 @@ class DetailedStatus extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(DetailedStatus);
diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.jsx
index c22e7f0bd..f01ad2dbe 100644
--- a/app/javascript/flavours/glitch/features/status/index.js
+++ b/app/javascript/flavours/glitch/features/status/index.jsx
@@ -5,7 +5,17 @@ import PropTypes from 'prop-types';
 import classNames from 'classnames';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { createSelector } from 'reselect';
-import { fetchStatus } from 'flavours/glitch/actions/statuses';
+import {
+  fetchStatus,
+  muteStatus,
+  unmuteStatus,
+  deleteStatus,
+  editStatus,
+  hideStatus,
+  revealStatus,
+  translateStatus,
+  undoStatusTranslation,
+} from 'flavours/glitch/actions/statuses';
 import MissingIndicator from 'flavours/glitch/components/missing_indicator';
 import LoadingIndicator from 'flavours/glitch/components/loading_indicator';
 import DetailedStatus from './components/detailed_status';
@@ -27,16 +37,6 @@ import {
   directCompose,
 } from 'flavours/glitch/actions/compose';
 import { changeLocalSetting } from 'flavours/glitch/actions/local_settings';
-import {
-  muteStatus,
-  unmuteStatus,
-  deleteStatus,
-  editStatus,
-  hideStatus,
-  revealStatus,
-  translateStatus,
-  undoStatusTranslation,
-} from 'flavours/glitch/actions/statuses';
 import { initMuteModal } from 'flavours/glitch/actions/mutes';
 import { initBlockModal } from 'flavours/glitch/actions/blocks';
 import { initReport } from 'flavours/glitch/actions/reports';
@@ -171,8 +171,6 @@ const titleFromStatus = status => {
   return `${prefix}: "${truncate(text, 30)}"`;
 };
 
-export default @injectIntl
-@connect(makeMapStateToProps)
 class Status extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -263,15 +261,15 @@ class Status extends ImmutablePureComponent {
     } else if (this.props.status.get('spoiler_text')) {
       this.setExpansion(!this.state.isExpanded);
     }
-  }
+  };
 
   handleToggleMediaVisibility = () => {
     this.setState({ showMedia: !this.state.showMedia });
-  }
+  };
 
   handleModalFavourite = (status) => {
     this.props.dispatch(favourite(status));
-  }
+  };
 
   handleFavouriteClick = (status, e) => {
     const { dispatch } = this.props;
@@ -294,7 +292,7 @@ class Status extends ImmutablePureComponent {
         url: status.get('url'),
       }));
     }
-  }
+  };
 
   handlePin = (status) => {
     if (status.get('pinned')) {
@@ -302,7 +300,7 @@ class Status extends ImmutablePureComponent {
     } else {
       this.props.dispatch(pin(status));
     }
-  }
+  };
 
   handleReplyClick = (status) => {
     const { askReplyConfirmation, dispatch, intl } = this.props;
@@ -326,7 +324,7 @@ class Status extends ImmutablePureComponent {
         url: status.get('url'),
       }));
     }
-  }
+  };
 
   handleModalReblog = (status, privacy) => {
     const { dispatch } = this.props;
@@ -336,7 +334,7 @@ class Status extends ImmutablePureComponent {
     } else {
       dispatch(reblog(status, privacy));
     }
-  }
+  };
 
   handleReblogClick = (status, e) => {
     const { settings, dispatch } = this.props;
@@ -357,7 +355,7 @@ class Status extends ImmutablePureComponent {
         url: status.get('url'),
       }));
     }
-  }
+  };
 
   handleBookmarkClick = (status) => {
     if (status.get('bookmarked')) {
@@ -365,7 +363,7 @@ class Status extends ImmutablePureComponent {
     } else {
       this.props.dispatch(bookmark(status));
     }
-  }
+  };
 
   handleDeleteClick = (status, history, withRedraft = false) => {
     const { dispatch, intl } = this.props;
@@ -379,27 +377,27 @@ class Status extends ImmutablePureComponent {
         onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
       }));
     }
-  }
+  };
 
   handleEditClick = (status, history) => {
     this.props.dispatch(editStatus(status.get('id'), history));
-  }
+  };
 
   handleDirectClick = (account, router) => {
     this.props.dispatch(directCompose(account, router));
-  }
+  };
 
   handleMentionClick = (account, router) => {
     this.props.dispatch(mentionCompose(account, router));
-  }
+  };
 
   handleOpenMedia = (media, index) => {
     this.props.dispatch(openModal('MEDIA', { statusId: this.props.status.get('id'), media, index }));
-  }
+  };
 
   handleOpenVideo = (media, options) => {
     this.props.dispatch(openModal('VIDEO', { statusId: this.props.status.get('id'), media, options }));
-  }
+  };
 
   handleHotkeyOpenMedia = e => {
     const { status } = this.props;
@@ -413,11 +411,11 @@ class Status extends ImmutablePureComponent {
         this.handleOpenMedia(status.get('media_attachments'), 0);
       }
     }
-  }
+  };
 
   handleMuteClick = (account) => {
     this.props.dispatch(initMuteModal(account));
-  }
+  };
 
   handleConversationMuteClick = (status) => {
     if (status.get('muted')) {
@@ -425,7 +423,7 @@ class Status extends ImmutablePureComponent {
     } else {
       this.props.dispatch(muteStatus(status.get('id')));
     }
-  }
+  };
 
   handleToggleAll = () => {
     const { status, ancestorsIds, descendantsIds, settings } = this.props;
@@ -442,7 +440,7 @@ class Status extends ImmutablePureComponent {
     }
 
     this.setState({ isExpanded: !isExpanded, threadExpanded: !isExpanded });
-  }
+  };
 
   handleTranslate = status => {
     const { dispatch } = this.props;
@@ -452,61 +450,61 @@ class Status extends ImmutablePureComponent {
     } else {
       dispatch(translateStatus(status.get('id')));
     }
-  }
+  };
 
   handleBlockClick = (status) => {
     const { dispatch } = this.props;
     const account = status.get('account');
     dispatch(initBlockModal(account));
-  }
+  };
 
   handleReport = (status) => {
     this.props.dispatch(initReport(status.get('account'), status));
-  }
+  };
 
   handleEmbed = (status) => {
     this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
-  }
+  };
 
   handleHotkeyToggleSensitive = () => {
     this.handleToggleMediaVisibility();
-  }
+  };
 
   handleHotkeyMoveUp = () => {
     this.handleMoveUp(this.props.status.get('id'));
-  }
+  };
 
   handleHotkeyMoveDown = () => {
     this.handleMoveDown(this.props.status.get('id'));
-  }
+  };
 
   handleHotkeyReply = e => {
     e.preventDefault();
     this.handleReplyClick(this.props.status);
-  }
+  };
 
   handleHotkeyFavourite = () => {
     this.handleFavouriteClick(this.props.status);
-  }
+  };
 
   handleHotkeyBoost = () => {
     this.handleReblogClick(this.props.status);
-  }
+  };
 
   handleHotkeyBookmark = () => {
     this.handleBookmarkClick(this.props.status);
-  }
+  };
 
   handleHotkeyMention = e => {
     e.preventDefault();
     this.handleMentionClick(this.props.status);
-  }
+  };
 
   handleHotkeyOpenProfile = () => {
-    let state = {...this.context.router.history.location.state};
+    let state = { ...this.context.router.history.location.state };
     state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
     this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`, state);
-  }
+  };
 
   handleMoveUp = id => {
     const { status, ancestorsIds, descendantsIds } = this.props;
@@ -523,7 +521,7 @@ class Status extends ImmutablePureComponent {
         this._selectChild(index - 1, true);
       }
     }
-  }
+  };
 
   handleMoveDown = id => {
     const { status, ancestorsIds, descendantsIds } = this.props;
@@ -540,7 +538,7 @@ class Status extends ImmutablePureComponent {
         this._selectChild(index + 1, false);
       }
     }
-  }
+  };
 
   _selectChild (index, align_top) {
     const container = this.node;
@@ -558,7 +556,7 @@ class Status extends ImmutablePureComponent {
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   renderChildren (list) {
     return list.map(id => (
@@ -575,15 +573,15 @@ class Status extends ImmutablePureComponent {
 
   setExpansion = value => {
     this.setState({ isExpanded: value });
-  }
+  };
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   setColumnRef = c => {
     this.column = c;
-  }
+  };
 
   componentDidUpdate (prevProps) {
     if (this.props.params.statusId && (this.props.params.statusId !== prevProps.params.statusId || prevProps.ancestorsIds.size < this.props.ancestorsIds.size)) {
@@ -605,7 +603,7 @@ class Status extends ImmutablePureComponent {
 
   onFullScreenChange = () => {
     this.setState({ fullscreen: isFullscreen() });
-  }
+  };
 
   render () {
     let ancestors, descendants;
@@ -674,7 +672,7 @@ class Status extends ImmutablePureComponent {
             {ancestors}
 
             <HotKeys handlers={handlers}>
-              <div className='focusable' tabIndex='0' aria-label={textForScreenReader(intl, status, false, isExpanded)}>
+              <div className='focusable' tabIndex={0} aria-label={textForScreenReader(intl, status, false, isExpanded)}>
                 <DetailedStatus
                   key={`details-${status.get('id')}`}
                   status={status}
@@ -724,3 +722,5 @@ class Status extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(connect(makeMapStateToProps)(Status));
diff --git a/app/javascript/flavours/glitch/features/subscribed_languages_modal/index.js b/app/javascript/flavours/glitch/features/subscribed_languages_modal/index.jsx
index fa69d82a4..85144a191 100644
--- a/app/javascript/flavours/glitch/features/subscribed_languages_modal/index.js
+++ b/app/javascript/flavours/glitch/features/subscribed_languages_modal/index.jsx
@@ -36,8 +36,6 @@ const mapDispatchToProps = (dispatch, { accountId }) => ({
 
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class SubscribedLanguagesModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -72,7 +70,7 @@ class SubscribedLanguagesModal extends ImmutablePureComponent {
   handleSubmit = () => {
     this.props.onSubmit(this.state.selectedLanguages.toArray());
     this.props.onClose();
-  }
+  };
 
   renderItem (value) {
     const language = this.props.languages.find(language => language[0] === value);
@@ -123,3 +121,5 @@ class SubscribedLanguagesModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(SubscribedLanguagesModal));
diff --git a/app/javascript/flavours/glitch/features/ui/components/actions_modal.js b/app/javascript/flavours/glitch/features/ui/components/actions_modal.jsx
index aae2e4426..9a9b1a3f1 100644
--- a/app/javascript/flavours/glitch/features/ui/components/actions_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/actions_modal.jsx
@@ -36,7 +36,7 @@ export default class ActionsModal extends ImmutablePureComponent {
     if (!contents) {
       contents = (
         <React.Fragment>
-          {icon && <IconButton title={text} icon={icon} role='presentation' tabIndex='-1' inverted />}
+          {icon && <IconButton title={text} icon={icon} role='presentation' tabIndex={-1} inverted />}
           <div>
             <div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
             <div>{meta}</div>
@@ -52,7 +52,7 @@ export default class ActionsModal extends ImmutablePureComponent {
         </a>
       </li>
     );
-  }
+  };
 
   render () {
     const status = this.props.status && (
diff --git a/app/javascript/flavours/glitch/features/ui/components/audio_modal.js b/app/javascript/flavours/glitch/features/ui/components/audio_modal.jsx
index fc98cc6af..0aeabd94c 100644
--- a/app/javascript/flavours/glitch/features/ui/components/audio_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/audio_modal.jsx
@@ -7,15 +7,16 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 import Footer from 'flavours/glitch/features/picture_in_picture/components/footer';
 
 const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
   accountStaticAvatar: state.getIn(['accounts', state.getIn(['statuses', statusId, 'account']), 'avatar_static']),
 });
 
-export default @connect(mapStateToProps)
 class AudioModal extends ImmutablePureComponent {
 
   static propTypes = {
     media: ImmutablePropTypes.map.isRequired,
     statusId: PropTypes.string.isRequired,
+    language: PropTypes.string,
     accountStaticAvatar: PropTypes.string.isRequired,
     options: PropTypes.shape({
       autoPlay: PropTypes.bool,
@@ -29,7 +30,7 @@ class AudioModal extends ImmutablePureComponent {
   };
 
   render () {
-    const { media, accountStaticAvatar, statusId, onClose } = this.props;
+    const { media, language, accountStaticAvatar, statusId, onClose } = this.props;
     const options = this.props.options || {};
 
     return (
@@ -38,6 +39,7 @@ class AudioModal extends ImmutablePureComponent {
           <Audio
             src={media.get('url')}
             alt={media.get('description')}
+            lang={language}
             duration={media.getIn(['meta', 'original', 'duration'], 0)}
             height={150}
             poster={media.get('preview_url') || accountStaticAvatar}
@@ -56,3 +58,5 @@ class AudioModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(AudioModal);
diff --git a/app/javascript/flavours/glitch/features/ui/components/block_modal.js b/app/javascript/flavours/glitch/features/ui/components/block_modal.jsx
index a07baeaa6..a9506aa69 100644
--- a/app/javascript/flavours/glitch/features/ui/components/block_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/block_modal.jsx
@@ -36,8 +36,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-export default @connect(makeMapStateToProps, mapDispatchToProps)
-@injectIntl
 class BlockModal extends React.PureComponent {
 
   static propTypes = {
@@ -55,20 +53,20 @@ class BlockModal extends React.PureComponent {
   handleClick = () => {
     this.props.onClose();
     this.props.onConfirm(this.props.account);
-  }
+  };
 
   handleSecondary = () => {
     this.props.onClose();
     this.props.onBlockAndReport(this.props.account);
-  }
+  };
 
   handleCancel = () => {
     this.props.onClose();
-  }
+  };
 
   setRef = (c) => {
     this.button = c;
-  }
+  };
 
   render () {
     const { account } = this.props;
@@ -101,3 +99,5 @@ class BlockModal extends React.PureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(BlockModal));
diff --git a/app/javascript/flavours/glitch/features/ui/components/boost_modal.js b/app/javascript/flavours/glitch/features/ui/components/boost_modal.jsx
index 8d9496bb9..d9523a26e 100644
--- a/app/javascript/flavours/glitch/features/ui/components/boost_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/boost_modal.jsx
@@ -35,8 +35,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class BoostModal extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -58,17 +56,17 @@ class BoostModal extends ImmutablePureComponent {
   handleReblog = () => {
     this.props.onReblog(this.props.status, this.props.privacy);
     this.props.onClose();
-  }
+  };
 
   handleAccountClick = (e) => {
     if (e.button === 0) {
       e.preventDefault();
       this.props.onClose();
-      let state = {...this.context.router.history.location.state};
+      let state = { ...this.context.router.history.location.state };
       state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
       this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`, state);
     }
-  }
+  };
 
   _findContainer = () => {
     return document.getElementsByClassName('modal-root__container')[0];
@@ -76,7 +74,7 @@ class BoostModal extends ImmutablePureComponent {
 
   setRef = (c) => {
     this.button = c;
-  }
+  };
 
   render () {
     const { status, missingMediaDescription, privacy, intl } = this.props;
@@ -116,9 +114,9 @@ class BoostModal extends ImmutablePureComponent {
         <div className='boost-modal__action-bar'>
           <div>
             { missingMediaDescription ?
-                <FormattedMessage id='boost_modal.missing_description' defaultMessage='This toot contains some media without description' />
+              <FormattedMessage id='boost_modal.missing_description' defaultMessage='This toot contains some media without description' />
               :
-                <FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <Icon id='retweet' /></span> }} />
+              <FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <Icon id='retweet' /></span> }} />
             }
           </div>
 
@@ -137,3 +135,5 @@ class BoostModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(BoostModal));
diff --git a/app/javascript/flavours/glitch/features/ui/components/bundle.js b/app/javascript/flavours/glitch/features/ui/components/bundle.jsx
index 8f0d7b8b1..27b13ecfe 100644
--- a/app/javascript/flavours/glitch/features/ui/components/bundle.js
+++ b/app/javascript/flavours/glitch/features/ui/components/bundle.jsx
@@ -15,7 +15,7 @@ class Bundle extends React.Component {
     onFetch: PropTypes.func,
     onFetchSuccess: PropTypes.func,
     onFetchFail: PropTypes.func,
-  }
+  };
 
   static defaultProps = {
     loading: emptyComponent,
@@ -24,14 +24,14 @@ class Bundle extends React.Component {
     onFetch: noop,
     onFetchSuccess: noop,
     onFetchFail: noop,
-  }
+  };
 
-  static cache = {}
+  static cache = {};
 
   state = {
     mod: undefined,
     forceRender: false,
-  }
+  };
 
   componentWillMount() {
     this.load(this.props);
@@ -84,7 +84,7 @@ class Bundle extends React.Component {
         this.setState({ mod: null });
         onFetchFail(error);
       });
-  }
+  };
 
   render() {
     const { loading: Loading, error: Error, children, renderDelay } = this.props;
diff --git a/app/javascript/flavours/glitch/features/ui/components/bundle_column_error.js b/app/javascript/flavours/glitch/features/ui/components/bundle_column_error.jsx
index 7cbe1413d..eaabbc460 100644
--- a/app/javascript/flavours/glitch/features/ui/components/bundle_column_error.js
+++ b/app/javascript/flavours/glitch/features/ui/components/bundle_column_error.jsx
@@ -31,7 +31,7 @@ class GIF extends React.PureComponent {
     if (!animate) {
       this.setState({ hovering: true });
     }
-  }
+  };
 
   handleMouseLeave = () => {
     const { animate } = this.props;
@@ -39,7 +39,7 @@ class GIF extends React.PureComponent {
     if (!animate) {
       this.setState({ hovering: false });
     }
-  }
+  };
 
   render () {
     const { src, staticSrc, className, animate } = this.props;
@@ -75,7 +75,7 @@ class CopyButton extends React.PureComponent {
     navigator.clipboard.writeText(value);
     this.setState({ copied: true });
     this.timeout = setTimeout(() => this.setState({ copied: false }), 700);
-  }
+  };
 
   componentWillUnmount () {
     if (this.timeout) clearTimeout(this.timeout);
@@ -92,7 +92,6 @@ class CopyButton extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class BundleColumnError extends React.PureComponent {
 
   static propTypes = {
@@ -113,7 +112,7 @@ class BundleColumnError extends React.PureComponent {
     if (onRetry) {
       onRetry();
     }
-  }
+  };
 
   render () {
     const { errorType, multiColumn, stacktrace } = this.props;
@@ -160,3 +159,5 @@ class BundleColumnError extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(BundleColumnError);
diff --git a/app/javascript/flavours/glitch/features/ui/components/bundle_modal_error.js b/app/javascript/flavours/glitch/features/ui/components/bundle_modal_error.jsx
index 2c14a1e5c..b79105450 100644
--- a/app/javascript/flavours/glitch/features/ui/components/bundle_modal_error.js
+++ b/app/javascript/flavours/glitch/features/ui/components/bundle_modal_error.jsx
@@ -16,11 +16,11 @@ class BundleModalError extends React.Component {
     onRetry: PropTypes.func.isRequired,
     onClose: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
-  }
+  };
 
   handleRetry = () => {
     this.props.onRetry();
-  }
+  };
 
   render () {
     const { onClose, intl: { formatMessage } } = this.props;
diff --git a/app/javascript/flavours/glitch/features/ui/components/column.js b/app/javascript/flavours/glitch/features/ui/components/column.jsx
index e9c1e2f87..cc2abc43a 100644
--- a/app/javascript/flavours/glitch/features/ui/components/column.js
+++ b/app/javascript/flavours/glitch/features/ui/components/column.jsx
@@ -25,7 +25,7 @@ export default class Column extends React.PureComponent {
     }
 
     this._interruptScrollAnimation = scrollTop(scrollable);
-  }
+  };
 
   scrollTop () {
     const scrollable = this.props.bindToDocument ? document.scrollingElement : this.node.querySelector('.scrollable');
@@ -42,11 +42,11 @@ export default class Column extends React.PureComponent {
     if (typeof this._interruptScrollAnimation !== 'undefined') {
       this._interruptScrollAnimation();
     }
-  }, 200)
+  }, 200);
 
   setRef = (c) => {
     this.node = c;
-  }
+  };
 
   render () {
     const { heading, icon, children, active, hideHeadingOnMobile, name } = this.props;
diff --git a/app/javascript/flavours/glitch/features/ui/components/column_header.js b/app/javascript/flavours/glitch/features/ui/components/column_header.jsx
index 528ff73a6..151476f8b 100644
--- a/app/javascript/flavours/glitch/features/ui/components/column_header.js
+++ b/app/javascript/flavours/glitch/features/ui/components/column_header.jsx
@@ -15,7 +15,7 @@ export default class ColumnHeader extends React.PureComponent {
 
   handleClick = () => {
     this.props.onClick();
-  }
+  };
 
   render () {
     const { icon, type, active, columnHeaderId } = this.props;
diff --git a/app/javascript/flavours/glitch/features/ui/components/column_link.js b/app/javascript/flavours/glitch/features/ui/components/column_link.jsx
index bd1c20b47..4fffa54f4 100644
--- a/app/javascript/flavours/glitch/features/ui/components/column_link.js
+++ b/app/javascript/flavours/glitch/features/ui/components/column_link.jsx
@@ -30,9 +30,9 @@ const ColumnLink = ({ icon, text, to, onClick, href, method, badge, transparent,
       e.preventDefault();
       e.stopPropagation();
       return onClick(e);
-    }
+    };
     return (
-      <a href='#' onClick={onClick && handleOnClick} className={className} title={text} {...other} tabIndex='0'>
+      <a href='#' onClick={onClick && handleOnClick} className={className} title={text} {...other} tabIndex={0}>
         {iconElement}
         <span>{text}</span>
         {badgeElement}
diff --git a/app/javascript/flavours/glitch/features/ui/components/column_loading.js b/app/javascript/flavours/glitch/features/ui/components/column_loading.jsx
index b07385397..b07385397 100644
--- a/app/javascript/flavours/glitch/features/ui/components/column_loading.js
+++ b/app/javascript/flavours/glitch/features/ui/components/column_loading.jsx
diff --git a/app/javascript/flavours/glitch/features/ui/components/column_subheading.js b/app/javascript/flavours/glitch/features/ui/components/column_subheading.jsx
index 8160c4aa3..8160c4aa3 100644
--- a/app/javascript/flavours/glitch/features/ui/components/column_subheading.js
+++ b/app/javascript/flavours/glitch/features/ui/components/column_subheading.jsx
diff --git a/app/javascript/flavours/glitch/features/ui/components/columns_area.js b/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx
index 993a50796..3b3b0d58f 100644
--- a/app/javascript/flavours/glitch/features/ui/components/columns_area.js
+++ b/app/javascript/flavours/glitch/features/ui/components/columns_area.jsx
@@ -59,7 +59,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
 
   state = {
     renderComposePanel: !(this.mediaQuery && this.mediaQuery.matches),
-  }
+  };
 
   componentDidMount() {
     if (!this.props.singleColumn) {
@@ -113,7 +113,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
 
   handleLayoutChange = (e) => {
     this.setState({ renderComposePanel: !e.matches });
-  }
+  };
 
   handleWheel = () => {
     if (typeof this._interruptScrollAnimation !== 'function') {
@@ -121,19 +121,19 @@ export default class ColumnsArea extends ImmutablePureComponent {
     }
 
     this._interruptScrollAnimation();
-  }
+  };
 
   setRef = (node) => {
     this.node = node;
-  }
+  };
 
   renderLoading = columnId => () => {
     return columnId === 'COMPOSE' ? <DrawerLoading /> : <ColumnLoading multiColumn />;
-  }
+  };
 
   renderError = (props) => {
     return <BundleColumnError multiColumn errorType='network' {...props} />;
-  }
+  };
 
   render () {
     const { columns, children, singleColumn, navbarUnder, openSettings } = this.props;
diff --git a/app/javascript/flavours/glitch/features/ui/components/compare_history_modal.js b/app/javascript/flavours/glitch/features/ui/components/compare_history_modal.jsx
index baf7f25be..cc3a16d17 100644
--- a/app/javascript/flavours/glitch/features/ui/components/compare_history_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/compare_history_modal.jsx
@@ -12,6 +12,7 @@ import RelativeTimestamp from 'flavours/glitch/components/relative_timestamp';
 import MediaAttachments from 'flavours/glitch/components/media_attachments';
 
 const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
   versions: state.getIn(['history', statusId, 'items']),
 });
 
@@ -23,18 +24,18 @@ const mapDispatchToProps = dispatch => ({
 
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
 class CompareHistoryModal extends React.PureComponent {
 
   static propTypes = {
     onClose: PropTypes.func.isRequired,
     index: PropTypes.number.isRequired,
     statusId: PropTypes.string.isRequired,
+    language: PropTypes.string.isRequired,
     versions: ImmutablePropTypes.list.isRequired,
   };
 
   render () {
-    const { index, versions, onClose } = this.props;
+    const { index, versions, language, onClose } = this.props;
     const currentVersion = versions.get(index);
 
     const emojiMap = currentVersion.get('emojis').reduce((obj, emoji) => {
@@ -65,12 +66,12 @@ class CompareHistoryModal extends React.PureComponent {
           <div className='status__content'>
             {currentVersion.get('spoiler_text').length > 0 && (
               <React.Fragment>
-                <div className='translate' dangerouslySetInnerHTML={spoilerContent} />
+                <div className='translate' dangerouslySetInnerHTML={spoilerContent} lang={language} />
                 <hr />
               </React.Fragment>
             )}
 
-            <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
+            <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} lang={language} />
 
             {!!currentVersion.get('poll') && (
               <div className='poll'>
@@ -82,6 +83,7 @@ class CompareHistoryModal extends React.PureComponent {
                       <span
                         className='poll__option__text translate'
                         dangerouslySetInnerHTML={{ __html: emojify(escapeTextContentForBrowser(option.get('title')), emojiMap) }}
+                        lang={language}
                       />
                     </li>
                   ))}
@@ -89,7 +91,7 @@ class CompareHistoryModal extends React.PureComponent {
               </div>
             )}
 
-            <MediaAttachments status={currentVersion} />
+            <MediaAttachments status={currentVersion} lang={language} />
           </div>
         </div>
       </div>
@@ -97,3 +99,5 @@ class CompareHistoryModal extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(CompareHistoryModal);
diff --git a/app/javascript/flavours/glitch/features/ui/components/compose_panel.js b/app/javascript/flavours/glitch/features/ui/components/compose_panel.jsx
index dde252a61..1dedf92ca 100644
--- a/app/javascript/flavours/glitch/features/ui/components/compose_panel.js
+++ b/app/javascript/flavours/glitch/features/ui/components/compose_panel.jsx
@@ -8,7 +8,6 @@ import LinkFooter from './link_footer';
 import ServerBanner from 'flavours/glitch/components/server_banner';
 import { mountCompose, unmountCompose } from 'flavours/glitch/actions/compose';
 
-export default @connect()
 class ComposePanel extends React.PureComponent {
 
   static contextTypes = {
@@ -55,4 +54,6 @@ class ComposePanel extends React.PureComponent {
     );
   }
 
-};
+}
+
+export default connect()(ComposePanel);
diff --git a/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js b/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.jsx
index a665b9fb1..08f55c125 100644
--- a/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/confirmation_modal.jsx
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import Button from 'flavours/glitch/components/button';
 
-export default @injectIntl
 class ConfirmationModal extends React.PureComponent {
 
   static propTypes = {
@@ -34,24 +33,24 @@ class ConfirmationModal extends React.PureComponent {
     if (this.props.onDoNotAsk && this.doNotAskCheckbox.checked) {
       this.props.onDoNotAsk();
     }
-  }
+  };
 
   handleSecondary = () => {
     this.props.onClose();
     this.props.onSecondary();
-  }
+  };
 
   handleCancel = () => {
     this.props.onClose();
-  }
+  };
 
   setRef = (c) => {
     this.button = c;
-  }
+  };
 
   setDoNotAskRef = (c) => {
     this.doNotAskCheckbox = c;
-  }
+  };
 
   render () {
     const { message, confirm, secondary, onDoNotAsk } = this.props;
@@ -86,3 +85,5 @@ class ConfirmationModal extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ConfirmationModal);
diff --git a/app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.js b/app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.jsx
index 68f04cb21..5a1c1ee1b 100644
--- a/app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/deprecated_settings_modal.jsx
@@ -13,7 +13,6 @@ const messages = defineMessages({
   user_setting_disable_swiping: { id: 'settings.swipe_to_change_columns', defaultMessage: 'Allow swiping to change columns (Mobile only)' },
 });
 
-export default @injectIntl
 class DeprecatedSettingsModal extends React.PureComponent {
 
   static propTypes = {
@@ -30,11 +29,11 @@ class DeprecatedSettingsModal extends React.PureComponent {
   handleClick = () => {
     this.props.onConfirm();
     this.props.onClose();
-  }
+  };
 
   setRef = (c) => {
     this.button = c;
-  }
+  };
 
   render () {
     const { settings, intl } = this.props;
@@ -84,3 +83,5 @@ class DeprecatedSettingsModal extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(DeprecatedSettingsModal);
diff --git a/app/javascript/flavours/glitch/features/ui/components/disabled_account_banner.js b/app/javascript/flavours/glitch/features/ui/components/disabled_account_banner.jsx
index c861d4d81..0ba79d648 100644
--- a/app/javascript/flavours/glitch/features/ui/components/disabled_account_banner.js
+++ b/app/javascript/flavours/glitch/features/ui/components/disabled_account_banner.jsx
@@ -28,8 +28,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
   },
 });
 
-export default @injectIntl
-@connect(mapStateToProps, mapDispatchToProps)
 class DisabledAccountBanner extends React.PureComponent {
 
   static propTypes = {
@@ -46,7 +44,7 @@ class DisabledAccountBanner extends React.PureComponent {
     this.props.onLogout();
 
     return false;
-  }
+  };
 
   render () {
     const { disabledAcct, movedToAcct } = this.props;
@@ -89,4 +87,6 @@ class DisabledAccountBanner extends React.PureComponent {
     );
   }
 
-};
+}
+
+export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(DisabledAccountBanner));
diff --git a/app/javascript/flavours/glitch/features/ui/components/doodle_modal.js b/app/javascript/flavours/glitch/features/ui/components/doodle_modal.jsx
index 0d10204fc..162957ad8 100644
--- a/app/javascript/flavours/glitch/features/ui/components/doodle_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/doodle_modal.jsx
@@ -145,7 +145,6 @@ const mapDispatchToProps = dispatch => ({
  * - Ctrl + left mouse button: pick background
  * - Right mouse button: pick background
  */
-export default @connect(mapStateToProps, mapDispatchToProps)
 class DoodleModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -279,7 +278,7 @@ class DoodleModal extends ImmutablePureComponent {
     this.swapped = false;
     window.addEventListener('keyup', this.handleKeyUp, false);
     window.addEventListener('keydown', this.handleKeyDown, false);
-  };
+  }
 
   /**
    * Tear component down
@@ -575,7 +574,7 @@ class DoodleModal extends ImmutablePureComponent {
             <div>
               <select aria-label='Canvas size' onInput={this.changeSize} defaultValue={this.size}>
                 { Object.values(mapValues(DOODLE_SIZES, (val, k) =>
-                  <option key={k} value={k}>{val[2]}</option>
+                  <option key={k} value={k}>{val[2]}</option>,
                 )) }
               </select>
             </div>
@@ -602,7 +601,7 @@ class DoodleModal extends ImmutablePureComponent {
                       'foreground': this.fg === c[0],
                       'background': this.bg === c[0],
                     })}
-                  />
+                  />,
               )
             }
           </div>
@@ -612,3 +611,5 @@ class DoodleModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(DoodleModal);
diff --git a/app/javascript/flavours/glitch/features/ui/components/drawer_loading.js b/app/javascript/flavours/glitch/features/ui/components/drawer_loading.jsx
index 08b0d2347..08b0d2347 100644
--- a/app/javascript/flavours/glitch/features/ui/components/drawer_loading.js
+++ b/app/javascript/flavours/glitch/features/ui/components/drawer_loading.jsx
diff --git a/app/javascript/flavours/glitch/features/ui/components/embed_modal.js b/app/javascript/flavours/glitch/features/ui/components/embed_modal.jsx
index 624b68f7e..4f1173fd5 100644
--- a/app/javascript/flavours/glitch/features/ui/components/embed_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/embed_modal.jsx
@@ -9,7 +9,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @injectIntl
 class EmbedModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -17,7 +16,7 @@ class EmbedModal extends ImmutablePureComponent {
     onClose: PropTypes.func.isRequired,
     onError: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
-  }
+  };
 
   state = {
     loading: false,
@@ -48,11 +47,11 @@ class EmbedModal extends ImmutablePureComponent {
 
   setIframeRef = c =>  {
     this.iframe = c;
-  }
+  };
 
   handleTextareaClick = (e) => {
     e.target.select();
-  }
+  };
 
   render () {
     const { intl, onClose } = this.props;
@@ -95,3 +94,5 @@ class EmbedModal extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(EmbedModal);
diff --git a/app/javascript/flavours/glitch/features/ui/components/favourite_modal.js b/app/javascript/flavours/glitch/features/ui/components/favourite_modal.jsx
index d7f671d58..fa6f11792 100644
--- a/app/javascript/flavours/glitch/features/ui/components/favourite_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/favourite_modal.jsx
@@ -17,7 +17,6 @@ const messages = defineMessages({
   favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
 });
 
-export default @injectIntl
 class FavouriteModal extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -38,21 +37,21 @@ class FavouriteModal extends ImmutablePureComponent {
   handleFavourite = () => {
     this.props.onFavourite(this.props.status);
     this.props.onClose();
-  }
+  };
 
   handleAccountClick = (e) => {
     if (e.button === 0) {
       e.preventDefault();
       this.props.onClose();
-      let state = {...this.context.router.history.location.state};
+      let state = { ...this.context.router.history.location.state };
       state.mastodonBackSteps = (state.mastodonBackSteps || 0) + 1;
       this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`, state);
     }
-  }
+  };
 
   setRef = (c) => {
     this.button = c;
-  }
+  };
 
   render () {
     const { status, intl } = this.props;
@@ -99,3 +98,5 @@ class FavouriteModal extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(FavouriteModal);
diff --git a/app/javascript/flavours/glitch/features/ui/components/filter_modal.js b/app/javascript/flavours/glitch/features/ui/components/filter_modal.jsx
index d2482e733..2d49312e5 100644
--- a/app/javascript/flavours/glitch/features/ui/components/filter_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/filter_modal.jsx
@@ -13,8 +13,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @connect(undefined)
-@injectIntl
 class FilterModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -132,3 +130,5 @@ class FilterModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(injectIntl(FilterModal));
diff --git a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.jsx
index 0dd07fb76..a5637d31c 100644
--- a/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/focal_point_modal.jsx
@@ -5,11 +5,10 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 import { connect } from 'react-redux';
 import classNames from 'classnames';
 import { changeUploadCompose, uploadThumbnail, onChangeMediaDescription, onChangeMediaFocus } from 'flavours/glitch/actions/compose';
-import { getPointerPosition } from 'flavours/glitch/features/video';
+import Video, { getPointerPosition } from 'flavours/glitch/features/video';
 import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
 import IconButton from 'flavours/glitch/components/icon_button';
 import Button from 'flavours/glitch/components/button';
-import Video from 'flavours/glitch/features/video';
 import Audio from 'flavours/glitch/features/audio';
 import Textarea from 'react-textarea-autosize';
 import UploadProgress from 'flavours/glitch/features/compose/components/upload_progress';
@@ -39,6 +38,7 @@ const mapStateToProps = (state, { id }) => ({
   account: state.getIn(['accounts', me]),
   isUploadingThumbnail: state.getIn(['compose', 'isUploadingThumbnail']),
   description: state.getIn(['compose', 'media_modal', 'description']),
+  lang: state.getIn(['compose', 'language']),
   focusX: state.getIn(['compose', 'media_modal', 'focusX']),
   focusY: state.getIn(['compose', 'media_modal', 'focusY']),
   dirty: state.getIn(['compose', 'media_modal', 'dirty']),
@@ -99,8 +99,6 @@ class ImageLoader extends React.PureComponent {
 
 }
 
-export default @connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })
-@(component => injectIntl(component, { withRef: true }))
 class FocalPointModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -134,7 +132,7 @@ class FocalPointModal extends ImmutablePureComponent {
 
     this.updatePosition(e);
     this.setState({ dragging: true });
-  }
+  };
 
   handleTouchStart = e => {
     document.addEventListener('touchmove', this.handleMouseMove);
@@ -142,25 +140,25 @@ class FocalPointModal extends ImmutablePureComponent {
 
     this.updatePosition(e);
     this.setState({ dragging: true });
-  }
+  };
 
   handleMouseMove = e => {
     this.updatePosition(e);
-  }
+  };
 
   handleMouseUp = () => {
     document.removeEventListener('mousemove', this.handleMouseMove);
     document.removeEventListener('mouseup', this.handleMouseUp);
 
     this.setState({ dragging: false });
-  }
+  };
 
   handleTouchEnd = () => {
     document.removeEventListener('touchmove', this.handleMouseMove);
     document.removeEventListener('touchend', this.handleTouchEnd);
 
     this.setState({ dragging: false });
-  }
+  };
 
   updatePosition = e => {
     const { x, y } = getPointerPosition(this.node, e);
@@ -168,11 +166,11 @@ class FocalPointModal extends ImmutablePureComponent {
     const focusY   = (y - .5) * -2;
 
     this.props.onChangeFocus(focusX, focusY);
-  }
+  };
 
   handleChange = e => {
     this.props.onChangeDescription(e.target.value);
-  }
+  };
 
   handleKeyDown = (e) => {
     if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
@@ -181,11 +179,11 @@ class FocalPointModal extends ImmutablePureComponent {
       this.props.onChangeDescription(e.target.value);
       this.handleSubmit();
     }
-  }
+  };
 
   handleSubmit = () => {
     this.props.onSave(this.props.description, this.props.focusX, this.props.focusY);
-  }
+  };
 
   getCloseConfirmationMessage = () => {
     const { intl, dirty } = this.props;
@@ -198,15 +196,15 @@ class FocalPointModal extends ImmutablePureComponent {
     } else {
       return null;
     }
-  }
+  };
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   handleTextDetection = () => {
     this._detectText();
-  }
+  };
 
   _detectText = (refreshCache = false) => {
     const { media } = this.props;
@@ -257,24 +255,24 @@ class FocalPointModal extends ImmutablePureComponent {
       console.error(e);
       this.setState({ detecting: false });
     });
-  }
+  };
 
   handleThumbnailChange = e => {
     if (e.target.files.length > 0) {
       this.props.onSelectThumbnail(e.target.files);
     }
-  }
+  };
 
   setFileInputRef = c => {
     this.fileInput = c;
-  }
+  };
 
   handleFileInputClick = () => {
     this.fileInput.click();
-  }
+  };
 
   render () {
-    const { media, intl, account, onClose, isUploadingThumbnail, description, focusX, focusY, dirty, is_changing_upload } = this.props;
+    const { media, intl, account, onClose, isUploadingThumbnail, description, lang, focusX, focusY, dirty, is_changing_upload } = this.props;
     const { dragging, detecting, progress, ocrStatus } = this.state;
     const x = (focusX /  2) + .5;
     const y = (focusY / -2) + .5;
@@ -320,7 +318,7 @@ class FocalPointModal extends ImmutablePureComponent {
               <React.Fragment>
                 <label className='setting-text-label' htmlFor='upload-modal__thumbnail'><FormattedMessage id='upload_form.thumbnail' defaultMessage='Change thumbnail' /></label>
 
-                <Button disabled={isUploadingThumbnail} text={intl.formatMessage(messages.chooseImage)} onClick={this.handleFileInputClick} />
+                <Button disabled={isUploadingThumbnail || !media.get('unattached')} text={intl.formatMessage(messages.chooseImage)} onClick={this.handleFileInputClick} />
 
                 <label>
                   <span style={{ display: 'none' }}>{intl.formatMessage(messages.chooseImage)}</span>
@@ -349,6 +347,7 @@ class FocalPointModal extends ImmutablePureComponent {
                 id='upload-modal__description'
                 className='setting-text light'
                 value={detecting ? '…' : description}
+                lang={lang}
                 onChange={this.handleChange}
                 onKeyDown={this.handleKeyDown}
                 disabled={detecting || is_changing_upload}
@@ -415,3 +414,7 @@ class FocalPointModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps, null, {
+  forwardRef: true,
+})(injectIntl(FocalPointModal, { withRef: true }));
diff --git a/app/javascript/flavours/glitch/features/ui/components/follow_requests_column_link.js b/app/javascript/flavours/glitch/features/ui/components/follow_requests_column_link.jsx
index 301392a52..f3e3b78ed 100644
--- a/app/javascript/flavours/glitch/features/ui/components/follow_requests_column_link.js
+++ b/app/javascript/flavours/glitch/features/ui/components/follow_requests_column_link.jsx
@@ -15,8 +15,6 @@ const mapStateToProps = state => ({
   count: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
 });
 
-export default @injectIntl
-@connect(mapStateToProps)
 class FollowRequestsColumnLink extends React.Component {
 
   static propTypes = {
@@ -49,3 +47,5 @@ class FollowRequestsColumnLink extends React.Component {
   }
 
 }
+
+export default injectIntl(connect(mapStateToProps)(FollowRequestsColumnLink));
diff --git a/app/javascript/flavours/glitch/features/ui/components/header.js b/app/javascript/flavours/glitch/features/ui/components/header.jsx
index d9ad94961..f7bab2487 100644
--- a/app/javascript/flavours/glitch/features/ui/components/header.js
+++ b/app/javascript/flavours/glitch/features/ui/components/header.jsx
@@ -23,8 +23,6 @@ const mapDispatchToProps = (dispatch) => ({
   },
 });
 
-export default @connect(null, mapDispatchToProps)
-@withRouter
 class Header extends React.PureComponent {
 
   static contextTypes = {
@@ -86,3 +84,5 @@ class Header extends React.PureComponent {
   }
 
 }
+
+export default withRouter(connect(null, mapDispatchToProps)(Header));
diff --git a/app/javascript/mastodon/features/ui/components/image_loader.js b/app/javascript/flavours/glitch/features/ui/components/image_loader.jsx
index dfa0efe49..9093eab28 100644
--- a/app/javascript/mastodon/features/ui/components/image_loader.js
+++ b/app/javascript/flavours/glitch/features/ui/components/image_loader.jsx
@@ -8,16 +8,18 @@ export default class ImageLoader extends PureComponent {
 
   static propTypes = {
     alt: PropTypes.string,
+    lang: PropTypes.string,
     src: PropTypes.string.isRequired,
     previewSrc: PropTypes.string,
     width: PropTypes.number,
     height: PropTypes.number,
     onClick: PropTypes.func,
     zoomButtonHidden: PropTypes.bool,
-  }
+  };
 
   static defaultProps = {
     alt: '',
+    lang: '',
     width: null,
     height: null,
   };
@@ -26,7 +28,7 @@ export default class ImageLoader extends PureComponent {
     loading: true,
     error: false,
     width: null,
-  }
+  };
 
   removers = [];
   canvas = null;
@@ -86,7 +88,7 @@ export default class ImageLoader extends PureComponent {
     image.addEventListener('load', handleLoad);
     image.src = previewSrc;
     this.removers.push(removeEventListeners);
-  })
+  });
 
   clearPreviewCanvas () {
     const { width, height } = this.canvas;
@@ -126,10 +128,10 @@ export default class ImageLoader extends PureComponent {
   setCanvasRef = c => {
     this.canvas = c;
     if (c) this.setState({ width: c.offsetWidth });
-  }
+  };
 
   render () {
-    const { alt, src, width, height, onClick } = this.props;
+    const { alt, lang, src, width, height, onClick } = this.props;
     const { loading } = this.state;
 
     const className = classNames('image-loader', {
@@ -154,6 +156,7 @@ export default class ImageLoader extends PureComponent {
         ) : (
           <ZoomableImage
             alt={alt}
+            lang={lang}
             src={src}
             onClick={onClick}
             width={width}
diff --git a/app/javascript/flavours/glitch/features/ui/components/image_modal.js b/app/javascript/flavours/glitch/features/ui/components/image_modal.jsx
index a792b9be7..5198b8809 100644
--- a/app/javascript/flavours/glitch/features/ui/components/image_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/image_modal.jsx
@@ -9,7 +9,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @injectIntl
 class ImageModal extends React.PureComponent {
 
   static propTypes = {
@@ -57,3 +56,5 @@ class ImageModal extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ImageModal);
diff --git a/app/javascript/flavours/glitch/features/ui/components/link_footer.js b/app/javascript/flavours/glitch/features/ui/components/link_footer.jsx
index ac0c78674..e813a72b0 100644
--- a/app/javascript/flavours/glitch/features/ui/components/link_footer.js
+++ b/app/javascript/flavours/glitch/features/ui/components/link_footer.jsx
@@ -3,7 +3,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
 import { Link } from 'react-router-dom';
-import { domain, version, source_url, profile_directory as profileDirectory } from 'flavours/glitch/initial_state';
+import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'flavours/glitch/initial_state';
 import { logOut } from 'flavours/glitch/utils/log_out';
 import { openModal } from 'flavours/glitch/actions/modal';
 import { PERMISSION_INVITE_USERS } from 'flavours/glitch/permissions';
@@ -24,8 +24,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
   },
 });
 
-export default @injectIntl
-@connect(null, mapDispatchToProps)
 class LinkFooter extends React.PureComponent {
 
   static contextTypes = {
@@ -44,7 +42,7 @@ class LinkFooter extends React.PureComponent {
     this.props.onLogout();
 
     return false;
-  }
+  };
 
   render () {
     const { signedIn, permissions } = this.context.identity;
@@ -59,21 +57,27 @@ class LinkFooter extends React.PureComponent {
         <p>
           <strong>{domain}</strong>:
           {' '}
-          <Link key='about' to='/about'><FormattedMessage id='footer.about' defaultMessage='About' /></Link>
+          <Link to='/about'><FormattedMessage id='footer.about' defaultMessage='About' /></Link>
+          {statusPageUrl && (
+            <>
+              {DividingCircle}
+              <a href={statusPageUrl} target='_blank' rel='noopener'><FormattedMessage id='footer.status' defaultMessage='Status' /></a>
+            </>
+          )}
           {canInvite && (
             <>
               {DividingCircle}
-              <a key='invites' href='/invites' target='_blank'><FormattedMessage id='footer.invite' defaultMessage='Invite people' /></a>
+              <a href='/invites' target='_blank'><FormattedMessage id='footer.invite' defaultMessage='Invite people' /></a>
             </>
           )}
           {canProfileDirectory && (
             <>
               {DividingCircle}
-              <Link key='directory' to='/directory'><FormattedMessage id='footer.directory' defaultMessage='Profiles directory' /></Link>
+              <Link to='/directory'><FormattedMessage id='footer.directory' defaultMessage='Profiles directory' /></Link>
             </>
           )}
           {DividingCircle}
-          <Link key='privacy-policy' to='/privacy-policy'><FormattedMessage id='footer.privacy_policy' defaultMessage='Privacy policy' /></Link>
+          <Link to='/privacy-policy'><FormattedMessage id='footer.privacy_policy' defaultMessage='Privacy policy' /></Link>
         </p>
 
         <p>
@@ -93,4 +97,6 @@ class LinkFooter extends React.PureComponent {
     );
   }
 
-};
+}
+
+export default injectIntl(connect(null, mapDispatchToProps)(LinkFooter));
diff --git a/app/javascript/flavours/glitch/features/ui/components/list_panel.js b/app/javascript/flavours/glitch/features/ui/components/list_panel.jsx
index dff830065..489f3a1b4 100644
--- a/app/javascript/flavours/glitch/features/ui/components/list_panel.js
+++ b/app/javascript/flavours/glitch/features/ui/components/list_panel.jsx
@@ -20,8 +20,6 @@ const mapStateToProps = state => ({
   lists: getOrderedLists(state),
 });
 
-export default @withRouter
-@connect(mapStateToProps)
 class ListPanel extends ImmutablePureComponent {
 
   static propTypes = {
@@ -53,3 +51,5 @@ class ListPanel extends ImmutablePureComponent {
   }
 
 }
+
+export default withRouter(connect(mapStateToProps)(ListPanel));
diff --git a/app/javascript/flavours/glitch/features/ui/components/media_modal.js b/app/javascript/flavours/glitch/features/ui/components/media_modal.jsx
index ec3af857d..fd2bd43cf 100644
--- a/app/javascript/flavours/glitch/features/ui/components/media_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/media_modal.jsx
@@ -3,6 +3,7 @@ import ReactSwipeableViews from 'react-swipeable-views';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Video from 'flavours/glitch/features/video';
+import { connect } from 'react-redux';
 import classNames from 'classnames';
 import { defineMessages, injectIntl } from 'react-intl';
 import IconButton from 'flavours/glitch/components/icon_button';
@@ -20,7 +21,10 @@ const messages = defineMessages({
   next: { id: 'lightbox.next', defaultMessage: 'Next' },
 });
 
-export default @injectIntl
+const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
+});
+
 class MediaModal extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -47,27 +51,27 @@ class MediaModal extends ImmutablePureComponent {
 
   handleSwipe = (index) => {
     this.setState({ index: index % this.props.media.size });
-  }
+  };
 
   handleTransitionEnd = () => {
     this.setState({
       zoomButtonHidden: false,
     });
-  }
+  };
 
   handleNextClick = () => {
     this.setState({
       index: (this.getIndex() + 1) % this.props.media.size,
       zoomButtonHidden: true,
     });
-  }
+  };
 
   handlePrevClick = () => {
     this.setState({
       index: (this.props.media.size + this.getIndex() - 1) % this.props.media.size,
       zoomButtonHidden: true,
     });
-  }
+  };
 
   handleChangeIndex = (e) => {
     const index = Number(e.currentTarget.getAttribute('data-index'));
@@ -76,7 +80,7 @@ class MediaModal extends ImmutablePureComponent {
       index: index % this.props.media.size,
       zoomButtonHidden: true,
     });
-  }
+  };
 
   handleKeyDown = (e) => {
     switch(e.key) {
@@ -91,7 +95,7 @@ class MediaModal extends ImmutablePureComponent {
       e.stopPropagation();
       break;
     }
-  }
+  };
 
   componentDidMount () {
     window.addEventListener('keydown', this.handleKeyDown, false);
@@ -131,13 +135,13 @@ class MediaModal extends ImmutablePureComponent {
   }
 
   render () {
-    const { media, statusId, intl, onClose } = this.props;
+    const { media, language, statusId, intl, onClose } = this.props;
     const { navigationHidden } = this.state;
 
     const index = this.getIndex();
 
-    const leftNav  = media.size > 1 && <button tabIndex='0' className='media-modal__nav media-modal__nav--left' onClick={this.handlePrevClick} aria-label={intl.formatMessage(messages.previous)}><Icon id='chevron-left' fixedWidth /></button>;
-    const rightNav = media.size > 1 && <button tabIndex='0' className='media-modal__nav  media-modal__nav--right' onClick={this.handleNextClick} aria-label={intl.formatMessage(messages.next)}><Icon id='chevron-right' fixedWidth /></button>;
+    const leftNav  = media.size > 1 && <button tabIndex={0} className='media-modal__nav media-modal__nav--left' onClick={this.handlePrevClick} aria-label={intl.formatMessage(messages.previous)}><Icon id='chevron-left' fixedWidth /></button>;
+    const rightNav = media.size > 1 && <button tabIndex={0} className='media-modal__nav  media-modal__nav--right' onClick={this.handleNextClick} aria-label={intl.formatMessage(messages.next)}><Icon id='chevron-right' fixedWidth /></button>;
 
     const content = media.map((image) => {
       const width  = image.getIn(['meta', 'original', 'width']) || null;
@@ -151,6 +155,7 @@ class MediaModal extends ImmutablePureComponent {
             width={width}
             height={height}
             alt={image.get('description')}
+            lang={language}
             key={image.get('url')}
             onClick={this.toggleNavigation}
             zoomButtonHidden={this.state.zoomButtonHidden}
@@ -173,6 +178,7 @@ class MediaModal extends ImmutablePureComponent {
             onCloseVideo={onClose}
             detailed
             alt={image.get('description')}
+            lang={language}
             key={image.get('url')}
           />
         );
@@ -184,6 +190,7 @@ class MediaModal extends ImmutablePureComponent {
             height={height}
             key={image.get('preview_url')}
             alt={image.get('description')}
+            lang={language}
             onClick={this.toggleNavigation}
           />
         );
@@ -250,3 +257,5 @@ class MediaModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(injectIntl(MediaModal));
diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_loading.js b/app/javascript/flavours/glitch/features/ui/components/modal_loading.jsx
index b1c322154..b1c322154 100644
--- a/app/javascript/flavours/glitch/features/ui/components/modal_loading.js
+++ b/app/javascript/flavours/glitch/features/ui/components/modal_loading.jsx
diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.jsx
index 379f57cbb..c133f2b6a 100644
--- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js
+++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.jsx
@@ -76,28 +76,28 @@ export default class ModalRoot extends React.PureComponent {
   };
 
   componentDidUpdate () {
-    if (!!this.props.type) {
+    if (this.props.type) {
       document.body.classList.add('with-modals--active');
       document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
     } else {
       document.body.classList.remove('with-modals--active');
-      document.documentElement.style.marginRight = 0;
+      document.documentElement.style.marginRight = '0';
     }
   }
 
   setBackgroundColor = color => {
     this.setState({ backgroundColor: color });
-  }
+  };
 
   renderLoading = modalId => () => {
     return ['MEDIA', 'VIDEO', 'BOOST', 'FAVOURITE', 'DOODLE', 'CONFIRM', 'ACTIONS'].indexOf(modalId) === -1 ? <ModalLoading /> : null;
-  }
+  };
 
   renderError = (props) => {
     const { onClose } = this.props;
 
     return <BundleModalError {...props} onClose={onClose} />;
-  }
+  };
 
   handleClose = (ignoreFocus = false) => {
     const { onClose } = this.props;
@@ -110,14 +110,14 @@ export default class ModalRoot extends React.PureComponent {
       // This would be much smoother with react-intl 3+ and `forwardRef`.
     }
     onClose(message, ignoreFocus);
-  }
+  };
 
   setModalRef = (c) => {
     this._modal = c;
-  }
+  };
 
   // prevent closing of modal when clicking the overlay
-  noop = () => {}
+  noop = () => {};
 
   render () {
     const { type, props, ignoreFocus } = this.props;
diff --git a/app/javascript/flavours/glitch/features/ui/components/mute_modal.js b/app/javascript/flavours/glitch/features/ui/components/mute_modal.jsx
index 7d25db316..a74ebfb05 100644
--- a/app/javascript/flavours/glitch/features/ui/components/mute_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/mute_modal.jsx
@@ -43,8 +43,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class MuteModal extends React.PureComponent {
 
   static propTypes = {
@@ -65,23 +63,23 @@ class MuteModal extends React.PureComponent {
   handleClick = () => {
     this.props.onClose();
     this.props.onConfirm(this.props.account, this.props.notifications, this.props.muteDuration);
-  }
+  };
 
   handleCancel = () => {
     this.props.onClose();
-  }
+  };
 
   setRef = (c) => {
     this.button = c;
-  }
+  };
 
   toggleNotifications = () => {
     this.props.onToggleNotifications();
-  }
+  };
 
   changeMuteDuration = (e) => {
     this.props.onChangeMuteDuration(e);
-  }
+  };
 
   render () {
     const { account, notifications, muteDuration, intl } = this.props;
@@ -138,3 +136,5 @@ class MuteModal extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(MuteModal));
diff --git a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx
index 3b46c6eec..6e8744ef0 100644
--- a/app/javascript/flavours/glitch/features/ui/components/navigation_panel.js
+++ b/app/javascript/flavours/glitch/features/ui/components/navigation_panel.jsx
@@ -29,7 +29,6 @@ const messages = defineMessages({
   app_settings: { id: 'navigation_bar.app_settings', defaultMessage: 'App settings' },
 });
 
-export default @injectIntl
 class NavigationPanel extends React.Component {
 
   static contextTypes = {
@@ -78,8 +77,8 @@ class NavigationPanel extends React.Component {
         {signedIn && (
           <React.Fragment>
             <ColumnLink transparent to='/conversations' icon='at' text={intl.formatMessage(messages.direct)} />
-            <ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
             <ColumnLink transparent to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} />
+            <ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
             <ColumnLink transparent to='/lists' icon='list-ul' text={intl.formatMessage(messages.lists)} />
 
             <ListPanel />
@@ -102,3 +101,5 @@ class NavigationPanel extends React.Component {
   }
 
 }
+
+export default injectIntl(NavigationPanel);
diff --git a/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.js b/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.jsx
index 5ca003ee9..7c9105c58 100644
--- a/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/onboarding_modal.jsx
@@ -49,7 +49,7 @@ const PageTwo = ({ intl, myAccount }) => (
           privacy='public'
           text='Awoo! #introductions'
           spoilerText=''
-          suggestions={ [] }
+          suggestions={[]}
         />
       </div>
     </div>
@@ -171,8 +171,6 @@ const mapStateToProps = state => ({
   domain: state.getIn(['meta', 'domain']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class OnboardingModal extends React.PureComponent {
 
   static propTypes = {
@@ -196,7 +194,7 @@ class OnboardingModal extends React.PureComponent {
       <PageFour domain={domain} intl={intl} />,
       <PageSix admin={admin} domain={domain} />,
     ];
-  };
+  }
 
   componentDidMount() {
     window.addEventListener('keyup', this.handleKeyUp);
@@ -209,30 +207,30 @@ class OnboardingModal extends React.PureComponent {
   handleSkip = (e) => {
     e.preventDefault();
     this.props.onClose();
-  }
+  };
 
   handleDot = (e) => {
     const i = Number(e.currentTarget.getAttribute('data-index'));
     e.preventDefault();
     this.setState({ currentIndex: i });
-  }
+  };
 
   handlePrev = () => {
     this.setState(({ currentIndex }) => ({
       currentIndex: Math.max(0, currentIndex - 1),
     }));
-  }
+  };
 
   handleNext = () => {
     const { pages } = this;
     this.setState(({ currentIndex }) => ({
       currentIndex: Math.min(currentIndex + 1, pages.length - 1),
     }));
-  }
+  };
 
   handleSwipe = (index) => {
     this.setState({ currentIndex: index });
-  }
+  };
 
   handleKeyUp = ({ key }) => {
     switch (key) {
@@ -243,11 +241,11 @@ class OnboardingModal extends React.PureComponent {
       this.handleNext();
       break;
     }
-  }
+  };
 
   handleClose = () => {
     this.props.onClose();
-  }
+  };
 
   render () {
     const { pages } = this;
@@ -302,7 +300,7 @@ class OnboardingModal extends React.PureComponent {
                 <div
                   key={`dot-${i}`}
                   role='button'
-                  tabIndex='0'
+                  tabIndex={0}
                   data-index={i}
                   onClick={this.handleDot}
                   className={className}
@@ -320,3 +318,5 @@ class OnboardingModal extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(OnboardingModal));
diff --git a/app/javascript/flavours/glitch/features/ui/components/report_modal.js b/app/javascript/flavours/glitch/features/ui/components/report_modal.jsx
index 7b6a4a784..79b495877 100644
--- a/app/javascript/flavours/glitch/features/ui/components/report_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/report_modal.jsx
@@ -31,8 +31,6 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class ReportModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -96,7 +94,7 @@ class ReportModal extends ImmutablePureComponent {
     } else {
       this.setState({ selectedRuleIds: selectedRuleIds.remove(ruleId) });
     }
-  }
+  };
 
   handleChangeCategory = category => {
     this.setState({ category });
@@ -219,3 +217,5 @@ class ReportModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(ReportModal));
diff --git a/app/javascript/flavours/glitch/features/ui/components/sign_in_banner.js b/app/javascript/flavours/glitch/features/ui/components/sign_in_banner.jsx
index e8023803f..c0d62aca0 100644
--- a/app/javascript/flavours/glitch/features/ui/components/sign_in_banner.js
+++ b/app/javascript/flavours/glitch/features/ui/components/sign_in_banner.jsx
@@ -30,7 +30,7 @@ const SignInBanner = () => {
 
   return (
     <div className='sign-in-banner'>
-      <p><FormattedMessage id='sign_in_banner.text' defaultMessage='Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.' /></p>
+      <p><FormattedMessage id='sign_in_banner.text' defaultMessage='Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.' /></p>
       <a href='/auth/sign_in' className='button button--block'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Sign in' /></a>
       {signupButton}
     </div>
diff --git a/app/javascript/flavours/glitch/features/ui/components/upload_area.js b/app/javascript/flavours/glitch/features/ui/components/upload_area.jsx
index 6958ba9df..0e07b67f8 100644
--- a/app/javascript/flavours/glitch/features/ui/components/upload_area.js
+++ b/app/javascript/flavours/glitch/features/ui/components/upload_area.jsx
@@ -22,7 +22,7 @@ export default class UploadArea extends React.PureComponent {
         break;
       }
     }
-  }
+  };
 
   componentDidMount () {
     window.addEventListener('keyup', this.handleKeyUp, false);
diff --git a/app/javascript/flavours/glitch/features/ui/components/video_modal.js b/app/javascript/flavours/glitch/features/ui/components/video_modal.jsx
index 90be11e4b..4cde0ebad 100644
--- a/app/javascript/flavours/glitch/features/ui/components/video_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/video_modal.jsx
@@ -2,11 +2,16 @@ import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Video from 'flavours/glitch/features/video';
+import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import Footer from 'flavours/glitch/features/picture_in_picture/components/footer';
 import { getAverageFromBlurhash } from 'flavours/glitch/blurhash';
 
-export default class VideoModal extends ImmutablePureComponent {
+const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
+});
+
+class VideoModal extends ImmutablePureComponent {
 
   static contextTypes = {
     router: PropTypes.object,
@@ -15,6 +20,7 @@ export default class VideoModal extends ImmutablePureComponent {
   static propTypes = {
     media: ImmutablePropTypes.map.isRequired,
     statusId: PropTypes.string,
+    language: PropTypes.string,
     options: PropTypes.shape({
       startTime: PropTypes.number,
       autoPlay: PropTypes.bool,
@@ -35,7 +41,7 @@ export default class VideoModal extends ImmutablePureComponent {
   }
 
   render () {
-    const { media, statusId, onClose } = this.props;
+    const { media, statusId, language, onClose } = this.props;
     const options = this.props.options || {};
 
     return (
@@ -53,6 +59,7 @@ export default class VideoModal extends ImmutablePureComponent {
             autoFocus
             detailed
             alt={media.get('description')}
+            lang={language}
           />
         </div>
 
@@ -64,3 +71,5 @@ export default class VideoModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(VideoModal);
diff --git a/app/javascript/flavours/glitch/features/ui/components/zoomable_image.js b/app/javascript/flavours/glitch/features/ui/components/zoomable_image.jsx
index caeeced64..47401cfe4 100644
--- a/app/javascript/flavours/glitch/features/ui/components/zoomable_image.js
+++ b/app/javascript/flavours/glitch/features/ui/components/zoomable_image.jsx
@@ -91,21 +91,22 @@ const normalizeWheel = event => {
   };
 };
 
-export default @injectIntl
 class ZoomableImage extends React.PureComponent {
 
   static propTypes = {
     alt: PropTypes.string,
+    lang: PropTypes.string,
     src: PropTypes.string.isRequired,
     width: PropTypes.number,
     height: PropTypes.number,
     onClick: PropTypes.func,
     zoomButtonHidden: PropTypes.bool,
     intl: PropTypes.object.isRequired,
-  }
+  };
 
   static defaultProps = {
     alt: '',
+    lang: '',
     width: null,
     height: null,
   };
@@ -132,7 +133,7 @@ class ZoomableImage extends React.PureComponent {
     dragged: false,
     lockScroll: { x: 0, y: 0 },
     lockTranslate: { x: 0, y: 0 },
-  }
+  };
 
   removers = [];
   container = null;
@@ -212,7 +213,7 @@ class ZoomableImage extends React.PureComponent {
 
     // lock horizontal scroll
     this.container.scrollLeft = Math.max(this.container.scrollLeft + event.pixelX, this.state.lockScroll.x);
-  }
+  };
 
   mouseDownHandler = e => {
     this.container.style.cursor = 'grabbing';
@@ -228,7 +229,7 @@ class ZoomableImage extends React.PureComponent {
 
     this.image.addEventListener('mousemove', this.mouseMoveHandler);
     this.image.addEventListener('mouseup', this.mouseUpHandler);
-  }
+  };
 
   mouseMoveHandler = e => {
     const dx = e.clientX - this.state.dragPosition.x;
@@ -238,7 +239,7 @@ class ZoomableImage extends React.PureComponent {
     this.container.scrollTop = Math.max(this.state.dragPosition.top - dy, this.state.lockScroll.y);
 
     this.setState({ dragged: true });
-  }
+  };
 
   mouseUpHandler = () => {
     this.container.style.cursor = 'grab';
@@ -246,13 +247,13 @@ class ZoomableImage extends React.PureComponent {
 
     this.image.removeEventListener('mousemove', this.mouseMoveHandler);
     this.image.removeEventListener('mouseup', this.mouseUpHandler);
-  }
+  };
 
   handleTouchStart = e => {
     if (e.touches.length !== 2) return;
 
     this.lastDistance = getDistance(...e.touches);
-  }
+  };
 
   handleTouchMove = e => {
     const { scrollTop, scrollHeight, clientHeight } = this.container;
@@ -275,7 +276,7 @@ class ZoomableImage extends React.PureComponent {
 
     this.lastMidpoint = midpoint;
     this.lastDistance = distance;
-  }
+  };
 
   zoom(nextScale, midpoint) {
     const { scale, zoomMatrix } = this.state;
@@ -314,11 +315,11 @@ class ZoomableImage extends React.PureComponent {
     const handler = this.props.onClick;
     if (handler) handler();
     this.setState({ navigationHidden: !this.state.navigationHidden });
-  }
+  };
 
   handleMouseDown = e => {
     e.preventDefault();
-  }
+  };
 
   initZoomMatrix = () => {
     const { width, height } = this.props;
@@ -350,7 +351,7 @@ class ZoomableImage extends React.PureComponent {
         translateY: translateY,
       },
     });
-  }
+  };
 
   handleZoomClick = e => {
     e.preventDefault();
@@ -392,18 +393,18 @@ class ZoomableImage extends React.PureComponent {
 
     this.container.style.cursor = 'grab';
     this.container.style.removeProperty('user-select');
-  }
+  };
 
   setContainerRef = c => {
     this.container = c;
-  }
+  };
 
   setImageRef = c => {
     this.image = c;
-  }
+  };
 
   render () {
-    const { alt, src, width, height, intl } = this.props;
+    const { alt, lang, src, width, height, intl } = this.props;
     const { scale, lockTranslate } = this.state;
     const overflow = scale === MIN_SCALE ? 'hidden' : 'scroll';
     const zoomButtonShouldHide = this.state.navigationHidden || this.props.zoomButtonHidden || this.state.zoomMatrix.rate <= MIN_SCALE ? 'media-modal__zoom-button--hidden' : '';
@@ -431,6 +432,7 @@ class ZoomableImage extends React.PureComponent {
             ref={this.setImageRef}
             alt={alt}
             title={alt}
+            lang={lang}
             src={src}
             width={width}
             height={height}
@@ -448,3 +450,5 @@ class ZoomableImage extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ZoomableImage);
diff --git a/app/javascript/flavours/glitch/features/ui/index.js b/app/javascript/flavours/glitch/features/ui/index.jsx
index 72e13d9d6..fa35f689d 100644
--- a/app/javascript/flavours/glitch/features/ui/index.js
+++ b/app/javascript/flavours/glitch/features/ui/index.jsx
@@ -10,7 +10,7 @@ import { debounce } from 'lodash';
 import { uploadCompose, resetCompose, changeComposeSpoilerness } from 'flavours/glitch/actions/compose';
 import { expandHomeTimeline } from 'flavours/glitch/actions/timelines';
 import { expandNotifications, notificationsSetVisibility } from 'flavours/glitch/actions/notifications';
-import { fetchServer } from 'flavours/glitch/actions/server';
+import { fetchServer, fetchServerTranslationLanguages } from 'flavours/glitch/actions/server';
 import { clearHeight } from 'flavours/glitch/actions/height_cache';
 import { changeLayout } from 'flavours/glitch/actions/app';
 import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'flavours/glitch/actions/markers';
@@ -42,6 +42,7 @@ import {
   FollowRequests,
   FavouritedStatuses,
   BookmarkedStatuses,
+  FollowedTags,
   ListTimeline,
   Blocks,
   DomainBlocks,
@@ -56,7 +57,7 @@ import {
   PrivacyPolicy,
 } from './util/async-components';
 import { HotKeys } from 'react-hotkeys';
-import initialState, { me, owner, singleUserMode, showTrends } from '../../initial_state';
+import initialState, { me, owner, singleUserMode, showTrends, trendsAsLanding } from '../../initial_state';
 import { closeOnboarding, INTRODUCTION_VERSION } from 'flavours/glitch/actions/onboarding';
 import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
 import { Helmet } from 'react-helmet';
@@ -161,7 +162,7 @@ class SwitchingColumnsArea extends React.PureComponent {
     if (c) {
       this.node = c;
     }
-  }
+  };
 
   render () {
     const { children, mobile, navbarUnder } = this.props;
@@ -177,7 +178,7 @@ class SwitchingColumnsArea extends React.PureComponent {
       }
     } else if (singleUserMode && owner && initialState?.accounts[owner]) {
       redirect = <Redirect from='/' to={`/@${initialState.accounts[owner].username}`} exact />;
-    } else if (showTrends) {
+    } else if (showTrends && trendsAsLanding) {
       redirect = <Redirect from='/' to='/explore' exact />;
     } else {
       redirect = <Redirect from='/' to='/about' exact />;
@@ -230,6 +231,7 @@ class SwitchingColumnsArea extends React.PureComponent {
           <WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
           <WrappedRoute path='/blocks' component={Blocks} content={children} />
           <WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} />
+          <WrappedRoute path='/followed_tags' component={FollowedTags} content={children} />
           <WrappedRoute path='/mutes' component={Mutes} content={children} />
           <WrappedRoute path='/lists' component={Lists} content={children} />
           <WrappedRoute path='/getting-started-misc' component={GettingStartedMisc} content={children} />
@@ -238,13 +240,10 @@ class SwitchingColumnsArea extends React.PureComponent {
         </WrappedSwitch>
       </ColumnsAreaContainer>
     );
-  };
+  }
 
 }
 
-export default @connect(mapStateToProps)
-@injectIntl
-@withRouter
 class UI extends React.Component {
 
   static contextTypes = {
@@ -290,7 +289,7 @@ class UI extends React.Component {
       // but we set user-friendly message for other browsers, e.g. Edge.
       e.returnValue = intl.formatMessage(messages.beforeUnload);
     }
-  }
+  };
 
   handleDragEnter = (e) => {
     e.preventDefault();
@@ -306,7 +305,7 @@ class UI extends React.Component {
     if (e.dataTransfer && e.dataTransfer.types.includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) {
       this.setState({ draggingOver: true });
     }
-  }
+  };
 
   handleDragOver = (e) => {
     if (this.dataTransferIsText(e.dataTransfer)) return false;
@@ -320,7 +319,7 @@ class UI extends React.Component {
     }
 
     return false;
-  }
+  };
 
   handleDrop = (e) => {
     if (this.dataTransferIsText(e.dataTransfer)) return;
@@ -333,7 +332,7 @@ class UI extends React.Component {
     if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.context.identity.signedIn) {
       this.props.dispatch(uploadCompose(e.dataTransfer.files));
     }
-  }
+  };
 
   handleDragLeave = (e) => {
     e.preventDefault();
@@ -346,15 +345,15 @@ class UI extends React.Component {
     }
 
     this.setState({ draggingOver: false });
-  }
+  };
 
   dataTransferIsText = (dataTransfer) => {
     return (dataTransfer && Array.from(dataTransfer.types).filter((type) => type === 'text/plain').length === 1);
-  }
+  };
 
   closeUploadModal = () => {
     this.setState({ draggingOver: false });
-  }
+  };
 
   handleServiceWorkerPostMessage = ({ data }) => {
     if (data.type === 'navigate') {
@@ -362,7 +361,7 @@ class UI extends React.Component {
     } else {
       console.warn('Unknown message type:', data.type);
     }
-  }
+  };
 
   handleVisibilityChange = () => {
     const visibility = !document[this.visibilityHiddenProp];
@@ -370,7 +369,7 @@ class UI extends React.Component {
     if (visibility) {
       this.props.dispatch(submitMarkers({ immediate: true }));
     }
-  }
+  };
 
   handleLayoutChange = debounce(() => {
     this.props.dispatch(clearHeight()); // The cached heights are no longer accurate, invalidate
@@ -387,7 +386,7 @@ class UI extends React.Component {
     } else {
       this.handleLayoutChange();
     }
-  }
+  };
 
   componentDidMount () {
     const { signedIn } = this.context.identity;
@@ -405,7 +404,7 @@ class UI extends React.Component {
       navigator.serviceWorker.addEventListener('message', this.handleServiceWorkerPostMessage);
     }
 
-    this.favicon = new Favico({ animation:"none" });
+    this.favicon = new Favico({ animation:'none' });
 
     // On first launch, redirect to the follow recommendations page
     if (signedIn && this.props.firstLaunch) {
@@ -417,6 +416,7 @@ class UI extends React.Component {
       this.props.dispatch(fetchMarkers());
       this.props.dispatch(expandHomeTimeline());
       this.props.dispatch(expandNotifications());
+      this.props.dispatch(fetchServerTranslationLanguages());
 
       setTimeout(() => this.props.dispatch(fetchServer()), 3000);
     }
@@ -485,7 +485,7 @@ class UI extends React.Component {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   handleHotkeyNew = e => {
     e.preventDefault();
@@ -495,7 +495,7 @@ class UI extends React.Component {
     if (element) {
       element.focus();
     }
-  }
+  };
 
   handleHotkeySearch = e => {
     e.preventDefault();
@@ -505,17 +505,17 @@ class UI extends React.Component {
     if (element) {
       element.focus();
     }
-  }
+  };
 
   handleHotkeyForceNew = e => {
     this.handleHotkeyNew(e);
     this.props.dispatch(resetCompose());
-  }
+  };
 
   handleHotkeyToggleComposeSpoilers = e => {
     e.preventDefault();
     this.props.dispatch(changeComposeSpoilerness());
-  }
+  };
 
   handleHotkeyFocusColumn = e => {
     const index  = (e.key * 1) + 1; // First child is drawer, skip that
@@ -533,7 +533,7 @@ class UI extends React.Component {
         status.focus();
       }
     }
-  }
+  };
 
   handleHotkeyBack = () => {
     // if history is exhausted, or we would leave mastodon, just go to root.
@@ -542,11 +542,11 @@ class UI extends React.Component {
     } else {
       this.props.history.push('/');
     }
-  }
+  };
 
   setHotkeysRef = c => {
     this.hotkeys = c;
-  }
+  };
 
   handleHotkeyToggleHelp = () => {
     if (this.props.location.pathname === '/keyboard-shortcuts') {
@@ -554,55 +554,55 @@ class UI extends React.Component {
     } else {
       this.props.history.push('/keyboard-shortcuts');
     }
-  }
+  };
 
   handleHotkeyGoToHome = () => {
     this.props.history.push('/home');
-  }
+  };
 
   handleHotkeyGoToNotifications = () => {
     this.props.history.push('/notifications');
-  }
+  };
 
   handleHotkeyGoToLocal = () => {
     this.props.history.push('/public/local');
-  }
+  };
 
   handleHotkeyGoToFederated = () => {
     this.props.history.push('/public');
-  }
+  };
 
   handleHotkeyGoToDirect = () => {
     this.props.history.push('/conversations');
-  }
+  };
 
   handleHotkeyGoToStart = () => {
     this.props.history.push('/getting-started');
-  }
+  };
 
   handleHotkeyGoToFavourites = () => {
     this.props.history.push('/favourites');
-  }
+  };
 
   handleHotkeyGoToPinned = () => {
     this.props.history.push('/pinned');
-  }
+  };
 
   handleHotkeyGoToProfile = () => {
     this.props.history.push(`/@${this.props.username}`);
-  }
+  };
 
   handleHotkeyGoToBlocked = () => {
     this.props.history.push('/blocks');
-  }
+  };
 
   handleHotkeyGoToMuted = () => {
     this.props.history.push('/mutes');
-  }
+  };
 
   handleHotkeyGoToRequests = () => {
     this.props.history.push('/follow_requests');
-  }
+  };
 
   render () {
     const { draggingOver } = this.state;
@@ -659,7 +659,7 @@ class UI extends React.Component {
                 <PermaLink href={moved.get('url')} to={`/@${moved.get('acct')}`}>
                   @{moved.get('acct')}
                 </PermaLink>
-              )}}
+              ) }}
             />
           </div>)}
 
@@ -680,3 +680,5 @@ class UI extends React.Component {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(withRouter(UI)));
diff --git a/app/javascript/flavours/glitch/features/ui/util/async-components.js b/app/javascript/flavours/glitch/features/ui/util/async-components.js
index 025b22e61..03e501628 100644
--- a/app/javascript/flavours/glitch/features/ui/util/async-components.js
+++ b/app/javascript/flavours/glitch/features/ui/util/async-components.js
@@ -98,6 +98,10 @@ export function FavouritedStatuses () {
   return import(/* webpackChunkName: "flavours/glitch/async/favourited_statuses" */'flavours/glitch/features/favourited_statuses');
 }
 
+export function FollowedTags () {
+  return import(/* webpackChunkName: "flavours/glitch/async/followed_tags" */'flavours/glitch/features/followed_tags');
+}
+
 export function BookmarkedStatuses () {
   return import(/* webpackChunkName: "flavours/glitch/async/bookmarked_statuses" */'flavours/glitch/features/bookmarked_statuses');
 }
diff --git a/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.js b/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.jsx
index 8946c8252..b1c952d87 100644
--- a/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.js
+++ b/app/javascript/flavours/glitch/features/ui/util/react_router_helpers.jsx
@@ -36,7 +36,7 @@ export class WrappedRoute extends React.Component {
     content: PropTypes.node,
     multiColumn: PropTypes.bool,
     componentParams: PropTypes.object,
-  }
+  };
 
   static defaultProps = {
     componentParams: {},
@@ -46,7 +46,7 @@ export class WrappedRoute extends React.Component {
     return {
       hasError: true,
     };
-  };
+  }
 
   state = {
     hasError: false,
@@ -80,17 +80,17 @@ export class WrappedRoute extends React.Component {
         {Component => <Component params={match.params} multiColumn={multiColumn} {...componentParams}>{content}</Component>}
       </BundleContainer>
     );
-  }
+  };
 
   renderLoading = () => {
     const { multiColumn } = this.props;
 
     return <ColumnLoading multiColumn={multiColumn} />;
-  }
+  };
 
   renderError = (props) => {
     return <BundleColumnError {...props} errorType='network' />;
-  }
+  };
 
   render () {
     const { component: Component, content, ...rest } = this.props;
diff --git a/app/javascript/flavours/glitch/features/ui/util/reduced_motion.js b/app/javascript/flavours/glitch/features/ui/util/reduced_motion.jsx
index 95519042b..1123b80ed 100644
--- a/app/javascript/flavours/glitch/features/ui/util/reduced_motion.js
+++ b/app/javascript/flavours/glitch/features/ui/util/reduced_motion.jsx
@@ -17,7 +17,7 @@ class ReducedMotion extends React.Component {
     defaultStyle: PropTypes.object,
     style: PropTypes.object,
     children: PropTypes.func,
-  }
+  };
 
   render() {
 
diff --git a/app/javascript/flavours/glitch/features/video/index.js b/app/javascript/flavours/glitch/features/video/index.jsx
index 0daab747b..28a8bb1fd 100644
--- a/app/javascript/flavours/glitch/features/video/index.js
+++ b/app/javascript/flavours/glitch/features/video/index.jsx
@@ -93,7 +93,6 @@ export const fileNameFromURL = str => {
   return pathname.slice(index + 1);
 };
 
-export default @injectIntl
 class Video extends React.PureComponent {
 
   static propTypes = {
@@ -101,6 +100,7 @@ class Video extends React.PureComponent {
     frameRate: PropTypes.string,
     src: PropTypes.string.isRequired,
     alt: PropTypes.string,
+    lang: PropTypes.string,
     width: PropTypes.number,
     height: PropTypes.number,
     sensitive: PropTypes.bool,
@@ -156,7 +156,7 @@ class Video extends React.PureComponent {
     if (this.player) {
       this._setDimensions();
     }
-  }
+  };
 
   _setDimensions () {
     const width = this.player.offsetWidth;
@@ -178,26 +178,26 @@ class Video extends React.PureComponent {
     if (this.video) {
       this.setState({ volume: this.video.volume, muted: this.video.muted });
     }
-  }
+  };
 
   setSeekRef = c => {
     this.seek = c;
-  }
+  };
 
   setVolumeRef = c => {
     this.volume = c;
-  }
+  };
 
   handleClickRoot = e => e.stopPropagation();
 
   handlePlay = () => {
     this.setState({ paused: false });
     this._updateTime();
-  }
+  };
 
   handlePause = () => {
     this.setState({ paused: true });
-  }
+  };
 
   _updateTime () {
     requestAnimationFrame(() => {
@@ -216,7 +216,7 @@ class Video extends React.PureComponent {
       currentTime: this.video.currentTime,
       duration:this.video.duration,
     });
-  }
+  };
 
   handleVolumeMouseDown = e => {
     document.addEventListener('mousemove', this.handleMouseVolSlide, true);
@@ -228,14 +228,14 @@ class Video extends React.PureComponent {
 
     e.preventDefault();
     e.stopPropagation();
-  }
+  };
 
   handleVolumeMouseUp = () => {
     document.removeEventListener('mousemove', this.handleMouseVolSlide, true);
     document.removeEventListener('mouseup', this.handleVolumeMouseUp, true);
     document.removeEventListener('touchmove', this.handleMouseVolSlide, true);
     document.removeEventListener('touchend', this.handleVolumeMouseUp, true);
-  }
+  };
 
   handleMouseVolSlide = throttle(e => {
     const { x } = getPointerPosition(this.volume, e);
@@ -259,7 +259,7 @@ class Video extends React.PureComponent {
 
     e.preventDefault();
     e.stopPropagation();
-  }
+  };
 
   handleMouseUp = () => {
     document.removeEventListener('mousemove', this.handleMouseMove, true);
@@ -269,7 +269,7 @@ class Video extends React.PureComponent {
 
     this.setState({ dragging: false });
     this.video.play();
-  }
+  };
 
   handleMouseMove = throttle(e => {
     const { x } = getPointerPosition(this.seek, e);
@@ -301,7 +301,7 @@ class Video extends React.PureComponent {
       e.stopPropagation();
       this.togglePlay();
     }
-  }
+  };
 
   handleKeyDown = e => {
     const frameTime = 1 / this.getFrameRate();
@@ -355,7 +355,7 @@ class Video extends React.PureComponent {
         exitFullscreen();
       }
     }
-  }
+  };
 
   togglePlay = () => {
     if (this.state.paused) {
@@ -363,7 +363,7 @@ class Video extends React.PureComponent {
     } else {
       this.setState({ paused: true }, () => this.video.pause());
     }
-  }
+  };
 
   toggleFullscreen = () => {
     if (isFullscreen()) {
@@ -371,7 +371,7 @@ class Video extends React.PureComponent {
     } else {
       requestFullscreen(this.player);
     }
-  }
+  };
 
   componentDidMount () {
     document.addEventListener('fullscreenchange', this.handleFullscreenChange, true);
@@ -444,19 +444,19 @@ class Video extends React.PureComponent {
 
       this.setState({ paused: true });
     }
-  }, 150, { trailing: true })
+  }, 150, { trailing: true });
 
   handleFullscreenChange = () => {
     this.setState({ fullscreen: isFullscreen() });
-  }
+  };
 
   handleMouseEnter = () => {
     this.setState({ hovered: true });
-  }
+  };
 
   handleMouseLeave = () => {
     this.setState({ hovered: false });
-  }
+  };
 
   toggleMute = () => {
     const muted = !this.video.muted;
@@ -464,7 +464,7 @@ class Video extends React.PureComponent {
     this.setState({ muted }, () => {
       this.video.muted = muted;
     });
-  }
+  };
 
   toggleReveal = () => {
     if (this.state.revealed) {
@@ -476,7 +476,7 @@ class Video extends React.PureComponent {
     } else {
       this.setState({ revealed: !this.state.revealed });
     }
-  }
+  };
 
   handleLoadedData = () => {
     const { currentTime, volume, muted, autoPlay } = this.props;
@@ -496,7 +496,7 @@ class Video extends React.PureComponent {
     if (autoPlay) {
       this.video.play();
     }
-  }
+  };
 
   handleProgress = () => {
     const lastTimeRange = this.video.buffered.length - 1;
@@ -504,11 +504,11 @@ class Video extends React.PureComponent {
     if (lastTimeRange > -1) {
       this.setState({ buffer: Math.ceil(this.video.buffered.end(lastTimeRange) / this.video.duration * 100) });
     }
-  }
+  };
 
   handleVolumeChange = () => {
     this.setState({ volume: this.video.volume, muted: this.video.muted });
-  }
+  };
 
   handleOpenVideo = () => {
     this.video.pause();
@@ -519,12 +519,12 @@ class Video extends React.PureComponent {
       defaultVolume: this.state.volume,
       componentIndex: this.props.componentIndex,
     });
-  }
+  };
 
   handleCloseVideo = () => {
     this.video.pause();
     this.props.onCloseVideo();
-  }
+  };
 
   getFrameRate () {
     if (this.props.frameRate && isNaN(this.props.frameRate)) {
@@ -538,7 +538,7 @@ class Video extends React.PureComponent {
   }
 
   render () {
-    const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, letterbox, fullwidth, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
+    const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, lang, letterbox, fullwidth, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
     const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
     const progress = Math.min((currentTime / duration) * 100, 100);
     const playerStyle = {};
@@ -553,7 +553,7 @@ class Video extends React.PureComponent {
 
       playerStyle.height = height;
     } else if (inline) {
-      return (<div className={computedClass} ref={this.setPlayerRef} tabindex={0}></div>);
+      return (<div className={computedClass} ref={this.setPlayerRef} tabindex={0} />);
     }
 
     let preload;
@@ -600,9 +600,10 @@ class Video extends React.PureComponent {
           preload={preload}
           loop
           role='button'
-          tabIndex='0'
+          tabIndex={0}
           aria-label={alt}
           title={alt}
+          lang={lang}
           width={width}
           height={height}
           volume={volume}
@@ -628,7 +629,7 @@ class Video extends React.PureComponent {
 
             <span
               className={classNames('video-player__seek__handle', { active: dragging })}
-              tabIndex='0'
+              tabIndex={0}
               style={{ left: `${progress}%` }}
               onKeyDown={this.handleVideoKeyDown}
             />
@@ -644,7 +645,7 @@ class Video extends React.PureComponent {
 
                 <span
                   className={classNames('video-player__volume__handle')}
-                  tabIndex='0'
+                  tabIndex={0}
                   style={{ left: `${volume * 100}%` }}
                 />
               </div>
@@ -671,3 +672,5 @@ class Video extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(Video);
diff --git a/app/javascript/flavours/glitch/initial_state.js b/app/javascript/flavours/glitch/initial_state.js
index bbf25c8a8..8b135006d 100644
--- a/app/javascript/flavours/glitch/initial_state.js
+++ b/app/javascript/flavours/glitch/initial_state.js
@@ -55,7 +55,7 @@
  * @property {boolean=} delete_modal
  * @property {boolean=} disable_swiping
  * @property {string=} disabled_account_id
- * @property {boolean} display_media
+ * @property {string} display_media
  * @property {string} domain
  * @property {boolean=} expand_spoilers
  * @property {boolean} limited_federation_mode
@@ -75,6 +75,7 @@
  * @property {boolean} timeline_preview
  * @property {string} title
  * @property {boolean} trends
+ * @property {boolean} trends_as_landing_page
  * @property {boolean} unfollow_modal
  * @property {boolean} use_blurhash
  * @property {boolean=} use_pending_items
@@ -134,12 +135,13 @@ export const singleUserMode = getMeta('single_user_mode');
 export const source_url = getMeta('source_url');
 export const timelinePreview = getMeta('timeline_preview');
 export const title = getMeta('title');
+export const trendsAsLanding = getMeta('trends_as_landing_page');
 export const unfollowModal = getMeta('unfollow_modal');
 export const useBlurhash = getMeta('use_blurhash');
 export const usePendingItems = getMeta('use_pending_items');
 export const version = getMeta('version');
-export const translationEnabled = getMeta('translation_enabled');
 export const languages = initialState?.languages;
+export const statusPageUrl = getMeta('status_page_url');
 
 // Glitch-soc-specific settings
 export const maxChars = (initialState && initialState.max_toot_chars) || 500;
diff --git a/app/javascript/flavours/glitch/load_polyfills.js b/app/javascript/flavours/glitch/load_polyfills.js
index f5a897f75..7909dc4ea 100644
--- a/app/javascript/flavours/glitch/load_polyfills.js
+++ b/app/javascript/flavours/glitch/load_polyfills.js
@@ -12,10 +12,8 @@ function importExtraPolyfills() {
 
 function loadPolyfills() {
   const needsBasePolyfills = !(
-    Array.prototype.includes &&
     HTMLCanvasElement.prototype.toBlob &&
     window.Intl &&
-    Number.isNaN &&
     Object.assign &&
     Object.values &&
     window.Symbol &&
diff --git a/app/javascript/flavours/glitch/locales/af.json b/app/javascript/flavours/glitch/locales/af.json
index 0967ef424..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/af.json
+++ b/app/javascript/flavours/glitch/locales/af.json
@@ -1 +1,206 @@
-{}
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/an.json b/app/javascript/flavours/glitch/locales/an.json
index 0967ef424..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/an.json
+++ b/app/javascript/flavours/glitch/locales/an.json
@@ -1 +1,206 @@
-{}
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/ar.json b/app/javascript/flavours/glitch/locales/ar.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ar.json
+++ b/app/javascript/flavours/glitch/locales/ar.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ast.json b/app/javascript/flavours/glitch/locales/ast.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ast.json
+++ b/app/javascript/flavours/glitch/locales/ast.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/be.json b/app/javascript/flavours/glitch/locales/be.json
index 0967ef424..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/be.json
+++ b/app/javascript/flavours/glitch/locales/be.json
@@ -1 +1,206 @@
-{}
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/bg.json b/app/javascript/flavours/glitch/locales/bg.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/bg.json
+++ b/app/javascript/flavours/glitch/locales/bg.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/bn.json b/app/javascript/flavours/glitch/locales/bn.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/bn.json
+++ b/app/javascript/flavours/glitch/locales/bn.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/br.json b/app/javascript/flavours/glitch/locales/br.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/br.json
+++ b/app/javascript/flavours/glitch/locales/br.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/bs.json b/app/javascript/flavours/glitch/locales/bs.json
index 0967ef424..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/bs.json
+++ b/app/javascript/flavours/glitch/locales/bs.json
@@ -1 +1,206 @@
-{}
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/ca.json b/app/javascript/flavours/glitch/locales/ca.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ca.json
+++ b/app/javascript/flavours/glitch/locales/ca.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ckb.json b/app/javascript/flavours/glitch/locales/ckb.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ckb.json
+++ b/app/javascript/flavours/glitch/locales/ckb.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/co.json b/app/javascript/flavours/glitch/locales/co.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/co.json
+++ b/app/javascript/flavours/glitch/locales/co.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/cs.json b/app/javascript/flavours/glitch/locales/cs.json
index 09ad53140..91fec35d6 100644
--- a/app/javascript/flavours/glitch/locales/cs.json
+++ b/app/javascript/flavours/glitch/locales/cs.json
@@ -1,5 +1,15 @@
 {
   "about.fork_disclaimer": "Glitch-soc je svobodný software s otevřeným zdrojovým kódem založený na Mastodonu.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
   "advanced_options.icon_title": "Pokročilá nastavení",
   "advanced_options.local-only.long": "Neposílat na jiné servery",
   "advanced_options.local-only.short": "Lokální příspěvek",
@@ -8,15 +18,33 @@
   "advanced_options.threaded_mode.short": "Režim vlákna",
   "advanced_options.threaded_mode.tooltip": "Režim vlákna je zapnutý",
   "boost_modal.missing_description": "Příspěvek obsahuje obrázky bez popisků",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
   "column.subheading": "Různé",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
   "compose.attach": "Připojit...",
   "compose.attach.doodle": "Něco namalovat",
   "compose.attach.upload": "Nahrát soubor",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
   "compose.content-type.plain": "Prostý text",
   "compose_form.poll.multiple_choices": "Povolit více odpovědí",
   "compose_form.poll.single_choice": "Povolit jednu odpověď",
   "compose_form.spoiler": "Přidat varování o obsahu",
   "confirmation_modal.do_not_ask_again": "Příště se už neptat",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
   "content-type.change": "Formát příspěvku",
   "direct.group_by_conversations": "Seskupit do konverzací",
   "endorsed_accounts_editor.endorsed_accounts": "Vybrané účty",
@@ -25,17 +53,20 @@
   "home.column_settings.advanced": "Pokročilé",
   "home.column_settings.filter_regex": "Filtrovat podle regulárních výrazů",
   "home.column_settings.show_direct": "Zobrazit přímé zprávy",
+  "home.settings": "Column settings",
   "keyboard_shortcuts.bookmark": "Přidat do záložek",
   "keyboard_shortcuts.secondary_toot": "Odeslat příspěvek s druhotným nastavením soukromí",
   "keyboard_shortcuts.toggle_collapse": "Sbalit/rozbalit příspěvek",
   "layout.auto": "Automatické",
+  "layout.desktop": "Desktop",
   "layout.hint.auto": "Vybrat rozložení automaticky v závislosti na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.",
   "layout.hint.desktop": "Použít vícesloupcové rozložení nezávisle na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.",
   "layout.hint.single": "Použít jednosloupcové rozložení nezávisle na nastavení “Povolit pokročilé webové rozhraní” a velikosti obrazovky.",
+  "layout.single": "Mobile",
   "media_gallery.sensitive": "Citlivý obsah",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
   "navigation_bar.app_settings": "Nastavení aplikace",
   "navigation_bar.featured_users": "Vybraní uživatelé",
-  "navigation_bar.info": "Rozšířené informace",
   "navigation_bar.keyboard_shortcuts": "Klávesové zkratky",
   "navigation_bar.misc": "Různé",
   "notification.markForDeletion": "Označit pro smazání",
@@ -54,18 +85,28 @@
   "onboarding.page_one.federation": "{domain} je 'instance' Mastodonu. Mastodon je síť nezávislých serverů, které jsou spolu propojené do jedné velké sociální sítě. Těmto serverům říkáme instance.",
   "onboarding.page_one.handle": "Jste na instanci {domain}, takže celá adresa vašeho profilu je {handle}",
   "onboarding.page_one.welcome": "Vítá vás {domain}!",
+  "onboarding.page_six.admin": "Your instance's admin is {admin}.",
   "onboarding.page_six.almost_done": "Skoro hotovo...",
   "onboarding.page_six.appetoot": "Veselé mastodonění!",
   "onboarding.page_six.apps_available": "Jsou dostupné {apps} pro iOS, Android i jiné platformy.",
   "onboarding.page_six.github": "Na serveru {domain} běží Glitchsoc. Glitchsoc je přátelský {fork} programu {Mastodon}, a je kompatibilní s jakoukoliv jinou mastodoní instancí nebo aplikací. Glitchsoc je zcela svobodný a má otevřený zdrojový kód. Na stránce {github} můžete hlásit chyby, žádat o nové funkce, nebo ke kódu vlastnoručně přispět.",
+  "onboarding.page_six.guidelines": "community guidelines",
+  "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!",
   "onboarding.page_six.various_app": "mobilní aplikace",
   "onboarding.page_three.profile": "Upravte si svůj profil a nastavte si profilový obrázek, jméno, a krátký text o sobě. Naleznete tam i další možnosti nastavení.",
   "onboarding.page_three.search": "Pomocí vyhledávací lišty můžete hledat lidi nebo hashtagy. Pokud hledáte někoho z jiné instance, musíte použít celou adresu jeho profilu.",
   "onboarding.page_two.compose": "Příspěvky se píší v levém sloupci. Pomocí ikon pod příspěvkem k němu můžete připojit obrázky, změnit úroveň soukromí nebo přidat varování o obsahu.",
   "onboarding.skip": "Přeskočit",
+  "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",
   "settings.always_show_spoilers_field": "Vždy zobrazit pole pro varování o obsahu",
   "settings.auto_collapse": "Automaticky sbalit",
   "settings.auto_collapse_all": "Všechno",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
   "settings.auto_collapse_lengthy": "Dlouhé příspěvky",
   "settings.auto_collapse_media": "Příspěvky s přílohami",
   "settings.auto_collapse_notifications": "Oznámení",
@@ -137,6 +178,7 @@
   "settings.status_icons_media": "Indikace obrázků a anket",
   "settings.status_icons_reply": "Indikace odpovědi",
   "settings.status_icons_visibility": "Indikace úrovně soukromí",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
   "settings.tag_misleading_links": "Označit zavádějící odkazy",
   "settings.tag_misleading_links.hint": "Zobrazit skutečný cíl u každého odkazu, který ho explicitně nezmiňuje",
   "settings.wide_view": "Široké sloupce (pouze v režimu Desktop)",
@@ -149,5 +191,16 @@
   "status.in_reply_to": "Tento příspěvek je odpověď",
   "status.is_poll": "Tento příspěvek je anketa",
   "status.local_only": "Viditelné pouze z vaší instance",
-  "status.uncollapse": "Rozbalit"
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Rozbalit",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/cy.json b/app/javascript/flavours/glitch/locales/cy.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/cy.json
+++ b/app/javascript/flavours/glitch/locales/cy.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/da.json b/app/javascript/flavours/glitch/locales/da.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/da.json
+++ b/app/javascript/flavours/glitch/locales/da.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/de.json b/app/javascript/flavours/glitch/locales/de.json
index c5e3cdb35..5851daaf7 100644
--- a/app/javascript/flavours/glitch/locales/de.json
+++ b/app/javascript/flavours/glitch/locales/de.json
@@ -67,7 +67,6 @@
   "moved_to_warning": "Dieses Konto ist als verschoben zu {moved_to_link} markiert und akzeptiert daher keine neuen Follower.",
   "navigation_bar.app_settings": "App-Einstellungen",
   "navigation_bar.featured_users": "Empfohlene Nutzer",
-  "navigation_bar.info": "Erweiterte Informationen",
   "navigation_bar.keyboard_shortcuts": "Tastaturkürzel",
   "navigation_bar.misc": "Sonstiges",
   "notification.markForDeletion": "Zum Entfernen auswählen",
@@ -98,9 +97,16 @@
   "onboarding.page_three.search": "Benutze die Suchleiste, um Leute zu finden und Hashtags anzusehen, wie etwa {illustration} und {introductions}. Um nach einer Person zu suchen, die nicht auf dieser Instanz ist, benutze deren vollständigen Nutzername.",
   "onboarding.page_two.compose": "Schreibe Posts in der Verfassen-Spalte. Mit den Symbolen unten kannst du Bilder hochladen, Privatsphäre-Einstellungen ändern, und Inhaltswarnungen hinzufügen.",
   "onboarding.skip": "Überspringen",
+  "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",
   "settings.always_show_spoilers_field": "Das Inhaltswarnungs-Feld immer aktivieren",
   "settings.auto_collapse": "Automatisches Einklappen",
   "settings.auto_collapse_all": "Alles",
+  "settings.auto_collapse_height": "Höhe (in Pixeln), ab der ein Toot als lang gilt",
   "settings.auto_collapse_lengthy": "Lange Toots",
   "settings.auto_collapse_media": "Toots mit Anhängen",
   "settings.auto_collapse_notifications": "Benachrichtigungen",
diff --git a/app/javascript/flavours/glitch/locales/defaultMessages.json b/app/javascript/flavours/glitch/locales/defaultMessages.json
index d7aec67ac..fe943f97f 100644
--- a/app/javascript/flavours/glitch/locales/defaultMessages.json
+++ b/app/javascript/flavours/glitch/locales/defaultMessages.json
@@ -338,6 +338,35 @@
   {
     "descriptors": [
       {
+        "defaultMessage": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
+        "id": "search_popout.tips.full_text"
+      },
+      {
+        "defaultMessage": "Simple text returns matching display names, usernames and hashtags",
+        "id": "search_popout.tips.text"
+      },
+      {
+        "defaultMessage": "Advanced search format",
+        "id": "search_popout.search_format"
+      },
+      {
+        "defaultMessage": "hashtag",
+        "id": "search_popout.tips.hashtag"
+      },
+      {
+        "defaultMessage": "user",
+        "id": "search_popout.tips.user"
+      },
+      {
+        "defaultMessage": "status",
+        "id": "search_popout.tips.status"
+      }
+    ],
+    "path": "app/javascript/flavours/glitch/features/compose/components/search.json"
+  },
+  {
+    "descriptors": [
+      {
         "defaultMessage": "This post is local-only",
         "id": "advanced_options.local-only.tooltip"
       },
@@ -406,10 +435,6 @@
         "id": "column.subheading"
       },
       {
-        "defaultMessage": "Extended information",
-        "id": "navigation_bar.info"
-      },
-      {
         "defaultMessage": "Show me around",
         "id": "getting_started.onboarding"
       },
@@ -803,6 +828,10 @@
         "id": "settings.auto_collapse_media"
       },
       {
+        "defaultMessage": "Height (in pixels) for a toot to be considered lengthy",
+        "id": "settings.auto_collapse_height"
+      },
+      {
         "defaultMessage": "Image backgrounds",
         "id": "settings.image_backgrounds"
       },
diff --git a/app/javascript/flavours/glitch/locales/el.json b/app/javascript/flavours/glitch/locales/el.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/el.json
+++ b/app/javascript/flavours/glitch/locales/el.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/en-GB.json b/app/javascript/flavours/glitch/locales/en-GB.json
index 0967ef424..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/en-GB.json
+++ b/app/javascript/flavours/glitch/locales/en-GB.json
@@ -1 +1,206 @@
-{}
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/en.json b/app/javascript/flavours/glitch/locales/en.json
index 59f2f74b1..81d88ca65 100644
--- a/app/javascript/flavours/glitch/locales/en.json
+++ b/app/javascript/flavours/glitch/locales/en.json
@@ -67,7 +67,6 @@
   "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
   "navigation_bar.app_settings": "App settings",
   "navigation_bar.featured_users": "Featured users",
-  "navigation_bar.info": "Extended information",
   "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
   "navigation_bar.misc": "Misc",
   "notification.markForDeletion": "Mark for deletion",
@@ -98,9 +97,16 @@
   "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",
+  "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",
   "settings.always_show_spoilers_field": "Always enable the Content Warning field",
   "settings.auto_collapse": "Automatic collapsing",
   "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
   "settings.auto_collapse_lengthy": "Lengthy toots",
   "settings.auto_collapse_media": "Toots with media",
   "settings.auto_collapse_notifications": "Notifications",
diff --git a/app/javascript/flavours/glitch/locales/eo.json b/app/javascript/flavours/glitch/locales/eo.json
index 87fe6c657..88396f186 100644
--- a/app/javascript/flavours/glitch/locales/eo.json
+++ b/app/javascript/flavours/glitch/locales/eo.json
@@ -1,52 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
   "account.add_account_note": "Aldoni noton por @{name}",
+  "account.disclaimer_full": "Subaj informoj povas nekomplete prezenti la profilon de la uzanto.",
+  "account.follows": "Sekvatoj",
+  "account.joined": "Kuniĝis {date}",
+  "account.suspended_disclaimer_full": "Ĉi tiu uzanto estis suspendita de moderiganto.",
+  "account.view_full_profile": "Vidi plenan profilon",
   "account_note.cancel": "Nuligi",
   "account_note.edit": "Redakti",
+  "account_note.glitch_placeholder": "No comment provided",
   "account_note.save": "Konservi",
+  "advanced_options.icon_title": "Pliaj opcioj",
+  "advanced_options.local-only.long": "Ne afiŝi al aliaj instancoj",
+  "advanced_options.local-only.short": "Nur loka",
+  "advanced_options.local-only.tooltip": "Ĉi tiu afiŝo estas nur-loka",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Stelumita per",
+  "column.heading": "Misc",
   "column.reblogged_by": "Diskonigita de",
   "column.subheading": "Diversaj agordoj",
   "column_header.profile": "Profilo",
   "column_subheading.lists": "Listoj",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Montri nur-lokajn afiŝojn",
   "compose.attach": "Aldoni…",
   "compose.attach.doodle": "Desegni ion",
   "compose.attach.upload": "Alŝuti dosieron",
   "compose.content-type.html": "HTML",
   "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plata teksto",
+  "compose_form.poll.multiple_choices": "Permesi multajn elekteblojn",
+  "compose_form.poll.single_choice": "Permesi unu elekteblon",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
   "confirmations.unfilter.author": "Aŭtoro",
   "confirmations.unfilter.confirm": "Montri",
   "confirmations.unfilter.edit_filter": "Redakti filtrilon",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
   "navigation_bar.keyboard_shortcuts": "Fulmoklavoj",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
   "notification_purge.btn_all": "Selekti ĉiujn",
   "notification_purge.btn_apply": "Forigi selektajn",
   "notification_purge.btn_invert": "Inverti selekton",
   "notification_purge.btn_none": "Elekti neniun",
+  "notification_purge.start": "Enter notification cleaning mode",
   "notifications.marked_clear": "Forigi selektajn sciigojn",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected notifications?",
+  "onboarding.done": "Done",
   "onboarding.next": "Sekva",
-  "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "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": "{domain} estas \"instanco\" de Mastodon. Mastodon estas reto de sendependaj serviloj, ke kuniĝas por fari unu pli grandan socian reton. Ni nomas tiujn servilojn \"instancoj\".",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "onboarding.page_six.admin": "La administranto de via instanco estas {admin}.",
   "onboarding.page_six.almost_done": "Preskaŭ finita…",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
   "onboarding.page_six.apps_available": "Estas {apps} disponeblaj por iOS, Android kaj aliaj sistemoj.",
   "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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": "poŝtelefonaj aplikaĵoj",
+  "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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
   "settings.auto_collapse_all": "Ĉiuj",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
   "settings.auto_collapse_lengthy": "Longaj afiŝoj",
   "settings.auto_collapse_media": "Afiŝoj kun aŭdovidaĵoj",
   "settings.auto_collapse_notifications": "Sciigoj",
   "settings.auto_collapse_reblogs": "Diskonigoj",
   "settings.auto_collapse_replies": "Respondoj",
   "settings.close": "Fermi",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
   "settings.content_warnings.regexp": "Regula esprimo",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
   "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
   "settings.shared_settings_link": "preferoj de uzanto",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
   "settings.side_arm": "Duaranga butono por afiŝi:",
   "settings.side_arm.none": "Neniu",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
   "settings.status_icons": "Ikonoj sur la afiŝoj",
   "settings.status_icons_language": "Indikilo de lingvo",
+  "settings.status_icons_local_only": "Local-only indicator",
   "settings.status_icons_media": "Indikilo de aŭdovidaĵojn kaj balotenketo",
   "settings.status_icons_reply": "Indikilo de respondoj",
   "settings.status_icons_visibility": "Indikilo de privateco de afiŝo",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Videbla nur el via instanco",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
   "web_app_crash.change_your_settings": "Ŝanĝi viajn {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
   "web_app_crash.reload": "Reŝarĝi",
   "web_app_crash.reload_page": "{reload} la nunan paĝon",
-  "web_app_crash.settings": "agordojn"
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "agordojn",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/es-AR.json b/app/javascript/flavours/glitch/locales/es-AR.json
index fc5a2e904..a802363f5 100644
--- a/app/javascript/flavours/glitch/locales/es-AR.json
+++ b/app/javascript/flavours/glitch/locales/es-AR.json
@@ -1,4 +1,15 @@
 {
+  "about.fork_disclaimer": "Glitch-soc es software gratuito, de código abierto, bifurcado de Mastodon.",
+  "account.add_account_note": "Añadir nota para @{name}",
+  "account.disclaimer_full": "La información aquí presentada puede reflejar de manera incompleta el perfil del usuario.",
+  "account.follows": "Sigue",
+  "account.joined": "Unido el {date}",
+  "account.suspended_disclaimer_full": "Este usuario ha sido suspendido por un moderador.",
+  "account.view_full_profile": "Ver perfil completo",
+  "account_note.cancel": "Cancelar",
+  "account_note.edit": "Editar",
+  "account_note.glitch_placeholder": "No se proporcionó comentario alguno",
+  "account_note.save": "Guardar",
   "advanced_options.icon_title": "Opciones avanzadas",
   "advanced_options.local-only.long": "No publicar a otras instancias",
   "advanced_options.local-only.short": "Local",
@@ -6,36 +17,96 @@
   "advanced_options.threaded_mode.long": "Al publicar abre automáticamente una respuesta",
   "advanced_options.threaded_mode.short": "Modo hilo",
   "advanced_options.threaded_mode.tooltip": "Modo hilo habilitado",
+  "boost_modal.missing_description": "Esta publicación contiene medios sin descripción",
+  "column.favourited_by": "Marcado como favorito por",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Impulsado por",
+  "column.subheading": "Opciones misceláneas",
+  "column_header.profile": "Perfil",
+  "column_subheading.lists": "Listas",
+  "column_subheading.navigation": "Navegación",
+  "community.column_settings.allow_local_only": "Mostrar sólo toots locales",
   "compose.attach": "Adjuntar...",
   "compose.attach.doodle": "Dibujar algo",
   "compose.attach.upload": "Subir un archivo",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Texto plano",
+  "compose_form.poll.multiple_choices": "Permitir múltiples opciones",
+  "compose_form.poll.single_choice": "Permitir sólo una opción",
+  "compose_form.spoiler": "Esconder el texto detrás de la advertencia",
+  "confirmation_modal.do_not_ask_again": "No preguntar por la confirmación de nuevo",
+  "confirmations.deprecated_settings.confirm": "Usar las preferencias de Mastodon",
+  "confirmations.deprecated_settings.message": "Algunas de las {app_settings} de glitch-soc, específicas para el dispositivo que estás usando han sido reemplazadas en las {preferences} de Mastodon y serán sobreescritas:",
+  "confirmations.missing_media_description.confirm": "Enviar de todos modos",
+  "confirmations.missing_media_description.edit": "Editar medios",
+  "confirmations.missing_media_description.message": "Al menos a un adjunto le falta una descripción. Considera describir toda la multimedia para los débiles visuales antes de mandar el toot.",
   "confirmations.unfilter.author": "Publicado por",
   "confirmations.unfilter.confirm": "Mostrar",
   "confirmations.unfilter.edit_filter": "Editar filtro",
   "confirmations.unfilter.filters": "Coincidencia con {count, plural, one {filtro} other {filtros}}",
+  "content-type.change": "Tipo de contenido",
+  "direct.group_by_conversations": "Agrupar por conversación",
+  "endorsed_accounts_editor.endorsed_accounts": "Cuentas destacadas",
   "favourite_modal.combo": "Puedes presionar {combo} para omitir esto la próxima vez",
   "getting_started.onboarding": "Paseo inicial",
+  "home.column_settings.advanced": "Avanzado",
+  "home.column_settings.filter_regex": "Filtrar por expresiones regulares",
   "home.column_settings.show_direct": "Mostrar mensajes directos",
+  "home.settings": "Configuraciones de columna",
+  "keyboard_shortcuts.bookmark": "a marcadores",
+  "keyboard_shortcuts.secondary_toot": "para enviar un toot usando lac onfiguración de privacidad secundaria",
+  "keyboard_shortcuts.toggle_collapse": "para colapsar/descolapsar toots",
   "layout.auto": "Automático",
   "layout.desktop": "Escritorio",
   "layout.hint.auto": "Seleccionar un diseño automáticamente basado en \"Habilitar interface web avanzada\" y tamaño de pantalla",
   "layout.hint.desktop": "Utiliza el diseño multi-columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
   "layout.hint.single": "Utiliza el diseño de una columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
+  "layout.single": "Móvil",
   "media_gallery.sensitive": "Sensible",
+  "moved_to_warning": "Esta cuenta está marcada como movida a {moved_to_link}, y por lo tanto no aceptará nuevos seguimientos.",
   "navigation_bar.app_settings": "Ajustes de aplicación",
+  "navigation_bar.featured_users": "Usuarios destacados",
+  "navigation_bar.keyboard_shortcuts": "Atajos de teclado",
+  "navigation_bar.misc": "Misc",
   "notification.markForDeletion": "Marcar para borrar",
   "notification_purge.btn_all": "Seleccionar\ntodo",
   "notification_purge.btn_apply": "Borrar\nselección",
   "notification_purge.btn_invert": "Invertir\nselección",
   "notification_purge.btn_none": "Seleccionar\nnada",
+  "notification_purge.start": "Entrar en modo de limpieza de notificaciones",
   "notifications.marked_clear": "Limpiar notificaciones seleccionadas",
   "notifications.marked_clear_confirmation": "¿Deseas borrar permanentemente todas las notificaciones seleccionadas?",
+  "onboarding.done": "Hecho",
+  "onboarding.next": "Siguiente",
+  "onboarding.page_five.public_timelines": "La línea de tiempo local muestra mensajes públicos de todos en {domain}. La línea de tiempo federada muestra mensajes públicos de todos aquellos que en {domain} siguen a otros servidores. Estas son las líneas cronológicas públicas, una gran manera de descubrir gente nueva.",
+  "onboarding.page_four.home": "La línea de tiempo principal muestra los mensajes de la gente que sigues.",
+  "onboarding.page_four.notifications": "La columna de notificaciones muestra cuando alguien interactúa contigo.",
   "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "Estás en {domain}, así que tu alias completo es {handle}",
   "onboarding.page_one.welcome": "¡Bienvenidx a {domain}!",
+  "onboarding.page_six.admin": "El administrador de tu instancia es {admin}.",
+  "onboarding.page_six.almost_done": "Casi listo...",
+  "onboarding.page_six.appetoot": "¡A tootear!",
+  "onboarding.page_six.apps_available": "Hay {apps} disponibles para iOS, Android y otras plataformas.",
   "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "normas de la comunidad",
+  "onboarding.page_six.read_guidelines": "¡Por favor lee las {guidelines} de {domain}!",
+  "onboarding.page_six.various_app": "aplicaciones para móviles",
+  "onboarding.page_three.profile": "Edita tu perfil para cambiar tu avatar, biografía y nombre para mostrar. Ahí, también encontrarás otras preferencias.",
+  "onboarding.page_three.search": "Usa la barra de búsqueda para encontrar gente y mirar las etiquetas (hashtags), como {illustration} y {introductions}. Para buscar a una persona que no esté en esta instancia, utiliza su alias completo.",
+  "onboarding.page_two.compose": "Escribe mensajes desde la columna de composición. Puedes subir imágenes, cambiar la configuración de privacidad y añadir advertencias de contenido con los iconos de abajo.",
+  "onboarding.skip": "Saltar",
+  "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",
   "settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido",
   "settings.auto_collapse": "Colapsar automáticamente",
   "settings.auto_collapse_all": "Todo",
+  "settings.auto_collapse_height": "Altura (en pixeles) para que un toot sea considerado largo",
   "settings.auto_collapse_lengthy": "Toots largos",
   "settings.auto_collapse_media": "Toots con medios",
   "settings.auto_collapse_notifications": "Notificaciones",
@@ -50,11 +121,21 @@
   "settings.content_warnings": "Content warnings",
   "settings.content_warnings.regexp": "Regexp (expresión regular)",
   "settings.content_warnings_filter": "No descolapsar estas advertencias de contenido:",
+  "settings.content_warnings_media_outside": "Mostrar archivos adjuntos fuera de las advertencias de contenido",
+  "settings.content_warnings_media_outside_hint": "Reproduce el comportamiento normal de Mastodon teniendo al tener el interruptor de advertencia de contenido activado, no afectando los archivos adjuntos",
+  "settings.content_warnings_shared_state": "Mostrar/ocultar el contenido de todas las copias a la vez",
+  "settings.content_warnings_shared_state_hint": "Reproduce el comportamiento normal de Mastodon al hacer que el botón Advertencia de contenido afecte a todas las copias de un mensaje a la vez. Esto evitará el colapso automático de cualquier copia de un toot con CW desplegado",
+  "settings.content_warnings_unfold_opts": "Opciones de Auto-desplegado",
+  "settings.deprecated_setting": "Esta configuración ahora está controlada desde {settings_page_link} de Mastodon",
   "settings.enable_collapsed": "Habilitar toots colapsados",
+  "settings.enable_collapsed_hint": "Las publicaciones colapsadas tienen partes de su contenido ocultas para ocupar menos espacio en pantalla. Esto es distinto de la función Advertencia de Contenido",
   "settings.enable_content_warnings_auto_unfold": "Descolapsar automáticamente advertencias de contenido",
+  "settings.general": "General",
   "settings.hicolor_privacy_icons": "Íconos de privacidad más visibles",
+  "settings.hicolor_privacy_icons.hint": "Mostrar iconos de privacidad en colores brillantes y fácilmente distinguibles",
   "settings.image_backgrounds": "Fondos de imágenes",
   "settings.image_backgrounds_media": "Vista previa de medios de toots colapsados",
+  "settings.image_backgrounds_media_hint": "Si la publicación tiene algún archivo adjunto, utilice el primero como fondo",
   "settings.image_backgrounds_users": "Darle fondo de imagen a toots colapsados",
   "settings.inline_preview_cards": "Vista previa para enlaces externos",
   "settings.layout": "Diseño",
@@ -69,6 +150,10 @@
   "settings.notifications.tab_badge": "Marcador de notificaciones no leídas",
   "settings.notifications.tab_badge.hint": "Muestra un marcador de notificaciones sin leer en el ícono de notificaciones cuando dicha columna no está abierta",
   "settings.notifications_opts": "Opciones de notificaciones",
+  "settings.pop_in_left": "Izquierda",
+  "settings.pop_in_player": "Habilitar reproductor emergente",
+  "settings.pop_in_position": "Posición del reproductor:",
+  "settings.pop_in_right": "Derecha",
   "settings.preferences": "Preferences",
   "settings.prepend_cw_re": "Anteponer \"re: \" a las advertencias de contenido al responder",
   "settings.preselect_on_reply": "Preseleccionar nombres de usuarix al responder",
@@ -77,6 +162,7 @@
   "settings.rewrite_mentions_acct": "Reescribir con nombre de usuarix y dominio (para cuentas remotas)",
   "settings.rewrite_mentions_no": "No reescribir menciones",
   "settings.rewrite_mentions_username": "Reescribir con nombre de usuarix",
+  "settings.shared_settings_link": "preferencias de usuario",
   "settings.show_action_bar": "Mostrar botones de acción en toots colapsados",
   "settings.show_content_type_choice": "Mostrar selección de tipo de contenido al crear toots",
   "settings.show_reply_counter": "Mostrar un conteo estimado de respuestas",
@@ -86,10 +172,35 @@
   "settings.side_arm_reply_mode.copy": "Copiar opción de privacidad del toot al que estás respondiendo",
   "settings.side_arm_reply_mode.keep": "Conservar opción de privacidad",
   "settings.side_arm_reply_mode.restrict": "Restringir la opción de privacidad a la misma del toot al que estás respondiendo",
+  "settings.status_icons": "Iconos del toot",
+  "settings.status_icons_language": "Indicador de lenguaje",
+  "settings.status_icons_local_only": "Indicador de sólo local",
+  "settings.status_icons_media": "Indicadores de medios y encuestas",
+  "settings.status_icons_reply": "Indicador de respuesta",
+  "settings.status_icons_visibility": "Indicador de privacidad de toot",
   "settings.swipe_to_change_columns": "Permitir deslizar para cambiar columnas (Sólo en móvil)",
   "settings.tag_misleading_links": "Marcar enlaces engañosos",
   "settings.tag_misleading_links.hint": "Añadir una indicación visual indicando el destino de los enlace que no los mencionen explícitamente",
   "settings.wide_view": "Vista amplia (solo modo de escritorio)",
+  "settings.wide_view_hint": "Expande las columnas para llenar mejor el espacio disponible.",
   "status.collapse": "Colapsar",
-  "status.uncollapse": "Descolapsar"
+  "status.has_audio": "Contiene archivos de audio",
+  "status.has_pictures": "Contiene imágenes adjuntas",
+  "status.has_preview_card": "Contiene una tarjeta de vista previa adjunta",
+  "status.has_video": "Contiene videos adjuntos",
+  "status.in_reply_to": "Esta publicación es una respuesta",
+  "status.is_poll": "Esta publicación es una encuesta",
+  "status.local_only": "Sólo visible para tu instancia",
+  "status.sensitive_toggle": "Haga clic para ver",
+  "status.uncollapse": "Descolapsar",
+  "web_app_crash.change_your_settings": "Cambiar las {settings}",
+  "web_app_crash.content": "Puedes probar lo siguiente:",
+  "web_app_crash.debug_info": "Información de depuración",
+  "web_app_crash.disable_addons": "Desactivar complementos del navegador o herramientas de traducción integradas",
+  "web_app_crash.issue_tracker": "rastreador de problemas",
+  "web_app_crash.reload": "Recargar",
+  "web_app_crash.reload_page": "{reload} la página actual",
+  "web_app_crash.report_issue": "Reportar un bug en el {issuetracker}",
+  "web_app_crash.settings": "configuraciones",
+  "web_app_crash.title": "Lo sentimos, pero algo salió mal con la app de Mastodon."
 }
diff --git a/app/javascript/flavours/glitch/locales/es-MX.json b/app/javascript/flavours/glitch/locales/es-MX.json
index fc5a2e904..a1cadec41 100644
--- a/app/javascript/flavours/glitch/locales/es-MX.json
+++ b/app/javascript/flavours/glitch/locales/es-MX.json
@@ -1,4 +1,15 @@
 {
+  "about.fork_disclaimer": "Glitch-soc es software gratuito, de código abierto, bifurcado de Mastodon.",
+  "account.add_account_note": "Añadir nota para @{name}",
+  "account.disclaimer_full": "La información aquí presentada puede reflejar de manera incompleta el perfil del usuario.",
+  "account.follows": "Seguir",
+  "account.joined": "Unido {date}",
+  "account.suspended_disclaimer_full": "Este usuario ha sido suspendido por un moderador.",
+  "account.view_full_profile": "Ver perfil completo",
+  "account_note.cancel": "Cancelar",
+  "account_note.edit": "Editar",
+  "account_note.glitch_placeholder": "No se proporcionó comentario alguno",
+  "account_note.save": "Guardar",
   "advanced_options.icon_title": "Opciones avanzadas",
   "advanced_options.local-only.long": "No publicar a otras instancias",
   "advanced_options.local-only.short": "Local",
@@ -6,36 +17,96 @@
   "advanced_options.threaded_mode.long": "Al publicar abre automáticamente una respuesta",
   "advanced_options.threaded_mode.short": "Modo hilo",
   "advanced_options.threaded_mode.tooltip": "Modo hilo habilitado",
+  "boost_modal.missing_description": "Esta publicación contiene medios sin descripción",
+  "column.favourited_by": "Marcado como favorito por",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Impulsado por",
+  "column.subheading": "Opciones misceláneas",
+  "column_header.profile": "Perfil",
+  "column_subheading.lists": "Listas",
+  "column_subheading.navigation": "Navegación",
+  "community.column_settings.allow_local_only": "Mostrar sólo toots locales",
   "compose.attach": "Adjuntar...",
   "compose.attach.doodle": "Dibujar algo",
   "compose.attach.upload": "Subir un archivo",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Texto plano",
+  "compose_form.poll.multiple_choices": "Permitir múltiples opciones",
+  "compose_form.poll.single_choice": "Permitir sólo una opción",
+  "compose_form.spoiler": "Esconder el texto detrás de la advertencia",
+  "confirmation_modal.do_not_ask_again": "No preguntar por la confirmación de nuevo",
+  "confirmations.deprecated_settings.confirm": "Usar las preferencias de Mastodon",
+  "confirmations.deprecated_settings.message": "Algunas de las {app_settings} de glitch-soc, específicas para el dispositivo que estás usando han sido reemplazadas en las {preferences} de Mastodon y serán sobreescritas:",
+  "confirmations.missing_media_description.confirm": "Enviar de todos modos",
+  "confirmations.missing_media_description.edit": "Editar medios",
+  "confirmations.missing_media_description.message": "Al menos a un adjunto le falta una descripción. Considera describir toda la multimedia para los débiles visuales antes de mandar el toot.",
   "confirmations.unfilter.author": "Publicado por",
   "confirmations.unfilter.confirm": "Mostrar",
   "confirmations.unfilter.edit_filter": "Editar filtro",
   "confirmations.unfilter.filters": "Coincidencia con {count, plural, one {filtro} other {filtros}}",
+  "content-type.change": "Tipo de contenido",
+  "direct.group_by_conversations": "Agrupar por conversación",
+  "endorsed_accounts_editor.endorsed_accounts": "Cuentas destacadas",
   "favourite_modal.combo": "Puedes presionar {combo} para omitir esto la próxima vez",
   "getting_started.onboarding": "Paseo inicial",
+  "home.column_settings.advanced": "Avanzado",
+  "home.column_settings.filter_regex": "Filtrar por expresiones regulares",
   "home.column_settings.show_direct": "Mostrar mensajes directos",
+  "home.settings": "Configuraciones de columna",
+  "keyboard_shortcuts.bookmark": "a marcadores",
+  "keyboard_shortcuts.secondary_toot": "para enviar un toot usando lac onfiguración de privacidad secundaria",
+  "keyboard_shortcuts.toggle_collapse": "para colapsar/descolapsar toots",
   "layout.auto": "Automático",
   "layout.desktop": "Escritorio",
   "layout.hint.auto": "Seleccionar un diseño automáticamente basado en \"Habilitar interface web avanzada\" y tamaño de pantalla",
   "layout.hint.desktop": "Utiliza el diseño multi-columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
   "layout.hint.single": "Utiliza el diseño de una columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
+  "layout.single": "Móvil",
   "media_gallery.sensitive": "Sensible",
+  "moved_to_warning": "Esta cuenta está marcada como movida a {moved_to_link}, y por lo tanto no aceptará nuevos seguimientos.",
   "navigation_bar.app_settings": "Ajustes de aplicación",
+  "navigation_bar.featured_users": "Usuarios destacados",
+  "navigation_bar.keyboard_shortcuts": "Atajos de teclado",
+  "navigation_bar.misc": "Misc",
   "notification.markForDeletion": "Marcar para borrar",
   "notification_purge.btn_all": "Seleccionar\ntodo",
   "notification_purge.btn_apply": "Borrar\nselección",
   "notification_purge.btn_invert": "Invertir\nselección",
   "notification_purge.btn_none": "Seleccionar\nnada",
+  "notification_purge.start": "Entrar en modo de limpieza de notificaciones",
   "notifications.marked_clear": "Limpiar notificaciones seleccionadas",
   "notifications.marked_clear_confirmation": "¿Deseas borrar permanentemente todas las notificaciones seleccionadas?",
+  "onboarding.done": "Hecho",
+  "onboarding.next": "Siguiente",
+  "onboarding.page_five.public_timelines": "La línea de tiempo local muestra mensajes públicos de todos en {domain}. La línea de tiempo federada muestra mensajes públicos de todos aquellos que en {domain} siguen a otros servidores. Estas son las líneas cronológicas públicas, una gran manera de descubrir gente nueva.",
+  "onboarding.page_four.home": "La línea de tiempo principal muestra los mensajes de la gente que sigues.",
+  "onboarding.page_four.notifications": "La columna de notificaciones muestra cuando alguien interactúa contigo.",
   "onboarding.page_one.federation": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "Estás en {domain}, así que tu alias completo es {handle}",
   "onboarding.page_one.welcome": "¡Bienvenidx a {domain}!",
+  "onboarding.page_six.admin": "El administrador de tu instancia es {admin}.",
+  "onboarding.page_six.almost_done": "Casi listo...",
+  "onboarding.page_six.appetoot": "¡A tootear!",
+  "onboarding.page_six.apps_available": "Hay {apps} disponibles para iOS, Android y otras plataformas.",
   "onboarding.page_six.github": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc is free open-source software. You can report bugs, request features, or contribute to the code on {github}.",
+  "onboarding.page_six.guidelines": "normas de la comunidad",
+  "onboarding.page_six.read_guidelines": "¡Por favor lee las {guidelines} de {domain}!",
+  "onboarding.page_six.various_app": "aplicaciones para móviles",
+  "onboarding.page_three.profile": "Edita tu perfil para cambiar tu avatar, biografía y nombre para mostrar. Ahí, también encontrarás otras preferencias.",
+  "onboarding.page_three.search": "Usa la barra de búsqueda para encontrar gente y mirar las etiquetas (hashtags), como {illustration} y {introductions}. Para buscar a una persona que no esté en esta instancia, utiliza su alias completo.",
+  "onboarding.page_two.compose": "Escribe mensajes desde la columna de composición. Puedes subir imágenes, cambiar la configuración de privacidad y añadir advertencias de contenido con los iconos de abajo.",
+  "onboarding.skip": "Saltar",
+  "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",
   "settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido",
   "settings.auto_collapse": "Colapsar automáticamente",
   "settings.auto_collapse_all": "Todo",
+  "settings.auto_collapse_height": "Altura (en pixeles) para que un toot sea considerado largo",
   "settings.auto_collapse_lengthy": "Toots largos",
   "settings.auto_collapse_media": "Toots con medios",
   "settings.auto_collapse_notifications": "Notificaciones",
@@ -50,11 +121,21 @@
   "settings.content_warnings": "Content warnings",
   "settings.content_warnings.regexp": "Regexp (expresión regular)",
   "settings.content_warnings_filter": "No descolapsar estas advertencias de contenido:",
+  "settings.content_warnings_media_outside": "Mostrar archivos adjuntos fuera de las advertencias de contenido",
+  "settings.content_warnings_media_outside_hint": "Reproduce el comportamiento normal de Mastodon teniendo al tener el interruptor de advertencia de contenido activado, no afectando los archivos adjuntos",
+  "settings.content_warnings_shared_state": "Mostrar/ocultar el contenido de todas las copias a la vez",
+  "settings.content_warnings_shared_state_hint": "Reproduce el comportamiento normal de Mastodon al hacer que el botón Advertencia de contenido afecte a todas las copias de un mensaje a la vez. Esto evitará el colapso automático de cualquier copia de un toot con CW desplegado",
+  "settings.content_warnings_unfold_opts": "Opciones de Auto-desplegado",
+  "settings.deprecated_setting": "Esta configuración ahora está controlada desde {settings_page_link} de Mastodon",
   "settings.enable_collapsed": "Habilitar toots colapsados",
+  "settings.enable_collapsed_hint": "Las publicaciones colapsadas tienen partes de su contenido ocultas para ocupar menos espacio en pantalla. Esto es distinto de la función Advertencia de Contenido",
   "settings.enable_content_warnings_auto_unfold": "Descolapsar automáticamente advertencias de contenido",
+  "settings.general": "General",
   "settings.hicolor_privacy_icons": "Íconos de privacidad más visibles",
+  "settings.hicolor_privacy_icons.hint": "Mostrar iconos de privacidad en colores brillantes y fácilmente distinguibles",
   "settings.image_backgrounds": "Fondos de imágenes",
   "settings.image_backgrounds_media": "Vista previa de medios de toots colapsados",
+  "settings.image_backgrounds_media_hint": "Si la publicación tiene algún archivo adjunto, utilice el primero como fondo",
   "settings.image_backgrounds_users": "Darle fondo de imagen a toots colapsados",
   "settings.inline_preview_cards": "Vista previa para enlaces externos",
   "settings.layout": "Diseño",
@@ -69,6 +150,10 @@
   "settings.notifications.tab_badge": "Marcador de notificaciones no leídas",
   "settings.notifications.tab_badge.hint": "Muestra un marcador de notificaciones sin leer en el ícono de notificaciones cuando dicha columna no está abierta",
   "settings.notifications_opts": "Opciones de notificaciones",
+  "settings.pop_in_left": "Izquierda",
+  "settings.pop_in_player": "Habilitar reproductor emergente",
+  "settings.pop_in_position": "Posición del reproductor:",
+  "settings.pop_in_right": "Derecha",
   "settings.preferences": "Preferences",
   "settings.prepend_cw_re": "Anteponer \"re: \" a las advertencias de contenido al responder",
   "settings.preselect_on_reply": "Preseleccionar nombres de usuarix al responder",
@@ -77,6 +162,7 @@
   "settings.rewrite_mentions_acct": "Reescribir con nombre de usuarix y dominio (para cuentas remotas)",
   "settings.rewrite_mentions_no": "No reescribir menciones",
   "settings.rewrite_mentions_username": "Reescribir con nombre de usuarix",
+  "settings.shared_settings_link": "preferencias de usuario",
   "settings.show_action_bar": "Mostrar botones de acción en toots colapsados",
   "settings.show_content_type_choice": "Mostrar selección de tipo de contenido al crear toots",
   "settings.show_reply_counter": "Mostrar un conteo estimado de respuestas",
@@ -86,10 +172,35 @@
   "settings.side_arm_reply_mode.copy": "Copiar opción de privacidad del toot al que estás respondiendo",
   "settings.side_arm_reply_mode.keep": "Conservar opción de privacidad",
   "settings.side_arm_reply_mode.restrict": "Restringir la opción de privacidad a la misma del toot al que estás respondiendo",
+  "settings.status_icons": "Iconos del toot",
+  "settings.status_icons_language": "Indicador de lenguaje",
+  "settings.status_icons_local_only": "Indicador de sólo local",
+  "settings.status_icons_media": "Indicadores de medios y encuestas",
+  "settings.status_icons_reply": "Indicador de respuesta",
+  "settings.status_icons_visibility": "Indicador de privacidad de toot",
   "settings.swipe_to_change_columns": "Permitir deslizar para cambiar columnas (Sólo en móvil)",
   "settings.tag_misleading_links": "Marcar enlaces engañosos",
   "settings.tag_misleading_links.hint": "Añadir una indicación visual indicando el destino de los enlace que no los mencionen explícitamente",
   "settings.wide_view": "Vista amplia (solo modo de escritorio)",
+  "settings.wide_view_hint": "Expande las columnas para llenar mejor el espacio disponible.",
   "status.collapse": "Colapsar",
-  "status.uncollapse": "Descolapsar"
+  "status.has_audio": "Contiene archivos de audio",
+  "status.has_pictures": "Contiene imágenes adjuntas",
+  "status.has_preview_card": "Contiene una tarjeta de vista previa adjunta",
+  "status.has_video": "Contiene videos adjuntos",
+  "status.in_reply_to": "Esta publicación es una respuesta",
+  "status.is_poll": "Esta publicación es una encuesta",
+  "status.local_only": "Sólo visible para tu instancia",
+  "status.sensitive_toggle": "Haga clic para ver",
+  "status.uncollapse": "Descolapsar",
+  "web_app_crash.change_your_settings": "Cambiar las {settings}",
+  "web_app_crash.content": "Puedes probar lo siguiente:",
+  "web_app_crash.debug_info": "Información de depuración",
+  "web_app_crash.disable_addons": "Desactivar complementos del navegador o herramientas de traducción integradas",
+  "web_app_crash.issue_tracker": "rastreador de problemas",
+  "web_app_crash.reload": "Recargar",
+  "web_app_crash.reload_page": "{reload} la página actual",
+  "web_app_crash.report_issue": "Reportar un bug en el {issuetracker}",
+  "web_app_crash.settings": "configuraciones",
+  "web_app_crash.title": "Lo sentimos, pero algo salió mal con la app de Mastodon."
 }
diff --git a/app/javascript/flavours/glitch/locales/es.json b/app/javascript/flavours/glitch/locales/es.json
index 07acbf914..0838496d2 100644
--- a/app/javascript/flavours/glitch/locales/es.json
+++ b/app/javascript/flavours/glitch/locales/es.json
@@ -1,61 +1,142 @@
 {
+  "about.fork_disclaimer": "Glitch-soc es software gratuito, de código abierto, bifurcado de Mastodon.",
+  "account.add_account_note": "Añadir nota para @{name}",
+  "account.disclaimer_full": "La información aquí presentada puede reflejar de manera incompleta el perfil del usuario.",
+  "account.follows": "Sigue",
+  "account.joined": "Unido el {date}",
+  "account.suspended_disclaimer_full": "Este usuario ha sido suspendido por un moderador.",
+  "account.view_full_profile": "Ver perfil completo",
+  "account_note.cancel": "Cancelar",
+  "account_note.edit": "Editar",
+  "account_note.glitch_placeholder": "No se proporcionó comentario alguno",
+  "account_note.save": "Guardar",
   "advanced_options.icon_title": "Opciones avanzadas",
   "advanced_options.local-only.long": "No publicar a otras instancias",
-  "advanced_options.local-only.short": "Local",
-  "advanced_options.local-only.tooltip": "Este toot es local",
-  "advanced_options.threaded_mode.long": "Al publicar abre automáticamente una respuesta",
+  "advanced_options.local-only.short": "Sólo local",
+  "advanced_options.local-only.tooltip": "Esta publicación es sólo local",
+  "advanced_options.threaded_mode.long": "Abre automáticamente una respuesta al publicar",
   "advanced_options.threaded_mode.short": "Modo hilo",
   "advanced_options.threaded_mode.tooltip": "Modo hilo habilitado",
+  "boost_modal.missing_description": "Esta publicación contiene medios sin descripción",
+  "column.favourited_by": "Marcado como favorito por",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Impulsado por",
+  "column.subheading": "Opciones misceláneas",
+  "column_header.profile": "Perfil",
+  "column_subheading.lists": "Listas",
+  "column_subheading.navigation": "Navegación",
+  "community.column_settings.allow_local_only": "Mostrar sólo toots locales",
   "compose.attach": "Adjuntar...",
   "compose.attach.doodle": "Dibujar algo",
   "compose.attach.upload": "Subir un archivo",
-  "confirmations.unfilter.author": "Publicado por",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Texto plano",
+  "compose_form.poll.multiple_choices": "Permitir múltiples opciones",
+  "compose_form.poll.single_choice": "Permitir sólo una opción",
+  "compose_form.spoiler": "Esconder el texto detrás de la advertencia",
+  "confirmation_modal.do_not_ask_again": "No preguntar por la confirmación de nuevo",
+  "confirmations.deprecated_settings.confirm": "Usar las preferencias de Mastodon",
+  "confirmations.deprecated_settings.message": "Algunas de las {app_settings} de glitch-soc, específicas para el dispositivo que estás usando han sido reemplazadas en las {preferences} de Mastodon y serán sobreescritas:",
+  "confirmations.missing_media_description.confirm": "Enviar de todos modos",
+  "confirmations.missing_media_description.edit": "Editar medios",
+  "confirmations.missing_media_description.message": "Al menos a un adjunto le falta una descripción. Considera describir toda la multimedia para los débiles visuales antes de mandar el toot.",
+  "confirmations.unfilter.author": "Autor",
   "confirmations.unfilter.confirm": "Mostrar",
   "confirmations.unfilter.edit_filter": "Editar filtro",
-  "confirmations.unfilter.filters": "Coincidencia con {count, plural, one {filtro} other {filtros}}",
+  "confirmations.unfilter.filters": "Coincidiendo {count, plural, one {filtro} other {filtros}}",
+  "content-type.change": "Tipo de contenido",
+  "direct.group_by_conversations": "Agrupar por conversación",
+  "endorsed_accounts_editor.endorsed_accounts": "Cuentas destacadas",
   "favourite_modal.combo": "Puedes presionar {combo} para omitir esto la próxima vez",
   "getting_started.onboarding": "Paseo inicial",
+  "home.column_settings.advanced": "Avanzado",
+  "home.column_settings.filter_regex": "Filtrar por expresiones regulares",
   "home.column_settings.show_direct": "Mostrar mensajes directos",
+  "home.settings": "Configuraciones de columna",
+  "keyboard_shortcuts.bookmark": "a marcadores",
+  "keyboard_shortcuts.secondary_toot": "para enviar un toot usando lac onfiguración de privacidad secundaria",
+  "keyboard_shortcuts.toggle_collapse": "para colapsar/descolapsar toots",
   "layout.auto": "Automático",
   "layout.desktop": "Escritorio",
-  "layout.hint.auto": "Seleccionar un diseño automáticamente basado en \"Habilitar interface web avanzada\" y tamaño de pantalla",
-  "layout.hint.desktop": "Utiliza el diseño multi-columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
-  "layout.hint.single": "Utiliza el diseño de una columna sin importar \"Habilitar interface web avanzada\" o tamaño de pantalla",
+  "layout.hint.auto": "Seleccionar un diseño automáticamente basado en \"Habilitar interface web avanzada\" y el tamaño de la pantalla.",
+  "layout.hint.desktop": "Utiliza el diseño multi-columna sin importar \"Habilitar interface web avanzada\" o el tamaño de la pantalla.",
+  "layout.hint.single": "Utiliza el diseño de una columna sin importar \"Habilitar interface web avanzada\" o el tamaño de la pantalla.",
+  "layout.single": "Móvil",
   "media_gallery.sensitive": "Sensible",
-  "navigation_bar.app_settings": "Ajustes de aplicación",
-  "notification.markForDeletion": "Marcar para borrar",
+  "moved_to_warning": "Esta cuenta está marcada como movida a {moved_to_link}, y por lo tanto no aceptará nuevos seguimientos.",
+  "navigation_bar.app_settings": "Ajustes de la aplicación",
+  "navigation_bar.featured_users": "Usuarios destacados",
+  "navigation_bar.keyboard_shortcuts": "Atajos de teclado",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Marcar para borrado",
   "notification_purge.btn_all": "Seleccionar\ntodo",
   "notification_purge.btn_apply": "Borrar\nselección",
   "notification_purge.btn_invert": "Invertir\nselección",
-  "notification_purge.btn_none": "Seleccionar\nnada",
-  "notifications.marked_clear": "Limpiar notificaciones seleccionadas",
-  "notifications.marked_clear_confirmation": "¿Deseas borrar permanentemente todas las notificaciones seleccionadas?",
-  "onboarding.page_one.federation": "{domain} es una \"instancia\" de Mastodon. Mastodon es una red de servidores independientes que se unen para crear una red social más grande. A estos servidores los llamamos instancias.",
-  "onboarding.page_one.welcome": "¡Bienvenidx a {domain}!",
+  "notification_purge.btn_none": "Seleccionar\nninguno",
+  "notification_purge.start": "Entrar en modo de limpieza de notificaciones",
+  "notifications.marked_clear": "Limpiar las notificaciones seleccionadas",
+  "notifications.marked_clear_confirmation": "¿Estás seguro de borrar permanentemente todas las notificaciones seleccionadas?",
+  "onboarding.done": "Hecho",
+  "onboarding.next": "Siguiente",
+  "onboarding.page_five.public_timelines": "La línea de tiempo local muestra mensajes públicos de todos en {domain}. La línea de tiempo federada muestra mensajes públicos de todos aquellos que en {domain} siguen a otros servidores. Estas son las líneas cronológicas públicas, una gran manera de descubrir gente nueva.",
+  "onboarding.page_four.home": "La línea de tiempo principal muestra los mensajes de la gente que sigues.",
+  "onboarding.page_four.notifications": "La columna de notificaciones muestra cuando alguien interactúa contigo.",
+  "onboarding.page_one.federation": "{domain} es una \"instancia\" de Mastodon. Mastodon es una red de servidores independientes uniéndose para crear una red social más grande. A estos servidores los llamamos instancias.",
+  "onboarding.page_one.handle": "Estás en {domain}, así que tu alias completo es {handle}",
+  "onboarding.page_one.welcome": "¡Bienvenido a {domain}!",
+  "onboarding.page_six.admin": "El administrador de tu instancia es {admin}.",
+  "onboarding.page_six.almost_done": "Casi listo...",
+  "onboarding.page_six.appetoot": "¡A tootear!",
+  "onboarding.page_six.apps_available": "Hay {apps} disponibles para iOS, Android y otras plataformas.",
   "onboarding.page_six.github": "{domain} usa Glitchsoc. Glitchsoc es una bifurcación {fork} amigable de {Mastodon}, y es compatible con cualquier instancia o aplicación de Mastodon. Glitchsoc es completamente gratuito y de código abierto. Puedes reportar errores, solicitar funciones o contribuir al código en {github}.",
+  "onboarding.page_six.guidelines": "normas de la comunidad",
+  "onboarding.page_six.read_guidelines": "¡Por favor lee las {guidelines} de {domain}!",
+  "onboarding.page_six.various_app": "aplicaciones para móviles",
+  "onboarding.page_three.profile": "Edita tu perfil para cambiar tu avatar, biografía y nombre para mostrar. Ahí, también encontrarás otras preferencias.",
+  "onboarding.page_three.search": "Usa la barra de búsqueda para encontrar gente y mirar las etiquetas (hashtags), como {illustration} y {introductions}. Para buscar a una persona que no esté en esta instancia, utiliza su alias completo.",
+  "onboarding.page_two.compose": "Escribe mensajes desde la columna de composición. Puedes subir imágenes, cambiar la configuración de privacidad y añadir advertencias de contenido con los iconos de abajo.",
+  "onboarding.skip": "Saltar",
+  "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",
   "settings.always_show_spoilers_field": "Siempre mostrar el campo de advertencia de contenido",
   "settings.auto_collapse": "Colapsar automáticamente",
   "settings.auto_collapse_all": "Todo",
-  "settings.auto_collapse_lengthy": "Toots largos",
-  "settings.auto_collapse_media": "Toots con medios",
+  "settings.auto_collapse_height": "Altura (en pixeles) para que un toot sea considerado largo",
+  "settings.auto_collapse_lengthy": "Publicaciones largas",
+  "settings.auto_collapse_media": "Publicaciones multimedia",
   "settings.auto_collapse_notifications": "Notificaciones",
-  "settings.auto_collapse_reblogs": "Retoots",
+  "settings.auto_collapse_reblogs": "Impulsos",
   "settings.auto_collapse_replies": "Respuestas",
   "settings.close": "Cerrar",
-  "settings.collapsed_statuses": "Toots colapsados",
+  "settings.collapsed_statuses": "Publicaciones colapsadas",
   "settings.compose_box_opts": "Cuadro de redacción",
-  "settings.confirm_before_clearing_draft": "Mostrar diálogo de confirmación antes de sobreescribir un mensaje estabas escribiendo",
-  "settings.confirm_boost_missing_media_description": "Mostrar diálogo de confirmación antes de retootear publicaciones con medios sin descripción",
-  "settings.confirm_missing_media_description": "Mostrar diálogo de confirmación antes de publicar toots con medios sin descripción",
+  "settings.confirm_before_clearing_draft": "Mostrar diálogo de confirmación antes de sobreescribir el mensaje siendo redactado",
+  "settings.confirm_boost_missing_media_description": "Mostrar diálogo de confirmación antes de impulsar publicaciones con medios sin descripciones",
+  "settings.confirm_missing_media_description": "Mostrar diálogo de confirmación antes de enviar publicaciones con medios sin descripciones",
   "settings.content_warnings": "Advertencias de contenido",
   "settings.content_warnings.regexp": "Regexp (expresión regular)",
   "settings.content_warnings_filter": "No descolapsar estas advertencias de contenido:",
-  "settings.enable_collapsed": "Habilitar toots colapsados",
-  "settings.enable_content_warnings_auto_unfold": "Descolapsar automáticamente advertencias de contenido",
+  "settings.content_warnings_media_outside": "Mostrar archivos adjuntos fuera de las advertencias de contenido",
+  "settings.content_warnings_media_outside_hint": "Reproduce el comportamiento normal de Mastodon teniendo al tener el interruptor de advertencia de contenido activado, no afectando los archivos adjuntos",
+  "settings.content_warnings_shared_state": "Mostrar/ocultar el contenido de todas las copias a la vez",
+  "settings.content_warnings_shared_state_hint": "Reproduce el comportamiento normal de Mastodon al hacer que el botón Advertencia de contenido afecte a todas las copias de un mensaje a la vez. Esto evitará el colapso automático de cualquier copia de un toot con CW desplegado",
+  "settings.content_warnings_unfold_opts": "Opciones de Auto-desplegado",
+  "settings.deprecated_setting": "Esta configuración ahora está controlada desde {settings_page_link} de Mastodon",
+  "settings.enable_collapsed": "Habilitar publicaciones colapsadas",
+  "settings.enable_collapsed_hint": "Las publicaciones colapsadas tienen partes de su contenido ocultas para ocupar menos espacio en pantalla. Esto es distinto de la función Advertencia de Contenido",
+  "settings.enable_content_warnings_auto_unfold": "Desplegar automáticamente advertencias de contenido",
+  "settings.general": "General",
   "settings.hicolor_privacy_icons": "Íconos de privacidad más visibles",
+  "settings.hicolor_privacy_icons.hint": "Mostrar iconos de privacidad en colores brillantes y fácilmente distinguibles",
   "settings.image_backgrounds": "Fondos de imágenes",
-  "settings.image_backgrounds_media": "Vista previa de medios de toots colapsados",
-  "settings.image_backgrounds_users": "Darle fondo de imagen a toots colapsados",
+  "settings.image_backgrounds_media": "Vista previa de medios de publicaciones colapsadas",
+  "settings.image_backgrounds_media_hint": "Si la publicación tiene algún archivo adjunto, utilice el primero como fondo",
+  "settings.image_backgrounds_users": "Darle fondo de imagen a publicaciones colapsadas",
   "settings.inline_preview_cards": "Vista previa para enlaces externos",
   "settings.layout": "Diseño",
   "settings.layout_opts": "Opciones de diseño",
@@ -69,27 +150,57 @@
   "settings.notifications.tab_badge": "Marcador de notificaciones no leídas",
   "settings.notifications.tab_badge.hint": "Muestra un marcador de notificaciones sin leer en el ícono de notificaciones cuando dicha columna no está abierta",
   "settings.notifications_opts": "Opciones de notificaciones",
-  "settings.preferences": "Preferencias de usuarix",
+  "settings.pop_in_left": "Izquierda",
+  "settings.pop_in_player": "Habilitar reproductor emergente",
+  "settings.pop_in_position": "Posición del reproductor:",
+  "settings.pop_in_right": "Derecha",
+  "settings.preferences": "Preferencias del usuario",
   "settings.prepend_cw_re": "Anteponer \"re: \" a las advertencias de contenido al responder",
-  "settings.preselect_on_reply": "Preseleccionar nombres de usuarix al responder",
-  "settings.preselect_on_reply_hint": "Al responder a conversaciones con múltiples participantes, preselecciona los nombres de usuarix subsecuentes del/la primerx",
+  "settings.preselect_on_reply": "Preseleccionar nombres de usuarios al responder",
+  "settings.preselect_on_reply_hint": "Al responder a conversaciones con múltiples participantes, preselecciona los nombres de usuario subsecuentes al primero",
   "settings.rewrite_mentions": "Reescribir menciones in publicaciones mostradas",
-  "settings.rewrite_mentions_acct": "Reescribir con nombre de usuarix y dominio (para cuentas remotas)",
+  "settings.rewrite_mentions_acct": "Reescribir con el nombre de usuario y dominio (para las cuentas remotas)",
   "settings.rewrite_mentions_no": "No reescribir menciones",
-  "settings.rewrite_mentions_username": "Reescribir con nombre de usuarix",
-  "settings.show_action_bar": "Mostrar botones de acción en toots colapsados",
-  "settings.show_content_type_choice": "Mostrar selección de tipo de contenido al crear toots",
+  "settings.rewrite_mentions_username": "Reescribir con nombre de usuario",
+  "settings.shared_settings_link": "preferencias de usuario",
+  "settings.show_action_bar": "Mostrar botones de acción en publicaciones colapsadas",
+  "settings.show_content_type_choice": "Mostrar selección de tipo de contenido al crear publicaciones",
   "settings.show_reply_counter": "Mostrar un conteo estimado de respuestas",
   "settings.side_arm": "Botón secundario:",
   "settings.side_arm.none": "Ninguno",
-  "settings.side_arm_reply_mode": "Al responder a un toot, el botón de toot secundario debe:",
-  "settings.side_arm_reply_mode.copy": "Copiar opción de privacidad del toot al que estás respondiendo",
+  "settings.side_arm_reply_mode": "Al responder a una publicación, el botón de publicación secundario debe:",
+  "settings.side_arm_reply_mode.copy": "Copiar opción de privacidad de la publicación a la que estás respondiendo",
   "settings.side_arm_reply_mode.keep": "Conservar opción de privacidad",
-  "settings.side_arm_reply_mode.restrict": "Restringir la opción de privacidad a la misma del toot al que estás respondiendo",
+  "settings.side_arm_reply_mode.restrict": "Restringir la opción de privacidad a la misma de la publicación a la que estás respondiendo",
+  "settings.status_icons": "Iconos del toot",
+  "settings.status_icons_language": "Indicador de lenguaje",
+  "settings.status_icons_local_only": "Indicador de sólo local",
+  "settings.status_icons_media": "Indicadores de medios y encuestas",
+  "settings.status_icons_reply": "Indicador de respuesta",
+  "settings.status_icons_visibility": "Indicador de privacidad de toot",
   "settings.swipe_to_change_columns": "Permitir deslizar para cambiar columnas (Sólo en móvil)",
   "settings.tag_misleading_links": "Marcar enlaces engañosos",
   "settings.tag_misleading_links.hint": "Añadir una indicación visual indicando el destino de los enlace que no los mencionen explícitamente",
   "settings.wide_view": "Vista amplia (solo modo de escritorio)",
+  "settings.wide_view_hint": "Expande las columnas para llenar mejor el espacio disponible.",
   "status.collapse": "Colapsar",
-  "status.uncollapse": "Descolapsar"
+  "status.has_audio": "Contiene archivos de audio",
+  "status.has_pictures": "Contiene imágenes adjuntas",
+  "status.has_preview_card": "Contiene una tarjeta de vista previa adjunta",
+  "status.has_video": "Contiene videos adjuntos",
+  "status.in_reply_to": "Esta publicación es una respuesta",
+  "status.is_poll": "Esta publicación es una encuesta",
+  "status.local_only": "Sólo visible para tu instancia",
+  "status.sensitive_toggle": "Haga clic para ver",
+  "status.uncollapse": "Descolapsar",
+  "web_app_crash.change_your_settings": "Cambiar las {settings}",
+  "web_app_crash.content": "Puedes probar lo siguiente:",
+  "web_app_crash.debug_info": "Información de depuración",
+  "web_app_crash.disable_addons": "Desactivar complementos del navegador o herramientas de traducción integradas",
+  "web_app_crash.issue_tracker": "rastreador de problemas",
+  "web_app_crash.reload": "Recargar",
+  "web_app_crash.reload_page": "{reload} la página actual",
+  "web_app_crash.report_issue": "Reportar un bug en el {issuetracker}",
+  "web_app_crash.settings": "configuraciones",
+  "web_app_crash.title": "Lo sentimos, pero algo salió mal con la app de Mastodon."
 }
diff --git a/app/javascript/flavours/glitch/locales/et.json b/app/javascript/flavours/glitch/locales/et.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/et.json
+++ b/app/javascript/flavours/glitch/locales/et.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/eu.json b/app/javascript/flavours/glitch/locales/eu.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/eu.json
+++ b/app/javascript/flavours/glitch/locales/eu.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/fa.json b/app/javascript/flavours/glitch/locales/fa.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/fa.json
+++ b/app/javascript/flavours/glitch/locales/fa.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/fi.json b/app/javascript/flavours/glitch/locales/fi.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/fi.json
+++ b/app/javascript/flavours/glitch/locales/fi.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/fo.json b/app/javascript/flavours/glitch/locales/fo.json
index 0967ef424..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/fo.json
+++ b/app/javascript/flavours/glitch/locales/fo.json
@@ -1 +1,206 @@
-{}
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/fr-QC.json b/app/javascript/flavours/glitch/locales/fr-QC.json
index 383d933b4..8ae47b49f 100644
--- a/app/javascript/flavours/glitch/locales/fr-QC.json
+++ b/app/javascript/flavours/glitch/locales/fr-QC.json
@@ -67,7 +67,6 @@
   "moved_to_warning": "Ce compte a déménagé vers {moved_to_link} et ne peut donc plus accepter de nouveaux abonné·e·s.",
   "navigation_bar.app_settings": "Paramètres de l'application",
   "navigation_bar.featured_users": "Utilisateurs mis en avant",
-  "navigation_bar.info": "Informations détaillées",
   "navigation_bar.keyboard_shortcuts": "Raccourcis clavier",
   "navigation_bar.misc": "Autres",
   "notification.markForDeletion": "Ajouter aux éléments à supprimer",
@@ -98,9 +97,16 @@
   "onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des personnes et regarder les hashtags comme {illustration} et {introductions}. Pour chercher une personne n'étant pas sur cette instance, utilisez son nom d'utilisateur complet.",
   "onboarding.page_two.compose": "Écrivez des posts depuis la colonne de rédaction. Vous pouvez téléverser des images, changer la confidentialité et ajouter des avertissements de contenu avec les boutons ci-dessous.",
   "onboarding.skip": "Passer",
+  "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",
   "settings.always_show_spoilers_field": "Toujours activer le champ de rédaction de l'avertissement de contenu",
   "settings.auto_collapse": "Repliage automatique",
   "settings.auto_collapse_all": "Tout",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
   "settings.auto_collapse_lengthy": "Posts longs",
   "settings.auto_collapse_media": "Posts avec média",
   "settings.auto_collapse_notifications": "Notifications",
diff --git a/app/javascript/flavours/glitch/locales/fr.json b/app/javascript/flavours/glitch/locales/fr.json
index 383d933b4..8ae47b49f 100644
--- a/app/javascript/flavours/glitch/locales/fr.json
+++ b/app/javascript/flavours/glitch/locales/fr.json
@@ -67,7 +67,6 @@
   "moved_to_warning": "Ce compte a déménagé vers {moved_to_link} et ne peut donc plus accepter de nouveaux abonné·e·s.",
   "navigation_bar.app_settings": "Paramètres de l'application",
   "navigation_bar.featured_users": "Utilisateurs mis en avant",
-  "navigation_bar.info": "Informations détaillées",
   "navigation_bar.keyboard_shortcuts": "Raccourcis clavier",
   "navigation_bar.misc": "Autres",
   "notification.markForDeletion": "Ajouter aux éléments à supprimer",
@@ -98,9 +97,16 @@
   "onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des personnes et regarder les hashtags comme {illustration} et {introductions}. Pour chercher une personne n'étant pas sur cette instance, utilisez son nom d'utilisateur complet.",
   "onboarding.page_two.compose": "Écrivez des posts depuis la colonne de rédaction. Vous pouvez téléverser des images, changer la confidentialité et ajouter des avertissements de contenu avec les boutons ci-dessous.",
   "onboarding.skip": "Passer",
+  "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",
   "settings.always_show_spoilers_field": "Toujours activer le champ de rédaction de l'avertissement de contenu",
   "settings.auto_collapse": "Repliage automatique",
   "settings.auto_collapse_all": "Tout",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
   "settings.auto_collapse_lengthy": "Posts longs",
   "settings.auto_collapse_media": "Posts avec média",
   "settings.auto_collapse_notifications": "Notifications",
diff --git a/app/javascript/flavours/glitch/locales/fy.json b/app/javascript/flavours/glitch/locales/fy.json
index 0967ef424..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/fy.json
+++ b/app/javascript/flavours/glitch/locales/fy.json
@@ -1 +1,206 @@
-{}
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/ga.json b/app/javascript/flavours/glitch/locales/ga.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ga.json
+++ b/app/javascript/flavours/glitch/locales/ga.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/gd.json b/app/javascript/flavours/glitch/locales/gd.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/gd.json
+++ b/app/javascript/flavours/glitch/locales/gd.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/gl.json b/app/javascript/flavours/glitch/locales/gl.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/gl.json
+++ b/app/javascript/flavours/glitch/locales/gl.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/he.json b/app/javascript/flavours/glitch/locales/he.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/he.json
+++ b/app/javascript/flavours/glitch/locales/he.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/hi.json b/app/javascript/flavours/glitch/locales/hi.json
index f6eb75f84..3af3b73e2 100644
--- a/app/javascript/flavours/glitch/locales/hi.json
+++ b/app/javascript/flavours/glitch/locales/hi.json
@@ -1,6 +1,7 @@
 {
   "about.fork_disclaimer": "ग्लिच-सोक एक मुफ्त और ओपन सोर्स सॉफ़्टवेर है जो मैस्टोडॉन से फोर्क किया गया है",
   "account.add_account_note": "@{name} के लिए कोई नोट लिखें",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
   "account.follows": "फ़ॉलोज़",
   "account.joined": "ज़ोईन करने की {date}",
   "account.suspended_disclaimer_full": "यह यूज़र एक मॉडरेटर द्वारा सस्पेंड कर दिया गया है",
@@ -11,8 +12,195 @@
   "account_note.save": "सेव",
   "advanced_options.icon_title": "एडवांस्ड ऑप्शन्स",
   "advanced_options.local-only.long": "दूसरे इंस्टेंसों में पोस्ट ना करें",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/hr.json b/app/javascript/flavours/glitch/locales/hr.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/hr.json
+++ b/app/javascript/flavours/glitch/locales/hr.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/hu.json b/app/javascript/flavours/glitch/locales/hu.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/hu.json
+++ b/app/javascript/flavours/glitch/locales/hu.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/hy.json b/app/javascript/flavours/glitch/locales/hy.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/hy.json
+++ b/app/javascript/flavours/glitch/locales/hy.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/id.json b/app/javascript/flavours/glitch/locales/id.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/id.json
+++ b/app/javascript/flavours/glitch/locales/id.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ig.json b/app/javascript/flavours/glitch/locales/ig.json
index 0967ef424..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ig.json
+++ b/app/javascript/flavours/glitch/locales/ig.json
@@ -1 +1,206 @@
-{}
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/io.json b/app/javascript/flavours/glitch/locales/io.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/io.json
+++ b/app/javascript/flavours/glitch/locales/io.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/is.json b/app/javascript/flavours/glitch/locales/is.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/is.json
+++ b/app/javascript/flavours/glitch/locales/is.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/it.json b/app/javascript/flavours/glitch/locales/it.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/it.json
+++ b/app/javascript/flavours/glitch/locales/it.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ja.json b/app/javascript/flavours/glitch/locales/ja.json
index 610cd7525..5a5365b13 100644
--- a/app/javascript/flavours/glitch/locales/ja.json
+++ b/app/javascript/flavours/glitch/locales/ja.json
@@ -1,7 +1,9 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
   "account.add_account_note": "@{name}のメモを追加",
   "account.disclaimer_full": "このユーザー情報は不正確な可能性があります。",
   "account.follows": "フォロー",
+  "account.joined": "Joined {date}",
   "account.suspended_disclaimer_full": "このユーザーはモデレータにより停止されました。",
   "account.view_full_profile": "正確な情報を見る",
   "account_note.cancel": "キャンセル",
@@ -16,16 +18,26 @@
   "advanced_options.threaded_mode.short": "スレッドモード",
   "advanced_options.threaded_mode.tooltip": "スレッドモードを有効にする",
   "boost_modal.missing_description": "このトゥートには少なくとも1つの画像に説明が付与されていません",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "その他",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "その他のオプション",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "リスト",
+  "column_subheading.navigation": "ナビゲーション",
   "community.column_settings.allow_local_only": "ローカル限定投稿を表示する",
   "compose.attach": "添付...",
   "compose.attach.doodle": "お絵描きをする",
   "compose.attach.upload": "ファイルをアップロード",
+  "compose.content-type.html": "HTML",
   "compose.content-type.markdown": "マークダウン",
   "compose.content-type.plain": "プレーンテキスト",
   "compose_form.poll.multiple_choices": "複数回答を許可",
   "compose_form.poll.single_choice": "単一回答を許可",
   "compose_form.spoiler": "本文は警告の後ろに隠す",
   "confirmation_modal.do_not_ask_again": "もう1度尋ねない",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
   "confirmations.missing_media_description.confirm": "このまま投稿",
   "confirmations.missing_media_description.edit": "メディアを編集",
   "confirmations.missing_media_description.message": "少なくとも1つの画像に視覚障害者のための画像説明が付与されていません。すべての画像に対して説明を付与することを望みます。",
@@ -34,35 +46,67 @@
   "confirmations.unfilter.edit_filter": "フィルターを編集",
   "confirmations.unfilter.filters": "適用されたフィルター",
   "content-type.change": "コンテンツ形式を変更",
+  "direct.group_by_conversations": "Group by conversation",
   "endorsed_accounts_editor.endorsed_accounts": "紹介しているユーザー",
   "favourite_modal.combo": "次からは {combo} を押せば、これをスキップできます。",
   "getting_started.onboarding": "解説を表示",
   "home.column_settings.advanced": "高度",
   "home.column_settings.filter_regex": "正規表現でフィルター",
   "home.column_settings.show_direct": "DMを表示",
+  "home.settings": "Column settings",
   "keyboard_shortcuts.bookmark": "ブックマーク",
   "keyboard_shortcuts.secondary_toot": "セカンダリートゥートの公開範囲でトゥートする",
   "keyboard_shortcuts.toggle_collapse": "折りたたむ/折りたたみを解除",
   "layout.auto": "自動",
   "layout.desktop": "デスクトップ",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
   "layout.single": "モバイル",
+  "media_gallery.sensitive": "Sensitive",
   "moved_to_warning": "このアカウント{moved_to_link}に引っ越したため、新しいフォロワーを受け入れていません。",
   "navigation_bar.app_settings": "アプリ設定",
   "navigation_bar.featured_users": "紹介しているアカウント",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
   "navigation_bar.misc": "その他",
   "notification.markForDeletion": "選択",
   "notification_purge.btn_all": "すべて\n選択",
   "notification_purge.btn_apply": "選択したものを\n削除",
   "notification_purge.btn_invert": "選択を\n反転",
   "notification_purge.btn_none": "選択\n解除",
+  "notification_purge.start": "Enter notification cleaning mode",
   "notifications.marked_clear": "選択した通知を削除する",
   "notifications.marked_clear_confirmation": "削除した全ての通知を完全に削除してもよろしいですか?",
+  "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": "{domain}はMastodonのインスタンスです。Mastodonとは、独立したサーバが連携して作るソーシャルネットワークです。これらのサーバーをインスタンスと呼びます。",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
   "onboarding.page_one.welcome": "{domain}へようこそ!",
+  "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": "{domain}はGlitchsocを使用しています。Glitchsocは{Mastodon}のフレンドリーな{fork}で、どんなMastodonアプリやインスタンスとも互換性があります。Glitchsocは完全に無料で、オープンソースです。{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",
+  "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",
   "settings.always_show_spoilers_field": "常にコンテンツワーニング設定を表示する(指定がない場合は通常投稿)",
   "settings.auto_collapse": "自動折りたたみ",
   "settings.auto_collapse_all": "すべて",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
   "settings.auto_collapse_lengthy": "長いトゥート",
   "settings.auto_collapse_media": "メディア付きトゥート",
   "settings.auto_collapse_notifications": "通知",
@@ -77,13 +121,21 @@
   "settings.content_warnings": "コンテンツワーニング",
   "settings.content_warnings.regexp": "正規表現",
   "settings.content_warnings_filter": "説明に指定した文字が含まれているものを自動で展開しないようにする",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
   "settings.enable_collapsed": "トゥート折りたたみを有効にする",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
   "settings.enable_content_warnings_auto_unfold": "コンテンツワーニング指定されている投稿を常に表示する",
   "settings.general": "一般",
   "settings.hicolor_privacy_icons": "ハイカラーの公開範囲アイコン",
   "settings.hicolor_privacy_icons.hint": "公開範囲アイコンを明るく表示し見分けやすい色にします",
   "settings.image_backgrounds": "画像背景",
   "settings.image_backgrounds_media": "折りたまれたメディア付きトゥートをプレビュー",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
   "settings.image_backgrounds_users": "折りたまれたトゥートの背景を変更する",
   "settings.inline_preview_cards": "外部リンクに埋め込みプレビューを有効にする",
   "settings.layout": "レイアウト",
@@ -91,9 +143,12 @@
   "settings.media": "メディア",
   "settings.media_fullwidth": "全幅メディアプレビュー",
   "settings.media_letterbox": "メディアをレターボックス式で表示",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
   "settings.media_reveal_behind_cw": "既定で警告指定されているトゥートの閲覧注意メディアを表示する",
   "settings.notifications.favicon_badge": "通知アイコンに未読件数を表示する",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
   "settings.notifications.tab_badge": "未読の通知があるとき、通知アイコンにマークを表示する",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
   "settings.notifications_opts": "通知の設定",
   "settings.pop_in_left": "左",
   "settings.pop_in_player": "ポップインプレイヤーを有効化する",
@@ -102,10 +157,12 @@
   "settings.preferences": "ユーザー設定",
   "settings.prepend_cw_re": "返信するとき警告に \"re: \"を付加する",
   "settings.preselect_on_reply": "返信するときユーザー名を事前選択する",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
   "settings.rewrite_mentions": "表示されたトゥートの返信先表示を書き換える",
   "settings.rewrite_mentions_acct": "ユーザー名とドメイン名(アカウントがリモートの場合)を表示するように書き換える",
   "settings.rewrite_mentions_no": "書き換えない",
   "settings.rewrite_mentions_username": "ユーザー名を表示するように書き換える",
+  "settings.shared_settings_link": "user preferences",
   "settings.show_action_bar": "アクションバーを表示",
   "settings.show_content_type_choice": "トゥートを書くときコンテンツ形式の選択ボタンを表示する",
   "settings.show_reply_counter": "投稿に対するリプライの数を表示する",
@@ -115,10 +172,35 @@
   "settings.side_arm_reply_mode.copy": "返信先の投稿範囲を利用する",
   "settings.side_arm_reply_mode.keep": "セカンダリートゥートボタンの設定を維持する",
   "settings.side_arm_reply_mode.restrict": "返信先の投稿範囲に制限する",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
   "settings.swipe_to_change_columns": "スワイプでカラムを切り替え可能にする(モバイルのみ)",
   "settings.tag_misleading_links": "誤解を招くリンクにタグをつける",
   "settings.tag_misleading_links.hint": "明示的に言及していないすべてのリンクに、リンクターゲットホストを含む視覚的な表示を追加します",
   "settings.wide_view": "ワイドビュー(デスクトップ レイアウトのみ)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
   "status.collapse": "折りたたむ",
-  "status.uncollapse": "折りたたみを解除"
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "折りたたみを解除",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ka.json b/app/javascript/flavours/glitch/locales/ka.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ka.json
+++ b/app/javascript/flavours/glitch/locales/ka.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/kab.json b/app/javascript/flavours/glitch/locales/kab.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/kab.json
+++ b/app/javascript/flavours/glitch/locales/kab.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/kk.json b/app/javascript/flavours/glitch/locales/kk.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/kk.json
+++ b/app/javascript/flavours/glitch/locales/kk.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/kn.json b/app/javascript/flavours/glitch/locales/kn.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/kn.json
+++ b/app/javascript/flavours/glitch/locales/kn.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ko.json b/app/javascript/flavours/glitch/locales/ko.json
index f17f32a9d..2b4f22c19 100644
--- a/app/javascript/flavours/glitch/locales/ko.json
+++ b/app/javascript/flavours/glitch/locales/ko.json
@@ -67,7 +67,6 @@
   "moved_to_warning": "이 계정은 {moved_to_link}로 이동한 것으로 표시되었고, 새 팔로우를 받지 않는 것 같습니다.",
   "navigation_bar.app_settings": "앱 설정",
   "navigation_bar.featured_users": "추천된 계정들",
-  "navigation_bar.info": "추가 정보",
   "navigation_bar.keyboard_shortcuts": "키보드 단축기",
   "navigation_bar.misc": "다양한 옵션들",
   "notification.markForDeletion": "삭제하기 위해 표시",
@@ -98,9 +97,16 @@
   "onboarding.page_three.search": "검색창을 사용해 사람들과 해시태그를 찾아보세요. 예를 들면 {illustration}이라든지 {introcustions} 같은 것으로요. 이 인스턴스에 있지 않은 사람을 찾으려면, 전체 핸들을 사용하세요.",
   "onboarding.page_two.compose": "작성 컬럼에서 게시물을 작성하세요. 그림을 업로드 할 수 있고, 공개설정을 바꿀 수도 있으며, 아래 아이콘을 통해 열람주의 텍스트를 설정할 수 있습니다.",
   "onboarding.skip": "건너뛰기",
+  "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",
   "settings.always_show_spoilers_field": "열람주의 항목을 언제나 활성화",
   "settings.auto_collapse": "자동으로 접기",
   "settings.auto_collapse_all": "모두",
+  "settings.auto_collapse_height": "길이가 긴 것으로 간주할 툿의 높이 (픽셀 단위)",
   "settings.auto_collapse_lengthy": "긴 글",
   "settings.auto_collapse_media": "미디어 포함 글",
   "settings.auto_collapse_notifications": "알림",
diff --git a/app/javascript/flavours/glitch/locales/ku.json b/app/javascript/flavours/glitch/locales/ku.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ku.json
+++ b/app/javascript/flavours/glitch/locales/ku.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/kw.json b/app/javascript/flavours/glitch/locales/kw.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/kw.json
+++ b/app/javascript/flavours/glitch/locales/kw.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/la.json b/app/javascript/flavours/glitch/locales/la.json
index 0967ef424..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/la.json
+++ b/app/javascript/flavours/glitch/locales/la.json
@@ -1 +1,206 @@
-{}
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/lt.json b/app/javascript/flavours/glitch/locales/lt.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/lt.json
+++ b/app/javascript/flavours/glitch/locales/lt.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/lv.json b/app/javascript/flavours/glitch/locales/lv.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/lv.json
+++ b/app/javascript/flavours/glitch/locales/lv.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/mk.json b/app/javascript/flavours/glitch/locales/mk.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/mk.json
+++ b/app/javascript/flavours/glitch/locales/mk.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ml.json b/app/javascript/flavours/glitch/locales/ml.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ml.json
+++ b/app/javascript/flavours/glitch/locales/ml.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/mr.json b/app/javascript/flavours/glitch/locales/mr.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/mr.json
+++ b/app/javascript/flavours/glitch/locales/mr.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ms.json b/app/javascript/flavours/glitch/locales/ms.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ms.json
+++ b/app/javascript/flavours/glitch/locales/ms.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/my.json b/app/javascript/flavours/glitch/locales/my.json
index 0967ef424..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/my.json
+++ b/app/javascript/flavours/glitch/locales/my.json
@@ -1 +1,206 @@
-{}
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/nl.json b/app/javascript/flavours/glitch/locales/nl.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/nl.json
+++ b/app/javascript/flavours/glitch/locales/nl.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/nn.json b/app/javascript/flavours/glitch/locales/nn.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/nn.json
+++ b/app/javascript/flavours/glitch/locales/nn.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/no.json b/app/javascript/flavours/glitch/locales/no.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/no.json
+++ b/app/javascript/flavours/glitch/locales/no.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/oc.json b/app/javascript/flavours/glitch/locales/oc.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/oc.json
+++ b/app/javascript/flavours/glitch/locales/oc.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/pa.json b/app/javascript/flavours/glitch/locales/pa.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/pa.json
+++ b/app/javascript/flavours/glitch/locales/pa.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/pl.json b/app/javascript/flavours/glitch/locales/pl.json
index 09acd098e..d28c62693 100644
--- a/app/javascript/flavours/glitch/locales/pl.json
+++ b/app/javascript/flavours/glitch/locales/pl.json
@@ -1,4 +1,15 @@
 {
+  "about.fork_disclaimer": "Glitch-soc jest wolnym i otwartym oprogramowaniem wywodzącym się z Mastodonu.",
+  "account.add_account_note": "Dodaj notatkę dla @{name}",
+  "account.disclaimer_full": "Poniższe informacje mogą niekompletnie odzwierciedlać profil tego użytkownika.",
+  "account.follows": "Obserwuje",
+  "account.joined": "Konto utworzono {date}",
+  "account.suspended_disclaimer_full": "Użytkownik został zawieszony przez moderatora.",
+  "account.view_full_profile": "Pokaż pełny profil",
+  "account_note.cancel": "Anuluj",
+  "account_note.edit": "Edytuj",
+  "account_note.glitch_placeholder": "Brak komentarza",
+  "account_note.save": "Zapisz",
   "advanced_options.icon_title": "Ustawienia zaawansowane",
   "advanced_options.local-only.long": "Nie wysyłaj na inne instancje",
   "advanced_options.local-only.short": "Tylko lokalnie",
@@ -6,17 +17,58 @@
   "advanced_options.threaded_mode.long": "Przechodzi do tworzenia odpowiedzi po publikacji wpisu",
   "advanced_options.threaded_mode.short": "Tryb wątków",
   "advanced_options.threaded_mode.tooltip": "Włączono tryb wątków",
+  "boost_modal.missing_description": "Ten wpis zawiera multimedialne załączniki bez opisu",
+  "column.favourited_by": "Polubiony przez",
+  "column.heading": "Różne",
+  "column.reblogged_by": "Podbity przez",
+  "column.subheading": "Różne opcje",
+  "column_header.profile": "Profil",
+  "column_subheading.lists": "Listy",
+  "column_subheading.navigation": "Nawigacja",
+  "community.column_settings.allow_local_only": "Pokazuj wyłącznie wpisy lokalne",
   "compose.attach": "Załącz coś",
   "compose.attach.doodle": "Narysuj coś",
   "compose.attach.upload": "Wyślij plik",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Czysty tekst",
+  "compose_form.poll.multiple_choices": "Pozwól na wybór wielokrotny",
+  "compose_form.poll.single_choice": "Pozwól na tylko jeden wybór",
   "compose_form.spoiler": "Ukryj tekst za ostrzeżeniem",
+  "confirmation_modal.do_not_ask_again": "Więcej nie pytaj się o potwierdzenie",
+  "confirmations.deprecated_settings.confirm": "Użyj preferencji Mastodonu",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Zignoruj i wyślij",
+  "confirmations.missing_media_description.edit": "Edytuj załącznik multimedialny",
+  "confirmations.missing_media_description.message": "Co najmniej jednemu załącznikowi multimedialnemu brakuje opisu. Z uwagi na osoby z zaburzeniami widzenia rozważ opisanie wszystkich załączników przed opublikowaniem wpisu.",
+  "confirmations.unfilter.author": "Autor",
+  "confirmations.unfilter.confirm": "Pokaż",
+  "confirmations.unfilter.edit_filter": "Edytuj filtr",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Typ zawartości",
+  "direct.group_by_conversations": "Grupuj rozmowami",
+  "endorsed_accounts_editor.endorsed_accounts": "Wybrane konta",
   "favourite_modal.combo": "Możesz nacisnąć {combo}, aby pominąć to następnym razem",
   "getting_started.onboarding": "Rozejrzyj się",
+  "home.column_settings.advanced": "Zaawansowane",
+  "home.column_settings.filter_regex": "Filtruj, używając wyrażeń regularnych",
   "home.column_settings.show_direct": "Pokaż wiadomości bezpośrednie",
+  "home.settings": "Ustawienia kolumn",
+  "keyboard_shortcuts.bookmark": "aby dodać do ulubionych",
+  "keyboard_shortcuts.secondary_toot": "aby opublikować wpis używając dodatkowych ustawień prywatności",
+  "keyboard_shortcuts.toggle_collapse": "aby zwinąć/rozwinąć wpisy",
   "layout.auto": "Automatyczny",
   "layout.desktop": "Desktopowy",
+  "layout.hint.auto": "Automatycznie wybierz układ na podstawie ustawienia „Włącz zaawansowany interfejs użytkownika” i rozmiaru ekranu.",
+  "layout.hint.desktop": "Użyj układu wielokolumnowego niezależnie od ustawienia „Włącz zaawansowany interfejs użytkownika” i rozmiaru ekranu.",
+  "layout.hint.single": "Użyj układu jednokolumnowego niezależnie od ustawienia „Włącz zaawansowany interfejs użytkownika” i rozmiaru ekranu.",
+  "layout.single": "Mobilny",
   "media_gallery.sensitive": "Zawartość wrażliwa",
+  "moved_to_warning": "To konto oznaczone jest jako przeniesione do {moved_to_link} i może z tego powodu nie akceptować nowych obserwujących.",
   "navigation_bar.app_settings": "Ustawienia aplikacji",
+  "navigation_bar.featured_users": "Użytkownicy wyróżnieni",
+  "navigation_bar.keyboard_shortcuts": "Skróty klawiszowe",
+  "navigation_bar.misc": "Różne",
   "notification.markForDeletion": "Oznacz do usunięcia",
   "notification_purge.btn_all": "Zaznacz\nwszystkie",
   "notification_purge.btn_apply": "Usuń\nzaznaczone",
@@ -25,11 +77,36 @@
   "notification_purge.start": "Przejdź do trybu usuwania powiadomień",
   "notifications.marked_clear": "Usuń zaznaczone powiadomienia",
   "notifications.marked_clear_confirmation": "Czy na pewno chcesz bezpowrtonie usunąć wszystkie powiadomienia?",
+  "onboarding.done": "Zakończ",
+  "onboarding.next": "Następny",
+  "onboarding.page_five.public_timelines": "Lokalna oś czasu pokazuje publiczne posty wszystkich użytkowników {domain}. Globalna oś czasu pokazuje publiczne posty wszystkich użytkowników obserwowanych przez osoby z {domain}. Te publiczne osi czasu są dobrą metodą na poznawanie nowych ludzi.",
+  "onboarding.page_four.home": "Domowa oś czasowa pokazuje wpisy ludzi, których obserwujesz.",
+  "onboarding.page_four.notifications": "Kolumna powiadomień pokazuje interakcje innych z tobą.",
   "onboarding.page_one.federation": "{domain} jest 'instancją' Mastodona. Mastodon to sieć działających niezależnie serwerów tworzących jedną sieć społecznościową. Te serwery nazywane są instancjami.",
+  "onboarding.page_one.handle": "Jesteś na serwerze {domain}, więc twój pełny adres to {handle}",
   "onboarding.page_one.welcome": "Witamy na {domain}!",
+  "onboarding.page_six.admin": "Administratorem twojego serwera jest {admin}.",
+  "onboarding.page_six.almost_done": "Prawie gotowe…",
+  "onboarding.page_six.appetoot": "Bon Appetoot!",
+  "onboarding.page_six.apps_available": "Na Android, iOS i inne systemy są dostępne {apps}.",
   "onboarding.page_six.github": "{domain} jest oparty na Glitchsoc. Glitchsoc jest {forkiem} {Mastodon}a kompatybilnym z każdym klientem i aplikacją Mastodona. Glitchsoc jest całkowicie wolnym i otwartoźródłowym oprogramowaniem. Możesz zgłaszać błędy i sugestie funkcji oraz współtworzyć projekt na {github}.",
+  "onboarding.page_six.guidelines": "wytyczne społeczności",
+  "onboarding.page_six.read_guidelines": "Proszę przeczytać {guidelines} {domain}!",
+  "onboarding.page_six.various_app": "aplikacje mobilne",
+  "onboarding.page_three.profile": "Edytuj Twój profil, aby zmienić awatar, biogram i widoczną nazwę. Znajdziesz tam również inne ustawienia.",
+  "onboarding.page_three.search": "Użyj paska wyszukiwania aby znaleźć osoby i hasztagi, takie jak {illustration} i {introductions}. Aby znaleźć osobę niebędącą na tym serwerze użyj jej pełnego adresu.",
+  "onboarding.page_two.compose": "Twórz nowe wpisy w lewej kolumnie. Możesz wysłać zdjęcia, zmienić ustawienia prywatności i ukryć wpis za ostrzeżeniem używając poniższych ikon.",
+  "onboarding.skip": "Pomiń",
+  "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",
+  "settings.always_show_spoilers_field": "Zawsze pokazuj pole ostrzeżenia o zawartości",
   "settings.auto_collapse": "Automatyczne zwijanie",
   "settings.auto_collapse_all": "Wszystko",
+  "settings.auto_collapse_height": "Wysokość (w pikselach) powyżej której wpis będzie uznawany za długi",
   "settings.auto_collapse_lengthy": "Długie wpisy",
   "settings.auto_collapse_media": "Wpisy z zawartością multimedialną",
   "settings.auto_collapse_notifications": "Powiadomienia",
@@ -37,19 +114,93 @@
   "settings.auto_collapse_replies": "Odpowiedzi",
   "settings.close": "Zamknij",
   "settings.collapsed_statuses": "Zwijanie wpisów",
+  "settings.compose_box_opts": "Pole edycji",
+  "settings.confirm_before_clearing_draft": "Wymuś potwierdzenie przez nadpisaniem aktualnie edytowanego wpisu",
+  "settings.confirm_boost_missing_media_description": "Wymuś potwierdzenie przed podbiciem wpisów z brakującym opisem załączników multimedialnych",
+  "settings.confirm_missing_media_description": "Wymuś potwierdzenie przed opublikowaniem wpisu z brakującymi opisami załączników multimedialnych",
   "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Wyrażenie regularne",
+  "settings.content_warnings_filter": "Ostrzeżenia o zawartości nieodkrywane automatycznie:",
+  "settings.content_warnings_media_outside": "Wyświetlaj załączniki multimedialne poza ostrzeżeniem o zawartości",
+  "settings.content_warnings_media_outside_hint": "Nie ukrywaj załączników multimedialnych, gdy wpis jest ukryty za ostrzeżeniem, tak jak robi to niezmodyfikowany Mastodon",
+  "settings.content_warnings_shared_state": "Pokaż/ukryj zawartość wszystkich kopii jednocześnie",
+  "settings.content_warnings_shared_state_hint": "Zachowaj się tak, jak niezmodyfikowany Mastodon, tj. wymuś działanie przycisku ostrzeżenia o zawartości na wszystkie kopie danego wpisu. Włączenie tego ustawienia spowoduje wyłączenie automatycznego zwijania kopii wpisów z odkrytym ostrzeżeniem o zawartości.",
+  "settings.content_warnings_unfold_opts": "Opcje automatycznego odkrywania",
+  "settings.deprecated_setting": "To ustawienie jest teraz kontrolowane przez {settings_page_link}",
   "settings.enable_collapsed": "Włącz zwijanie wpisów",
+  "settings.enable_collapsed_hint": "Zwinięte wpisy są częściowo ukryte, przez co zajmują mniej miejsca. Ta opcja różni się od ukrywania wpisów za ostrzeżeniem",
+  "settings.enable_content_warnings_auto_unfold": "Automatycznie odkrywaj wpisy ukryte za ostrzeżeniem",
   "settings.general": "Ogólne",
+  "settings.hicolor_privacy_icons": "Ikony ustawień prywatności o jaskrawych kolorach",
+  "settings.hicolor_privacy_icons.hint": "Wyświetl ikony ustawień prywatności używając łatwo rozróżnialnych kolorów",
   "settings.image_backgrounds": "Obrazy w tle",
   "settings.image_backgrounds_media": "Wyświetlaj zawartość multimedialną zwiniętych wpisów",
+  "settings.image_backgrounds_media_hint": "Jeśli wpis ma co najmniej jeden załącznik multimedialny, użyj pierwszego z nich, jako tła.",
   "settings.image_backgrounds_users": "Nadaj tło zwiniętym wpisom",
+  "settings.inline_preview_cards": "Karty podglądu zewnętrznych linków w tekście",
   "settings.layout": "Układ",
+  "settings.layout_opts": "Opcje układu",
   "settings.media": "Zawartość multimedialna",
   "settings.media_fullwidth": "Podgląd zawartości multimedialnej o pełnej szerokości",
+  "settings.media_letterbox": "Dopasuj proporcje multimedialnych załączników",
+  "settings.media_letterbox_hint": "Przeskaluj multimedialne załączniki w sposób umożliwiający zachowanie proporcji.",
+  "settings.media_reveal_behind_cw": "Domyślnie odkrywaj załączniki multimedialne wpisów ukrytych za ostrzeżeniem",
+  "settings.notifications.favicon_badge": "Znacznik nieprzeczytanych powiadomień ikony ulubionych",
+  "settings.notifications.favicon_badge.hint": "Dodaj znacznik nieprzeczytanych powiadomień do ikony ulubionych.",
+  "settings.notifications.tab_badge": "Znacznik nieprzeczytanych powiadomień",
+  "settings.notifications.tab_badge.hint": "Dodaj znacznik nieprzeczytanych powiadomień do ikon kolumn, gdy kolumna powiadomień jest zamknięta.",
+  "settings.notifications_opts": "Opcje powiadomień",
+  "settings.pop_in_left": "Po lewej",
+  "settings.pop_in_player": "Włącz odtwarzacz w wyskakującym okienku",
+  "settings.pop_in_position": "Pozycja wyskakującego okienka:",
+  "settings.pop_in_right": "Po prawej",
   "settings.preferences": "Preferencje użytkownika",
+  "settings.prepend_cw_re": "Dodaj „re: ” na początku ostrzeżenia o zawartości podczas odpowiadania na wpis z ostrzeżeniem",
+  "settings.preselect_on_reply": "Automatycznie wybierz adresy podczas odpowiadania",
+  "settings.preselect_on_reply_hint": "Podczas odpowiadania w rozmowie z kilkoma uczestnikami automatycznie wybierz adresy inne niż pierwszy.",
+  "settings.rewrite_mentions": "Przerabianie nawiązań w wyświetlonych statusach",
+  "settings.rewrite_mentions_acct": "Przerób na pełny adres, gdy konto jest z innego serwera",
+  "settings.rewrite_mentions_no": "Nie przerabiaj",
+  "settings.rewrite_mentions_username": "Przerób na nazwę użytkownika",
+  "settings.shared_settings_link": "ustawienia użytkownika",
+  "settings.show_action_bar": "Pokazuj przyciski akcji pod zwiniętymi wpisami",
+  "settings.show_content_type_choice": "Podczas tworzenia wpisów umożliw wybór typu zawartości",
+  "settings.show_reply_counter": "Wyświetl szacowaną ilości odpowiedzi",
   "settings.side_arm": "Drugi przycisk wysyłania",
   "settings.side_arm.none": "Żaden",
+  "settings.side_arm_reply_mode": "Podczas odpowiadania na wpis, dodatkowy przycisk publikowania powinien:",
+  "settings.side_arm_reply_mode.copy": "Powielić ustawienia prywatności wpisu, na który publikowana jest odpowiedź",
+  "settings.side_arm_reply_mode.keep": "Zachować wcześniej ustawiony tryb prywatności",
+  "settings.side_arm_reply_mode.restrict": "Ograniczyć ustawienia prywatności do tych używanych przez wpis, na który publikowana jest odpowiedź",
+  "settings.status_icons": "Ikony wpisów",
+  "settings.status_icons_language": "Wskaźnik języka",
+  "settings.status_icons_local_only": "Wskaźnik wpisu lokalnego",
+  "settings.status_icons_media": "Wskaźniki załączników multimedialnych i ankiet",
+  "settings.status_icons_reply": "Wskaźnik odpowiedzi",
+  "settings.status_icons_visibility": "Wskaźnik ustawień prywatności wpisu",
+  "settings.swipe_to_change_columns": "W wypadku wersji mobilnej pozwól na zmianę kolumny przez przesunięcie palcem",
+  "settings.tag_misleading_links": "Oznacz mylące linki",
+  "settings.tag_misleading_links.hint": "Dodaj oznaczenie domeny do każdego linku, który nie ma jej w swojej treści",
   "settings.wide_view": "Szeroki widok (tylko w trybie desktopowym)",
+  "settings.wide_view_hint": "Wykorzystaj więcej dostępnego miejsca, rozciągając kolumny.",
   "status.collapse": "Zwiń",
-  "status.uncollapse": "Rozwiń"
+  "status.has_audio": "Posiada załączone pliki dźwiękowe",
+  "status.has_pictures": "Posiada załączone obrazki",
+  "status.has_preview_card": "Posiada załączoną kartę podglądu",
+  "status.has_video": "Posiada załączone wideo",
+  "status.in_reply_to": "Ten wpis jest odpowiedzią",
+  "status.is_poll": "Ten wpis zawiera ankietę",
+  "status.local_only": "Widoczne tylko na twoim serwerze",
+  "status.sensitive_toggle": "Kliknij, aby zobaczyć",
+  "status.uncollapse": "Rozwiń",
+  "web_app_crash.change_your_settings": "Zmień swoje {settings}",
+  "web_app_crash.content": "Możesz spróbować:",
+  "web_app_crash.debug_info": "Informacje pomocne w debugowaniu",
+  "web_app_crash.disable_addons": "Wyłączyć dodatki Twojej przeglądarki lub wbudowane narzędzia do tłumaczenia",
+  "web_app_crash.issue_tracker": "stronie śledzenia błędów",
+  "web_app_crash.reload": "Odświeżyć",
+  "web_app_crash.reload_page": "{reload} tą stronę",
+  "web_app_crash.report_issue": "Zgłosić błąd na {issuetracker}",
+  "web_app_crash.settings": "ustawienia",
+  "web_app_crash.title": "Przepraszamy, ale coś jest nie tak z tą stroną Mastodonu."
 }
diff --git a/app/javascript/flavours/glitch/locales/pt-BR.json b/app/javascript/flavours/glitch/locales/pt-BR.json
index 0bc0d2bea..d2fefcbff 100644
--- a/app/javascript/flavours/glitch/locales/pt-BR.json
+++ b/app/javascript/flavours/glitch/locales/pt-BR.json
@@ -2,7 +2,7 @@
   "about.fork_disclaimer": "O Glitch-soc é um software gratuito de código aberto bifurcado a partir do Mastodon.",
   "account.add_account_note": "Adicionar nota para @{name}",
   "account.disclaimer_full": "As informações abaixo podem refletir o perfil do usuário de forma incompleta.",
-  "account.follows": "Seguidores",
+  "account.follows": "Segue",
   "account.joined": "Entrou em {date}",
   "account.suspended_disclaimer_full": "Este usuário foi suspenso por um moderador.",
   "account.view_full_profile": "Ver o perfil completo",
@@ -67,7 +67,6 @@
   "moved_to_warning": "Esta conta foi como movida para {moved_to_link} e, portanto, pode não aceitar novos seguidores.",
   "navigation_bar.app_settings": "Configurações do aplicativo",
   "navigation_bar.featured_users": "Usuários em destaque",
-  "navigation_bar.info": "Informação estendida",
   "navigation_bar.keyboard_shortcuts": "Atalhos de teclado",
   "navigation_bar.misc": "Diversos",
   "notification.markForDeletion": "Marcar para exclusão",
@@ -98,9 +97,16 @@
   "onboarding.page_three.search": "Use a barra de busca para encontrar pessoas e procure hashtags, tais como {illustration} e {introductions}. Para procurar uma pessoa que não esteja neste caso, use o identificador completo.",
   "onboarding.page_two.compose": "Escreva as postagens a partir da coluna de composição. Você pode enviar imagens, alterar as configurações de privacidade e adicionar avisos de conteúdo com os ícones abaixo.",
   "onboarding.skip": "Pular",
+  "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",
   "settings.always_show_spoilers_field": "Sempre ativar o campo Aviso de Conteúdo",
   "settings.auto_collapse": "Colapso automático",
   "settings.auto_collapse_all": "Tudo",
+  "settings.auto_collapse_height": "Altura (em pixels) para um toot ser considerado longo",
   "settings.auto_collapse_lengthy": "Toots longos",
   "settings.auto_collapse_media": "Toots com mídia",
   "settings.auto_collapse_notifications": "Notificações",
@@ -112,7 +118,7 @@
   "settings.confirm_before_clearing_draft": "Mostrar diálogo de confirmação antes de sobrescrever a mensagem que está sendo composta",
   "settings.confirm_boost_missing_media_description": "Mostrar diálogo antes de inpulsionar os toots sem descrições de mídia",
   "settings.confirm_missing_media_description": "Mostrar diálogo antes de enviar toots sem descrições de mídia",
-  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings": "Aviso de Conteúdo",
   "settings.content_warnings.regexp": "Expressão regular",
   "settings.content_warnings_filter": "Avisos de conteúdo para não revelar automaticamente:",
   "settings.content_warnings_media_outside": "Exibir anexos de mídia fora avisos de conteúdo",
@@ -148,7 +154,7 @@
   "settings.pop_in_player": "Ativar player pop-in",
   "settings.pop_in_position": "Posição do player:",
   "settings.pop_in_right": "Direita",
-  "settings.preferences": "Preferences",
+  "settings.preferences": "Preferências do usuário",
   "settings.prepend_cw_re": "Preparar \"re: \" para avisos de conteúdo quando responder",
   "settings.preselect_on_reply": "Nome de usuário pré-selecionado na resposta",
   "settings.preselect_on_reply_hint": "Ao responder a uma conversa com vários participantes, pré-selecionar nomes de usuários após o primeiro",
diff --git a/app/javascript/flavours/glitch/locales/pt-PT.json b/app/javascript/flavours/glitch/locales/pt-PT.json
index 4d243f94c..9fc3d05b4 100644
--- a/app/javascript/flavours/glitch/locales/pt-PT.json
+++ b/app/javascript/flavours/glitch/locales/pt-PT.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "O Glitch-soc é um software livre de código aberto, derivado (fork) do Mastodon.",
+  "account.add_account_note": "Juntar uma nota sobre @{name}",
+  "account.disclaimer_full": "As informações abaixo podem não refletir completamente o perfil do utilizador.",
+  "account.follows": "A seguir",
+  "account.joined": "Juntou-se em {date}",
+  "account.suspended_disclaimer_full": "Este utilizador foi suspenso por um elemento da moderação.",
+  "account.view_full_profile": "Ver o perfil completo",
+  "account_note.cancel": "Cancelar",
+  "account_note.edit": "Editar",
+  "account_note.glitch_placeholder": "Nenhum comentário dado",
+  "account_note.save": "Gravar",
+  "advanced_options.icon_title": "Opções avançadas",
+  "advanced_options.local-only.long": "Não publicar noutras instâncias",
+  "advanced_options.local-only.short": "Apenas local",
+  "advanced_options.local-only.tooltip": "Este post é apenas local",
+  "advanced_options.threaded_mode.long": "Abrir automaticamente uma resposta ao publicar",
+  "advanced_options.threaded_mode.short": "Modo de fio",
+  "advanced_options.threaded_mode.tooltip": "Modo de fio ativado",
+  "boost_modal.missing_description": "Este post contém alguns media sem descrição",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ro.json b/app/javascript/flavours/glitch/locales/ro.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ro.json
+++ b/app/javascript/flavours/glitch/locales/ro.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ru.json b/app/javascript/flavours/glitch/locales/ru.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ru.json
+++ b/app/javascript/flavours/glitch/locales/ru.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/sa.json b/app/javascript/flavours/glitch/locales/sa.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/sa.json
+++ b/app/javascript/flavours/glitch/locales/sa.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/sc.json b/app/javascript/flavours/glitch/locales/sc.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/sc.json
+++ b/app/javascript/flavours/glitch/locales/sc.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/sco.json b/app/javascript/flavours/glitch/locales/sco.json
index 0967ef424..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/sco.json
+++ b/app/javascript/flavours/glitch/locales/sco.json
@@ -1 +1,206 @@
-{}
+{
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
+  "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
+}
diff --git a/app/javascript/flavours/glitch/locales/si.json b/app/javascript/flavours/glitch/locales/si.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/si.json
+++ b/app/javascript/flavours/glitch/locales/si.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/sk.json b/app/javascript/flavours/glitch/locales/sk.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/sk.json
+++ b/app/javascript/flavours/glitch/locales/sk.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/sl.json b/app/javascript/flavours/glitch/locales/sl.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/sl.json
+++ b/app/javascript/flavours/glitch/locales/sl.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/sq.json b/app/javascript/flavours/glitch/locales/sq.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/sq.json
+++ b/app/javascript/flavours/glitch/locales/sq.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/sr-Latn.json b/app/javascript/flavours/glitch/locales/sr-Latn.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/sr-Latn.json
+++ b/app/javascript/flavours/glitch/locales/sr-Latn.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/sr.json b/app/javascript/flavours/glitch/locales/sr.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/sr.json
+++ b/app/javascript/flavours/glitch/locales/sr.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/sv.json b/app/javascript/flavours/glitch/locales/sv.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/sv.json
+++ b/app/javascript/flavours/glitch/locales/sv.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/szl.json b/app/javascript/flavours/glitch/locales/szl.json
index 807ed8207..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/szl.json
+++ b/app/javascript/flavours/glitch/locales/szl.json
@@ -67,7 +67,6 @@
   "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
   "navigation_bar.app_settings": "App settings",
   "navigation_bar.featured_users": "Featured users",
-  "navigation_bar.info": "Extended information",
   "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
   "navigation_bar.misc": "Misc",
   "notification.markForDeletion": "Mark for deletion",
@@ -98,9 +97,16 @@
   "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",
+  "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",
   "settings.always_show_spoilers_field": "Always enable the Content Warning field",
   "settings.auto_collapse": "Automatic collapsing",
   "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
   "settings.auto_collapse_lengthy": "Lengthy toots",
   "settings.auto_collapse_media": "Toots with media",
   "settings.auto_collapse_notifications": "Notifications",
@@ -124,7 +130,6 @@
   "settings.enable_collapsed": "Enable collapsed toots",
   "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
   "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
-  "settings.filters": "Filters",
   "settings.general": "General",
   "settings.hicolor_privacy_icons": "High color privacy icons",
   "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
diff --git a/app/javascript/flavours/glitch/locales/ta.json b/app/javascript/flavours/glitch/locales/ta.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ta.json
+++ b/app/javascript/flavours/glitch/locales/ta.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/tai.json b/app/javascript/flavours/glitch/locales/tai.json
index 807ed8207..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/tai.json
+++ b/app/javascript/flavours/glitch/locales/tai.json
@@ -67,7 +67,6 @@
   "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
   "navigation_bar.app_settings": "App settings",
   "navigation_bar.featured_users": "Featured users",
-  "navigation_bar.info": "Extended information",
   "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
   "navigation_bar.misc": "Misc",
   "notification.markForDeletion": "Mark for deletion",
@@ -98,9 +97,16 @@
   "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",
+  "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",
   "settings.always_show_spoilers_field": "Always enable the Content Warning field",
   "settings.auto_collapse": "Automatic collapsing",
   "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
   "settings.auto_collapse_lengthy": "Lengthy toots",
   "settings.auto_collapse_media": "Toots with media",
   "settings.auto_collapse_notifications": "Notifications",
@@ -124,7 +130,6 @@
   "settings.enable_collapsed": "Enable collapsed toots",
   "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
   "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
-  "settings.filters": "Filters",
   "settings.general": "General",
   "settings.hicolor_privacy_icons": "High color privacy icons",
   "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
diff --git a/app/javascript/flavours/glitch/locales/te.json b/app/javascript/flavours/glitch/locales/te.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/te.json
+++ b/app/javascript/flavours/glitch/locales/te.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/th.json b/app/javascript/flavours/glitch/locales/th.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/th.json
+++ b/app/javascript/flavours/glitch/locales/th.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/tr.json b/app/javascript/flavours/glitch/locales/tr.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/tr.json
+++ b/app/javascript/flavours/glitch/locales/tr.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/tt.json b/app/javascript/flavours/glitch/locales/tt.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/tt.json
+++ b/app/javascript/flavours/glitch/locales/tt.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ug.json b/app/javascript/flavours/glitch/locales/ug.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ug.json
+++ b/app/javascript/flavours/glitch/locales/ug.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/uk.json b/app/javascript/flavours/glitch/locales/uk.json
index b21584659..1304732f4 100644
--- a/app/javascript/flavours/glitch/locales/uk.json
+++ b/app/javascript/flavours/glitch/locales/uk.json
@@ -1,29 +1,112 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
   "advanced_options.local-only.long": "Не дмухати це на інші сервери",
   "advanced_options.local-only.short": "Лише локальне",
   "advanced_options.local-only.tooltip": "Цей дмух лише локальний",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
   "compose.attach": "Вкласти...",
   "compose.attach.doodle": "Помалювати",
   "compose.attach.upload": "Завантажити сюди файл",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
   "favourite_modal.combo": "Ви можете натиснути {combo}, щоб пропустити це наступного разу",
   "getting_started.onboarding": "Шо тут",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
   "home.column_settings.show_direct": "Показати прямі повідомлення",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
   "layout.auto": "Автоматичний",
   "layout.desktop": "Настільний",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
   "media_gallery.sensitive": "Чутливі",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
   "navigation_bar.app_settings": "Налаштування програми",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
   "notification.markForDeletion": "Позначити для видалення",
   "notification_purge.btn_all": "Вибрати\nвсе",
   "notification_purge.btn_apply": "Очистити\nвибір",
   "notification_purge.btn_invert": "Інвертувати\nвибір",
   "notification_purge.btn_none": "Вибрати\nнічого",
+  "notification_purge.start": "Enter notification cleaning mode",
   "notifications.marked_clear": "Очистити вибрані сповіщення",
   "notifications.marked_clear_confirmation": "Ви впевнені, що хочете незворотньо очистити всі вибрані сповіщення?",
+  "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": "{domain} є сервером of Mastodon. Mastodon — мережа незалежних серверів, які працюють разом великою соціяльною мережою. Сервери Mastodon також називають „інстансами“.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
   "onboarding.page_one.welcome": "Ласкаво просимо до {domain}!",
+  "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": "{domain} використовує Glitchsoc. Glitchsoc — дружній {fork} {Mastodon}, сумісний з будь-яким сервером Mastodon або програмою для нього. Glitchsoc повністю вільний та відкритий. Повідомляти про баги, просити фічі, або працювати з кодом можна на {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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
   "settings.auto_collapse": "Автоматичне згортання",
   "settings.auto_collapse_all": "Все",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
   "settings.auto_collapse_lengthy": "Довгі дмухи",
   "settings.auto_collapse_media": "Дмухи з медіафайлами",
   "settings.auto_collapse_notifications": "Сповіщення",
@@ -31,18 +114,93 @@
   "settings.auto_collapse_replies": "Відповіді",
   "settings.close": "Закрити",
   "settings.collapsed_statuses": "Згорнуті дмухи",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
   "settings.enable_collapsed": "Увімкути згорнутання дмухів",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
   "settings.general": "Основне",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
   "settings.image_backgrounds": "Картинки на тлі",
   "settings.image_backgrounds_media": "Підглядати медіа зі схованих дмухів",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
   "settings.image_backgrounds_users": "Давати схованим дмухам тло-картинку",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
   "settings.media": "Медіа",
   "settings.media_fullwidth": "Показувати медіа повною шириною",
   "settings.media_letterbox": "Обрізати медіа",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
   "settings.preferences": "Користувацькі налаштування",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
   "settings.show_action_bar": "Показувати кнопки у згорнутих дмухах",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
   "settings.wide_view": "Широкий вид (тільки в режимі для комп'ютерів)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
   "status.collapse": "Згорнути",
-  "status.uncollapse": "Розгорнути"
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Розгорнути",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/ur.json b/app/javascript/flavours/glitch/locales/ur.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/ur.json
+++ b/app/javascript/flavours/glitch/locales/ur.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/vi.json b/app/javascript/flavours/glitch/locales/vi.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/vi.json
+++ b/app/javascript/flavours/glitch/locales/vi.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/whitelist_an.json b/app/javascript/flavours/glitch/locales/whitelist_an.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_an.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_be.json b/app/javascript/flavours/glitch/locales/whitelist_be.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_be.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_bs.json b/app/javascript/flavours/glitch/locales/whitelist_bs.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_bs.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_en-GB.json b/app/javascript/flavours/glitch/locales/whitelist_en-GB.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_en-GB.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_fo.json b/app/javascript/flavours/glitch/locales/whitelist_fo.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_fo.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_fr-QC.json b/app/javascript/flavours/glitch/locales/whitelist_fr-QC.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_fr-QC.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_fy.json b/app/javascript/flavours/glitch/locales/whitelist_fy.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_fy.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_ig.json b/app/javascript/flavours/glitch/locales/whitelist_ig.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_ig.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_la.json b/app/javascript/flavours/glitch/locales/whitelist_la.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_la.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_my.json b/app/javascript/flavours/glitch/locales/whitelist_my.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_my.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/whitelist_sco.json b/app/javascript/flavours/glitch/locales/whitelist_sco.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/flavours/glitch/locales/whitelist_sco.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/flavours/glitch/locales/zgh.json b/app/javascript/flavours/glitch/locales/zgh.json
index 807ed8207..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/zgh.json
+++ b/app/javascript/flavours/glitch/locales/zgh.json
@@ -67,7 +67,6 @@
   "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
   "navigation_bar.app_settings": "App settings",
   "navigation_bar.featured_users": "Featured users",
-  "navigation_bar.info": "Extended information",
   "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
   "navigation_bar.misc": "Misc",
   "notification.markForDeletion": "Mark for deletion",
@@ -98,9 +97,16 @@
   "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",
+  "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",
   "settings.always_show_spoilers_field": "Always enable the Content Warning field",
   "settings.auto_collapse": "Automatic collapsing",
   "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
   "settings.auto_collapse_lengthy": "Lengthy toots",
   "settings.auto_collapse_media": "Toots with media",
   "settings.auto_collapse_notifications": "Notifications",
@@ -124,7 +130,6 @@
   "settings.enable_collapsed": "Enable collapsed toots",
   "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
   "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
-  "settings.filters": "Filters",
   "settings.general": "General",
   "settings.hicolor_privacy_icons": "High color privacy icons",
   "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
diff --git a/app/javascript/flavours/glitch/locales/zh-CN.json b/app/javascript/flavours/glitch/locales/zh-CN.json
index 9b5a7713f..46a66c960 100644
--- a/app/javascript/flavours/glitch/locales/zh-CN.json
+++ b/app/javascript/flavours/glitch/locales/zh-CN.json
@@ -1,7 +1,9 @@
 {
+  "about.fork_disclaimer": "Glitch-soc是从Mastodon派生的免费开源软件。",
   "account.add_account_note": "为 @{name} 添加备注",
   "account.disclaimer_full": "以下信息可能无法完整代表你的个人资料。",
   "account.follows": "正在关注",
+  "account.joined": "在 {date} 加入",
   "account.suspended_disclaimer_full": "该用户已被封禁。",
   "account.view_full_profile": "查看完整资料",
   "account_note.cancel": "取消",
@@ -27,11 +29,15 @@
   "compose.attach": "附上...",
   "compose.attach.doodle": "画点什么",
   "compose.attach.upload": "上传文件",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
   "compose.content-type.plain": "纯文本",
   "compose_form.poll.multiple_choices": "允许多选",
   "compose_form.poll.single_choice": "允许单选",
   "compose_form.spoiler": "隐藏为内容警告",
   "confirmation_modal.do_not_ask_again": "下次不显示确认窗口",
+  "confirmations.deprecated_settings.confirm": "使用 Mastodon 偏好设置",
+  "confirmations.deprecated_settings.message": "您正使用的glitch-soc的特定于此设备的 {app_settings} 已被Mastodon {preferences} 替换,并将被覆盖:",
   "confirmations.missing_media_description.confirm": "确认",
   "confirmations.missing_media_description.edit": "编辑",
   "confirmations.missing_media_description.message": "你没有为一种或多种媒体撰写描述。请考虑为视障人士添加描述。",
@@ -40,6 +46,7 @@
   "confirmations.unfilter.edit_filter": "编辑过滤器",
   "confirmations.unfilter.filters": "应用 {count, plural, one {过滤器} other {过滤器}}",
   "content-type.change": "内容类型 ",
+  "direct.group_by_conversations": "以对话分组",
   "endorsed_accounts_editor.endorsed_accounts": "推荐用户",
   "favourite_modal.combo": "下次你可以按 {combo} 跳过这个",
   "getting_started.onboarding": "参观一下",
@@ -60,6 +67,7 @@
   "moved_to_warning": "此帐户已被标记为移至 {moved_to_link},并且似乎没有收到新关注者。",
   "navigation_bar.app_settings": "应用选项",
   "navigation_bar.featured_users": "推荐用户",
+  "navigation_bar.keyboard_shortcuts": "键盘快捷键",
   "navigation_bar.misc": "杂项",
   "notification.markForDeletion": "标记以删除",
   "notification_purge.btn_all": "全选",
@@ -89,9 +97,16 @@
   "onboarding.page_three.search": "使用搜索栏查找用户并查看标签,例如 #illustration 和 #introductions。要查找不在此实例中的用户,请使用他们的完整用户名。",
   "onboarding.page_two.compose": "在撰写框中撰写嘟文。你可以使用下方图标上传图像、更改隐私设置和添加内容警告。",
   "onboarding.skip": "跳过",
+  "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",
   "settings.always_show_spoilers_field": "始终显示内容警告框",
   "settings.auto_collapse": "自动折叠",
   "settings.auto_collapse_all": "所有",
+  "settings.auto_collapse_height": "嘟文被视作长嘟文的临界高度(像素)",
   "settings.auto_collapse_lengthy": "长嘟文",
   "settings.auto_collapse_media": "带媒体文件的嘟文",
   "settings.auto_collapse_notifications": "通知",
@@ -106,13 +121,21 @@
   "settings.content_warnings": "内容警告",
   "settings.content_warnings.regexp": "正则表达式",
   "settings.content_warnings_filter": "不会自动展开的内容警告:",
+  "settings.content_warnings_media_outside": "在内容警告外显示媒体附件",
+  "settings.content_warnings_media_outside_hint": "通过让内容警告开关不影响媒体附件来复制上游Mastodon行为",
+  "settings.content_warnings_shared_state": "一次显示/隐藏所有副本的内容",
+  "settings.content_warnings_shared_state_hint": "通过让内容警告按钮同时影响所有帖子的副本来重现上游Mastodon行为。这将防止任何展开内容警告的嘟文自动折叠。",
+  "settings.content_warnings_unfold_opts": "自动展开设置项",
+  "settings.deprecated_setting": "此设置现在被 Mastodon 的 {settings_page_link} 控制",
   "settings.enable_collapsed": "启用折叠嘟文",
+  "settings.enable_collapsed_hint": "让折叠的帖子隐藏部分内容以占用较少的屏幕空间。这与“内容警告”功能不同。",
   "settings.enable_content_warnings_auto_unfold": "自动展开内容警告",
   "settings.general": "一般",
   "settings.hicolor_privacy_icons": "彩色隐私图标 ",
   "settings.hicolor_privacy_icons.hint": "以明亮且易于区分的颜色显示隐私图标",
   "settings.image_backgrounds": "图片背景",
   "settings.image_backgrounds_media": "预览折叠嘟文的媒体文件",
+  "settings.image_backgrounds_media_hint": "如果帖子有任何媒体附件,则使用第一个作为背景",
   "settings.image_backgrounds_users": "为折叠嘟文附加图片背景",
   "settings.inline_preview_cards": "外部链接的内嵌预览卡片",
   "settings.layout": "布局:",
@@ -139,6 +162,7 @@
   "settings.rewrite_mentions_acct": "重写为用户名和域名(当帐户为远程时)",
   "settings.rewrite_mentions_no": "不要重写",
   "settings.rewrite_mentions_username": "重写为用户名",
+  "settings.shared_settings_link": "用户偏好设置",
   "settings.show_action_bar": "在折叠的嘟文中显示操作按钮",
   "settings.show_content_type_choice": "允许你在撰写嘟文时选择格式类型",
   "settings.show_reply_counter": "显示回复的大致数量",
@@ -148,6 +172,12 @@
   "settings.side_arm_reply_mode.copy": "复制被回复嘟文的隐私设置",
   "settings.side_arm_reply_mode.keep": "保留辅助发嘟按钮以设置隐私",
   "settings.side_arm_reply_mode.restrict": "将隐私设置限制为正在回复的那条嘟文",
+  "settings.status_icons": "嘟文图标",
+  "settings.status_icons_language": "语言指示器",
+  "settings.status_icons_local_only": "仅本地指示器",
+  "settings.status_icons_media": "媒体和投票指示器",
+  "settings.status_icons_reply": "回复指示器",
+  "settings.status_icons_visibility": "嘟文隐私状态指示器",
   "settings.swipe_to_change_columns": "允许滑动以在列之间切换(仅限移动模式)",
   "settings.tag_misleading_links": "标记误导性链接",
   "settings.tag_misleading_links.hint": "将带有目标网页链接的视觉指示添加到每个未明确的链接",
diff --git a/app/javascript/flavours/glitch/locales/zh-HK.json b/app/javascript/flavours/glitch/locales/zh-HK.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/zh-HK.json
+++ b/app/javascript/flavours/glitch/locales/zh-HK.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/locales/zh-TW.json b/app/javascript/flavours/glitch/locales/zh-TW.json
index 4d243f94c..6fd7dc269 100644
--- a/app/javascript/flavours/glitch/locales/zh-TW.json
+++ b/app/javascript/flavours/glitch/locales/zh-TW.json
@@ -1,6 +1,206 @@
 {
+  "about.fork_disclaimer": "Glitch-soc is free open source software forked from Mastodon.",
+  "account.add_account_note": "Add note for @{name}",
+  "account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
+  "account.follows": "Follows",
+  "account.joined": "Joined {date}",
+  "account.suspended_disclaimer_full": "This user has been suspended by a moderator.",
+  "account.view_full_profile": "View full profile",
+  "account_note.cancel": "Cancel",
+  "account_note.edit": "Edit",
+  "account_note.glitch_placeholder": "No comment provided",
+  "account_note.save": "Save",
+  "advanced_options.icon_title": "Advanced options",
+  "advanced_options.local-only.long": "Do not post to other instances",
+  "advanced_options.local-only.short": "Local-only",
+  "advanced_options.local-only.tooltip": "This post is local-only",
+  "advanced_options.threaded_mode.long": "Automatically opens a reply on posting",
+  "advanced_options.threaded_mode.short": "Threaded mode",
+  "advanced_options.threaded_mode.tooltip": "Threaded mode enabled",
+  "boost_modal.missing_description": "This toot contains some media without description",
+  "column.favourited_by": "Favourited by",
+  "column.heading": "Misc",
+  "column.reblogged_by": "Boosted by",
+  "column.subheading": "Miscellaneous options",
+  "column_header.profile": "Profile",
+  "column_subheading.lists": "Lists",
+  "column_subheading.navigation": "Navigation",
+  "community.column_settings.allow_local_only": "Show local-only toots",
+  "compose.attach": "Attach...",
+  "compose.attach.doodle": "Draw something",
+  "compose.attach.upload": "Upload a file",
+  "compose.content-type.html": "HTML",
+  "compose.content-type.markdown": "Markdown",
+  "compose.content-type.plain": "Plain text",
+  "compose_form.poll.multiple_choices": "Allow multiple choices",
+  "compose_form.poll.single_choice": "Allow one choice",
+  "compose_form.spoiler": "Hide text behind warning",
+  "confirmation_modal.do_not_ask_again": "Do not ask for confirmation again",
+  "confirmations.deprecated_settings.confirm": "Use Mastodon preferences",
+  "confirmations.deprecated_settings.message": "Some of the glitch-soc device-specific {app_settings} you are using have been replaced by Mastodon {preferences} and will be overriden:",
+  "confirmations.missing_media_description.confirm": "Send anyway",
+  "confirmations.missing_media_description.edit": "Edit media",
+  "confirmations.missing_media_description.message": "At least one media attachment is lacking a description. Consider describing all media attachments for the visually impaired before sending your toot.",
+  "confirmations.unfilter.author": "Author",
+  "confirmations.unfilter.confirm": "Show",
+  "confirmations.unfilter.edit_filter": "Edit filter",
+  "confirmations.unfilter.filters": "Matching {count, plural, one {filter} other {filters}}",
+  "content-type.change": "Content type",
+  "direct.group_by_conversations": "Group by conversation",
+  "endorsed_accounts_editor.endorsed_accounts": "Featured accounts",
+  "favourite_modal.combo": "You can press {combo} to skip this next time",
+  "getting_started.onboarding": "Show me around",
+  "home.column_settings.advanced": "Advanced",
+  "home.column_settings.filter_regex": "Filter out by regular expressions",
+  "home.column_settings.show_direct": "Show DMs",
+  "home.settings": "Column settings",
+  "keyboard_shortcuts.bookmark": "to bookmark",
+  "keyboard_shortcuts.secondary_toot": "to send toot using secondary privacy setting",
+  "keyboard_shortcuts.toggle_collapse": "to collapse/uncollapse toots",
+  "layout.auto": "Auto",
+  "layout.desktop": "Desktop",
+  "layout.hint.auto": "Automatically chose layout based on “Enable advanced web interface” setting and screen size.",
+  "layout.hint.desktop": "Use multiple-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.hint.single": "Use single-column layout regardless of the “Enable advanced web interface” setting or screen size.",
+  "layout.single": "Mobile",
+  "media_gallery.sensitive": "Sensitive",
+  "moved_to_warning": "This account is marked as moved to {moved_to_link}, and may thus not accept new follows.",
+  "navigation_bar.app_settings": "App settings",
+  "navigation_bar.featured_users": "Featured users",
+  "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
+  "navigation_bar.misc": "Misc",
+  "notification.markForDeletion": "Mark for deletion",
+  "notification_purge.btn_all": "Select\nall",
+  "notification_purge.btn_apply": "Clear\nselected",
+  "notification_purge.btn_invert": "Invert\nselection",
+  "notification_purge.btn_none": "Select\nnone",
+  "notification_purge.start": "Enter notification cleaning mode",
+  "notifications.marked_clear": "Clear selected notifications",
+  "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected 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": "{domain} is an \"instance\" of Mastodon. Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.",
+  "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}",
+  "onboarding.page_one.welcome": "Welcome to {domain}!",
+  "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": "{domain} runs on Glitchsoc. Glitchsoc is a friendly {fork} of {Mastodon}. Glitchsoc is fully compatible with all Mastodon apps and instances. Glitchsoc 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",
+  "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",
+  "settings.always_show_spoilers_field": "Always enable the Content Warning field",
+  "settings.auto_collapse": "Automatic collapsing",
+  "settings.auto_collapse_all": "Everything",
+  "settings.auto_collapse_height": "Height (in pixels) for a toot to be considered lengthy",
+  "settings.auto_collapse_lengthy": "Lengthy toots",
+  "settings.auto_collapse_media": "Toots with media",
+  "settings.auto_collapse_notifications": "Notifications",
+  "settings.auto_collapse_reblogs": "Boosts",
+  "settings.auto_collapse_replies": "Replies",
+  "settings.close": "Close",
+  "settings.collapsed_statuses": "Collapsed toots",
+  "settings.compose_box_opts": "Compose box",
+  "settings.confirm_before_clearing_draft": "Show confirmation dialog before overwriting the message being composed",
+  "settings.confirm_boost_missing_media_description": "Show confirmation dialog before boosting toots lacking media descriptions",
+  "settings.confirm_missing_media_description": "Show confirmation dialog before sending toots lacking media descriptions",
   "settings.content_warnings": "Content warnings",
-  "settings.preferences": "Preferences"
+  "settings.content_warnings.regexp": "Regular expression",
+  "settings.content_warnings_filter": "Content warnings to not automatically unfold:",
+  "settings.content_warnings_media_outside": "Display media attachments outside content warnings",
+  "settings.content_warnings_media_outside_hint": "Reproduce upstream Mastodon behavior by having the Content Warning toggle not affect media attachments",
+  "settings.content_warnings_shared_state": "Show/hide content of all copies at once",
+  "settings.content_warnings_shared_state_hint": "Reproduce upstream Mastodon behavior by having the Content Warning button affect all copies of a post at once. This will prevent automatic collapsing of any copy of a toot with unfolded CW",
+  "settings.content_warnings_unfold_opts": "Auto-unfolding options",
+  "settings.deprecated_setting": "This setting is now controlled from Mastodon's {settings_page_link}",
+  "settings.enable_collapsed": "Enable collapsed toots",
+  "settings.enable_collapsed_hint": "Collapsed posts have parts of their contents hidden to take up less screen space. This is distinct from the Content Warning feature",
+  "settings.enable_content_warnings_auto_unfold": "Automatically unfold content-warnings",
+  "settings.general": "General",
+  "settings.hicolor_privacy_icons": "High color privacy icons",
+  "settings.hicolor_privacy_icons.hint": "Display privacy icons in bright and easily distinguishable colors",
+  "settings.image_backgrounds": "Image backgrounds",
+  "settings.image_backgrounds_media": "Preview collapsed toot media",
+  "settings.image_backgrounds_media_hint": "If the post has any media attachment, use the first one as a background",
+  "settings.image_backgrounds_users": "Give collapsed toots an image background",
+  "settings.inline_preview_cards": "Inline preview cards for external links",
+  "settings.layout": "Layout:",
+  "settings.layout_opts": "Layout options",
+  "settings.media": "Media",
+  "settings.media_fullwidth": "Full-width media previews",
+  "settings.media_letterbox": "Letterbox media",
+  "settings.media_letterbox_hint": "Scale down and letterbox media to fill the image containers instead of stretching and cropping them",
+  "settings.media_reveal_behind_cw": "Reveal sensitive media behind a CW by default",
+  "settings.notifications.favicon_badge": "Unread notifications favicon badge",
+  "settings.notifications.favicon_badge.hint": "Add a badge for unread notifications to the favicon",
+  "settings.notifications.tab_badge": "Unread notifications badge",
+  "settings.notifications.tab_badge.hint": "Display a badge for unread notifications in the column icons when the notifications column isn't open",
+  "settings.notifications_opts": "Notifications options",
+  "settings.pop_in_left": "Left",
+  "settings.pop_in_player": "Enable pop-in player",
+  "settings.pop_in_position": "Pop-in player position:",
+  "settings.pop_in_right": "Right",
+  "settings.preferences": "Preferences",
+  "settings.prepend_cw_re": "Prepend “re: ” to content warnings when replying",
+  "settings.preselect_on_reply": "Pre-select usernames on reply",
+  "settings.preselect_on_reply_hint": "When replying to a conversation with multiple participants, pre-select usernames past the first",
+  "settings.rewrite_mentions": "Rewrite mentions in displayed statuses",
+  "settings.rewrite_mentions_acct": "Rewrite with username and domain (when the account is remote)",
+  "settings.rewrite_mentions_no": "Do not rewrite mentions",
+  "settings.rewrite_mentions_username": "Rewrite with username",
+  "settings.shared_settings_link": "user preferences",
+  "settings.show_action_bar": "Show action buttons in collapsed toots",
+  "settings.show_content_type_choice": "Show content-type choice when authoring toots",
+  "settings.show_reply_counter": "Display an estimate of the reply count",
+  "settings.side_arm": "Secondary toot button:",
+  "settings.side_arm.none": "None",
+  "settings.side_arm_reply_mode": "When replying to a toot, the secondary toot button should:",
+  "settings.side_arm_reply_mode.copy": "Copy privacy setting of the toot being replied to",
+  "settings.side_arm_reply_mode.keep": "Keep its set privacy",
+  "settings.side_arm_reply_mode.restrict": "Restrict privacy setting to that of the toot being replied to",
+  "settings.status_icons": "Toot icons",
+  "settings.status_icons_language": "Language indicator",
+  "settings.status_icons_local_only": "Local-only indicator",
+  "settings.status_icons_media": "Media and poll indicators",
+  "settings.status_icons_reply": "Reply indicator",
+  "settings.status_icons_visibility": "Toot privacy indicator",
+  "settings.swipe_to_change_columns": "Allow swiping to change columns (Mobile only)",
+  "settings.tag_misleading_links": "Tag misleading links",
+  "settings.tag_misleading_links.hint": "Add a visual indication with the link target host to every link not mentioning it explicitly",
+  "settings.wide_view": "Wide view (Desktop mode only)",
+  "settings.wide_view_hint": "Stretches columns to better fill the available space.",
+  "status.collapse": "Collapse",
+  "status.has_audio": "Features attached audio files",
+  "status.has_pictures": "Features attached pictures",
+  "status.has_preview_card": "Features an attached preview card",
+  "status.has_video": "Features attached videos",
+  "status.in_reply_to": "This toot is a reply",
+  "status.is_poll": "This toot is a poll",
+  "status.local_only": "Only visible from your instance",
+  "status.sensitive_toggle": "Click to view",
+  "status.uncollapse": "Uncollapse",
+  "web_app_crash.change_your_settings": "Change your {settings}",
+  "web_app_crash.content": "You could try any of the following:",
+  "web_app_crash.debug_info": "Debug information",
+  "web_app_crash.disable_addons": "Disable browser add-ons or built-in translation tools",
+  "web_app_crash.issue_tracker": "issue tracker",
+  "web_app_crash.reload": "Reload",
+  "web_app_crash.reload_page": "{reload} the current page",
+  "web_app_crash.report_issue": "Report a bug in the {issuetracker}",
+  "web_app_crash.settings": "settings",
+  "web_app_crash.title": "We're sorry, but something went wrong with the Mastodon app."
 }
diff --git a/app/javascript/flavours/glitch/main.js b/app/javascript/flavours/glitch/main.jsx
index 14a6effbb..14a6effbb 100644
--- a/app/javascript/flavours/glitch/main.js
+++ b/app/javascript/flavours/glitch/main.jsx
diff --git a/app/javascript/flavours/glitch/middleware/errors.js b/app/javascript/flavours/glitch/middleware/errors.js
index ade529a4e..3639a5951 100644
--- a/app/javascript/flavours/glitch/middleware/errors.js
+++ b/app/javascript/flavours/glitch/middleware/errors.js
@@ -14,4 +14,4 @@ export default function errorsMiddleware() {
 
     return next(action);
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/middleware/loading_bar.js b/app/javascript/flavours/glitch/middleware/loading_bar.js
index a98f1bb2b..da8cc4c7d 100644
--- a/app/javascript/flavours/glitch/middleware/loading_bar.js
+++ b/app/javascript/flavours/glitch/middleware/loading_bar.js
@@ -22,4 +22,4 @@ export default function loadingBarMiddleware(config = {}) {
 
     return next(action);
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/middleware/sounds.js b/app/javascript/flavours/glitch/middleware/sounds.js
index 9f1bc02b9..7f2388983 100644
--- a/app/javascript/flavours/glitch/middleware/sounds.js
+++ b/app/javascript/flavours/glitch/middleware/sounds.js
@@ -43,4 +43,4 @@ export default function soundsMiddleware() {
 
     return next(action);
   };
-};
+}
diff --git a/app/javascript/flavours/glitch/packs/admin.js b/app/javascript/flavours/glitch/packs/admin.jsx
index 56cdfc30a..56cdfc30a 100644
--- a/app/javascript/flavours/glitch/packs/admin.js
+++ b/app/javascript/flavours/glitch/packs/admin.jsx
diff --git a/app/javascript/flavours/glitch/packs/public.js b/app/javascript/flavours/glitch/packs/public.jsx
index b256fdbd5..335a0710d 100644
--- a/app/javascript/flavours/glitch/packs/public.js
+++ b/app/javascript/flavours/glitch/packs/public.jsx
@@ -96,11 +96,12 @@ function main() {
       const datetime = new Date(content.getAttribute('datetime'));
       const now      = new Date();
 
-      content.title = dateTimeFormat.format(datetime);
+      const timeGiven = content.getAttribute('datetime').includes('T');
+      content.title = timeGiven ? dateTimeFormat.format(datetime) : dateFormat.format(datetime);
       content.textContent = timeAgoString({
         formatMessage: ({ id, defaultMessage }, values) => (new IntlMessageFormat(messages[id] || defaultMessage, locale)).format(values),
         formatDate: (date, options) => (new Intl.DateTimeFormat(locale, options)).format(date),
-      }, datetime, now, now.getFullYear(), content.getAttribute('datetime').includes('T'));
+      }, datetime, now, now.getFullYear(), timeGiven);
     });
 
     const reactComponents = document.querySelectorAll('[data-component]');
@@ -183,10 +184,10 @@ function main() {
 
     if (sidebar.classList.contains('visible')) {
       document.body.style.overflow = null;
-      toggleButton.setAttribute('aria-expanded', false);
+      toggleButton.setAttribute('aria-expanded', 'false');
     } else {
       document.body.style.overflow = 'hidden';
-      toggleButton.setAttribute('aria-expanded', true);
+      toggleButton.setAttribute('aria-expanded', 'true');
     }
 
     toggleButton.classList.toggle('active');
diff --git a/app/javascript/flavours/glitch/packs/settings.js b/app/javascript/flavours/glitch/packs/settings.js
index 31c88b2b5..55a8ae1c6 100644
--- a/app/javascript/flavours/glitch/packs/settings.js
+++ b/app/javascript/flavours/glitch/packs/settings.js
@@ -1,6 +1,5 @@
 import 'packs/public-path';
 import loadPolyfills from 'flavours/glitch/load_polyfills';
-import ready from 'flavours/glitch/ready';
 import loadKeyboardExtensions from 'flavours/glitch/load_keyboard_extensions';
 import 'cocoon-js-vanilla';
 
@@ -13,10 +12,10 @@ function main() {
 
     if (sidebar.classList.contains('visible')) {
       document.body.style.overflow = null;
-      toggleButton.setAttribute('aria-expanded', false);
+      toggleButton.setAttribute('aria-expanded', 'false');
     } else {
       document.body.style.overflow = 'hidden';
-      toggleButton.setAttribute('aria-expanded', true);
+      toggleButton.setAttribute('aria-expanded', 'true');
     }
 
     toggleButton.classList.toggle('active');
diff --git a/app/javascript/flavours/glitch/packs/share.js b/app/javascript/flavours/glitch/packs/share.jsx
index e5a79849a..e5a79849a 100644
--- a/app/javascript/flavours/glitch/packs/share.js
+++ b/app/javascript/flavours/glitch/packs/share.jsx
diff --git a/app/javascript/flavours/glitch/performance.js b/app/javascript/flavours/glitch/performance.js
index 450a90626..2b7e1bda8 100644
--- a/app/javascript/flavours/glitch/performance.js
+++ b/app/javascript/flavours/glitch/performance.js
@@ -12,6 +12,7 @@ if (process.env.NODE_ENV === 'development') {
     // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1331135
     performance.setResourceTimingBufferSize(Infinity);
   }
+  // eslint-disable-next-line import/no-extraneous-dependencies
   marky = require('marky');
   // allows us to easily do e.g. ReactPerf.printWasted() while debugging
   //window.ReactPerf = require('react-addons-perf');
diff --git a/app/javascript/flavours/glitch/reducers/accounts.js b/app/javascript/flavours/glitch/reducers/accounts.js
index e02a5592e..07f45f98b 100644
--- a/app/javascript/flavours/glitch/reducers/accounts.js
+++ b/app/javascript/flavours/glitch/reducers/accounts.js
@@ -35,4 +35,4 @@ export default function accounts(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/accounts_counters.js b/app/javascript/flavours/glitch/reducers/accounts_counters.js
index 9ebf72af9..4e1256d1b 100644
--- a/app/javascript/flavours/glitch/reducers/accounts_counters.js
+++ b/app/javascript/flavours/glitch/reducers/accounts_counters.js
@@ -35,4 +35,4 @@ export default function accountsCounters(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/accounts_map.js b/app/javascript/flavours/glitch/reducers/accounts_map.js
index 444bbda19..8412ad4d0 100644
--- a/app/javascript/flavours/glitch/reducers/accounts_map.js
+++ b/app/javascript/flavours/glitch/reducers/accounts_map.js
@@ -17,4 +17,4 @@ export default function accountsMap(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/alerts.js b/app/javascript/flavours/glitch/reducers/alerts.js
index ee3d54ab0..f0a696164 100644
--- a/app/javascript/flavours/glitch/reducers/alerts.js
+++ b/app/javascript/flavours/glitch/reducers/alerts.js
@@ -23,4 +23,4 @@ export default function alerts(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/announcements.js b/app/javascript/flavours/glitch/reducers/announcements.js
index 34e08eac8..b53f93a4a 100644
--- a/app/javascript/flavours/glitch/reducers/announcements.js
+++ b/app/javascript/flavours/glitch/reducers/announcements.js
@@ -99,4 +99,4 @@ export default function announcementsReducer(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/compose.js b/app/javascript/flavours/glitch/reducers/compose.js
index 814b6a1a7..109e4c723 100644
--- a/app/javascript/flavours/glitch/reducers/compose.js
+++ b/app/javascript/flavours/glitch/reducers/compose.js
@@ -140,7 +140,7 @@ function statusToTextMentions(state, status) {
   }
 
   return set.union(status.get('mentions').filterNot(mention => mention.get('id') === me).map(mention => `@${mention.get('acct')} `)).join('');
-};
+}
 
 function apiStatusToTextMentions (state, status) {
   let set = ImmutableOrderedSet([]);
@@ -150,16 +150,16 @@ function apiStatusToTextMentions (state, status) {
   }
 
   return set.union(status.mentions.filter(
-    mention => mention.id !== me
+    mention => mention.id !== me,
   ).map(
-    mention => `@${mention.acct} `
+    mention => `@${mention.acct} `,
   )).join('');
 }
 
 function apiStatusToTextHashtags (state, status) {
   const text = unescapeHTML(status.content);
   return ImmutableOrderedSet([]).union(recoverHashtags(status.tags, text).map(
-    (name) => `#${name} `
+    (name) => `#${name} `,
   )).join('');
 }
 
@@ -175,7 +175,7 @@ function clearAll(state) {
     map.set('in_reply_to', null);
     map.update(
       'advanced_options',
-      map => map.mergeWith(overwrite, state.get('default_advanced_options'))
+      map => map.mergeWith(overwrite, state.get('default_advanced_options')),
     );
     map.set('privacy', state.get('default_privacy'));
     map.set('sensitive', state.get('default_sensitive'));
@@ -184,7 +184,7 @@ function clearAll(state) {
     map.set('poll', null);
     map.set('idempotencyKey', uuid());
   });
-};
+}
 
 function continueThread (state, status) {
   return state.withMutations(function (map) {
@@ -202,7 +202,7 @@ function continueThread (state, status) {
     map.set('in_reply_to', status.id);
     map.update(
       'advanced_options',
-      map => map.merge(new ImmutableMap({ do_not_federate: status.local_only }))
+      map => map.merge(new ImmutableMap({ do_not_federate: status.local_only })),
     );
     map.set('privacy', status.visibility);
     map.set('sensitive', false);
@@ -233,7 +233,7 @@ function appendMedia(state, media, file) {
       map.set('sensitive', true);
     }
   });
-};
+}
 
 function removeMedia(state, mediaId) {
   const prevSize = state.get('media_attachments').size;
@@ -246,7 +246,7 @@ function removeMedia(state, mediaId) {
       map.set('sensitive', false);
     }
   });
-};
+}
 
 const insertSuggestion = (state, position, token, completion, path) => {
   return state.withMutations(map => {
@@ -273,20 +273,23 @@ const ignoreSuggestion = (state, position, token, completion, path) => {
 };
 
 const sortHashtagsByUse = (state, tags) => {
-  const personalHistory = state.get('tagHistory');
+  const personalHistory = state.get('tagHistory').map(tag => tag.toLowerCase());
 
-  return tags.sort((a, b) => {
-    const usedA = personalHistory.includes(a.name);
-    const usedB = personalHistory.includes(b.name);
+  const tagsWithLowercase = tags.map(t => ({ ...t, lowerName: t.name.toLowerCase() }));
+  const sorted = tagsWithLowercase.sort((a, b) => {
+    const usedA = personalHistory.includes(a.lowerName);
+    const usedB = personalHistory.includes(b.lowerName);
 
     if (usedA === usedB) {
       return 0;
     } else if (usedA && !usedB) {
-      return 1;
-    } else {
       return -1;
+    } else {
+      return 1;
     }
   });
+  sorted.forEach(tag => delete tag.lowerName);
+  return sorted;
 };
 
 const insertEmoji = (state, position, emojiData) => {
@@ -303,8 +306,8 @@ const insertEmoji = (state, position, emojiData) => {
 const hydrate = (state, hydratedState) => {
   state = clearAll(state.merge(hydratedState));
 
-  if (hydratedState.has('text')) {
-    state = state.set('text', hydratedState.get('text'));
+  if (hydratedState.get('text')) {
+    state = state.set('text', hydratedState.get('text')).set('focusDate', new Date());
   }
 
   return state;
@@ -414,13 +417,15 @@ export default function compose(state = initialState, action) {
       map.set('privacy', privacyPreference(action.status.get('visibility'), state.get('default_privacy')));
       map.update(
         'advanced_options',
-        map => map.merge(new ImmutableMap({ do_not_federate: !!action.status.get('local_only') }))
+        map => map.merge(new ImmutableMap({ do_not_federate: !!action.status.get('local_only') })),
       );
       map.set('focusDate', new Date());
       map.set('caretPosition', null);
       map.set('preselectDate', new Date());
       map.set('idempotencyKey', uuid());
 
+      map.update('media_attachments', list => list.filter(media => media.get('unattached')));
+
       if (action.status.get('language') && !action.status.has('translation')) {
         map.set('language', action.status.get('language'));
       } else {
@@ -453,7 +458,7 @@ export default function compose(state = initialState, action) {
       map.set('poll', null);
       map.update(
         'advanced_options',
-        map => map.mergeWith(overwrite, state.get('default_advanced_options'))
+        map => map.mergeWith(overwrite, state.get('default_advanced_options')),
       );
       map.set('idempotencyKey', uuid());
     });
@@ -551,7 +556,7 @@ export default function compose(state = initialState, action) {
       .setIn(['media_modal', 'dirty'], false)
       .update('media_attachments', list => list.map(item => {
         if (item.get('id') === action.media.id) {
-          return fromJS(action.media).set('unattached', true);
+          return fromJS(action.media).set('unattached', !action.attached);
         }
 
         return item;
@@ -575,7 +580,7 @@ export default function compose(state = initialState, action) {
       map.set('language', action.status.get('language'));
       map.update(
         'advanced_options',
-        map => map.merge(new ImmutableMap({ do_not_federate }))
+        map => map.merge(new ImmutableMap({ do_not_federate })),
       );
       map.set('id', null);
 
@@ -646,4 +651,4 @@ export default function compose(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/contexts.js b/app/javascript/flavours/glitch/reducers/contexts.js
index a0fcc4158..aea77ae41 100644
--- a/app/javascript/flavours/glitch/reducers/contexts.js
+++ b/app/javascript/flavours/glitch/reducers/contexts.js
@@ -67,7 +67,7 @@ const deleteFromContexts = (immutableState, ids) => immutableState.withMutations
 
 const filterContexts = (state, relationship, statuses) => {
   const ownedStatusIds = statuses.filter(status => status.get('account') === relationship.id)
-                                 .map(status => status.get('id'));
+    .map(status => status.get('id'));
 
   return deleteFromContexts(state, ownedStatusIds);
 };
@@ -102,4 +102,4 @@ export default function replies(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/conversations.js b/app/javascript/flavours/glitch/reducers/conversations.js
index 4407dcf04..48b70cc33 100644
--- a/app/javascript/flavours/glitch/reducers/conversations.js
+++ b/app/javascript/flavours/glitch/reducers/conversations.js
@@ -113,4 +113,4 @@ export default function conversations(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/custom_emojis.js b/app/javascript/flavours/glitch/reducers/custom_emojis.js
index f490d0db1..7f71ab791 100644
--- a/app/javascript/flavours/glitch/reducers/custom_emojis.js
+++ b/app/javascript/flavours/glitch/reducers/custom_emojis.js
@@ -12,4 +12,4 @@ export default function custom_emojis(state = initialState, action) {
   }
 
   return state;
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/domain_lists.js b/app/javascript/flavours/glitch/reducers/domain_lists.js
index eff97fbd6..6bf8cee68 100644
--- a/app/javascript/flavours/glitch/reducers/domain_lists.js
+++ b/app/javascript/flavours/glitch/reducers/domain_lists.js
@@ -22,4 +22,4 @@ export default function domainLists(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/filters.js b/app/javascript/flavours/glitch/reducers/filters.js
index f4f97cd3a..e1f014046 100644
--- a/app/javascript/flavours/glitch/reducers/filters.js
+++ b/app/javascript/flavours/glitch/reducers/filters.js
@@ -41,4 +41,4 @@ export default function filters(state = ImmutableMap(), action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/followed_tags.js b/app/javascript/flavours/glitch/reducers/followed_tags.js
new file mode 100644
index 000000000..84c744640
--- /dev/null
+++ b/app/javascript/flavours/glitch/reducers/followed_tags.js
@@ -0,0 +1,42 @@
+import {
+  FOLLOWED_HASHTAGS_FETCH_REQUEST,
+  FOLLOWED_HASHTAGS_FETCH_SUCCESS,
+  FOLLOWED_HASHTAGS_FETCH_FAIL,
+  FOLLOWED_HASHTAGS_EXPAND_REQUEST,
+  FOLLOWED_HASHTAGS_EXPAND_SUCCESS,
+  FOLLOWED_HASHTAGS_EXPAND_FAIL,
+} from 'flavours/glitch/actions/tags';
+import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
+
+const initialState = ImmutableMap({
+  items: ImmutableList(),
+  isLoading: false,
+  next: null,
+});
+
+export default function followed_tags(state = initialState, action) {
+  switch(action.type) {
+  case FOLLOWED_HASHTAGS_FETCH_REQUEST:
+    return state.set('isLoading', true);
+  case FOLLOWED_HASHTAGS_FETCH_SUCCESS:
+    return state.withMutations(map => {
+      map.set('items', fromJS(action.followed_tags));
+      map.set('isLoading', false);
+      map.set('next', action.next);
+    });
+  case FOLLOWED_HASHTAGS_FETCH_FAIL:
+    return state.set('isLoading', false);
+  case FOLLOWED_HASHTAGS_EXPAND_REQUEST:
+    return state.set('isLoading', true);
+  case FOLLOWED_HASHTAGS_EXPAND_SUCCESS:
+    return state.withMutations(map => {
+      map.update('items', set => set.concat(fromJS(action.followed_tags)));
+      map.set('isLoading', false);
+      map.set('next', action.next);
+    });
+  case FOLLOWED_HASHTAGS_EXPAND_FAIL:
+    return state.set('isLoading', false);
+  default:
+    return state;
+  }
+}
diff --git a/app/javascript/flavours/glitch/reducers/height_cache.js b/app/javascript/flavours/glitch/reducers/height_cache.js
index 8b05e0b19..660a2d1d7 100644
--- a/app/javascript/flavours/glitch/reducers/height_cache.js
+++ b/app/javascript/flavours/glitch/reducers/height_cache.js
@@ -20,4 +20,4 @@ export default function statuses(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/index.js b/app/javascript/flavours/glitch/reducers/index.js
index 09c08a362..5b7bdbf69 100644
--- a/app/javascript/flavours/glitch/reducers/index.js
+++ b/app/javascript/flavours/glitch/reducers/index.js
@@ -42,6 +42,7 @@ import picture_in_picture from './picture_in_picture';
 import accounts_map from './accounts_map';
 import history from './history';
 import tags from './tags';
+import followed_tags from './followed_tags';
 
 const reducers = {
   announcements,
@@ -87,6 +88,7 @@ const reducers = {
   picture_in_picture,
   history,
   tags,
+  followed_tags,
 };
 
 export default combineReducers(reducers);
diff --git a/app/javascript/flavours/glitch/reducers/list_adder.js b/app/javascript/flavours/glitch/reducers/list_adder.js
index b8c1b0e26..b144610a5 100644
--- a/app/javascript/flavours/glitch/reducers/list_adder.js
+++ b/app/javascript/flavours/glitch/reducers/list_adder.js
@@ -44,4 +44,4 @@ export default function listAdderReducer(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/list_editor.js b/app/javascript/flavours/glitch/reducers/list_editor.js
index 5427ac098..6e020dbe6 100644
--- a/app/javascript/flavours/glitch/reducers/list_editor.js
+++ b/app/javascript/flavours/glitch/reducers/list_editor.js
@@ -54,10 +54,10 @@ export default function listEditorReducer(state = initialState, action) {
     });
   case LIST_CREATE_REQUEST:
   case LIST_UPDATE_REQUEST:
-      return state.withMutations(map => {
-        map.set('isSubmitting', true);
-        map.set('isChanged', false);
-      });
+    return state.withMutations(map => {
+      map.set('isSubmitting', true);
+      map.set('isChanged', false);
+    });
   case LIST_CREATE_FAIL:
   case LIST_UPDATE_FAIL:
     return state.set('isSubmitting', false);
@@ -93,4 +93,4 @@ export default function listEditorReducer(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/lists.js b/app/javascript/flavours/glitch/reducers/lists.js
index f30ffbcbd..ba3e2b3cb 100644
--- a/app/javascript/flavours/glitch/reducers/lists.js
+++ b/app/javascript/flavours/glitch/reducers/lists.js
@@ -34,4 +34,4 @@ export default function lists(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/local_settings.js b/app/javascript/flavours/glitch/reducers/local_settings.js
index 81ab1cb0d..887e0e135 100644
--- a/app/javascript/flavours/glitch/reducers/local_settings.js
+++ b/app/javascript/flavours/glitch/reducers/local_settings.js
@@ -37,6 +37,7 @@ const initialState = ImmutableMap({
       reblogs          : false,
       replies          : false,
       media            : false,
+      height           : 400,
     }),
     backgrounds : ImmutableMap({
       user_backgrounds : false,
@@ -77,4 +78,4 @@ export default function localSettings(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/markers.js b/app/javascript/flavours/glitch/reducers/markers.js
index fb1572ff5..e3d1b1936 100644
--- a/app/javascript/flavours/glitch/reducers/markers.js
+++ b/app/javascript/flavours/glitch/reducers/markers.js
@@ -22,4 +22,4 @@ export default function markers(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/media_attachments.js b/app/javascript/flavours/glitch/reducers/media_attachments.js
index 6e6058576..dfd8ea42d 100644
--- a/app/javascript/flavours/glitch/reducers/media_attachments.js
+++ b/app/javascript/flavours/glitch/reducers/media_attachments.js
@@ -12,4 +12,4 @@ export default function meta(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/meta.js b/app/javascript/flavours/glitch/reducers/meta.js
index b1482777a..7a38a9090 100644
--- a/app/javascript/flavours/glitch/reducers/meta.js
+++ b/app/javascript/flavours/glitch/reducers/meta.js
@@ -13,14 +13,12 @@ const initialState = ImmutableMap({
 export default function meta(state = initialState, action) {
   switch(action.type) {
   case STORE_HYDRATE:
-    return state.merge(
-      action.state.get('meta'))
-        .set('permissions', action.state.getIn(['role', 'permissions']))
-        .set('layout', layoutFromWindow(action.state.getIn(['local_settings', 'layout']))
-      );
+    return state.merge(action.state.get('meta'))
+      .set('permissions', action.state.getIn(['role', 'permissions']))
+      .set('layout', layoutFromWindow(action.state.getIn(['local_settings', 'layout'])));
   case APP_LAYOUT_CHANGE:
     return state.set('layout', action.layout);
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/modal.js b/app/javascript/flavours/glitch/reducers/modal.js
index 2ef0aef24..c48117181 100644
--- a/app/javascript/flavours/glitch/reducers/modal.js
+++ b/app/javascript/flavours/glitch/reducers/modal.js
@@ -36,4 +36,4 @@ export default function modal(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/notifications.js b/app/javascript/flavours/glitch/reducers/notifications.js
index 18610e758..d5b1568e9 100644
--- a/app/javascript/flavours/glitch/reducers/notifications.js
+++ b/app/javascript/flavours/glitch/reducers/notifications.js
@@ -371,4 +371,4 @@ export default function notifications(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/picture_in_picture.js b/app/javascript/flavours/glitch/reducers/picture_in_picture.js
index 13a3d1aa2..395c21245 100644
--- a/app/javascript/flavours/glitch/reducers/picture_in_picture.js
+++ b/app/javascript/flavours/glitch/reducers/picture_in_picture.js
@@ -22,4 +22,4 @@ export default function pictureInPicture(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js b/app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js
index 267521bb8..144418d12 100644
--- a/app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js
+++ b/app/javascript/flavours/glitch/reducers/pinned_accounts_editor.js
@@ -54,4 +54,4 @@ export default function listEditorReducer(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/push_notifications.js b/app/javascript/flavours/glitch/reducers/push_notifications.js
index 117fb5167..116c3732f 100644
--- a/app/javascript/flavours/glitch/reducers/push_notifications.js
+++ b/app/javascript/flavours/glitch/reducers/push_notifications.js
@@ -50,4 +50,4 @@ export default function push_subscriptions(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/relationships.js b/app/javascript/flavours/glitch/reducers/relationships.js
index e4b9acea2..b53f0238c 100644
--- a/app/javascript/flavours/glitch/reducers/relationships.js
+++ b/app/javascript/flavours/glitch/reducers/relationships.js
@@ -82,4 +82,4 @@ export default function relationships(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/search.js b/app/javascript/flavours/glitch/reducers/search.js
index 4b8913e96..bc0433d1f 100644
--- a/app/javascript/flavours/glitch/reducers/search.js
+++ b/app/javascript/flavours/glitch/reducers/search.js
@@ -64,4 +64,4 @@ export default function search(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/server.js b/app/javascript/flavours/glitch/reducers/server.js
index cc5798fb3..af0cfc7a9 100644
--- a/app/javascript/flavours/glitch/reducers/server.js
+++ b/app/javascript/flavours/glitch/reducers/server.js
@@ -2,6 +2,9 @@ import {
   SERVER_FETCH_REQUEST,
   SERVER_FETCH_SUCCESS,
   SERVER_FETCH_FAIL,
+  SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST,
+  SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS,
+  SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL,
   EXTENDED_DESCRIPTION_REQUEST,
   EXTENDED_DESCRIPTION_SUCCESS,
   EXTENDED_DESCRIPTION_FAIL,
@@ -35,6 +38,12 @@ export default function server(state = initialState, action) {
     return state.set('server', fromJS(action.server)).setIn(['server', 'isLoading'], false);
   case SERVER_FETCH_FAIL:
     return state.setIn(['server', 'isLoading'], false);
+  case SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST:
+    return state.setIn(['translationLanguages', 'isLoading'], true);
+  case SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS:
+    return state.setIn(['translationLanguages', 'items'], fromJS(action.translationLanguages)).setIn(['translationLanguages', 'isLoading'], false);
+  case SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL:
+    return state.setIn(['translationLanguages', 'isLoading'], false);
   case EXTENDED_DESCRIPTION_REQUEST:
     return state.setIn(['extendedDescription', 'isLoading'], true);
   case EXTENDED_DESCRIPTION_SUCCESS:
diff --git a/app/javascript/flavours/glitch/reducers/settings.js b/app/javascript/flavours/glitch/reducers/settings.js
index 82927f7cd..e69eee966 100644
--- a/app/javascript/flavours/glitch/reducers/settings.js
+++ b/app/javascript/flavours/glitch/reducers/settings.js
@@ -176,4 +176,4 @@ export default function settings(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/status_lists.js b/app/javascript/flavours/glitch/reducers/status_lists.js
index 7ac0dab47..a279d3d34 100644
--- a/app/javascript/flavours/glitch/reducers/status_lists.js
+++ b/app/javascript/flavours/glitch/reducers/status_lists.js
@@ -145,4 +145,4 @@ export default function statusLists(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/statuses.js b/app/javascript/flavours/glitch/reducers/statuses.js
index f0c4c804b..ca220c54d 100644
--- a/app/javascript/flavours/glitch/reducers/statuses.js
+++ b/app/javascript/flavours/glitch/reducers/statuses.js
@@ -94,4 +94,4 @@ export default function statuses(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/suggestions.js b/app/javascript/flavours/glitch/reducers/suggestions.js
index 2bc30d2c6..3c1ea3fa8 100644
--- a/app/javascript/flavours/glitch/reducers/suggestions.js
+++ b/app/javascript/flavours/glitch/reducers/suggestions.js
@@ -34,4 +34,4 @@ export default function suggestionsReducer(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/tags.js b/app/javascript/flavours/glitch/reducers/tags.js
index 266b21177..b280bc4dd 100644
--- a/app/javascript/flavours/glitch/reducers/tags.js
+++ b/app/javascript/flavours/glitch/reducers/tags.js
@@ -22,4 +22,4 @@ export default function tags(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/timelines.js b/app/javascript/flavours/glitch/reducers/timelines.js
index 407293c62..96a6ca961 100644
--- a/app/javascript/flavours/glitch/reducers/timelines.js
+++ b/app/javascript/flavours/glitch/reducers/timelines.js
@@ -229,4 +229,4 @@ export default function timelines(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/trends.js b/app/javascript/flavours/glitch/reducers/trends.js
index e2bac6199..0b8e0882d 100644
--- a/app/javascript/flavours/glitch/reducers/trends.js
+++ b/app/javascript/flavours/glitch/reducers/trends.js
@@ -43,4 +43,4 @@ export default function trendsReducer(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/reducers/user_lists.js b/app/javascript/flavours/glitch/reducers/user_lists.js
index 0a75e85c1..9e020fd91 100644
--- a/app/javascript/flavours/glitch/reducers/user_lists.js
+++ b/app/javascript/flavours/glitch/reducers/user_lists.js
@@ -187,4 +187,4 @@ export default function userLists(state = initialState, action) {
   default:
     return state;
   }
-};
+}
diff --git a/app/javascript/flavours/glitch/store/configureStore.js b/app/javascript/flavours/glitch/store/configureStore.js
index e18af842f..0e0d45c66 100644
--- a/app/javascript/flavours/glitch/store/configureStore.js
+++ b/app/javascript/flavours/glitch/store/configureStore.js
@@ -12,4 +12,4 @@ export default function configureStore() {
     errorsMiddleware(),
     soundsMiddleware(),
   ), window.__REDUX_DEVTOOLS_EXTENSION__ ? window.__REDUX_DEVTOOLS_EXTENSION__() : f => f));
-};
+}
diff --git a/app/javascript/flavours/glitch/styles/_mixins.scss b/app/javascript/flavours/glitch/styles/_mixins.scss
index 9f6314f3f..b23c4dbb7 100644
--- a/app/javascript/flavours/glitch/styles/_mixins.scss
+++ b/app/javascript/flavours/glitch/styles/_mixins.scss
@@ -4,7 +4,7 @@
   background-clip: padding-box;
 }
 
-@mixin avatar-size($size:48px) {
+@mixin avatar-size($size: 48px) {
   width: $size;
   height: $size;
   background-size: $size $size;
@@ -22,7 +22,8 @@
 }
 
 @mixin limited-single-column($media, $parent: '&') {
-  .auto-columns #{$parent}, .single-column #{$parent} {
+  .auto-columns #{$parent},
+  .single-column #{$parent} {
     @media #{$media} {
       @content;
     }
@@ -47,7 +48,7 @@
     width: inherit;
     max-width: none;
     height: 250px;
-    border-radius: 0px;
+    border-radius: 0;
   }
 }
 
diff --git a/app/javascript/flavours/glitch/styles/accessibility.scss b/app/javascript/flavours/glitch/styles/accessibility.scss
index 7bffb2e26..fb2376abf 100644
--- a/app/javascript/flavours/glitch/styles/accessibility.scss
+++ b/app/javascript/flavours/glitch/styles/accessibility.scss
@@ -1,4 +1,7 @@
-$emojis-requiring-inversion: 'back' 'copyright' 'curly_loop' 'currency_exchange' 'end' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign' 'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'on' 'registered' 'soon' 'spider' 'telephone_receiver' 'tm' 'top' 'wavy_dash' !default;
+$emojis-requiring-inversion: 'back' 'copyright' 'curly_loop' 'currency_exchange'
+  'end' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign'
+  'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'on'
+  'registered' 'soon' 'spider' 'telephone_receiver' 'tm' 'top' 'wavy_dash' !default;
 
 %emoji-color-inversion {
   filter: invert(1);
@@ -19,7 +22,7 @@ $emojis-requiring-inversion: 'back' 'copyright' 'curly_loop' 'currency_exchange'
 
   &.active::after {
     position: absolute;
-    content: "\F00C";
+    content: '\F00C';
     font-size: 50%;
     font-family: FontAwesome;
     right: -0.55em;
diff --git a/app/javascript/flavours/glitch/styles/accounts.scss b/app/javascript/flavours/glitch/styles/accounts.scss
index 04a18de04..cee93e25b 100644
--- a/app/javascript/flavours/glitch/styles/accounts.scss
+++ b/app/javascript/flavours/glitch/styles/accounts.scss
@@ -36,10 +36,6 @@
     @media screen and (max-width: 600px) {
       height: 200px;
     }
-
-    @media screen and (max-width: $no-gap-breakpoint) {
-      display: none;
-    }
   }
 
   &__bar {
@@ -60,6 +56,7 @@
       width: 48px;
       height: 48px;
       @include avatar-size(48px);
+
       padding-top: 2px;
 
       img {
@@ -68,7 +65,8 @@
         display: block;
         margin: 0;
         border-radius: 4px;
-        @include avatar-radius();
+        @include avatar-radius;
+
         background: darken($ui-base-color, 8%);
         object-fit: cover;
       }
@@ -212,7 +210,7 @@
   font-size: 12px;
   line-height: 12px;
   font-weight: 500;
-  color: var(--user-role-accent, $ui-secondary-color);
+  color: $ui-secondary-color;
   background-color: var(--user-role-background, rgba($ui-secondary-color, 0.1));
   border: 1px solid var(--user-role-border, rgba($ui-secondary-color, 0.5));
 
diff --git a/app/javascript/flavours/glitch/styles/admin.scss b/app/javascript/flavours/glitch/styles/admin.scss
index 8ddf815c3..240c90735 100644
--- a/app/javascript/flavours/glitch/styles/admin.scss
+++ b/app/javascript/flavours/glitch/styles/admin.scss
@@ -1,4 +1,4 @@
-@use "sass:math";
+@use 'sass:math';
 
 $no-columns-breakpoint: 600px;
 $sidebar-width: 240px;
@@ -350,7 +350,7 @@ $content-width: 840px;
       width: 100%;
       height: 0;
       border: 0;
-      border-bottom: 1px solid rgba($ui-base-lighter-color, .6);
+      border-bottom: 1px solid rgba($ui-base-lighter-color, 0.6);
       margin: 20px 0;
 
       &.spacer {
@@ -384,7 +384,7 @@ $content-width: 840px;
           position: fixed;
           z-index: 10;
           width: 100%;
-          height: calc(100vh - 56px);
+          height: calc(100% - 56px);
           left: 0;
           bottom: 0;
           overflow-y: auto;
@@ -1147,7 +1147,10 @@ a.name-tag,
 
       @for $i from 0 through 10 {
         &--#{10 * $i} {
-          background-color: rgba($ui-highlight-color, 1 * (math.div(max(1, $i), 10)));
+          background-color: rgba(
+            $ui-highlight-color,
+            1 * (math.div(max(1, $i), 10))
+          );
         }
       }
     }
@@ -1216,7 +1219,7 @@ a.name-tag,
 
     path:first-child {
       fill: rgba($highlight-text-color, 0.25) !important;
-      fill-opacity: 100% !important;
+      fill-opacity: 1 !important;
     }
 
     path:last-child {
@@ -1236,7 +1239,12 @@ a.sparkline {
 
 .skeleton {
   background-color: lighten($ui-base-color, 8%);
-  background-image: linear-gradient(90deg, lighten($ui-base-color, 8%), lighten($ui-base-color, 12%), lighten($ui-base-color, 8%));
+  background-image: linear-gradient(
+    90deg,
+    lighten($ui-base-color, 8%),
+    lighten($ui-base-color, 12%),
+    lighten($ui-base-color, 8%)
+  );
   background-size: 200px 100%;
   background-repeat: no-repeat;
   border-radius: 4px;
@@ -1285,7 +1293,10 @@ a.sparkline {
 
       @for $i from 0 through 10 {
         &--#{10 * $i} {
-          background-color: rgba($ui-highlight-color, 1 * (math.div(max(1, $i), 10)));
+          background-color: rgba(
+            $ui-highlight-color,
+            1 * (math.div(max(1, $i), 10))
+          );
         }
       }
     }
@@ -1431,7 +1442,7 @@ a.sparkline {
 
     &::after {
       display: block;
-      content: "";
+      content: '';
       width: 50px;
       height: 21px;
       position: absolute;
@@ -1588,6 +1599,15 @@ a.sparkline {
           margin-bottom: 0;
         }
       }
+
+      a {
+        color: $highlight-text-color;
+        text-decoration: none;
+
+        &:hover {
+          text-decoration: underline;
+        }
+      }
     }
 
     &__actions {
@@ -1816,7 +1836,7 @@ a.sparkline {
 
     &::after {
       position: absolute;
-      content: "";
+      content: '';
       width: 1px;
       background: $highlight-text-color;
       bottom: 0;
diff --git a/app/javascript/flavours/glitch/styles/basics.scss b/app/javascript/flavours/glitch/styles/basics.scss
index a00b2936f..84977eb39 100644
--- a/app/javascript/flavours/glitch/styles/basics.scss
+++ b/app/javascript/flavours/glitch/styles/basics.scss
@@ -2,7 +2,8 @@
   @if type-of($color) == 'color' {
     $color: str-slice(ie-hex-str($color), 4);
   }
-  @return '%23' + unquote($color)
+
+  @return '%23' + unquote($color);
 }
 
 body {
@@ -13,9 +14,9 @@ body {
   font-weight: 400;
   color: $primary-text-color;
   text-rendering: optimizelegibility;
-  font-feature-settings: "kern";
+  font-feature-settings: 'kern';
   text-size-adjust: none;
-  -webkit-tap-highlight-color: rgba(0,0,0,0);
+  -webkit-tap-highlight-color: rgba(0, 0, 0, 0%);
   -webkit-tap-highlight-color: transparent;
 
   &.system-font {
@@ -30,7 +31,9 @@ body {
     // Droid Sans => Older Androids (<4.0)
     // Helvetica Neue => Older macOS <10.11
     // $font-sans-serif => web-font (Roboto) fallback and newer Androids (>=4.0)
-    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", $font-sans-serif, sans-serif;
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
+      Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+      $font-sans-serif, sans-serif;
   }
 
   &.app-body {
@@ -129,12 +132,14 @@ body {
       vertical-align: middle;
       margin: 20px;
 
-      img {
-        display: block;
-        max-width: 470px;
-        width: 100%;
-        height: auto;
-        margin-top: -120px;
+      &__illustration {
+        img {
+          display: block;
+          max-width: 470px;
+          width: 100%;
+          height: auto;
+          margin-top: -120px;
+        }
       }
 
       h1 {
@@ -157,13 +162,18 @@ button {
 
 .app-holder {
   &,
-  & > div {
+  & > div,
+  & > noscript {
     display: flex;
     width: 100%;
     align-items: center;
     justify-content: center;
     outline: 0 !important;
   }
+
+  & > noscript {
+    height: 100vh;
+  }
 }
 
 .layout-single-column .app-holder {
@@ -180,6 +190,72 @@ button {
   }
 }
 
+.app-holder noscript {
+  flex-direction: column;
+  font-size: 16px;
+  font-weight: 400;
+  line-height: 1.7;
+  color: lighten($error-red, 4%);
+  text-align: center;
+
+  & > div {
+    max-width: 500px;
+  }
+
+  p {
+    margin-bottom: 0.85em;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  a {
+    color: $highlight-text-color;
+
+    &:hover,
+    &:focus,
+    &:active {
+      text-decoration: none;
+    }
+  }
+
+  &__footer {
+    color: $dark-text-color;
+    font-size: 13px;
+
+    a {
+      color: $dark-text-color;
+    }
+  }
+
+  button {
+    display: inline;
+    border: 0;
+    background: transparent;
+    color: $dark-text-color;
+    font: inherit;
+    padding: 0;
+    margin: 0;
+    line-height: inherit;
+    cursor: pointer;
+    outline: 0;
+    transition: color 300ms linear;
+    text-decoration: underline;
+
+    &:hover,
+    &:focus,
+    &:active {
+      text-decoration: none;
+    }
+
+    &.copied {
+      color: $valid-value-color;
+      transition: none;
+    }
+  }
+}
+
 .logo-resources {
   // Not using display: none because of https://bugs.chromium.org/p/chromium/issues/detail?id=258029
   visibility: hidden;
diff --git a/app/javascript/flavours/glitch/styles/components/accounts.scss b/app/javascript/flavours/glitch/styles/components/accounts.scss
index 5b3e1db1b..b95cffbb4 100644
--- a/app/javascript/flavours/glitch/styles/components/accounts.scss
+++ b/app/javascript/flavours/glitch/styles/components/accounts.scss
@@ -30,7 +30,9 @@
     border: 0;
     padding: 0;
 
-    & > .account__avatar-wrapper { margin: 0 8px 0 0 }
+    & > .account__avatar-wrapper {
+      margin: 0 8px 0 0;
+    }
 
     & > .display-name {
       height: 24px;
@@ -60,11 +62,11 @@
 }
 
 .account__avatar {
-  @include avatar-radius();
+  @include avatar-radius;
+
   display: block;
   position: relative;
   cursor: pointer;
-
   width: 36px;
   height: 36px;
   background-size: 36px 36px;
@@ -77,11 +79,13 @@
 
   &-composite {
     @include avatar-radius;
+
     overflow: hidden;
     position: relative;
 
     & div {
       @include avatar-radius;
+
       float: left;
       position: relative;
       box-sizing: border-box;
@@ -102,24 +106,24 @@
 }
 
 .account__avatar-overlay {
-  position: relative;
   @include avatar-size(48px);
 
   position: relative;
 
   &-base {
-    @include avatar-radius();
+    @include avatar-radius;
     @include avatar-size(36px);
 
     img {
       @include avatar-radius;
+
       width: 100%;
       height: 100%;
     }
   }
 
   &-overlay {
-    @include avatar-radius();
+    @include avatar-radius;
     @include avatar-size(24px);
 
     position: absolute;
@@ -129,6 +133,7 @@
 
     img {
       @include avatar-radius;
+
       width: 100%;
       height: 100%;
     }
@@ -379,7 +384,7 @@
       &::before,
       &::after {
         display: block;
-        content: "";
+        content: '';
         position: absolute;
         bottom: 0;
         left: 50%;
@@ -533,14 +538,22 @@
 
   &__tabs {
     display: flex;
-    align-items: flex-start;
+    align-items: flex-end;
     justify-content: space-between;
     padding: 7px 10px;
-    margin-top: -55px;
-    gap: 8px;
+    margin-top: -81px;
+    height: 130px;
     overflow: hidden;
     margin-left: -2px; // aligns the pfp with content below
 
+    .account-role {
+      margin: 0 2px;
+      padding: 4px 0;
+      box-sizing: border-box;
+      min-width: 90px;
+      text-align: center;
+    }
+
     &__buttons {
       display: flex;
       align-items: center;
@@ -702,22 +715,22 @@
         padding: 2px 6px;
         color: $darker-text-color;
 
-  &:hover,
-  &:active,
-  &:focus {
-    color: lighten($darker-text-color, 7%);
-    background-color: rgba($darker-text-color, 0.15);
-  }
+        &:hover,
+        &:active,
+        &:focus {
+          color: lighten($darker-text-color, 7%);
+          background-color: rgba($darker-text-color, 0.15);
+        }
 
-  &:focus {
-    background-color: rgba($darker-text-color, 0.3);
-  }
+        &:focus {
+          background-color: rgba($darker-text-color, 0.3);
+        }
 
-  &[disabled] {
-    color: darken($darker-text-color, 13%);
-    background-color: transparent;
-    cursor: default;
-  }
+        &[disabled] {
+          color: darken($darker-text-color, 13%);
+          background-color: transparent;
+          cursor: default;
+        }
       }
 
       .flex-spacer {
@@ -736,8 +749,6 @@
       display: block;
       box-sizing: border-box;
       width: calc(100% + 20px);
-      margin: 0;
-      margin-top: 5px;
       color: $secondary-text-color;
       background: $ui-base-color;
       padding: 10px;
@@ -764,6 +775,7 @@
   display: flex;
   align-items: center;
   flex-direction: column;
+
   &__message {
     color: $darker-text-color;
     padding: 8px 0;
@@ -774,6 +786,7 @@
     text-align: center;
     margin-bottom: 16px;
   }
+
   &__action {
     display: flex;
     justify-content: space-between;
diff --git a/app/javascript/flavours/glitch/styles/components/announcements.scss b/app/javascript/flavours/glitch/styles/components/announcements.scss
index 85af9afc8..feaff81f5 100644
--- a/app/javascript/flavours/glitch/styles/components/announcements.scss
+++ b/app/javascript/flavours/glitch/styles/components/announcements.scss
@@ -181,7 +181,11 @@
     &.active {
       transition: all 100ms ease-in;
       transition-property: background-color, color;
-      background-color: mix(lighten($ui-base-color, 12%), $ui-highlight-color, 80%);
+      background-color: mix(
+        lighten($ui-base-color, 12%),
+        $ui-highlight-color,
+        80%
+      );
 
       .reactions-bar__item__count {
         color: lighten($highlight-text-color, 8%);
diff --git a/app/javascript/flavours/glitch/styles/components/columns.scss b/app/javascript/flavours/glitch/styles/components/columns.scss
index 42a591931..907f820d6 100644
--- a/app/javascript/flavours/glitch/styles/components/columns.scss
+++ b/app/javascript/flavours/glitch/styles/components/columns.scss
@@ -286,7 +286,7 @@ $ui-header-height: 55px;
 
     &::before {
       display: block;
-      content: "";
+      content: '';
       position: absolute;
       bottom: -13px;
       left: 0;
@@ -296,7 +296,11 @@ $ui-header-height: 55px;
       pointer-events: none;
       height: 28px;
       z-index: 1;
-      background: radial-gradient(ellipse, rgba($ui-highlight-color, 0.23) 0%, rgba($ui-highlight-color, 0) 60%);
+      background: radial-gradient(
+        ellipse,
+        rgba($ui-highlight-color, 0.23) 0%,
+        rgba($ui-highlight-color, 0) 60%
+      );
     }
   }
 
@@ -424,6 +428,7 @@ $ui-header-height: 55px;
 
   button {
     @extend .column-header__button;
+
     background: transparent;
     text-align: center;
     padding: 10px 5px;
@@ -435,10 +440,10 @@ $ui-header-height: 55px;
   }
 }
 
-
 .layout-single-column .column-header__notif-cleaning-buttons {
   @media screen and (min-width: $no-gap-breakpoint) {
-    b, i {
+    b,
+    i {
       margin-right: 5px;
     }
 
@@ -487,6 +492,7 @@ $ui-header-height: 55px;
   // notif cleaning drawer
   &.ncd {
     transition: none;
+
     &.collapsed {
       max-height: 0;
       opacity: 0.7;
@@ -575,8 +581,8 @@ $ui-header-height: 55px;
   font-size: inherit;
   flex: auto;
   background-color: $ui-base-color;
-  transition-property: background-color, box-shadow;
   transition: all 0.2s ease;
+  transition-property: background-color, box-shadow;
 
   &[disabled] {
     cursor: not-allowed;
@@ -622,7 +628,8 @@ $ui-header-height: 55px;
   flex: 1 1 auto;
   align-items: center;
   justify-content: center;
-  @supports(display: grid) { // hack to fix Chrome <57
+  @supports (display: grid) {
+    // hack to fix Chrome <57
     contain: strict;
   }
 
@@ -643,6 +650,7 @@ $ui-header-height: 55px;
 .follow_requests-unlocked_explanation {
   background: darken($ui-base-color, 4%);
   contain: initial;
+  flex-grow: 0;
 }
 
 .error-column {
@@ -766,7 +774,7 @@ $ui-header-height: 55px;
 
   .column-select {
     &__control {
-      @include search-input();
+      @include search-input;
 
       &::placeholder {
         color: lighten($darker-text-color, 4%);
@@ -840,7 +848,8 @@ $ui-header-height: 55px;
     }
 
     &__menu {
-      @include search-popout();
+      @include search-popout;
+
       padding: 0;
       background: $ui-secondary-color;
     }
@@ -916,7 +925,7 @@ $ui-header-height: 55px;
 
   p {
     font-size: 16px;
-   line-height: 24px;
+    line-height: 24px;
     font-weight: 400;
     color: $darker-text-color;
   }
diff --git a/app/javascript/flavours/glitch/styles/components/compose_form.scss b/app/javascript/flavours/glitch/styles/components/compose_form.scss
index aa2d52ed0..1c2e0aeb4 100644
--- a/app/javascript/flavours/glitch/styles/components/compose_form.scss
+++ b/app/javascript/flavours/glitch/styles/components/compose_form.scss
@@ -32,12 +32,12 @@
 .spoiler-input {
   height: 0;
   transform-origin: bottom;
-  opacity: 0.0;
+  opacity: 0;
 
   &.spoiler-input--visible {
     height: 36px;
     margin-bottom: 11px;
-    opacity: 1.0;
+    opacity: 1;
   }
 
   input {
@@ -59,8 +59,12 @@
       color: $dark-text-color;
     }
 
-    &:focus { outline: 0 }
-    @include single-column('screen and (max-width: 630px)') { font-size: 16px }
+    &:focus {
+      outline: 0;
+    }
+    @include single-column('screen and (max-width: 630px)') {
+      font-size: 16px;
+    }
   }
 }
 
@@ -90,7 +94,6 @@
 .compose-form__sensitive-button {
   padding: 10px;
   padding-top: 0;
-
   font-size: 14px;
   font-weight: 500;
 
@@ -98,7 +101,7 @@
     color: $highlight-text-color;
   }
 
-  input[type=checkbox] {
+  input[type='checkbox'] {
     display: none;
   }
 
@@ -118,7 +121,9 @@
 
     &.active {
       border-color: $highlight-text-color;
-      background: $highlight-text-color;
+      background: $highlight-text-color
+        url("data:image/svg+xml;utf8,<svg width='18' height='18' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M4.5 8.5L8 12l6-6' stroke='white' stroke-width='1.5'/></svg>")
+        center center no-repeat;
     }
   }
 }
@@ -137,7 +142,9 @@
   margin-bottom: 5px;
   overflow: hidden;
 
-  & > .account.small { color: $inverted-text-color; }
+  & > .account.small {
+    color: $inverted-text-color;
+  }
 }
 
 .reply-indicator__cancel {
@@ -147,19 +154,17 @@
 
 .reply-indicator__content {
   position: relative;
-  margin: 10px 0;
-  padding: 0 12px;
   font-size: 14px;
   line-height: 20px;
-  color: $inverted-text-color;
   word-wrap: break-word;
   font-weight: 400;
-  overflow: visible;
-  white-space: pre-wrap;
-  padding-top: 5px;
   overflow: hidden;
+  padding-top: 5px;
+  color: $inverted-text-color;
+  white-space: pre-wrap;
 
-  p, pre, blockquote {
+  p,
+  pre {
     margin-bottom: 20px;
     white-space: pre-wrap;
 
@@ -168,80 +173,21 @@
     }
   }
 
-  h1, h2, h3, h4, h5 {
-    margin-top: 20px;
-    margin-bottom: 20px;
-  }
-
-  h1, h2 {
-    font-weight: 700;
-    font-size: 18px;
-  }
-
-  h2 {
-    font-size: 16px;
-  }
-
-  h3, h4, h5 {
-    font-weight: 500;
-  }
-
-  blockquote {
-    padding-left: 10px;
-    border-left: 3px solid $inverted-text-color;
-    color: $inverted-text-color;
-    white-space: normal;
-
-    p:last-child {
-      margin-bottom: 0;
-    }
-  }
-
-  b, strong {
-    font-weight: 700;
-  }
-
-  em, i {
-    font-style: italic;
-  }
-
-  sub {
-    font-size: smaller;
-    vertical-align: sub;
-  }
-
-  sup {
-    font-size: smaller;
-    vertical-align: super;
-  }
-
-  ul, ol {
-    margin-left: 1em;
-
-    p {
-      margin: 0;
-    }
-  }
-
-  ul {
-    list-style-type: disc;
-  }
-
-  ol {
-    list-style-type: decimal;
-  }
-
   a {
     color: $lighter-text-color;
     text-decoration: none;
 
-    &:hover { text-decoration: underline }
+    &:hover {
+      text-decoration: underline;
+    }
 
     &.mention {
       &:hover {
         text-decoration: none;
 
-        span { text-decoration: underline }
+        span {
+          text-decoration: underline;
+        }
       }
     }
   }
@@ -321,7 +267,7 @@
     font-size: 18px;
     line-height: 24px;
     text-align: center;
-    opacity: .8;
+    opacity: 0.8;
   }
 }
 
@@ -331,19 +277,18 @@
 }
 
 .autosuggest-textarea__suggestions {
-  display: block;
-  position: absolute;
   box-sizing: border-box;
+  display: none;
+  position: absolute;
   top: 100%;
-  border-radius: 0 0 4px 4px;
-  padding: 6px;
   width: 100%;
-  color: $inverted-text-color;
-  background: $ui-secondary-color;
+  z-index: 99;
   box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
+  background: $ui-secondary-color;
+  border-radius: 0 0 4px 4px;
+  color: $inverted-text-color;
   font-size: 14px;
-  z-index: 99;
-  display: none;
+  padding: 6px;
 }
 
 .autosuggest-textarea__suggestions--visible {
@@ -358,7 +303,9 @@
   &:hover,
   &:focus,
   &:active,
-  &.selected { background: darken($ui-secondary-color, 10%) }
+  &.selected {
+    background: darken($ui-secondary-color, 10%);
+  }
 
   > .account,
   > .emoji,
@@ -396,7 +343,9 @@
 
   & > .account.small {
     .display-name {
-      & > span { color: $lighter-text-color }
+      & > span {
+        color: $lighter-text-color;
+      }
     }
   }
 }
@@ -430,7 +379,9 @@
     background-repeat: no-repeat;
     overflow: hidden;
 
-    & > .close { mix-blend-mode: difference }
+    & > .close {
+      mix-blend-mode: difference;
+    }
   }
 
   .icon-button {
@@ -455,12 +406,22 @@
     left: 0;
     right: 0;
     box-sizing: border-box;
-    background: linear-gradient(0deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 80%, transparent);
+    background: linear-gradient(
+      0deg,
+      rgba($base-shadow-color, 0.8) 0,
+      rgba($base-shadow-color, 0.35) 80%,
+      transparent
+    );
   }
 }
 
 .compose-form__upload__actions {
-  background: linear-gradient(180deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 80%, transparent);
+  background: linear-gradient(
+    180deg,
+    rgba($base-shadow-color, 0.8) 0,
+    rgba($base-shadow-color, 0.35) 80%,
+    transparent
+  );
   display: flex;
   align-items: flex-start;
   justify-content: space-between;
@@ -495,7 +456,7 @@
   border-radius: 6px;
   width: 100%;
   height: 6px;
-  background: $ui-base-lighter-color;
+  background: darken($simple-background-color, 8%);
 }
 
 .upload-progress__tracker {
@@ -543,7 +504,8 @@
     margin: 0 3px;
     border-width: 0 0 0 1px;
     border-style: none none none solid;
-    border-color: transparent transparent transparent darken($simple-background-color, 24%);
+    border-color: transparent transparent transparent
+      darken($simple-background-color, 24%);
     padding: 0;
     width: 0;
     height: 27px;
@@ -604,7 +566,9 @@
     flex: 1 1 auto;
     color: $lighter-text-color;
 
-    &:not(:first-child) { margin-left: 10px }
+    &:not(:first-child) {
+      margin-left: 10px;
+    }
 
     strong {
       display: block;
@@ -621,11 +585,15 @@
     .privacy-dropdown__option__content {
       color: $primary-text-color;
 
-      strong { color: $primary-text-color }
+      strong {
+        color: $primary-text-color;
+      }
     }
   }
 
-  &.active:hover { background: lighten($ui-highlight-color, 4%) }
+  &.active:hover {
+    background: lighten($ui-highlight-color, 4%);
+  }
 }
 
 .compose-form__publish {
diff --git a/app/javascript/flavours/glitch/styles/components/directory.scss b/app/javascript/flavours/glitch/styles/components/directory.scss
index 803e075c9..5c763764d 100644
--- a/app/javascript/flavours/glitch/styles/components/directory.scss
+++ b/app/javascript/flavours/glitch/styles/components/directory.scss
@@ -11,7 +11,11 @@
 }
 
 .scrollable .account-card__bio::after {
-  background: linear-gradient(to left, lighten($ui-base-color, 8%), transparent);
+  background: linear-gradient(
+    to left,
+    lighten($ui-base-color, 8%),
+    transparent
+  );
 }
 
 .filter-form {
@@ -33,14 +37,13 @@
   display: inline-block;
   padding: 6px 0;
   line-height: 18px;
-  cursor: default;
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
   cursor: pointer;
 
-  input[type=radio],
-  input[type=checkbox] {
+  input[type='radio'],
+  input[type='checkbox'] {
     display: none;
   }
 
diff --git a/app/javascript/flavours/glitch/styles/components/doodle.scss b/app/javascript/flavours/glitch/styles/components/doodle.scss
index a4a1cfc84..52c7cd54a 100644
--- a/app/javascript/flavours/glitch/styles/components/doodle.scss
+++ b/app/javascript/flavours/glitch/styles/components/doodle.scss
@@ -1,15 +1,17 @@
-$doodleBg: #d9e1e8;
+$doodle-background: #d9e1e8;
+
 .doodle-modal {
   @extend .boost-modal;
+
   width: unset;
 }
 
 .doodle-modal__container {
-  background: $doodleBg;
+  background: $doodle-background;
   text-align: center;
   line-height: 0; // remove weird gap under canvas
   canvas {
-    border: 5px solid $doodleBg;
+    border: 5px solid $doodle-background;
   }
 }
 
@@ -24,7 +26,6 @@ $doodleBg: #d9e1e8;
 
   .doodle-toolbar {
     line-height: 1;
-
     display: flex;
     flex-direction: column;
     flex-grow: 0;
@@ -38,9 +39,11 @@ $doodleBg: #d9e1e8;
         margin-right: 2px;
       }
 
-      input[type="number"],input[type="text"] {
+      input[type='number'],
+      input[type='text'] {
         width: 40px;
       }
+
       span.val {
         display: inline-block;
         text-align: left;
@@ -52,7 +55,7 @@ $doodleBg: #d9e1e8;
   .doodle-palette {
     padding-right: 0 !important;
     border: 1px solid black;
-    line-height: .2rem;
+    line-height: 0.2rem;
     flex-grow: 0;
     background: white;
 
@@ -60,14 +63,15 @@ $doodleBg: #d9e1e8;
       appearance: none;
       width: 1rem;
       height: 1rem;
-      margin: 0; padding: 0;
+      margin: 0;
+      padding: 0;
       text-align: center;
       color: black;
       text-shadow: 0 0 1px white;
       cursor: pointer;
-      box-shadow: inset 0 0 1px rgba(white, .5);
+      box-shadow: inset 0 0 1px rgba(white, 0.5);
       border: 1px solid black;
-      outline-offset:-1px;
+      outline-offset: -1px;
 
       &.foreground {
         outline: 1px dashed white;
diff --git a/app/javascript/flavours/glitch/styles/components/drawer.scss b/app/javascript/flavours/glitch/styles/components/drawer.scss
index 3e2604d4d..3e482774e 100644
--- a/app/javascript/flavours/glitch/styles/components/drawer.scss
+++ b/app/javascript/flavours/glitch/styles/components/drawer.scss
@@ -34,7 +34,8 @@
   }
 
   @include single-column('screen and (max-width: 630px)') {
-    :root & {  //  Overrides `.wide` for single-column view
+    :root & {
+      //  Overrides `.wide` for single-column view
       flex: auto;
       width: 100%;
       min-width: 0;
@@ -43,16 +44,20 @@
     }
   }
 
-  .react-swipeable-view-container & { height: 100% }
+  .react-swipeable-view-container & {
+    height: 100%;
+  }
 }
 
 .drawer--header {
-  display: flex;
-  flex-direction: row;
-  margin-bottom: 10px;
   flex: none;
-  background: lighten($ui-base-color, 8%);
   font-size: 16px;
+  background: lighten($ui-base-color, 8%);
+  margin-bottom: 10px;
+  display: flex;
+  flex-direction: row;
+  border-radius: 4px;
+  overflow: hidden;
 
   & > * {
     display: block;
@@ -84,12 +89,18 @@
   margin-bottom: 10px;
   flex: none;
 
-  @include limited-single-column('screen and (max-width: #{$no-gap-breakpoint})') { margin-bottom: 0 }
-  @include single-column('screen and (max-width: 630px)') { font-size: 16px }
+  @include limited-single-column(
+    'screen and (max-width: #{$no-gap-breakpoint})'
+  ) {
+    margin-bottom: 0;
+  }
+  @include single-column('screen and (max-width: 630px)') {
+    font-size: 16px;
+  }
 }
 
 .search-popout {
-  @include search-popout();
+  @include search-popout;
 }
 
 .navigation-bar {
@@ -174,6 +185,7 @@
   position: relative;
   overflow: hidden;
   display: flex;
+  border-radius: 4px;
 }
 
 .drawer__inner {
@@ -196,7 +208,9 @@
 }
 
 .drawer__inner__mastodon {
-  background: lighten($ui-base-color, 13%) 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($ui-base-color)}"/></svg>') no-repeat bottom / 100% auto;
+  background: lighten($ui-base-color, 13%)
+    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($ui-base-color)}"/></svg>')
+    no-repeat bottom / 100% auto;
   flex: 1;
   min-height: 47px;
   display: none;
@@ -208,7 +222,6 @@
     width: 85%;
     height: 100%;
     pointer-events: none;
-    user-drag: none;
     user-select: none;
   }
 
@@ -244,13 +257,27 @@
 @for $i from 0 through 3 {
   .mbstobon-#{$i} .drawer__inner__mastodon {
     @if $i == 3 {
-      background: url('~flavours/glitch/images/wave-drawer.png') no-repeat bottom / 100% auto, lighten($ui-base-color, 13%);
+      background: url('~flavours/glitch/images/wave-drawer.png')
+          no-repeat
+          bottom /
+          100%
+          auto,
+        lighten($ui-base-color, 13%);
     } @else {
-      background: url('~flavours/glitch/images/wave-drawer-glitched.png') no-repeat bottom / 100% auto, lighten($ui-base-color, 13%);
+      background: url('~flavours/glitch/images/wave-drawer-glitched.png')
+          no-repeat
+          bottom /
+          100%
+          auto,
+        lighten($ui-base-color, 13%);
     }
 
     & > .mastodon {
-      background: url("~flavours/glitch/images/mbstobon-ui-#{$i}.png") no-repeat left bottom / contain;
+      background: url('~flavours/glitch/images/mbstobon-ui-#{$i}.png')
+        no-repeat
+        left
+        bottom /
+        contain;
 
       @if $i != 3 {
         filter: contrast(50%) brightness(50%);
diff --git a/app/javascript/flavours/glitch/styles/components/emoji.scss b/app/javascript/flavours/glitch/styles/components/emoji.scss
index c037e03f9..4427f2080 100644
--- a/app/javascript/flavours/glitch/styles/components/emoji.scss
+++ b/app/javascript/flavours/glitch/styles/components/emoji.scss
@@ -2,7 +2,7 @@
   font-size: inherit;
   vertical-align: middle;
   object-fit: contain;
-  margin: -.2ex .15em .2ex;
+  margin: -0.2ex 0.15em 0.2ex;
   width: 16px;
   height: 16px;
 
diff --git a/app/javascript/flavours/glitch/styles/components/emoji_picker.scss b/app/javascript/flavours/glitch/styles/components/emoji_picker.scss
index 790650cfa..6bb9827b3 100644
--- a/app/javascript/flavours/glitch/styles/components/emoji_picker.scss
+++ b/app/javascript/flavours/glitch/styles/components/emoji_picker.scss
@@ -46,7 +46,7 @@
   text-align: center;
   padding: 12px 4px;
   overflow: hidden;
-  transition: color .1s ease-out;
+  transition: color 0.1s ease-out;
   cursor: pointer;
   background: transparent;
   border: 0;
@@ -174,7 +174,7 @@
 
   &:hover::before {
     z-index: 0;
-    content: "";
+    content: '';
     position: absolute;
     top: 0;
     left: 0;
@@ -246,8 +246,8 @@
   padding: 5px 6px;
   padding-top: 70px;
 
- .emoji-mart-no-results-label {
-    margin-top: .2em;
+  .emoji-mart-no-results-label {
+    margin-top: 0.2em;
   }
 
   .emoji-mart-emoji:hover::before {
diff --git a/app/javascript/flavours/glitch/styles/components/index.scss b/app/javascript/flavours/glitch/styles/components/index.scss
index d50316366..497b66b3e 100644
--- a/app/javascript/flavours/glitch/styles/components/index.scss
+++ b/app/javascript/flavours/glitch/styles/components/index.scss
@@ -1,1807 +1,4 @@
-.app-body {
-  -webkit-overflow-scrolling: touch;
-  -ms-overflow-style: -ms-autohiding-scrollbar;
-}
-
-.animated-number {
-  display: inline-flex;
-  flex-direction: column;
-  align-items: stretch;
-  overflow: hidden;
-  position: relative;
-}
-
-.link-button {
-  display: block;
-  font-size: 15px;
-  line-height: 20px;
-  color: $highlight-text-color;
-  border: 0;
-  background: transparent;
-  padding: 0;
-  cursor: pointer;
-  text-decoration: none;
-
-  &--destructive {
-    color: $error-value-color;
-  }
-
-  &:hover,
-  &:active {
-    text-decoration: underline;
-  }
-
-  &:disabled {
-    color: $ui-primary-color;
-    cursor: default;
-  }
-}
-
-.button {
-  background-color: darken($ui-highlight-color, 3%);
-  border: 10px none;
-  border-radius: 4px;
-  box-sizing: border-box;
-  color: $primary-text-color;
-  cursor: pointer;
-  display: inline-block;
-  font-family: inherit;
-  font-size: 15px;
-  font-weight: 500;
-  letter-spacing: 0;
-  line-height: 22px;
-  overflow: hidden;
-  padding: 7px 18px;
-  position: relative;
-  text-align: center;
-  text-decoration: none;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  width: auto;
-
-  &:active,
-  &:focus,
-  &:hover {
-    background-color: $ui-highlight-color;
-  }
-
-  &--destructive {
-    &:active,
-    &:focus,
-    &:hover {
-      background-color: $error-red;
-      transition: none;
-    }
-  }
-
-  &:disabled {
-    background-color: $ui-primary-color;
-    cursor: default;
-  }
-
-  &.button-alternative {
-    color: $inverted-text-color;
-    background: $ui-primary-color;
-
-    &:active,
-    &:focus,
-    &:hover {
-      background-color: lighten($ui-primary-color, 4%);
-    }
-  }
-
-  &.button-alternative-2 {
-    background: $ui-base-lighter-color;
-
-    &:active,
-    &:focus,
-    &:hover {
-      background-color: lighten($ui-base-lighter-color, 4%);
-    }
-  }
-
-  &.button-secondary {
-    font-size: 16px;
-    line-height: 36px;
-    height: auto;
-    color: $darker-text-color;
-    text-transform: none;
-    background: transparent;
-    padding: 6px 17px;
-    border: 1px solid $ui-primary-color;
-
-    &:active,
-    &:focus,
-    &:hover {
-      border-color: lighten($ui-primary-color, 4%);
-      color: lighten($darker-text-color, 4%);
-      text-decoration: none;
-    }
-
-    &:disabled {
-      opacity: 0.5;
-    }
-  }
-
-  &.button-tertiary {
-    background: transparent;
-    padding: 6px 17px;
-    color: $highlight-text-color;
-    border: 1px solid $highlight-text-color;
-
-    &:active,
-    &:focus,
-    &:hover {
-      background: $ui-highlight-color;
-      color: $primary-text-color;
-      border: 0;
-      padding: 7px 18px;
-    }
-
-    &:disabled {
-      opacity: 0.5;
-    }
-
-    &.button--confirmation {
-      color: $valid-value-color;
-      border-color: $valid-value-color;
-
-      &:active,
-      &:focus,
-      &:hover {
-        background: $valid-value-color;
-        color: $primary-text-color;
-      }
-    }
-
-    &.button--destructive {
-      color: $error-value-color;
-      border-color: $error-value-color;
-
-      &:active,
-      &:focus,
-      &:hover {
-        background: $error-value-color;
-        color: $primary-text-color;
-      }
-    }
-  }
-
-  &.button--block {
-    display: block;
-    width: 100%;
-  }
-
-  .layout-multiple-columns &.button--with-bell {
-    font-size: 12px;
-    padding: 0 8px;
-  }
-}
-
-.icon-button {
-  display: inline-block;
-  padding: 0;
-  color: $action-button-color;
-  border: 0;
-  border-radius: 4px;
-  background: transparent;
-  cursor: pointer;
-  transition: all 100ms ease-in;
-  transition-property: background-color, color;
-  text-decoration: none;
-
-  a {
-    color: inherit;
-    text-decoration: none;
-  }
-
-  &:hover,
-  &:active,
-  &:focus {
-    color: lighten($action-button-color, 7%);
-    background-color: rgba($action-button-color, 0.15);
-    transition: all 200ms ease-out;
-    transition-property: background-color, color;
-  }
-
-  &:focus {
-    background-color: rgba($action-button-color, 0.3);
-  }
-
-  &.disabled {
-    color: darken($action-button-color, 13%);
-    background-color: transparent;
-    cursor: default;
-  }
-
-  &.active {
-    color: $highlight-text-color;
-  }
-
-  &.copyable {
-    transition: background 300ms linear;
-  }
-
-  &.copied {
-    background: $valid-value-color;
-    transition: none;
-  }
-
-  &::-moz-focus-inner {
-    border: 0;
-  }
-
-  &::-moz-focus-inner,
-  &:focus,
-  &:active {
-    outline: 0 !important;
-  }
-
-  &.inverted {
-    color: $lighter-text-color;
-
-    &:hover,
-    &:active,
-    &:focus {
-      color: darken($lighter-text-color, 7%);
-      background-color: rgba($lighter-text-color, 0.15);
-    }
-
-    &:focus {
-      background-color: rgba($lighter-text-color, 0.3);
-    }
-
-    &.disabled {
-      color: lighten($lighter-text-color, 7%);
-      background-color: transparent;
-    }
-
-    &.active {
-      color: $highlight-text-color;
-
-      &.disabled {
-        color: lighten($highlight-text-color, 13%);
-      }
-    }
-  }
-
-  &.overlayed {
-    box-sizing: content-box;
-    background: rgba($base-overlay-background, 0.6);
-    color: rgba($primary-text-color, 0.7);
-    border-radius: 4px;
-    padding: 2px;
-
-    &:hover {
-      background: rgba($base-overlay-background, 0.9);
-    }
-  }
-
-  &--with-counter {
-    display: inline-flex;
-    align-items: center;
-    width: auto !important;
-    padding: 0 4px 0 2px;
-  }
-
-  &__counter {
-    display: inline-block;
-    width: auto;
-    margin-left: 4px;
-    font-size: 12px;
-    font-weight: 500;
-  }
-}
-
-.text-icon,
-.text-icon-button {
-  font-weight: 600;
-  font-size: 11px;
-  line-height: 27px;
-  cursor: default;
-}
-
-.text-icon-button {
-  color: $lighter-text-color;
-  border: 0;
-  border-radius: 4px;
-  background: transparent;
-  cursor: pointer;
-  padding: 0 3px;
-  outline: 0;
-  transition: all 100ms ease-in;
-  transition-property: background-color, color;
-
-  &:hover,
-  &:active,
-  &:focus {
-    color: darken($lighter-text-color, 7%);
-    background-color: rgba($lighter-text-color, 0.15);
-    transition: all 200ms ease-out;
-    transition-property: background-color, color;
-  }
-
-  &:focus {
-    background-color: rgba($lighter-text-color, 0.3);
-  }
-
-  &.disabled {
-    color: lighten($lighter-text-color, 20%);
-    background-color: transparent;
-    cursor: default;
-  }
-
-  &.active {
-    color: $highlight-text-color;
-  }
-
-  &::-moz-focus-inner {
-    border: 0;
-  }
-
-  &::-moz-focus-inner,
-  &:focus,
-  &:active {
-    outline: 0 !important;
-  }
-}
-
-body > [data-popper-placement] {
-  z-index: 3;
-}
-
-.invisible {
-  font-size: 0;
-  line-height: 0;
-  display: inline-block;
-  width: 0;
-  height: 0;
-  position: absolute;
-
-  img,
-  svg {
-    margin: 0 !important;
-    border: 0 !important;
-    padding: 0 !important;
-    width: 0 !important;
-    height: 0 !important;
-  }
-}
-
-.ellipsis {
-  &::after {
-    content: "…";
-  }
-}
-
-.notification__favourite-icon-wrapper {
-  left: 0;
-  position: absolute;
-
-  .fa.star-icon {
-    color: $gold-star;
-  }
-}
-
-.icon-button.star-icon.active {
-  color: $gold-star;
-}
-
-.icon-button.bookmark-icon.active {
-  color: $red-bookmark;
-}
-
-.no-reduce-motion .icon-button.star-icon {
-  &.activate {
-    & > .fa-star {
-      animation: spring-rotate-in 1s linear;
-    }
-  }
-
-  &.deactivate {
-    & > .fa-star {
-      animation: spring-rotate-out 1s linear;
-    }
-  }
-}
-
-.notification__display-name {
-  color: inherit;
-  font-weight: 500;
-  text-decoration: none;
-
-  &:hover {
-    color: $primary-text-color;
-    text-decoration: underline;
-  }
-}
-
-.display-name {
-  display: block;
-  max-width: 100%;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-
-  a {
-    color: inherit;
-    text-decoration: inherit;
-  }
-
-  strong {
-    display: block;
-  }
-
-  > a:hover {
-    strong {
-      text-decoration: underline;
-    }
-  }
-
-  &.inline {
-    padding: 0;
-    height: 18px;
-    font-size: 15px;
-    line-height: 18px;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    overflow: hidden;
-
-    strong {
-      display: inline;
-      height: auto;
-      font-size: inherit;
-      line-height: inherit;
-    }
-
-    span {
-      display: inline;
-      height: auto;
-      font-size: inherit;
-      line-height: inherit;
-    }
-  }
-}
-
-.display-name__html {
-  font-weight: 500;
-}
-
-.display-name__account {
-  font-size: 14px;
-}
-
-.image-loader {
-  position: relative;
-  width: 100%;
-  height: 100%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  flex-direction: column;
-  scrollbar-width: none; /* Firefox */
-  -ms-overflow-style: none;  /* IE 10+ */
-
-  * {
-    scrollbar-width: none; /* Firefox */
-    -ms-overflow-style: none;  /* IE 10+ */
-  }
-
-  &::-webkit-scrollbar,
-  *::-webkit-scrollbar {
-    width: 0;
-    height: 0;
-    background: transparent; /* Chrome/Safari/Webkit */
-  }
-
-  .image-loader__preview-canvas {
-    max-width: $media-modal-media-max-width;
-    max-height: $media-modal-media-max-height;
-    background: url('~images/void.png') repeat;
-    object-fit: contain;
-  }
-
-  .loading-bar__container {
-    position: relative;
-  }
-
-  .loading-bar {
-    position: absolute;
-  }
-
-  &.image-loader--amorphous .image-loader__preview-canvas {
-    display: none;
-  }
-}
-
-.zoomable-image {
-  position: relative;
-  width: 100%;
-  height: 100%;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-
-  img {
-    max-width: $media-modal-media-max-width;
-    max-height: $media-modal-media-max-height;
-    width: auto;
-    height: auto;
-    object-fit: contain;
-  }
-}
-
-.dropdown-animation {
-  animation: dropdown 300ms cubic-bezier(0.1, 0.7, 0.1, 1);
-
-  @keyframes dropdown {
-    from {
-      opacity: 0;
-      transform: scaleX(0.85) scaleY(0.75);
-    }
-
-    to {
-      opacity: 1;
-      transform: scaleX(1) scaleY(1);
-    }
-  }
-
-  &.top {
-    transform-origin: bottom;
-  }
-
-  &.right {
-    transform-origin: left;
-  }
-
-  &.bottom {
-    transform-origin: top;
-  }
-
-  &.left {
-    transform-origin: right;
-  }
-
-  .reduce-motion & {
-    animation: none;
-  }
-}
-
-.dropdown {
-  display: inline-block;
-}
-
-.dropdown__content {
-  display: none;
-  position: absolute;
-}
-
-.dropdown-menu__separator {
-  border-bottom: 1px solid darken($ui-secondary-color, 8%);
-  margin: 5px 7px 6px;
-  height: 0;
-}
-
-.dropdown-menu {
-  background: $ui-secondary-color;
-  padding: 4px 0;
-  border-radius: 4px;
-  box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
-  z-index: 9999;
-
-  &__text-button {
-    display: inline;
-    color: inherit;
-    background: transparent;
-    border: 0;
-    margin: 0;
-    padding: 0;
-    font-family: inherit;
-    font-size: inherit;
-    line-height: inherit;
-
-    &:focus {
-      outline: 1px dotted;
-    }
-  }
-
-  &__container {
-    &__header {
-      border-bottom: 1px solid darken($ui-secondary-color, 8%);
-      padding: 4px 14px;
-      padding-bottom: 8px;
-      font-size: 13px;
-      line-height: 18px;
-      color: $inverted-text-color;
-    }
-
-    &__list {
-      list-style: none;
-
-      &--scrollable {
-        max-height: 300px;
-        overflow-y: scroll;
-      }
-    }
-
-    &--loading {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      padding: 30px 45px;
-    }
-  }
-}
-
-.dropdown-menu__arrow {
-  position: absolute;
-
-  &::before {
-    content: '';
-    display: block;
-    width: 14px;
-    height: 5px;
-    background-color: $ui-secondary-color;
-    mask-image: url("data:image/svg+xml;utf8,<svg width='14' height='5' xmlns='http://www.w3.org/2000/svg'><path d='M7 0L0 5h14L7 0z' fill='white'/></svg>");
-  }
-
-  &.top {
-    bottom: -5px;
-
-    &::before {
-      transform: rotate(180deg);
-    }
-  }
-
-  &.right {
-    left: -9px;
-
-    &::before {
-      transform: rotate(-90deg);
-    }
-  }
-
-  &.bottom {
-    top: -5px;
-  }
-
-  &.left {
-    right: -9px;
-
-    &::before {
-      transform: rotate(90deg);
-    }
-  }
-}
-
-.dropdown-menu__item {
-  font-size: 13px;
-  line-height: 18px;
-  display: block;
-  color: $inverted-text-color;
-
-  a,
-  button {
-    font-family: inherit;
-    font-size: inherit;
-    line-height: inherit;
-    display: block;
-    width: 100%;
-    padding: 4px 14px;
-    border: 0;
-    margin: 0;
-    box-sizing: border-box;
-    text-decoration: none;
-    background: $ui-secondary-color;
-    color: inherit;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-    text-align: inherit;
-
-    &:focus,
-    &:hover,
-    &:active {
-      background: $ui-highlight-color;
-      color: $secondary-text-color;
-      outline: 0;
-    }
-  }
-}
-
-.dropdown-menu__item--text {
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  padding: 4px 14px;
-}
-
-.dropdown-menu__item.edited-timestamp__history__item {
-  border-bottom: 1px solid darken($ui-secondary-color, 8%);
-
-  &:last-child {
-    border-bottom: 0;
-  }
-
-  &.dropdown-menu__item--text,
-  a,
-  button {
-    padding: 8px 14px;
-  }
-}
-
-.inline-account {
-  display: inline-flex;
-  align-items: center;
-  vertical-align: top;
-
-  .account__avatar {
-    margin-right: 5px;
-    border-radius: 50%;
-  }
-
-  strong {
-    font-weight: 600;
-  }
-}
-
-.dropdown--active .dropdown__content {
-  display: block;
-  line-height: 18px;
-  max-width: 311px;
-  right: 0;
-  text-align: left;
-  z-index: 9999;
-
-  & > ul {
-    list-style: none;
-    background: $ui-secondary-color;
-    padding: 4px 0;
-    border-radius: 4px;
-    box-shadow: 0 0 15px rgba($base-shadow-color, 0.4);
-    min-width: 140px;
-    position: relative;
-  }
-
-  &.dropdown__right {
-    right: 0;
-  }
-
-  &.dropdown__left {
-    & > ul {
-      left: -98px;
-    }
-  }
-
-  & > ul > li > a {
-    font-size: 13px;
-    line-height: 18px;
-    display: block;
-    padding: 4px 14px;
-    box-sizing: border-box;
-    text-decoration: none;
-    background: $ui-secondary-color;
-    color: $inverted-text-color;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    white-space: nowrap;
-
-    &:focus {
-      outline: 0;
-    }
-
-    &:hover {
-      background: $ui-highlight-color;
-      color: $secondary-text-color;
-    }
-  }
-}
-
-.dropdown__icon {
-  vertical-align: middle;
-}
-
-.static-content {
-  padding: 10px;
-  padding-top: 20px;
-  color: $dark-text-color;
-
-  h1 {
-    font-size: 16px;
-    font-weight: 500;
-    margin-bottom: 40px;
-    text-align: center;
-  }
-
-  p {
-    font-size: 13px;
-    margin-bottom: 20px;
-  }
-}
-
-.column,
-.drawer {
-  flex: 1 1 100%;
-  overflow: hidden;
-}
-
-@media screen and (min-width: 631px) {
-  .columns-area {
-    padding: 0;
-  }
-
-  .column,
-  .drawer {
-    flex: 0 0 auto;
-    padding: 10px;
-    padding-left: 5px;
-    padding-right: 5px;
-
-    &:first-child {
-      padding-left: 10px;
-    }
-
-    &:last-child {
-      padding-right: 10px;
-    }
-  }
-
-  .columns-area > div {
-    .column,
-    .drawer {
-      padding-left: 5px;
-      padding-right: 5px;
-    }
-  }
-}
-
-.tabs-bar {
-  box-sizing: border-box;
-  display: flex;
-  background: lighten($ui-base-color, 8%);
-  flex: 0 0 auto;
-  overflow-y: auto;
-}
-
-.tabs-bar__link {
-  display: block;
-  flex: 1 1 auto;
-  padding: 15px 10px;
-  padding-bottom: 13px;
-  color: $primary-text-color;
-  text-decoration: none;
-  text-align: center;
-  font-size: 14px;
-  font-weight: 500;
-  border-bottom: 2px solid lighten($ui-base-color, 8%);
-  transition: all 50ms linear;
-  transition-property: border-bottom, background, color;
-
-  .fa {
-    font-weight: 400;
-    font-size: 16px;
-  }
-
-  &:hover,
-  &:focus,
-  &:active {
-    @include multi-columns('screen and (min-width: 631px)') {
-      background: lighten($ui-base-color, 14%);
-      border-bottom-color: lighten($ui-base-color, 14%);
-    }
-  }
-
-  &.active {
-    border-bottom: 2px solid $ui-highlight-color;
-    color: $highlight-text-color;
-  }
-
-  span {
-    margin-left: 5px;
-    display: none;
-  }
-
-  span.icon {
-    margin-left: 0;
-    display: inline;
-  }
-}
-
-.icon-with-badge {
-  position: relative;
-
-  &__badge {
-    position: absolute;
-    left: 9px;
-    top: -13px;
-    background: $ui-highlight-color;
-    border: 2px solid lighten($ui-base-color, 8%);
-    padding: 1px 6px;
-    border-radius: 6px;
-    font-size: 10px;
-    font-weight: 500;
-    line-height: 14px;
-    color: $primary-text-color;
-  }
-
-  &__issue-badge {
-    position: absolute;
-    left: 11px;
-    bottom: 1px;
-    display: block;
-    background: $error-red;
-    border-radius: 50%;
-    width: 0.625rem;
-    height: 0.625rem;
-  }
-}
-
-.column-link--transparent .icon-with-badge__badge {
-  border-color: darken($ui-base-color, 8%);
-}
-
-.scrollable {
-  overflow-y: scroll;
-  overflow-x: hidden;
-  flex: 1 1 auto;
-  -webkit-overflow-scrolling: touch;
-
-  &.optionally-scrollable {
-    overflow-y: auto;
-  }
-
-  @supports(display: grid) { // hack to fix Chrome <57
-    contain: strict;
-  }
-
-  &--flex {
-    display: flex;
-    flex-direction: column;
-  }
-
-  &__append {
-    flex: 1 1 auto;
-    position: relative;
-    min-height: 120px;
-  }
-
-  .scrollable {
-    flex: 1 1 auto;
-  }
-}
-
-.scrollable.fullscreen {
-  @supports(display: grid) { // hack to fix Chrome <57
-    contain: none;
-  }
-}
-
-.react-toggle {
-  display: inline-block;
-  position: relative;
-  cursor: pointer;
-  background-color: transparent;
-  border: 0;
-  padding: 0;
-  user-select: none;
-  -webkit-tap-highlight-color: rgba($base-overlay-background, 0);
-  -webkit-tap-highlight-color: transparent;
-}
-
-.react-toggle-screenreader-only {
-  border: 0;
-  clip: rect(0 0 0 0);
-  height: 1px;
-  margin: -1px;
-  overflow: hidden;
-  padding: 0;
-  position: absolute;
-  width: 1px;
-}
-
-.react-toggle--disabled {
-  cursor: not-allowed;
-  opacity: 0.5;
-  transition: opacity 0.25s;
-}
-
-.react-toggle-track {
-  width: 50px;
-  height: 24px;
-  padding: 0;
-  border-radius: 30px;
-  background-color: $ui-base-color;
-  transition: background-color 0.2s ease;
-}
-
-.react-toggle:is(:hover, :focus-within):not(.react-toggle--disabled) .react-toggle-track {
-  background-color: darken($ui-base-color, 10%);
-}
-
-.react-toggle--checked .react-toggle-track {
-  background-color: darken($ui-highlight-color, 2%);
-}
-
-.react-toggle--checked:is(:hover, :focus-within):not(.react-toggle--disabled) .react-toggle-track {
-  background-color: $ui-highlight-color;
-}
-
-.react-toggle-track-check {
-  position: absolute;
-  width: 14px;
-  height: 10px;
-  top: 0;
-  bottom: 0;
-  margin-top: auto;
-  margin-bottom: auto;
-  line-height: 0;
-  left: 8px;
-  opacity: 0;
-  transition: opacity 0.25s ease;
-}
-
-.react-toggle--checked .react-toggle-track-check {
-  opacity: 1;
-  transition: opacity 0.25s ease;
-}
-
-.react-toggle-track-x {
-  position: absolute;
-  width: 10px;
-  height: 10px;
-  top: 0;
-  bottom: 0;
-  margin-top: auto;
-  margin-bottom: auto;
-  line-height: 0;
-  right: 10px;
-  opacity: 1;
-  transition: opacity 0.25s ease;
-}
-
-.react-toggle--checked .react-toggle-track-x {
-  opacity: 0;
-}
-
-.react-toggle-thumb {
-  position: absolute;
-  top: 1px;
-  left: 1px;
-  width: 22px;
-  height: 22px;
-  border: 1px solid $ui-base-color;
-  border-radius: 50%;
-  background-color: darken($simple-background-color, 2%);
-  box-sizing: border-box;
-  transition: all 0.25s ease;
-  transition-property: border-color, left;
-}
-
-.react-toggle--checked .react-toggle-thumb {
-  left: 27px;
-  border-color: $ui-highlight-color;
-}
-
-.getting-started__wrapper,
-.getting_started,
-.flex-spacer {
-  background: $ui-base-color;
-}
-
-.getting-started__wrapper {
-  position: relative;
-  overflow-y: auto;
-}
-
-.flex-spacer {
-  flex: 1 1 auto;
-}
-
-.getting-started {
-  background: $ui-base-color;
-  flex: 1 0 auto;
-
-  p {
-    color: $secondary-text-color;
-  }
-
-  a {
-    color: $dark-text-color;
-  }
-
-  &__trends {
-    flex: 0 1 auto;
-    opacity: 1;
-    animation: fade 150ms linear;
-    margin-top: 10px;
-
-    h4 {
-      border-bottom: 1px solid lighten($ui-base-color, 8%);
-      padding: 10px;
-      font-size: 12px;
-      text-transform: uppercase;
-      font-weight: 500;
-
-      a {
-        color: $darker-text-color;
-        text-decoration: none;
-      }
-    }
-
-    @media screen and (max-height: 810px) {
-      .trends__item:nth-of-type(3) {
-        display: none;
-      }
-    }
-
-    @media screen and (max-height: 720px) {
-      .trends__item:nth-of-type(2) {
-        display: none;
-      }
-    }
-
-    @media screen and (max-height: 670px) {
-      display: none;
-    }
-
-    .trends__item {
-      border-bottom: 0;
-      padding: 10px;
-
-      &__current {
-        color: $darker-text-color;
-      }
-    }
-  }
-}
-
-.column-link__badge {
-  display: inline-block;
-  border-radius: 4px;
-  font-size: 12px;
-  line-height: 19px;
-  font-weight: 500;
-  background: $ui-base-color;
-  padding: 4px 8px;
-  margin: -6px 10px;
-}
-
-.keyboard-shortcuts {
-  padding: 8px 0 0;
-  overflow: hidden;
-
-  thead {
-    position: absolute;
-    left: -9999px;
-  }
-
-  td {
-    padding: 0 10px 8px;
-  }
-
-  kbd {
-    display: inline-block;
-    padding: 3px 5px;
-    background-color: lighten($ui-base-color, 8%);
-    border: 1px solid darken($ui-base-color, 4%);
-  }
-}
-
-.setting-text {
-  color: $darker-text-color;
-  background: transparent;
-  border: 0;
-  border-bottom: 2px solid $ui-primary-color;
-  outline: 0;
-  box-sizing: border-box;
-  display: block;
-  font-family: inherit;
-  margin-bottom: 10px;
-  padding: 7px 0;
-  width: 100%;
-
-  &:focus,
-  &:active {
-    color: $primary-text-color;
-    border-bottom-color: $ui-highlight-color;
-  }
-
-  @include limited-single-column('screen and (max-width: 600px)') {
-    font-size: 16px;
-  }
-
-  &.light {
-    color: $inverted-text-color;
-    border-bottom: 2px solid lighten($ui-base-color, 27%);
-
-    &:focus,
-    &:active {
-      color: $inverted-text-color;
-      border-bottom-color: $ui-highlight-color;
-    }
-  }
-}
-
-button.icon-button i.fa-retweet {
-  background-position: 0 0;
-  height: 19px;
-  transition: background-position 0.9s steps(10);
-  transition-duration: 0s;
-  vertical-align: middle;
-  width: 22px;
-
-  &::before {
-    display: none !important;
-  }
-}
-
-button.icon-button.active i.fa-retweet {
-  transition-duration: 0.9s;
-  background-position: 0 100%;
-}
-
-.reduce-motion button.icon-button i.fa-retweet,
-.reduce-motion button.icon-button.active i.fa-retweet {
-  transition: none;
-}
-
-.reduce-motion button.icon-button.disabled i.fa-retweet {
-  color: darken($action-button-color, 13%);
-}
-
-.load-more {
-  display: block;
-  color: $dark-text-color;
-  background-color: transparent;
-  border: 0;
-  font-size: inherit;
-  text-align: center;
-  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%);
-  }
-}
-
-.load-gap {
-  border-bottom: 1px solid lighten($ui-base-color, 8%);
-}
-
-.timeline-hint {
-  text-align: center;
-  color: $darker-text-color;
-  padding: 15px;
-  box-sizing: border-box;
-  width: 100%;
-  cursor: default;
-
-  strong {
-    font-weight: 500;
-  }
-
-  a {
-    color: $highlight-text-color;
-    text-decoration: none;
-
-    &:hover,
-    &:focus,
-    &:active {
-      text-decoration: underline;
-      color: lighten($highlight-text-color, 4%);
-    }
-  }
-}
-
-.missing-indicator {
-  padding-top: 20px + 48px;
-
-  .regeneration-indicator__figure {
-    background-image: url('~flavours/glitch/images/elephant_ui_disappointed.svg');
-  }
-}
-
-.scrollable > div > :first-child .notification__dismiss-overlay > .wrappy {
-  border-top: 1px solid $ui-base-color;
-}
-
-.notification__dismiss-overlay {
-  overflow: hidden;
-  position: absolute;
-  top: 0;
-  right: 0;
-  bottom: -1px;
-  padding-left: 15px; // space for the box shadow to be visible
-
-  z-index: 999;
-  align-items: center;
-  justify-content: flex-end;
-  cursor: pointer;
-
-  display: flex;
-
-  .wrappy {
-    width: $dismiss-overlay-width;
-    align-self: stretch;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    background: lighten($ui-base-color, 8%);
-    border-left: 1px solid lighten($ui-base-color, 20%);
-    box-shadow: 0 0 5px black;
-    border-bottom: 1px solid $ui-base-color;
-  }
-
-  .ckbox {
-    border: 2px solid $ui-primary-color;
-    border-radius: 2px;
-    width: 30px;
-    height: 30px;
-    font-size: 20px;
-    color: $darker-text-color;
-    text-shadow: 0 0 5px black;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-  }
-
-  &:focus {
-    outline: 0 !important;
-
-    .ckbox {
-      box-shadow: 0 0 1px 1px $ui-highlight-color;
-    }
-  }
-}
-
-.text-btn {
-  display: inline-block;
-  padding: 0;
-  font-family: inherit;
-  font-size: inherit;
-  color: inherit;
-  border: 0;
-  background: transparent;
-  cursor: pointer;
-}
-
-.loading-indicator {
-  color: $dark-text-color;
-  font-size: 12px;
-  font-weight: 400;
-  text-transform: uppercase;
-  overflow: visible;
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  transform: translate(-50%, -50%);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.circular-progress {
-  color: lighten($ui-base-color, 26%);
-  animation: 1.4s linear 0s infinite normal none running simple-rotate;
-
-  circle {
-    stroke: currentColor;
-    stroke-dasharray: 80px, 200px;
-    stroke-dashoffset: 0;
-    animation: circular-progress 1.4s ease-in-out infinite;
-  }
-}
-
-@keyframes circular-progress {
-  0% {
-    stroke-dasharray: 1px, 200px;
-    stroke-dashoffset: 0;
-  }
-
-  50% {
-    stroke-dasharray: 100px, 200px;
-    stroke-dashoffset: -15px;
-  }
-
-  100% {
-    stroke-dasharray: 100px, 200px;
-    stroke-dashoffset: -125px;
-  }
-}
-
-@keyframes simple-rotate {
-  0% {
-    transform: rotate(0deg);
-  }
-
-  100% {
-    transform: rotate(360deg);
-  }
-}
-
-@keyframes spring-rotate-in {
-  0% {
-    transform: rotate(0deg);
-  }
-
-  30% {
-    transform: rotate(-484.8deg);
-  }
-
-  60% {
-    transform: rotate(-316.7deg);
-  }
-
-  90% {
-    transform: rotate(-375deg);
-  }
-
-  100% {
-    transform: rotate(-360deg);
-  }
-}
-
-@keyframes spring-rotate-out {
-  0% {
-    transform: rotate(-360deg);
-  }
-
-  30% {
-    transform: rotate(124.8deg);
-  }
-
-  60% {
-    transform: rotate(-43.27deg);
-  }
-
-  90% {
-    transform: rotate(15deg);
-  }
-
-  100% {
-    transform: rotate(0deg);
-  }
-}
-
-.spoiler-button {
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  position: absolute;
-  z-index: 100;
-
-  &--minified {
-    display: flex;
-    left: 4px;
-    top: 4px;
-    width: auto;
-    height: auto;
-    align-items: center;
-  }
-
-  &--click-thru {
-    pointer-events: none;
-  }
-
-  &--hidden {
-    display: none;
-  }
-
-  &__overlay {
-    display: block;
-    background: transparent;
-    width: 100%;
-    height: 100%;
-    border: 0;
-
-    &__label {
-      display: inline-block;
-      background: rgba($base-overlay-background, 0.5);
-      border-radius: 8px;
-      padding: 8px 12px;
-      color: $primary-text-color;
-      font-weight: 500;
-      font-size: 14px;
-    }
-
-    &:hover,
-    &:focus,
-    &:active {
-      .spoiler-button__overlay__label {
-        background: rgba($base-overlay-background, 0.8);
-      }
-    }
-
-    &:disabled {
-      .spoiler-button__overlay__label {
-        background: rgba($base-overlay-background, 0.5);
-      }
-    }
-  }
-}
-
-.setting-toggle {
-  display: block;
-  line-height: 24px;
-}
-
-.setting-toggle__label,
-.setting-meta__label {
-  color: $darker-text-color;
-  display: inline-block;
-  margin-bottom: 14px;
-  margin-left: 8px;
-  vertical-align: middle;
-}
-
-.column-settings__row .radio-button {
-  display: block;
-}
-
-.setting-meta__label {
-  float: right;
-}
-
-@keyframes heartbeat {
-  from {
-    transform: scale(1);
-    transform-origin: center center;
-    animation-timing-function: ease-out;
-  }
-
-  10% {
-    transform: scale(0.91);
-    animation-timing-function: ease-in;
-  }
-
-  17% {
-    transform: scale(0.98);
-    animation-timing-function: ease-out;
-  }
-
-  33% {
-    transform: scale(0.87);
-    animation-timing-function: ease-in;
-  }
-
-  45% {
-    transform: scale(1);
-    animation-timing-function: ease-out;
-  }
-}
-
-.pulse-loading {
-  animation: heartbeat 1.5s ease-in-out infinite both;
-}
-
-.upload-area {
-  align-items: center;
-  background: rgba($base-overlay-background, 0.8);
-  display: flex;
-  height: 100%;
-  justify-content: center;
-  left: 0;
-  opacity: 0;
-  position: absolute;
-  top: 0;
-  visibility: hidden;
-  width: 100%;
-  z-index: 2000;
-
-  * {
-    pointer-events: none;
-  }
-}
-
-.upload-area__drop {
-  width: 320px;
-  height: 160px;
-  display: flex;
-  box-sizing: border-box;
-  position: relative;
-  padding: 8px;
-}
-
-.upload-area__background {
-  position: absolute;
-  top: 0;
-  right: 0;
-  bottom: 0;
-  left: 0;
-  z-index: -1;
-  border-radius: 4px;
-  background: $ui-base-color;
-  box-shadow: 0 0 5px rgba($base-shadow-color, 0.2);
-}
-
-.upload-area__content {
-  flex: 1;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  color: $secondary-text-color;
-  font-size: 18px;
-  font-weight: 500;
-  border: 2px dashed $ui-base-lighter-color;
-  border-radius: 4px;
-}
-
-.dropdown--active .emoji-button img {
-  opacity: 1;
-  filter: none;
-}
-
-.loading-bar {
-  background-color: $ui-highlight-color;
-  height: 3px;
-  position: fixed;
-  top: 0;
-  left: 0;
-  z-index: 9999;
-}
-
-.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;
-}
-
-.conversation {
-  display: flex;
-  border-bottom: 1px solid lighten($ui-base-color, 8%);
-  padding: 5px;
-  padding-bottom: 0;
-
-  &:focus {
-    background: lighten($ui-base-color, 2%);
-    outline: 0;
-  }
-
-  &__avatar {
-    flex: 0 0 auto;
-    padding: 10px;
-    padding-top: 12px;
-    position: relative;
-    cursor: pointer;
-  }
-
-  &__unread {
-    display: inline-block;
-    background: $highlight-text-color;
-    border-radius: 50%;
-    width: 0.625rem;
-    height: 0.625rem;
-    margin: -.1ex .15em .1ex;
-  }
-
-  &__content {
-    flex: 1 1 auto;
-    padding: 10px 5px;
-    padding-right: 15px;
-    overflow: hidden;
-
-    &__info {
-      overflow: hidden;
-      display: flex;
-      flex-direction: row-reverse;
-      justify-content: space-between;
-    }
-
-    &__relative-time {
-      font-size: 15px;
-      color: $darker-text-color;
-      padding-left: 15px;
-    }
-
-    &__names {
-      color: $darker-text-color;
-      font-size: 15px;
-      white-space: nowrap;
-      overflow: hidden;
-      text-overflow: ellipsis;
-      margin-bottom: 4px;
-      flex-basis: 90px;
-      flex-grow: 1;
-
-      a {
-        color: $primary-text-color;
-        text-decoration: none;
-
-        &:hover,
-        &:focus,
-        &:active {
-          text-decoration: underline;
-        }
-      }
-    }
-
-    .status__content {
-      margin: 0;
-    }
-  }
-
-  &--unread {
-    background: lighten($ui-base-color, 2%);
-
-    &:focus {
-      background: lighten($ui-base-color, 4%);
-    }
-
-    .conversation__content__info {
-      font-weight: 700;
-    }
-
-    .conversation__content__relative-time {
-      color: $primary-text-color;
-    }
-  }
-}
-
-.ui .flash-message {
-  margin-top: 10px;
-  margin-left: auto;
-  margin-right: auto;
-  margin-bottom: 0;
-  min-width: 75%;
-}
-
-::-webkit-scrollbar-thumb {
-  border-radius: 0;
-}
-
-noscript {
-  text-align: center;
-
-  img {
-    width: 200px;
-    opacity: 0.5;
-    animation: flicker 4s infinite;
-  }
-
-  div {
-    font-size: 14px;
-    margin: 30px auto;
-    color: $secondary-text-color;
-    max-width: 400px;
-
-    a {
-      color: $highlight-text-color;
-      text-decoration: underline;
-
-      &:hover {
-        text-decoration: none;
-      }
-    }
-
-    a {
-      word-break: break-word;
-    }
-  }
-}
-
-@keyframes flicker {
-  0% { opacity: 1; }
-  30% { opacity: 0.75; }
-  100% { opacity: 1; }
-}
-
+@import 'misc';
 @import 'boost';
 @import 'accounts';
 @import 'domains';
diff --git a/app/javascript/flavours/glitch/styles/components/local_settings.scss b/app/javascript/flavours/glitch/styles/components/local_settings.scss
index db2b9f154..52516cfb5 100644
--- a/app/javascript/flavours/glitch/styles/components/local_settings.scss
+++ b/app/javascript/flavours/glitch/styles/components/local_settings.scss
@@ -11,12 +11,14 @@
   max-height: 450px;
   overflow: hidden;
 
-  label, legend {
+  label,
+  legend {
     display: block;
     font-size: 14px;
   }
 
-  .boolean label, .radio_buttons label {
+  .boolean label,
+  .radio_buttons label {
     position: relative;
     padding-left: 28px;
     padding-top: 3px;
@@ -58,7 +60,7 @@
   cursor: pointer;
   text-decoration: none;
   outline: none;
-  transition: background .3s;
+  transition: background 0.3s;
 
   .text-icon-button {
     color: inherit;
@@ -74,7 +76,8 @@
     color: $primary-text-color;
   }
 
-  &.close, &.close:hover {
+  &.close,
+  &.close:hover {
     background: $error-value-color;
     color: $primary-text-color;
   }
@@ -91,7 +94,7 @@
 .glitch.local-settings__page {
   display: block;
   flex: auto;
-  padding: 15px 20px 15px 20px;
+  padding: 15px 20px;
   width: 360px;
   overflow-y: auto;
 }
@@ -110,6 +113,10 @@
       text-decoration: none;
     }
   }
+
+  #mastodon-settings--collapsed-auto-height {
+    width: calc(4ch + 20px);
+  }
 }
 
 .glitch.local-settings__page__item.string,
diff --git a/app/javascript/flavours/glitch/styles/components/media.scss b/app/javascript/flavours/glitch/styles/components/media.scss
index 9776e2265..6d6b8bc0e 100644
--- a/app/javascript/flavours/glitch/styles/components/media.scss
+++ b/app/javascript/flavours/glitch/styles/components/media.scss
@@ -348,7 +348,7 @@
   padding: 0;
   border: 0;
   font-size: 0;
-  transition: opacity .2s ease-in-out;
+  transition: opacity 0.2s ease-in-out;
 
   &.active {
     opacity: 1;
@@ -372,7 +372,6 @@
   .video-player__volume__handle {
     bottom: 23px;
   }
-
 }
 
 .audio-player {
@@ -506,10 +505,15 @@
     left: 0;
     right: 0;
     box-sizing: border-box;
-    background: linear-gradient(0deg, rgba($base-shadow-color, 0.85) 0, rgba($base-shadow-color, 0.45) 60%, transparent);
+    background: linear-gradient(
+      0deg,
+      rgba($base-shadow-color, 0.85) 0,
+      rgba($base-shadow-color, 0.45) 60%,
+      transparent
+    );
     padding: 0 15px;
     opacity: 0;
-    transition: opacity .1s ease;
+    transition: opacity 0.1s ease;
 
     &.active {
       opacity: 1;
@@ -591,7 +595,6 @@
     .player-button {
       display: inline-block;
       outline: 0;
-
       flex: 0 0 auto;
       background: transparent;
       padding: 5px;
@@ -655,7 +658,7 @@
     }
 
     &::before {
-      content: "";
+      content: '';
       width: 50px;
       background: rgba($white, 0.35);
       border-radius: 4px;
@@ -725,7 +728,7 @@
     position: relative;
 
     &::before {
-      content: "";
+      content: '';
       width: 100%;
       background: rgba($white, 0.35);
       border-radius: 4px;
@@ -762,7 +765,7 @@
       box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
 
       .no-reduce-motion & {
-        transition: opacity .1s ease;
+        transition: opacity 0.1s ease;
       }
 
       &.active {
diff --git a/app/javascript/flavours/glitch/styles/components/metadata.scss b/app/javascript/flavours/glitch/styles/components/metadata.scss
deleted file mode 100644
index e69de29bb..000000000
--- a/app/javascript/flavours/glitch/styles/components/metadata.scss
+++ /dev/null
diff --git a/app/javascript/flavours/glitch/styles/components/misc.scss b/app/javascript/flavours/glitch/styles/components/misc.scss
new file mode 100644
index 000000000..86b8b99c1
--- /dev/null
+++ b/app/javascript/flavours/glitch/styles/components/misc.scss
@@ -0,0 +1,1814 @@
+.app-body {
+  -webkit-overflow-scrolling: touch;
+  -ms-overflow-style: -ms-autohiding-scrollbar;
+}
+
+.animated-number {
+  display: inline-flex;
+  flex-direction: column;
+  align-items: stretch;
+  overflow: hidden;
+  position: relative;
+}
+
+.link-button {
+  display: block;
+  font-size: 15px;
+  line-height: 20px;
+  color: $highlight-text-color;
+  border: 0;
+  background: transparent;
+  padding: 0;
+  cursor: pointer;
+  text-decoration: none;
+
+  &--destructive {
+    color: $error-value-color;
+  }
+
+  &:hover,
+  &:active {
+    text-decoration: underline;
+  }
+
+  &:disabled {
+    color: $ui-primary-color;
+    cursor: default;
+  }
+}
+
+.button {
+  background-color: darken($ui-highlight-color, 3%);
+  border: 10px none;
+  border-radius: 4px;
+  box-sizing: border-box;
+  color: $primary-text-color;
+  cursor: pointer;
+  display: inline-block;
+  font-family: inherit;
+  font-size: 15px;
+  font-weight: 500;
+  letter-spacing: 0;
+  line-height: 22px;
+  overflow: hidden;
+  padding: 7px 18px;
+  position: relative;
+  text-align: center;
+  text-decoration: none;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  width: auto;
+
+  &:active,
+  &:focus,
+  &:hover {
+    background-color: $ui-highlight-color;
+  }
+
+  &--destructive {
+    &:active,
+    &:focus,
+    &:hover {
+      background-color: $error-red;
+      transition: none;
+    }
+  }
+
+  &:disabled {
+    background-color: $ui-primary-color;
+    cursor: default;
+  }
+
+  &.button-alternative {
+    color: $inverted-text-color;
+    background: $ui-primary-color;
+
+    &:active,
+    &:focus,
+    &:hover {
+      background-color: lighten($ui-primary-color, 4%);
+    }
+  }
+
+  &.button-alternative-2 {
+    background: $ui-base-lighter-color;
+
+    &:active,
+    &:focus,
+    &:hover {
+      background-color: lighten($ui-base-lighter-color, 4%);
+    }
+  }
+
+  &.button-secondary {
+    font-size: 16px;
+    line-height: 36px;
+    height: auto;
+    color: $darker-text-color;
+    text-transform: none;
+    background: transparent;
+    padding: 6px 17px;
+    border: 1px solid $ui-primary-color;
+
+    &:active,
+    &:focus,
+    &:hover {
+      border-color: lighten($ui-primary-color, 4%);
+      color: lighten($darker-text-color, 4%);
+      text-decoration: none;
+    }
+
+    &:disabled {
+      opacity: 0.5;
+    }
+  }
+
+  &.button-tertiary {
+    background: transparent;
+    padding: 6px 17px;
+    color: $highlight-text-color;
+    border: 1px solid $highlight-text-color;
+
+    &:active,
+    &:focus,
+    &:hover {
+      background: $ui-highlight-color;
+      color: $primary-text-color;
+      border: 0;
+      padding: 7px 18px;
+    }
+
+    &:disabled {
+      opacity: 0.5;
+    }
+
+    &.button--confirmation {
+      color: $valid-value-color;
+      border-color: $valid-value-color;
+
+      &:active,
+      &:focus,
+      &:hover {
+        background: $valid-value-color;
+        color: $primary-text-color;
+      }
+    }
+
+    &.button--destructive {
+      color: $error-value-color;
+      border-color: $error-value-color;
+
+      &:active,
+      &:focus,
+      &:hover {
+        background: $error-value-color;
+        color: $primary-text-color;
+      }
+    }
+  }
+
+  &.button--block {
+    display: block;
+    width: 100%;
+  }
+
+  .layout-multiple-columns &.button--with-bell {
+    font-size: 12px;
+    padding: 0 8px;
+  }
+}
+
+.icon-button {
+  display: inline-block;
+  padding: 0;
+  color: $action-button-color;
+  border: 0;
+  border-radius: 4px;
+  background: transparent;
+  cursor: pointer;
+  transition: all 100ms ease-in;
+  transition-property: background-color, color;
+  text-decoration: none;
+
+  a {
+    color: inherit;
+    text-decoration: none;
+  }
+
+  &:hover,
+  &:active,
+  &:focus {
+    color: lighten($action-button-color, 7%);
+    background-color: rgba($action-button-color, 0.15);
+    transition: all 200ms ease-out;
+    transition-property: background-color, color;
+  }
+
+  &:focus {
+    background-color: rgba($action-button-color, 0.3);
+  }
+
+  &.disabled {
+    color: darken($action-button-color, 13%);
+    background-color: transparent;
+    cursor: default;
+  }
+
+  &.active {
+    color: $highlight-text-color;
+  }
+
+  &.copyable {
+    transition: background 300ms linear;
+  }
+
+  &.copied {
+    background: $valid-value-color;
+    transition: none;
+  }
+
+  &::-moz-focus-inner {
+    border: 0;
+  }
+
+  &::-moz-focus-inner,
+  &:focus,
+  &:active {
+    outline: 0 !important;
+  }
+
+  &.inverted {
+    color: $lighter-text-color;
+
+    &:hover,
+    &:active,
+    &:focus {
+      color: darken($lighter-text-color, 7%);
+      background-color: rgba($lighter-text-color, 0.15);
+    }
+
+    &:focus {
+      background-color: rgba($lighter-text-color, 0.3);
+    }
+
+    &.disabled {
+      color: lighten($lighter-text-color, 7%);
+      background-color: transparent;
+    }
+
+    &.active {
+      color: $highlight-text-color;
+
+      &.disabled {
+        color: lighten($highlight-text-color, 13%);
+      }
+    }
+  }
+
+  &.overlayed {
+    box-sizing: content-box;
+    background: rgba($base-overlay-background, 0.6);
+    color: rgba($primary-text-color, 0.7);
+    border-radius: 4px;
+    padding: 2px;
+
+    &:hover {
+      background: rgba($base-overlay-background, 0.9);
+    }
+  }
+
+  &--with-counter {
+    display: inline-flex;
+    align-items: center;
+    width: auto !important;
+    padding: 0 4px 0 2px;
+  }
+
+  &__counter {
+    display: inline-block;
+    width: auto;
+    margin-left: 4px;
+    font-size: 12px;
+    font-weight: 500;
+  }
+}
+
+.text-icon,
+.text-icon-button {
+  font-weight: 600;
+  font-size: 11px;
+  line-height: 27px;
+  cursor: default;
+}
+
+.text-icon-button {
+  color: $lighter-text-color;
+  border: 0;
+  border-radius: 4px;
+  background: transparent;
+  cursor: pointer;
+  padding: 0 3px;
+  outline: 0;
+  transition: all 100ms ease-in;
+  transition-property: background-color, color;
+
+  &:hover,
+  &:active,
+  &:focus {
+    color: darken($lighter-text-color, 7%);
+    background-color: rgba($lighter-text-color, 0.15);
+    transition: all 200ms ease-out;
+    transition-property: background-color, color;
+  }
+
+  &:focus {
+    background-color: rgba($lighter-text-color, 0.3);
+  }
+
+  &.disabled {
+    color: lighten($lighter-text-color, 20%);
+    background-color: transparent;
+    cursor: default;
+  }
+
+  &.active {
+    color: $highlight-text-color;
+  }
+
+  &::-moz-focus-inner {
+    border: 0;
+  }
+
+  &::-moz-focus-inner,
+  &:focus,
+  &:active {
+    outline: 0 !important;
+  }
+}
+
+body > [data-popper-placement] {
+  z-index: 3;
+}
+
+.invisible {
+  font-size: 0;
+  line-height: 0;
+  display: inline-block;
+  width: 0;
+  height: 0;
+  position: absolute;
+
+  img,
+  svg {
+    margin: 0 !important;
+    border: 0 !important;
+    padding: 0 !important;
+    width: 0 !important;
+    height: 0 !important;
+  }
+}
+
+.ellipsis {
+  &::after {
+    content: '…';
+  }
+}
+
+.notification__favourite-icon-wrapper {
+  left: 0;
+  position: absolute;
+
+  .fa.star-icon {
+    color: $gold-star;
+  }
+}
+
+.icon-button.star-icon.active {
+  color: $gold-star;
+}
+
+.icon-button.bookmark-icon.active {
+  color: $red-bookmark;
+}
+
+.no-reduce-motion .icon-button.star-icon {
+  &.activate {
+    & > .fa-star {
+      animation: spring-rotate-in 1s linear;
+    }
+  }
+
+  &.deactivate {
+    & > .fa-star {
+      animation: spring-rotate-out 1s linear;
+    }
+  }
+}
+
+.notification__display-name {
+  color: inherit;
+  font-weight: 500;
+  text-decoration: none;
+
+  &:hover {
+    color: $primary-text-color;
+    text-decoration: underline;
+  }
+}
+
+.display-name {
+  display: block;
+  max-width: 100%;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+
+  a {
+    color: inherit;
+    text-decoration: inherit;
+  }
+
+  strong {
+    display: block;
+  }
+
+  > a:hover {
+    strong {
+      text-decoration: underline;
+    }
+  }
+
+  &.inline {
+    padding: 0;
+    height: 18px;
+    font-size: 15px;
+    line-height: 18px;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    overflow: hidden;
+
+    strong {
+      display: inline;
+      height: auto;
+      font-size: inherit;
+      line-height: inherit;
+    }
+
+    span {
+      display: inline;
+      height: auto;
+      font-size: inherit;
+      line-height: inherit;
+    }
+  }
+}
+
+.display-name__html {
+  font-weight: 500;
+}
+
+.display-name__account {
+  font-size: 14px;
+}
+
+.image-loader {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+  scrollbar-width: none; /* Firefox */
+  -ms-overflow-style: none; /* IE 10+ */
+
+  * {
+    scrollbar-width: none; /* Firefox */
+    -ms-overflow-style: none; /* IE 10+ */
+  }
+
+  &::-webkit-scrollbar,
+  *::-webkit-scrollbar {
+    width: 0;
+    height: 0;
+    background: transparent; /* Chrome/Safari/Webkit */
+  }
+
+  .image-loader__preview-canvas {
+    max-width: $media-modal-media-max-width;
+    max-height: $media-modal-media-max-height;
+    background: url('~images/void.png') repeat;
+    object-fit: contain;
+  }
+
+  .loading-bar__container {
+    position: relative;
+  }
+
+  .loading-bar {
+    position: absolute;
+  }
+
+  &.image-loader--amorphous .image-loader__preview-canvas {
+    display: none;
+  }
+}
+
+.zoomable-image {
+  position: relative;
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  img {
+    max-width: $media-modal-media-max-width;
+    max-height: $media-modal-media-max-height;
+    width: auto;
+    height: auto;
+    object-fit: contain;
+  }
+}
+
+.dropdown-animation {
+  animation: dropdown 300ms cubic-bezier(0.1, 0.7, 0.1, 1);
+
+  @keyframes dropdown {
+    from {
+      opacity: 0;
+      transform: scaleX(0.85) scaleY(0.75);
+    }
+
+    to {
+      opacity: 1;
+      transform: scaleX(1) scaleY(1);
+    }
+  }
+
+  &.top {
+    transform-origin: bottom;
+  }
+
+  &.right {
+    transform-origin: left;
+  }
+
+  &.bottom {
+    transform-origin: top;
+  }
+
+  &.left {
+    transform-origin: right;
+  }
+
+  .reduce-motion & {
+    animation: none;
+  }
+}
+
+.dropdown {
+  display: inline-block;
+}
+
+.dropdown__content {
+  display: none;
+  position: absolute;
+}
+
+.dropdown-menu__separator {
+  border-bottom: 1px solid darken($ui-secondary-color, 8%);
+  margin: 5px 7px 6px;
+  height: 0;
+}
+
+.dropdown-menu {
+  background: $ui-secondary-color;
+  padding: 4px 0;
+  border-radius: 4px;
+  box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
+  z-index: 9999;
+
+  &__text-button {
+    display: inline;
+    color: inherit;
+    background: transparent;
+    border: 0;
+    margin: 0;
+    padding: 0;
+    font-family: inherit;
+    font-size: inherit;
+    line-height: inherit;
+
+    &:focus {
+      outline: 1px dotted;
+    }
+  }
+
+  &__container {
+    &__header {
+      border-bottom: 1px solid darken($ui-secondary-color, 8%);
+      padding: 4px 14px;
+      padding-bottom: 8px;
+      font-size: 13px;
+      line-height: 18px;
+      color: $inverted-text-color;
+    }
+
+    &__list {
+      list-style: none;
+
+      &--scrollable {
+        max-height: 300px;
+        overflow-y: scroll;
+      }
+    }
+
+    &--loading {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      padding: 30px 45px;
+    }
+  }
+}
+
+.dropdown-menu__arrow {
+  position: absolute;
+
+  &::before {
+    content: '';
+    display: block;
+    width: 14px;
+    height: 5px;
+    background-color: $ui-secondary-color;
+    mask-image: url("data:image/svg+xml;utf8,<svg width='14' height='5' xmlns='http://www.w3.org/2000/svg'><path d='M7 0L0 5h14L7 0z' fill='white'/></svg>");
+  }
+
+  &.top {
+    bottom: -5px;
+
+    &::before {
+      transform: rotate(180deg);
+    }
+  }
+
+  &.right {
+    left: -9px;
+
+    &::before {
+      transform: rotate(-90deg);
+    }
+  }
+
+  &.bottom {
+    top: -5px;
+  }
+
+  &.left {
+    right: -9px;
+
+    &::before {
+      transform: rotate(90deg);
+    }
+  }
+}
+
+.dropdown-menu__item {
+  font-size: 13px;
+  line-height: 18px;
+  display: block;
+  color: $inverted-text-color;
+
+  a,
+  button {
+    font-family: inherit;
+    font-size: inherit;
+    line-height: inherit;
+    display: block;
+    width: 100%;
+    padding: 4px 14px;
+    border: 0;
+    margin: 0;
+    box-sizing: border-box;
+    text-decoration: none;
+    background: $ui-secondary-color;
+    color: inherit;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    text-align: inherit;
+
+    &:focus,
+    &:hover,
+    &:active {
+      background: $ui-highlight-color;
+      color: $secondary-text-color;
+      outline: 0;
+    }
+  }
+}
+
+.dropdown-menu__item--text {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  padding: 4px 14px;
+}
+
+.dropdown-menu__item.edited-timestamp__history__item {
+  border-bottom: 1px solid darken($ui-secondary-color, 8%);
+
+  &:last-child {
+    border-bottom: 0;
+  }
+
+  &.dropdown-menu__item--text,
+  a,
+  button {
+    padding: 8px 14px;
+  }
+}
+
+.inline-account {
+  display: inline-flex;
+  align-items: center;
+  vertical-align: top;
+
+  .account__avatar {
+    margin-right: 5px;
+    border-radius: 50%;
+  }
+
+  strong {
+    font-weight: 600;
+  }
+}
+
+.dropdown--active .dropdown__content {
+  display: block;
+  line-height: 18px;
+  max-width: 311px;
+  right: 0;
+  text-align: left;
+  z-index: 9999;
+
+  & > ul {
+    list-style: none;
+    background: $ui-secondary-color;
+    padding: 4px 0;
+    border-radius: 4px;
+    box-shadow: 0 0 15px rgba($base-shadow-color, 0.4);
+    min-width: 140px;
+    position: relative;
+  }
+
+  &.dropdown__right {
+    right: 0;
+  }
+
+  &.dropdown__left {
+    & > ul {
+      left: -98px;
+    }
+  }
+
+  & > ul > li > a {
+    font-size: 13px;
+    line-height: 18px;
+    display: block;
+    padding: 4px 14px;
+    box-sizing: border-box;
+    text-decoration: none;
+    background: $ui-secondary-color;
+    color: $inverted-text-color;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+
+    &:focus {
+      outline: 0;
+    }
+
+    &:hover {
+      background: $ui-highlight-color;
+      color: $secondary-text-color;
+    }
+  }
+}
+
+.dropdown__icon {
+  vertical-align: middle;
+}
+
+.static-content {
+  padding: 10px;
+  padding-top: 20px;
+  color: $dark-text-color;
+
+  h1 {
+    font-size: 16px;
+    font-weight: 500;
+    margin-bottom: 40px;
+    text-align: center;
+  }
+
+  p {
+    font-size: 13px;
+    margin-bottom: 20px;
+  }
+}
+
+.column,
+.drawer {
+  flex: 1 1 100%;
+  overflow: hidden;
+}
+
+@media screen and (min-width: 631px) {
+  .columns-area {
+    padding: 0;
+  }
+
+  .column,
+  .drawer {
+    flex: 0 0 auto;
+    padding: 10px;
+    padding-left: 5px;
+    padding-right: 5px;
+
+    &:first-child {
+      padding-left: 10px;
+    }
+
+    &:last-child {
+      padding-right: 10px;
+    }
+  }
+
+  .columns-area > div {
+    .column,
+    .drawer {
+      padding-left: 5px;
+      padding-right: 5px;
+    }
+  }
+}
+
+.tabs-bar {
+  box-sizing: border-box;
+  display: flex;
+  background: lighten($ui-base-color, 8%);
+  flex: 0 0 auto;
+  overflow-y: auto;
+}
+
+.tabs-bar__link {
+  display: block;
+  flex: 1 1 auto;
+  padding: 15px 10px;
+  padding-bottom: 13px;
+  color: $primary-text-color;
+  text-decoration: none;
+  text-align: center;
+  font-size: 14px;
+  font-weight: 500;
+  border-bottom: 2px solid lighten($ui-base-color, 8%);
+  transition: all 50ms linear;
+  transition-property: border-bottom, background, color;
+
+  .fa {
+    font-weight: 400;
+    font-size: 16px;
+  }
+
+  &:hover,
+  &:focus,
+  &:active {
+    @include multi-columns('screen and (min-width: 631px)') {
+      background: lighten($ui-base-color, 14%);
+      border-bottom-color: lighten($ui-base-color, 14%);
+    }
+  }
+
+  &.active {
+    border-bottom: 2px solid $ui-highlight-color;
+    color: $highlight-text-color;
+  }
+
+  span {
+    margin-left: 5px;
+    display: none;
+  }
+
+  span.icon {
+    margin-left: 0;
+    display: inline;
+  }
+}
+
+.icon-with-badge {
+  position: relative;
+
+  &__badge {
+    position: absolute;
+    left: 9px;
+    top: -13px;
+    background: $ui-highlight-color;
+    border: 2px solid lighten($ui-base-color, 8%);
+    padding: 1px 6px;
+    border-radius: 6px;
+    font-size: 10px;
+    font-weight: 500;
+    line-height: 14px;
+    color: $primary-text-color;
+  }
+
+  &__issue-badge {
+    position: absolute;
+    left: 11px;
+    bottom: 1px;
+    display: block;
+    background: $error-red;
+    border-radius: 50%;
+    width: 0.625rem;
+    height: 0.625rem;
+  }
+}
+
+.column-link--transparent .icon-with-badge__badge {
+  border-color: darken($ui-base-color, 8%);
+}
+
+.scrollable {
+  overflow-y: scroll;
+  overflow-x: hidden;
+  flex: 1 1 auto;
+  -webkit-overflow-scrolling: touch;
+
+  &.optionally-scrollable {
+    overflow-y: auto;
+  }
+
+  @supports (display: grid) {
+    // hack to fix Chrome <57
+    contain: strict;
+  }
+
+  &--flex {
+    display: flex;
+    flex-direction: column;
+  }
+
+  &__append {
+    flex: 1 1 auto;
+    position: relative;
+    min-height: 120px;
+  }
+
+  .scrollable {
+    flex: 1 1 auto;
+  }
+}
+
+.scrollable.fullscreen {
+  @supports (display: grid) {
+    // hack to fix Chrome <57
+    contain: none;
+  }
+}
+
+.react-toggle {
+  display: inline-block;
+  position: relative;
+  cursor: pointer;
+  background-color: transparent;
+  border: 0;
+  padding: 0;
+  user-select: none;
+  -webkit-tap-highlight-color: rgba($base-overlay-background, 0);
+  -webkit-tap-highlight-color: transparent;
+}
+
+.react-toggle-screenreader-only {
+  border: 0;
+  clip: rect(0 0 0 0);
+  height: 1px;
+  margin: -1px;
+  overflow: hidden;
+  padding: 0;
+  position: absolute;
+  width: 1px;
+}
+
+.react-toggle--disabled {
+  cursor: not-allowed;
+  opacity: 0.5;
+  transition: opacity 0.25s;
+}
+
+.react-toggle-track {
+  width: 50px;
+  height: 24px;
+  padding: 0;
+  border-radius: 30px;
+  background-color: $ui-base-color;
+  transition: background-color 0.2s ease;
+}
+
+.react-toggle:is(:hover, :focus-within):not(.react-toggle--disabled)
+  .react-toggle-track {
+  background-color: darken($ui-base-color, 10%);
+}
+
+.react-toggle--checked .react-toggle-track {
+  background-color: darken($ui-highlight-color, 2%);
+}
+
+.react-toggle--checked:is(:hover, :focus-within):not(.react-toggle--disabled)
+  .react-toggle-track {
+  background-color: $ui-highlight-color;
+}
+
+.react-toggle-track-check {
+  position: absolute;
+  width: 14px;
+  height: 10px;
+  top: 0;
+  bottom: 0;
+  margin-top: auto;
+  margin-bottom: auto;
+  line-height: 0;
+  left: 8px;
+  opacity: 0;
+  transition: opacity 0.25s ease;
+}
+
+.react-toggle--checked .react-toggle-track-check {
+  opacity: 1;
+  transition: opacity 0.25s ease;
+}
+
+.react-toggle-track-x {
+  position: absolute;
+  width: 10px;
+  height: 10px;
+  top: 0;
+  bottom: 0;
+  margin-top: auto;
+  margin-bottom: auto;
+  line-height: 0;
+  right: 10px;
+  opacity: 1;
+  transition: opacity 0.25s ease;
+}
+
+.react-toggle--checked .react-toggle-track-x {
+  opacity: 0;
+}
+
+.react-toggle-thumb {
+  position: absolute;
+  top: 1px;
+  left: 1px;
+  width: 22px;
+  height: 22px;
+  border: 1px solid $ui-base-color;
+  border-radius: 50%;
+  background-color: darken($simple-background-color, 2%);
+  box-sizing: border-box;
+  transition: all 0.25s ease;
+  transition-property: border-color, left;
+}
+
+.react-toggle--checked .react-toggle-thumb {
+  left: 27px;
+  border-color: $ui-highlight-color;
+}
+
+.getting-started__wrapper,
+.getting_started,
+.flex-spacer {
+  background: $ui-base-color;
+}
+
+.getting-started__wrapper {
+  position: relative;
+  overflow-y: auto;
+}
+
+.flex-spacer {
+  flex: 1 1 auto;
+}
+
+.getting-started {
+  background: $ui-base-color;
+  flex: 1 0 auto;
+
+  p {
+    color: $secondary-text-color;
+  }
+
+  a {
+    color: $dark-text-color;
+  }
+
+  &__trends {
+    flex: 0 1 auto;
+    opacity: 1;
+    animation: fade 150ms linear;
+    margin-top: 10px;
+
+    h4 {
+      border-bottom: 1px solid lighten($ui-base-color, 8%);
+      padding: 10px;
+      font-size: 12px;
+      text-transform: uppercase;
+      font-weight: 500;
+
+      a {
+        color: $darker-text-color;
+        text-decoration: none;
+      }
+    }
+
+    @media screen and (max-height: 810px) {
+      .trends__item:nth-of-type(3) {
+        display: none;
+      }
+    }
+
+    @media screen and (max-height: 720px) {
+      .trends__item:nth-of-type(2) {
+        display: none;
+      }
+    }
+
+    @media screen and (max-height: 670px) {
+      display: none;
+    }
+
+    .trends__item {
+      border-bottom: 0;
+      padding: 10px;
+
+      &__current {
+        color: $darker-text-color;
+      }
+    }
+  }
+}
+
+.column-link__badge {
+  display: inline-block;
+  border-radius: 4px;
+  font-size: 12px;
+  line-height: 19px;
+  font-weight: 500;
+  background: $ui-base-color;
+  padding: 4px 8px;
+  margin: -6px 10px;
+}
+
+.keyboard-shortcuts {
+  padding: 8px 0 0;
+  overflow: hidden;
+
+  thead {
+    position: absolute;
+    left: -9999px;
+  }
+
+  td {
+    padding: 0 10px 8px;
+  }
+
+  kbd {
+    display: inline-block;
+    padding: 3px 5px;
+    background-color: lighten($ui-base-color, 8%);
+    border: 1px solid darken($ui-base-color, 4%);
+  }
+}
+
+.setting-text {
+  color: $darker-text-color;
+  background: transparent;
+  border: 0;
+  border-bottom: 2px solid $ui-primary-color;
+  outline: 0;
+  box-sizing: border-box;
+  display: block;
+  font-family: inherit;
+  margin-bottom: 10px;
+  padding: 7px 0;
+  width: 100%;
+
+  &:focus,
+  &:active {
+    color: $primary-text-color;
+    border-bottom-color: $ui-highlight-color;
+  }
+
+  @include limited-single-column('screen and (max-width: 600px)') {
+    font-size: 16px;
+  }
+
+  &.light {
+    color: $inverted-text-color;
+    border-bottom: 2px solid lighten($ui-base-color, 27%);
+
+    &:focus,
+    &:active {
+      color: $inverted-text-color;
+      border-bottom-color: $ui-highlight-color;
+    }
+  }
+}
+
+button.icon-button i.fa-retweet {
+  background-position: 0 0;
+  height: 19px;
+  transition: background-position 0.9s steps(10);
+  transition-duration: 0s;
+  vertical-align: middle;
+  width: 22px;
+
+  &::before {
+    display: none !important;
+  }
+}
+
+button.icon-button.active i.fa-retweet {
+  transition-duration: 0.9s;
+  background-position: 0 100%;
+}
+
+.reduce-motion button.icon-button i.fa-retweet,
+.reduce-motion button.icon-button.active i.fa-retweet {
+  transition: none;
+}
+
+.reduce-motion button.icon-button.disabled i.fa-retweet {
+  color: darken($action-button-color, 13%);
+}
+
+.load-more {
+  display: block;
+  color: $dark-text-color;
+  background-color: transparent;
+  border: 0;
+  font-size: inherit;
+  text-align: center;
+  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%);
+  }
+}
+
+.load-gap {
+  border-bottom: 1px solid lighten($ui-base-color, 8%);
+}
+
+.timeline-hint {
+  text-align: center;
+  color: $darker-text-color;
+  padding: 15px;
+  box-sizing: border-box;
+  width: 100%;
+  cursor: default;
+
+  strong {
+    font-weight: 500;
+  }
+
+  a {
+    color: $highlight-text-color;
+    text-decoration: none;
+
+    &:hover,
+    &:focus,
+    &:active {
+      text-decoration: underline;
+      color: lighten($highlight-text-color, 4%);
+    }
+  }
+}
+
+.missing-indicator {
+  padding-top: 20px + 48px;
+
+  .regeneration-indicator__figure {
+    background-image: url('~flavours/glitch/images/elephant_ui_disappointed.svg');
+  }
+}
+
+.scrollable > div > :first-child .notification__dismiss-overlay > .wrappy {
+  border-top: 1px solid $ui-base-color;
+}
+
+.notification__dismiss-overlay {
+  overflow: hidden;
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: -1px;
+  padding-left: 15px; // space for the box shadow to be visible
+  z-index: 999;
+  align-items: center;
+  justify-content: flex-end;
+  cursor: pointer;
+  display: flex;
+
+  .wrappy {
+    width: $dismiss-overlay-width;
+    align-self: stretch;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    background: lighten($ui-base-color, 8%);
+    border-left: 1px solid lighten($ui-base-color, 20%);
+    box-shadow: 0 0 5px black;
+    border-bottom: 1px solid $ui-base-color;
+  }
+
+  .ckbox {
+    border: 2px solid $ui-primary-color;
+    border-radius: 2px;
+    width: 30px;
+    height: 30px;
+    font-size: 20px;
+    color: $darker-text-color;
+    text-shadow: 0 0 5px black;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+
+  &:focus {
+    outline: 0 !important;
+
+    .ckbox {
+      box-shadow: 0 0 1px 1px $ui-highlight-color;
+    }
+  }
+}
+
+.text-btn {
+  display: inline-block;
+  padding: 0;
+  font-family: inherit;
+  font-size: inherit;
+  color: inherit;
+  border: 0;
+  background: transparent;
+  cursor: pointer;
+}
+
+.loading-indicator {
+  color: $dark-text-color;
+  font-size: 12px;
+  font-weight: 400;
+  text-transform: uppercase;
+  overflow: visible;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.circular-progress {
+  color: lighten($ui-base-color, 26%);
+  animation: 1.4s linear 0s infinite normal none running simple-rotate;
+
+  circle {
+    stroke: currentColor;
+    stroke-dasharray: 80px, 200px;
+    stroke-dashoffset: 0;
+    animation: circular-progress 1.4s ease-in-out infinite;
+  }
+}
+
+@keyframes circular-progress {
+  0% {
+    stroke-dasharray: 1px, 200px;
+    stroke-dashoffset: 0;
+  }
+
+  50% {
+    stroke-dasharray: 100px, 200px;
+    stroke-dashoffset: -15px;
+  }
+
+  100% {
+    stroke-dasharray: 100px, 200px;
+    stroke-dashoffset: -125px;
+  }
+}
+
+@keyframes simple-rotate {
+  0% {
+    transform: rotate(0deg);
+  }
+
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+@keyframes spring-rotate-in {
+  0% {
+    transform: rotate(0deg);
+  }
+
+  30% {
+    transform: rotate(-484.8deg);
+  }
+
+  60% {
+    transform: rotate(-316.7deg);
+  }
+
+  90% {
+    transform: rotate(-375deg);
+  }
+
+  100% {
+    transform: rotate(-360deg);
+  }
+}
+
+@keyframes spring-rotate-out {
+  0% {
+    transform: rotate(-360deg);
+  }
+
+  30% {
+    transform: rotate(124.8deg);
+  }
+
+  60% {
+    transform: rotate(-43.27deg);
+  }
+
+  90% {
+    transform: rotate(15deg);
+  }
+
+  100% {
+    transform: rotate(0deg);
+  }
+}
+
+.spoiler-button {
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  z-index: 100;
+
+  &--minified {
+    display: flex;
+    left: 4px;
+    top: 4px;
+    width: auto;
+    height: auto;
+    align-items: center;
+  }
+
+  &--click-thru {
+    pointer-events: none;
+  }
+
+  &--hidden {
+    display: none;
+  }
+
+  &__overlay {
+    display: block;
+    background: transparent;
+    width: 100%;
+    height: 100%;
+    border: 0;
+
+    &__label {
+      display: inline-block;
+      background: rgba($base-overlay-background, 0.5);
+      border-radius: 8px;
+      padding: 8px 12px;
+      color: $primary-text-color;
+      font-weight: 500;
+      font-size: 14px;
+    }
+
+    &:hover,
+    &:focus,
+    &:active {
+      .spoiler-button__overlay__label {
+        background: rgba($base-overlay-background, 0.8);
+      }
+    }
+
+    &:disabled {
+      .spoiler-button__overlay__label {
+        background: rgba($base-overlay-background, 0.5);
+      }
+    }
+  }
+}
+
+.setting-toggle {
+  display: block;
+  line-height: 24px;
+}
+
+.setting-toggle__label,
+.setting-meta__label {
+  color: $darker-text-color;
+  display: inline-block;
+  margin-bottom: 14px;
+  margin-left: 8px;
+  vertical-align: middle;
+}
+
+.column-settings__row .radio-button {
+  display: block;
+}
+
+.setting-meta__label {
+  float: right;
+}
+
+@keyframes heartbeat {
+  0% {
+    transform: scale(1);
+    transform-origin: center center;
+    animation-timing-function: ease-out;
+  }
+
+  10% {
+    transform: scale(0.91);
+    animation-timing-function: ease-in;
+  }
+
+  17% {
+    transform: scale(0.98);
+    animation-timing-function: ease-out;
+  }
+
+  33% {
+    transform: scale(0.87);
+    animation-timing-function: ease-in;
+  }
+
+  45% {
+    transform: scale(1);
+    animation-timing-function: ease-out;
+  }
+}
+
+.pulse-loading {
+  animation: heartbeat 1.5s ease-in-out infinite both;
+}
+
+.upload-area {
+  align-items: center;
+  background: rgba($base-overlay-background, 0.8);
+  display: flex;
+  height: 100vh;
+  justify-content: center;
+  left: 0;
+  opacity: 0;
+  position: fixed;
+  top: 0;
+  visibility: hidden;
+  width: 100vw;
+  z-index: 2000;
+
+  * {
+    pointer-events: none;
+  }
+}
+
+.upload-area__drop {
+  width: 320px;
+  height: 160px;
+  display: flex;
+  box-sizing: border-box;
+  position: relative;
+  padding: 8px;
+}
+
+.upload-area__background {
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: -1;
+  border-radius: 4px;
+  background: $ui-base-color;
+  box-shadow: 0 0 5px rgba($base-shadow-color, 0.2);
+}
+
+.upload-area__content {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  text-align: center;
+  color: $secondary-text-color;
+  font-size: 18px;
+  font-weight: 500;
+  border: 2px dashed $ui-base-lighter-color;
+  border-radius: 4px;
+}
+
+.dropdown--active .emoji-button img {
+  opacity: 1;
+  filter: none;
+}
+
+.loading-bar {
+  background-color: $ui-highlight-color;
+  height: 3px;
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 9999;
+}
+
+.icon-badge-wrapper {
+  position: relative;
+}
+
+.icon-badge {
+  position: absolute;
+  display: block;
+  right: -0.25em;
+  top: -0.25em;
+  background-color: $ui-highlight-color;
+  border-radius: 50%;
+  font-size: 75%;
+  width: 1em;
+  height: 1em;
+}
+
+.conversation {
+  display: flex;
+  border-bottom: 1px solid lighten($ui-base-color, 8%);
+  padding: 5px;
+  padding-bottom: 0;
+
+  &:focus {
+    background: lighten($ui-base-color, 2%);
+    outline: 0;
+  }
+
+  &__avatar {
+    flex: 0 0 auto;
+    padding: 10px;
+    padding-top: 12px;
+    position: relative;
+    cursor: pointer;
+  }
+
+  &__unread {
+    display: inline-block;
+    background: $highlight-text-color;
+    border-radius: 50%;
+    width: 0.625rem;
+    height: 0.625rem;
+    margin: -0.1ex 0.15em 0.1ex;
+  }
+
+  &__content {
+    flex: 1 1 auto;
+    padding: 10px 5px;
+    padding-right: 15px;
+    overflow: hidden;
+
+    &__info {
+      overflow: hidden;
+      display: flex;
+      flex-direction: row-reverse;
+      justify-content: space-between;
+    }
+
+    &__relative-time {
+      font-size: 15px;
+      color: $darker-text-color;
+      padding-left: 15px;
+    }
+
+    &__names {
+      color: $darker-text-color;
+      font-size: 15px;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      margin-bottom: 4px;
+      flex-basis: 90px;
+      flex-grow: 1;
+
+      a {
+        color: $primary-text-color;
+        text-decoration: none;
+
+        &:hover,
+        &:focus,
+        &:active {
+          text-decoration: underline;
+        }
+      }
+    }
+
+    .status__content {
+      margin: 0;
+    }
+  }
+
+  &--unread {
+    background: lighten($ui-base-color, 2%);
+
+    &:focus {
+      background: lighten($ui-base-color, 4%);
+    }
+
+    .conversation__content__info {
+      font-weight: 700;
+    }
+
+    .conversation__content__relative-time {
+      color: $primary-text-color;
+    }
+  }
+}
+
+.ui .flash-message {
+  margin-top: 10px;
+  margin-left: auto;
+  margin-right: auto;
+  margin-bottom: 0;
+  min-width: 75%;
+}
+
+::-webkit-scrollbar-thumb {
+  border-radius: 0;
+}
+
+noscript {
+  text-align: center;
+
+  img {
+    width: 200px;
+    opacity: 0.5;
+    animation: flicker 4s infinite;
+  }
+
+  div {
+    font-size: 14px;
+    margin: 30px auto;
+    color: $secondary-text-color;
+    max-width: 400px;
+
+    a {
+      color: $highlight-text-color;
+      text-decoration: underline;
+
+      &:hover {
+        text-decoration: none;
+      }
+    }
+
+    a {
+      word-break: break-word;
+    }
+  }
+}
+
+@keyframes flicker {
+  0% {
+    opacity: 1;
+  }
+
+  30% {
+    opacity: 0.75;
+  }
+
+  100% {
+    opacity: 1;
+  }
+}
diff --git a/app/javascript/flavours/glitch/styles/components/modal.scss b/app/javascript/flavours/glitch/styles/components/modal.scss
index 972e01e7d..65060f422 100644
--- a/app/javascript/flavours/glitch/styles/components/modal.scss
+++ b/app/javascript/flavours/glitch/styles/components/modal.scss
@@ -75,7 +75,6 @@
     width: 100%;
     height: 100%;
     box-sizing: border-box;
-    display: none;
     flex-direction: column;
     align-items: center;
     justify-content: center;
@@ -99,7 +98,6 @@
     height: 100%;
     box-sizing: border-box;
     padding: 25px;
-    display: none;
     flex-direction: column;
     align-items: center;
     justify-content: center;
@@ -269,7 +267,8 @@
 }
 
 .onboarding-modal__page__wrapper-0 {
-  background: url('~images/elephant_ui_greeting.svg') no-repeat left bottom / auto 250px;
+  background: url('~images/elephant_ui_greeting.svg') no-repeat left bottom /
+    auto 250px;
   height: 100%;
   padding: 0;
 }
@@ -684,7 +683,6 @@
     display: block;
     box-sizing: border-box;
     width: 100%;
-    margin: 0;
     color: $inverted-text-color;
     background: $simple-background-color;
     padding: 10px;
@@ -819,7 +817,6 @@
     font-family: inherit;
     font-size: 14px;
     resize: none;
-    border: 0;
     outline: 0;
     border-radius: 4px;
     border: 1px solid $ui-secondary-color;
@@ -986,10 +983,10 @@
   padding-left: 20px;
   padding-right: 20px;
   padding-bottom: 10px;
-
   font-size: 14px;
 
-  label, input {
+  label,
+  input {
     vertical-align: middle;
   }
 }
@@ -1020,7 +1017,9 @@
     width: auto;
     outline: 0;
     font-family: inherit;
-    background: $simple-background-color 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(darken($simple-background-color, 14%))}'/></svg>") no-repeat right 8px center / auto 16px;
+    background: $simple-background-color
+      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(darken($simple-background-color, 14%))}'/></svg>")
+      no-repeat right 8px center / auto 16px;
     border: 1px solid darken($simple-background-color, 14%);
     border-radius: 4px;
     padding: 6px 10px;
@@ -1071,6 +1070,7 @@
   &__container {
     padding: 30px;
     pointer-events: all;
+    overflow-y: auto;
   }
 
   .status__content {
@@ -1130,7 +1130,7 @@
       width: 100%;
       border: 0;
       padding: 10px;
-      font-family: 'mastodon-font-monospace', monospace;
+      font-family: mastodon-font-monospace, monospace;
       background: $ui-base-color;
       color: $primary-text-color;
       font-size: 14px;
@@ -1279,7 +1279,7 @@
     text-decoration: none;
 
     &:hover {
-      text-decoration: underline
+      text-decoration: underline;
     }
   }
 }
diff --git a/app/javascript/flavours/glitch/styles/components/privacy_policy.scss b/app/javascript/flavours/glitch/styles/components/privacy_policy.scss
index 96cf06742..93123075e 100644
--- a/app/javascript/flavours/glitch/styles/components/privacy_policy.scss
+++ b/app/javascript/flavours/glitch/styles/components/privacy_policy.scss
@@ -26,11 +26,13 @@
   img {
     margin-top: 2em;
     margin-bottom: 2em;
+    max-width: 100%;
   }
 
   video {
     margin-top: 2em;
     margin-bottom: 2em;
+    max-width: 100%;
   }
 
   figure {
@@ -85,14 +87,14 @@
     counter-increment: list-counter;
 
     &::before {
-      content: counter(list-counter) ".";
+      content: counter(list-counter) '.';
       position: absolute;
       left: 0;
     }
   }
 
   ul > li::before {
-    content: "";
+    content: '';
     position: absolute;
     background-color: $darker-text-color;
     border-radius: 50%;
diff --git a/app/javascript/flavours/glitch/styles/components/search.scss b/app/javascript/flavours/glitch/styles/components/search.scss
index b8078bdb6..a6e98a868 100644
--- a/app/javascript/flavours/glitch/styles/components/search.scss
+++ b/app/javascript/flavours/glitch/styles/components/search.scss
@@ -4,7 +4,7 @@
 }
 
 .search__input {
-  @include search-input();
+  @include search-input;
 
   display: block;
   padding: 15px;
@@ -181,7 +181,7 @@
 
       path:first-child {
         fill: rgba($highlight-text-color, 0.25) !important;
-        fill-opacity: 100% !important;
+        fill-opacity: 1 !important;
       }
 
       path:last-child {
diff --git a/app/javascript/flavours/glitch/styles/components/sensitive.scss b/app/javascript/flavours/glitch/styles/components/sensitive.scss
index 67b01c886..490951fb4 100644
--- a/app/javascript/flavours/glitch/styles/components/sensitive.scss
+++ b/app/javascript/flavours/glitch/styles/components/sensitive.scss
@@ -17,8 +17,10 @@
   font-size: 12px;
   line-height: 18px;
   text-transform: uppercase;
-  opacity: .9;
-  transition: opacity .1s ease;
+  opacity: 0.9;
+  transition: opacity 0.1s ease;
 
-  .media-gallery:hover & { opacity: 1 }
+  .media-gallery:hover & {
+    opacity: 1;
+  }
 }
diff --git a/app/javascript/flavours/glitch/styles/components/single_column.scss b/app/javascript/flavours/glitch/styles/components/single_column.scss
index 74e5d0884..036b3f6ef 100644
--- a/app/javascript/flavours/glitch/styles/components/single_column.scss
+++ b/app/javascript/flavours/glitch/styles/components/single_column.scss
@@ -140,7 +140,7 @@
   .scrollable {
     overflow: visible;
 
-    @supports(display: grid) {
+    @supports (display: grid) {
       contain: content;
     }
   }
diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss
index a46fb94b2..21c28919a 100644
--- a/app/javascript/flavours/glitch/styles/components/status.scss
+++ b/app/javascript/flavours/glitch/styles/components/status.scss
@@ -67,7 +67,8 @@
     margin: -3px 0 0;
   }
 
-  p, pre, blockquote {
+  p,
+  pre {
     margin-bottom: 20px;
     white-space: pre-wrap;
     unicode-bidi: plaintext;
@@ -77,79 +78,6 @@
     }
   }
 
-  .status__content__text,
-  .e-content {
-    overflow: hidden;
-
-    & > ul,
-    & > ol {
-      margin-bottom: 20px;
-    }
-
-    h1, h2, h3, h4, h5 {
-      margin-top: 20px;
-      margin-bottom: 20px;
-    }
-
-    h1, h2 {
-      font-weight: 700;
-      font-size: 1.2em;
-    }
-
-    h2 {
-      font-size: 1.1em;
-    }
-
-    h3, h4, h5 {
-      font-weight: 500;
-    }
-
-    blockquote {
-      padding-left: 10px;
-      border-left: 3px solid $darker-text-color;
-      color: $darker-text-color;
-      white-space: normal;
-
-      p:last-child {
-        margin-bottom: 0;
-      }
-    }
-
-    b, strong {
-      font-weight: 700;
-    }
-
-    em, i {
-      font-style: italic;
-    }
-
-    sub {
-      font-size: smaller;
-      vertical-align: sub;
-    }
-
-    sup {
-      font-size: smaller;
-      vertical-align: super;
-    }
-
-    ul, ol {
-      p {
-        margin: 0;
-      }
-    }
-
-    ul {
-      margin-left: 1em;
-      list-style-type: disc;
-    }
-
-    ol {
-      list-style-type: decimal;
-      list-style-position: inside;
-    }
-  }
-
   a {
     color: $secondary-text-color;
     text-decoration: none;
@@ -317,8 +245,13 @@
   }
 
   @keyframes fade {
-    0% { opacity: 0; }
-    100% { opacity: 1; }
+    0% {
+      opacity: 0;
+    }
+
+    100% {
+      opacity: 1;
+    }
   }
 
   opacity: 1;
@@ -381,9 +314,14 @@
       right: 0;
       top: 0;
       bottom: 0;
-      background-image: linear-gradient(to bottom, rgba($base-shadow-color, .75), rgba($base-shadow-color, .65) 24px, rgba($base-shadow-color, .8));
+      background-image: linear-gradient(
+        to bottom,
+        rgba($base-shadow-color, 0.75),
+        rgba($base-shadow-color, 0.65) 24px,
+        rgba($base-shadow-color, 0.8)
+      );
       pointer-events: none;
-      content: "";
+      content: '';
     }
 
     .display-name:hover .display-name__html {
@@ -396,26 +334,37 @@
       text-overflow: ellipsis;
       padding-top: 0;
 
-      &:after {
-        content: "";
+      &::after {
+        content: '';
         position: absolute;
         top: 0;
         bottom: 0;
         left: 0;
         right: 0;
-        background: linear-gradient(rgba($ui-base-color, 0), rgba($ui-base-color, 1));
+        background: linear-gradient(
+          rgba($ui-base-color, 0),
+          rgba($ui-base-color, 1)
+        );
         pointer-events: none;
       }
-      
+
       a:hover {
         text-decoration: underline;
       }
     }
-    &:focus > .status__content:after {
-      background: linear-gradient(rgba(lighten($ui-base-color, 4%), 0), rgba(lighten($ui-base-color, 4%), 1));
+
+    &:focus > .status__content::after {
+      background: linear-gradient(
+        rgba(lighten($ui-base-color, 4%), 0),
+        rgba(lighten($ui-base-color, 4%), 1)
+      );
     }
-    &.status-direct > .status__content:after {
-      background: linear-gradient(rgba(lighten($ui-base-color, 8%), 0), rgba(lighten($ui-base-color, 8%), 1));
+
+    &.status-direct > .status__content::after {
+      background: linear-gradient(
+        rgba(lighten($ui-base-color, 8%), 0),
+        rgba(lighten($ui-base-color, 8%), 1)
+      );
     }
 
     .notification__message {
@@ -428,7 +377,7 @@
   }
 
   .notification__message {
-    margin: -10px 0px 10px 0;
+    margin: -10px 0px 10px;
 
 	a:hover {
 	  text-decoration: underline;
@@ -840,7 +789,8 @@ a.status__display-name,
       bottom: -1px;
     }
 
-    a .fa, a:hover .fa {
+    a .fa,
+    a:hover .fa {
       color: inherit;
     }
   }
@@ -858,9 +808,9 @@ a.status-card {
   cursor: zoom-in;
   display: block;
   text-decoration: none;
-    width: 100%;
-    height: auto;
-    margin: 0;
+  width: 100%;
+  height: auto;
+  margin: 0;
 }
 
 .status-card-video {
@@ -1071,11 +1021,10 @@ a.status-card.compact:hover {
 
   &.unread {
     &::before {
-      content: "";
+      content: '';
       position: absolute;
       top: 0;
       left: 0;
-      pointer-events: 0;
       width: 100%;
       height: 100%;
       border-left: 4px solid $highlight-text-color;
diff --git a/app/javascript/flavours/glitch/styles/containers.scss b/app/javascript/flavours/glitch/styles/containers.scss
index a3aee7eef..b90851546 100644
--- a/app/javascript/flavours/glitch/styles/containers.scss
+++ b/app/javascript/flavours/glitch/styles/containers.scss
@@ -74,6 +74,7 @@
     width: 40px;
     height: 40px;
     @include avatar-size(40px);
+
     margin-right: 10px;
 
     img {
@@ -82,7 +83,7 @@
       display: block;
       margin: 0;
       border-radius: 4px;
-      @include avatar-radius();
+      @include avatar-radius;
     }
   }
 
diff --git a/app/javascript/flavours/glitch/styles/contrast/variables.scss b/app/javascript/flavours/glitch/styles/contrast/variables.scss
index e272b6ca3..e38d24b27 100644
--- a/app/javascript/flavours/glitch/styles/contrast/variables.scss
+++ b/app/javascript/flavours/glitch/styles/contrast/variables.scss
@@ -18,5 +18,5 @@ $highlight-text-color: lighten($ui-highlight-color, 10%) !default;
 $action-button-color: lighten($ui-base-color, 50%);
 
 $inverted-text-color: $black !default;
-$lighter-text-color: darken($ui-base-color,6%) !default;
+$lighter-text-color: darken($ui-base-color, 6%) !default;
 $light-text-color: darken($ui-primary-color, 40%) !default;
diff --git a/app/javascript/flavours/glitch/styles/dashboard.scss b/app/javascript/flavours/glitch/styles/dashboard.scss
index bb103e9ce..f25765d1d 100644
--- a/app/javascript/flavours/glitch/styles/dashboard.scss
+++ b/app/javascript/flavours/glitch/styles/dashboard.scss
@@ -37,7 +37,6 @@
     text-align: center;
     font-weight: 500;
     font-size: 24px;
-    line-height: 21px;
     color: $primary-text-color;
     margin-bottom: 20px;
     line-height: 30px;
diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss
index fd0ff6d93..9692df786 100644
--- a/app/javascript/flavours/glitch/styles/forms.scss
+++ b/app/javascript/flavours/glitch/styles/forms.scss
@@ -154,6 +154,15 @@ code {
       padding: 0.2em 0.4em;
       background: darken($ui-base-color, 12%);
     }
+
+    li {
+      list-style: disc;
+      margin-left: 18px;
+    }
+  }
+
+  ul.hint {
+    margin-bottom: 15px;
   }
 
   span.hint {
@@ -288,7 +297,7 @@ code {
       max-width: 100%;
       height: auto;
       border-radius: 4px;
-      background: url("images/void.png");
+      background: url('images/void.png');
 
       &:last-child {
         margin-bottom: 0;
@@ -373,7 +382,7 @@ code {
         flex: 1 1 auto;
       }
 
-      input[type=checkbox] {
+      input[type='checkbox'] {
         position: absolute;
         left: 0;
         top: 5px;
@@ -389,12 +398,12 @@ code {
     border-radius: 4px;
   }
 
-  input[type=text],
-  input[type=number],
-  input[type=email],
-  input[type=password],
-  input[type=url],
-  input[type=datetime-local],
+  input[type='text'],
+  input[type='number'],
+  input[type='email'],
+  input[type='password'],
+  input[type='url'],
+  input[type='datetime-local'],
   textarea {
     box-sizing: border-box;
     font-size: 16px;
@@ -432,11 +441,11 @@ code {
     }
   }
 
-  input[type=text],
-  input[type=number],
-  input[type=email],
-  input[type=password],
-  input[type=datetime-local] {
+  input[type='text'],
+  input[type='number'],
+  input[type='email'],
+  input[type='password'],
+  input[type='datetime-local'] {
     &:focus:invalid:not(:placeholder-shown),
     &:required:invalid:not(:placeholder-shown) {
       border-color: lighten($error-red, 12%);
@@ -448,11 +457,11 @@ code {
       color: lighten($error-red, 12%);
     }
 
-    input[type=text],
-    input[type=number],
-    input[type=email],
-    input[type=password],
-    input[type=datetime-local],
+    input[type='text'],
+    input[type='number'],
+    input[type='email'],
+    input[type='password'],
+    input[type='datetime-local'],
     textarea,
     select {
       border-color: lighten($error-red, 12%);
@@ -556,7 +565,9 @@ code {
     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;
+    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-left: 10px;
@@ -596,7 +607,11 @@ code {
         right: 0;
         bottom: 1px;
         width: 5px;
-        background-image: linear-gradient(to right, rgba(darken($ui-base-color, 10%), 0), darken($ui-base-color, 10%));
+        background-image: linear-gradient(
+          to right,
+          rgba(darken($ui-base-color, 10%), 0),
+          darken($ui-base-color, 10%)
+        );
       }
     }
   }
@@ -984,7 +999,7 @@ code {
     flex: 1 1 auto;
   }
 
-  input[type=text] {
+  input[type='text'] {
     background: transparent;
     border: 0;
     padding: 10px;
diff --git a/app/javascript/flavours/glitch/styles/index.scss b/app/javascript/flavours/glitch/styles/index.scss
index fbb02c788..1cb913c8b 100644
--- a/app/javascript/flavours/glitch/styles/index.scss
+++ b/app/javascript/flavours/glitch/styles/index.scss
@@ -21,3 +21,4 @@
 @import 'accessibility';
 @import 'rtl';
 @import 'dashboard';
+@import 'rich_text';
diff --git a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss
index 2ec2da833..ef248bf4f 100644
--- a/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss
+++ b/app/javascript/flavours/glitch/styles/mastodon-light/diff.scss
@@ -12,6 +12,14 @@ html {
   &.button-alternative-2 {
     color: $white;
   }
+
+  &.button-tertiary {
+    color: $highlight-text-color;
+  }
+}
+
+.simple_form .button.button-tertiary {
+  color: $highlight-text-color;
 }
 
 .status-card__actions button,
@@ -144,7 +152,7 @@ html {
 }
 
 .compose-form__autosuggest-wrapper,
-.poll__option input[type="text"],
+.poll__option input[type='text'],
 .compose-form .spoiler-input__input,
 .compose-form__poll-wrapper select,
 .search__input,
@@ -171,7 +179,9 @@ html {
 }
 
 .compose-form__poll-wrapper select {
-  background: $simple-background-color url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 8%))}'/></svg>") no-repeat right 8px center / auto 16px;
+  background: $simple-background-color
+    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 8%))}'/></svg>")
+    no-repeat right 8px center / auto 16px;
 }
 
 .compose-form__poll-wrapper,
@@ -197,7 +207,9 @@ html {
 }
 
 .drawer__inner__mastodon {
-  background: $white 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($ui-base-color)}"/></svg>') no-repeat bottom / 100% auto;
+  background: $white
+    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($ui-base-color)}"/></svg>')
+    no-repeat bottom / 100% auto;
 }
 
 // Change the colors used in compose-form
@@ -246,6 +258,10 @@ html {
   border-color: $ui-base-color;
 }
 
+.upload-progress__backdrop {
+  background: $ui-base-color;
+}
+
 // Change the background colors of statuses
 .focusable:focus {
   background: $ui-base-color;
@@ -324,11 +340,13 @@ html {
   color: $white;
 }
 
-.language-dropdown__dropdown__results__item .language-dropdown__dropdown__results__item__common-name {
+.language-dropdown__dropdown__results__item
+  .language-dropdown__dropdown__results__item__common-name {
   color: lighten($ui-base-color, 8%);
 }
 
-.language-dropdown__dropdown__results__item.active .language-dropdown__dropdown__results__item__common-name {
+.language-dropdown__dropdown__results__item.active
+  .language-dropdown__dropdown__results__item__common-name {
   color: darken($ui-base-color, 12%);
 }
 
@@ -482,7 +500,8 @@ html {
   background: darken($ui-secondary-color, 10%);
 }
 
-.react-toggle.react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track {
+.react-toggle.react-toggle--checked:hover:not(.react-toggle--disabled)
+  .react-toggle-track {
   background: lighten($ui-highlight-color, 10%);
 }
 
@@ -514,10 +533,10 @@ html {
 }
 
 .simple_form {
-  input[type="text"],
-  input[type="number"],
-  input[type="email"],
-  input[type="password"],
+  input[type='text'],
+  input[type='number'],
+  input[type='email'],
+  input[type='password'],
   textarea {
     &:hover {
       border-color: lighten($ui-base-color, 12%);
@@ -543,25 +562,6 @@ html {
   }
 }
 
-.directory__tag.active > a,
-.directory__tag.active > div {
-  border-color: $ui-highlight-color;
-
-  &,
-  h4,
-  h4 small,
-  .fa,
-  .trends__item__current {
-    color: $white;
-  }
-
-  &:hover,
-  &:active,
-  &:focus {
-    background: $ui-highlight-color;
-  }
-}
-
 .batch-table {
   &__toolbar,
   &__row,
@@ -693,7 +693,9 @@ html {
 
 .mute-modal select {
   border: 1px solid lighten($ui-base-color, 8%);
-  background: $simple-background-color url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 8%))}'/></svg>") no-repeat right 8px center / auto 16px;
+  background: $simple-background-color
+    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 8%))}'/></svg>")
+    no-repeat right 8px center / auto 16px;
 }
 
 // Glitch-soc-specific changes
@@ -740,7 +742,8 @@ html {
     color: $white;
   }
 
-  &.close, &.close:hover {
+  &.close,
+  &.close:hover {
     background: $error-value-color;
     color: $primary-text-color;
   }
@@ -756,12 +759,17 @@ html {
   }
 }
 
-.status.collapsed .status__content:after {
-  background: linear-gradient(rgba(darken($ui-base-color, 13%), 0), rgba(darken($ui-base-color, 13%), 1));
+.status.collapsed .status__content::after {
+  background: linear-gradient(
+    rgba(darken($ui-base-color, 13%), 0),
+    rgba(darken($ui-base-color, 13%), 1)
+  );
 }
 
 .drawer__inner__mastodon {
-  background: $white 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($ui-base-color)}"/></svg>') no-repeat bottom / 100% auto !important;
+  background: $white
+    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($ui-base-color)}"/></svg>')
+    no-repeat bottom / 100% auto !important;
 
   .mastodon {
     filter: contrast(75%) brightness(75%) !important;
diff --git a/app/javascript/flavours/glitch/styles/modal.scss b/app/javascript/flavours/glitch/styles/modal.scss
index a333926dd..6170877b2 100644
--- a/app/javascript/flavours/glitch/styles/modal.scss
+++ b/app/javascript/flavours/glitch/styles/modal.scss
@@ -1,5 +1,7 @@
 .modal-layout {
-  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($ui-base-lighter-color)}33"/></svg>') repeat-x bottom fixed;
+  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($ui-base-lighter-color)}33"/></svg>')
+    repeat-x bottom fixed;
   display: flex;
   flex-direction: column;
   height: 100vh;
diff --git a/app/javascript/flavours/glitch/styles/polls.scss b/app/javascript/flavours/glitch/styles/polls.scss
index 43924829d..a4ce14a09 100644
--- a/app/javascript/flavours/glitch/styles/polls.scss
+++ b/app/javascript/flavours/glitch/styles/polls.scss
@@ -70,8 +70,8 @@
       max-width: calc(100% - 45px - 25px);
     }
 
-    input[type=radio],
-    input[type=checkbox] {
+    input[type='radio'],
+    input[type='checkbox'] {
       display: none;
     }
 
@@ -79,13 +79,12 @@
       flex: 1 1 auto;
     }
 
-    input[type=text] {
+    input[type='text'] {
       display: block;
       box-sizing: border-box;
       width: 100%;
       font-size: 14px;
       color: $inverted-text-color;
-      display: block;
       outline: 0;
       font-family: inherit;
       background: $simple-background-color;
@@ -116,8 +115,7 @@
     box-sizing: border-box;
     width: 18px;
     height: 18px;
-    flex: 0 0 auto;
-    margin-right: 10px;
+    margin-inline-end: 10px;
     top: -1px;
     border-radius: 50%;
     vertical-align: middle;
@@ -205,14 +203,14 @@
 
     &:active,
     &:focus {
-      background-color: rgba($dark-text-color, .1);
+      background-color: rgba($dark-text-color, 0.1);
     }
   }
 
   .button {
     height: 36px;
     padding: 0 16px;
-    margin-right: 10px;
+    margin-inline-end: 10px;
     font-size: 14px;
   }
 }
@@ -250,7 +248,7 @@
     line-height: inherit;
     color: $action-button-color;
     border-color: $action-button-color;
-    margin-right: 5px;
+    margin-inline-end: 5px;
   }
 
   li {
@@ -260,7 +258,7 @@
     .poll__option {
       flex: 0 0 auto;
       width: calc(100% - (23px + 6px));
-      margin-right: 6px;
+      margin-inline-end: 6px;
     }
   }
 
@@ -273,7 +271,9 @@
     width: auto;
     outline: 0;
     font-family: inherit;
-    background: $simple-background-color 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(darken($simple-background-color, 14%))}'/></svg>") no-repeat right 8px center / auto 16px;
+    background: $simple-background-color
+      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(darken($simple-background-color, 14%))}'/></svg>")
+      no-repeat right 8px center / auto 16px;
     border: 1px solid darken($simple-background-color, 14%);
     border-radius: 4px;
     padding: 6px 10px;
diff --git a/app/javascript/flavours/glitch/styles/rich_text.scss b/app/javascript/flavours/glitch/styles/rich_text.scss
new file mode 100644
index 000000000..081641f0b
--- /dev/null
+++ b/app/javascript/flavours/glitch/styles/rich_text.scss
@@ -0,0 +1,101 @@
+.status__content__text,
+.e-content,
+.reply-indicator__content {
+  pre,
+  blockquote {
+    margin-bottom: 20px;
+    white-space: pre-wrap;
+    unicode-bidi: plaintext;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  blockquote {
+    padding-left: 10px;
+    border-left: 3px solid $darker-text-color;
+    color: $darker-text-color;
+    white-space: normal;
+
+    p:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  & > ul,
+  & > ol {
+    margin-bottom: 20px;
+  }
+
+  h1,
+  h2,
+  h3,
+  h4,
+  h5 {
+    margin-top: 20px;
+    margin-bottom: 20px;
+  }
+
+  h1,
+  h2 {
+    font-weight: 700;
+    font-size: 1.2em;
+  }
+
+  h2 {
+    font-size: 1.1em;
+  }
+
+  h3,
+  h4,
+  h5 {
+    font-weight: 500;
+  }
+
+  b,
+  strong {
+    font-weight: 700;
+  }
+
+  em,
+  i {
+    font-style: italic;
+  }
+
+  sub {
+    font-size: smaller;
+    vertical-align: sub;
+  }
+
+  sup {
+    font-size: smaller;
+    vertical-align: super;
+  }
+
+  ul,
+  ol {
+    margin-left: 2em;
+
+    p {
+      margin: 0;
+    }
+  }
+
+  ul {
+	margin-left: 1em;
+    list-style-type: disc;
+  }
+
+  ol {
+    list-style-type: decimal;
+	list-style-position: inside;
+  }
+}
+
+.reply-indicator__content {
+  blockquote {
+    border-left-color: $inverted-text-color;
+    color: $inverted-text-color;
+  }
+}
diff --git a/app/javascript/flavours/glitch/styles/rtl.scss b/app/javascript/flavours/glitch/styles/rtl.scss
index c14c07cb9..64a5c2c03 100644
--- a/app/javascript/flavours/glitch/styles/rtl.scss
+++ b/app/javascript/flavours/glitch/styles/rtl.scss
@@ -255,8 +255,8 @@ body.rtl {
     padding-right: 0;
   }
 
-  .simple_form .check_boxes .checkbox input[type="checkbox"],
-  .simple_form .input.boolean input[type="checkbox"] {
+  .simple_form .check_boxes .checkbox input[type='checkbox'],
+  .simple_form .input.boolean input[type='checkbox'] {
     left: auto;
     right: 0;
   }
@@ -294,12 +294,18 @@ body.rtl {
     &::after {
       right: auto;
       left: 0;
-      background-image: linear-gradient(to left, rgba(darken($ui-base-color, 10%), 0), darken($ui-base-color, 10%));
+      background-image: linear-gradient(
+        to left,
+        rgba(darken($ui-base-color, 10%), 0),
+        darken($ui-base-color, 10%)
+      );
     }
   }
 
   .simple_form select {
-    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 left 8px center / auto 16px;
+    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 left 8px center / auto 16px;
   }
 
   .table th,
@@ -346,11 +352,11 @@ body.rtl {
   }
 
   .fa-chevron-left::before {
-    content: "\F054";
+    content: '\F054';
   }
 
   .fa-chevron-right::before {
-    content: "\F053";
+    content: '\F053';
   }
 
   .column-back-button__icon {
diff --git a/app/javascript/flavours/glitch/styles/statuses.scss b/app/javascript/flavours/glitch/styles/statuses.scss
index 88fa3ffa0..f7037d9dc 100644
--- a/app/javascript/flavours/glitch/styles/statuses.scss
+++ b/app/javascript/flavours/glitch/styles/statuses.scss
@@ -134,7 +134,7 @@ a.button.logo-button {
 }
 
 .embed {
-  .status__content[data-spoiler=folded] {
+  .status__content[data-spoiler='folded'] {
     .e-content {
       display: none;
     }
diff --git a/app/javascript/flavours/glitch/styles/variables.scss b/app/javascript/flavours/glitch/styles/variables.scss
index b865b5a2d..0132da51f 100644
--- a/app/javascript/flavours/glitch/styles/variables.scss
+++ b/app/javascript/flavours/glitch/styles/variables.scss
@@ -1,18 +1,18 @@
 // Commonly used web colors
-$black: #000000;            // Black
-$white: #ffffff;            // White
-$success-green: #79bd9a;    // Padua
-$error-red: #df405a;        // Cerise
-$warning-red: #ff5050;      // Sunset Orange
-$gold-star: #ca8f04;        // Dark Goldenrod
+$black: #000000; // Black
+$white: #ffffff; // White
+$success-green: #79bd9a; // Padua
+$error-red: #df405a; // Cerise
+$warning-red: #ff5050; // Sunset Orange
+$gold-star: #ca8f04; // Dark Goldenrod
 
 $red-bookmark: $warning-red;
 
 // Values from the classic Mastodon UI
-$classic-base-color: #282c37;         // Midnight Express
-$classic-primary-color: #9baec8;      // Echo Blue
-$classic-secondary-color: #d9e1e8;    // Pattens Blue
-$classic-highlight-color: #6364ff;    // Brand purple
+$classic-base-color: #282c37; // Midnight Express
+$classic-primary-color: #9baec8; // Echo Blue
+$classic-secondary-color: #d9e1e8; // Pattens Blue
+$classic-highlight-color: #6364ff; // Brand purple
 
 // Variables for defaults in UI
 $base-shadow-color: $black !default;
@@ -23,10 +23,13 @@ $valid-value-color: $success-green !default;
 $error-value-color: $error-red !default;
 
 // Tell UI to use selected colors
-$ui-base-color: $classic-base-color !default;                  // Darkest
-$ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest
-$ui-primary-color: $classic-primary-color !default;            // Lighter
-$ui-secondary-color: $classic-secondary-color !default;        // Lightest
+$ui-base-color: $classic-base-color !default; // Darkest
+$ui-base-lighter-color: lighten(
+  $ui-base-color,
+  26%
+) !default; // Lighter darkest
+$ui-primary-color: $classic-primary-color !default; // Lighter
+$ui-secondary-color: $classic-secondary-color !default; // Lightest
 $ui-highlight-color: $classic-highlight-color !default;
 
 // Variables for texts
@@ -38,6 +41,7 @@ $highlight-text-color: lighten($ui-highlight-color, 8%) !default;
 $action-button-color: $ui-base-lighter-color !default;
 $passive-text-color: $gold-star !default;
 $active-passive-text-color: $success-green !default;
+
 // For texts on inverted backgrounds
 $inverted-text-color: $ui-base-color !default;
 $lighter-text-color: $ui-base-lighter-color !default;
@@ -48,6 +52,7 @@ $cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW;
 
 // Variables for components
 $media-modal-media-max-width: 100%;
+
 // put margins on top and bottom of image to avoid the screen covered by image.
 $media-modal-media-max-height: 80%;
 
diff --git a/app/javascript/flavours/glitch/styles/widgets.scss b/app/javascript/flavours/glitch/styles/widgets.scss
index fd091ee89..0f2b7ac5b 100644
--- a/app/javascript/flavours/glitch/styles/widgets.scss
+++ b/app/javascript/flavours/glitch/styles/widgets.scss
@@ -1,4 +1,4 @@
-@use "sass:math";
+@use 'sass:math';
 
 .hero-widget {
   margin-bottom: 10px;
diff --git a/app/javascript/flavours/glitch/theme.yml b/app/javascript/flavours/glitch/theme.yml
index 2a2cf30b5..672dd5440 100644
--- a/app/javascript/flavours/glitch/theme.yml
+++ b/app/javascript/flavours/glitch/theme.yml
@@ -1,13 +1,13 @@
 #  (REQUIRED) The location of the pack files.
 pack:
   admin:
-    - packs/admin.js
-    - packs/public.js
-  auth: packs/public.js
+    - packs/admin.jsx
+    - packs/public.jsx
+  auth: packs/public.jsx
   common:
     filename: packs/common.js
     stylesheet: true
-  embed: packs/public.js
+  embed: packs/public.jsx
   error: packs/error.js
   home:
     filename: packs/home.js
@@ -18,9 +18,9 @@ pack:
       - flavours/glitch/async/notifications
   mailer:
   modal:
-  public: packs/public.js
+  public: packs/public.jsx
   settings: packs/settings.js
-  share: packs/share.js
+  share: packs/share.jsx
 
 #  (OPTIONAL) The directory which contains localization files for
 #  the flavour, relative to this directory. The contents of this
diff --git a/app/javascript/flavours/glitch/utils/hashtag.js b/app/javascript/flavours/glitch/utils/hashtag.js
index 9b663487f..d91cd5591 100644
--- a/app/javascript/flavours/glitch/utils/hashtag.js
+++ b/app/javascript/flavours/glitch/utils/hashtag.js
@@ -1,8 +1,8 @@
 export function recoverHashtags (recognizedTags, text) {
   return recognizedTags.map(tag => {
-      const re = new RegExp(`(?:^|[^\/\)\w])#(${tag.name})`, 'i');
-      const matched_hashtag = text.match(re);
-      return matched_hashtag ? matched_hashtag[1] : null;
-    }
+    const re = new RegExp(`(?:^|[^/)\w])#(${tag.name})`, 'i');
+    const matched_hashtag = text.match(re);
+    return matched_hashtag ? matched_hashtag[1] : null;
+  },
   ).filter(x => x !== null);
 }
diff --git a/app/javascript/flavours/glitch/utils/icons.js b/app/javascript/flavours/glitch/utils/icons.jsx
index c3e362e39..c3e362e39 100644
--- a/app/javascript/flavours/glitch/utils/icons.js
+++ b/app/javascript/flavours/glitch/utils/icons.jsx
diff --git a/app/javascript/flavours/glitch/utils/notifications.js b/app/javascript/flavours/glitch/utils/notifications.js
index 7634cac21..3cdf7caea 100644
--- a/app/javascript/flavours/glitch/utils/notifications.js
+++ b/app/javascript/flavours/glitch/utils/notifications.js
@@ -3,7 +3,7 @@
 
 const checkNotificationPromise = () => {
   try {
-    // eslint-disable-next-line promise/catch-or-return
+    // eslint-disable-next-line promise/catch-or-return, promise/valid-params
     Notification.requestPermission().then();
   } catch(e) {
     return false;
diff --git a/app/javascript/flavours/glitch/utils/privacy_preference.js b/app/javascript/flavours/glitch/utils/privacy_preference.js
index 7781ca7fa..51bdf072d 100644
--- a/app/javascript/flavours/glitch/utils/privacy_preference.js
+++ b/app/javascript/flavours/glitch/utils/privacy_preference.js
@@ -2,4 +2,4 @@ export const order = ['public', 'unlisted', 'private', 'direct'];
 
 export function privacyPreference (a, b) {
   return order[Math.max(order.indexOf(a), order.indexOf(b), 0)];
-};
+}
diff --git a/app/javascript/flavours/glitch/utils/react_helpers.js b/app/javascript/flavours/glitch/utils/react_helpers.js
index 082a58e62..ea11acdb6 100644
--- a/app/javascript/flavours/glitch/utils/react_helpers.js
+++ b/app/javascript/flavours/glitch/utils/react_helpers.js
@@ -7,7 +7,7 @@ export function assignHandlers (target, handlers) {
   //  We just bind each handler to the `target`.
   const handle = target.handlers = {};
   Object.keys(handlers).forEach(
-    key => handle[key] = handlers[key].bind(target)
+    key => handle[key] = handlers[key].bind(target),
   );
 }
 
diff --git a/app/javascript/flavours/glitch/uuid.js b/app/javascript/flavours/glitch/uuid.js
index be1899305..0d2cfaa77 100644
--- a/app/javascript/flavours/glitch/uuid.js
+++ b/app/javascript/flavours/glitch/uuid.js
@@ -1,3 +1,3 @@
 export default function uuid(a) {
   return a ? (a^Math.random() * 16 >> a / 4).toString(16) : ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, uuid);
-};
+}
diff --git a/app/javascript/flavours/vanilla/theme.yml b/app/javascript/flavours/vanilla/theme.yml
index 3f0b27899..ccab925aa 100644
--- a/app/javascript/flavours/vanilla/theme.yml
+++ b/app/javascript/flavours/vanilla/theme.yml
@@ -1,13 +1,13 @@
 #  (REQUIRED) The location of the pack files inside `pack_directory`.
 pack:
   admin:
-    - admin.js
-    - public.js
-  auth: public.js
+    - admin.jsx
+    - public.jsx
+  auth: public.jsx
   common:
     filename: common.js
     stylesheet: true
-  embed: public.js
+  embed: public.jsx
   error: error.js
   home:
     filename: application.js
@@ -18,9 +18,9 @@ pack:
       - features/notifications
   mailer:
   modal:
-  public: public.js
-  settings: public.js
-  share: share.js
+  public: public.jsx
+  settings: public.jsx
+  share: share.jsx
 
 #  (OPTIONAL) The directory which contains localization files for
 #  the flavour, relative to this directory.
diff --git a/app/javascript/hooks/useHovering.ts b/app/javascript/hooks/useHovering.ts
new file mode 100644
index 000000000..2062e70d2
--- /dev/null
+++ b/app/javascript/hooks/useHovering.ts
@@ -0,0 +1,17 @@
+import { useCallback, useState } from 'react';
+
+export const useHovering = (animate?: boolean) => {
+  const [hovering, setHovering] = useState<boolean>(animate ?? false);
+
+  const handleMouseEnter = useCallback(() => {
+    if (animate) return;
+    setHovering(true);
+  }, [animate]);
+
+  const handleMouseLeave = useCallback(() => {
+    if (animate) return;
+    setHovering(false);
+  }, [animate]);
+
+  return { hovering, handleMouseEnter, handleMouseLeave };
+};
diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js
index 12d25490b..e1db44359 100644
--- a/app/javascript/mastodon/actions/compose.js
+++ b/app/javascript/mastodon/actions/compose.js
@@ -4,7 +4,6 @@ import { defineMessages } from 'react-intl';
 import api from 'mastodon/api';
 import { search as emojiSearch } from 'mastodon/features/emoji/emoji_mart_search_light';
 import { tagHistory } from 'mastodon/settings';
-import resizeImage from 'mastodon/utils/resize_image';
 import { showAlert, showAlertForError } from './alerts';
 import { useEmoji } from './emojis';
 import { importFetchedAccounts, importFetchedStatus } from './importer';
@@ -160,6 +159,26 @@ export function submitCompose(routerHistory) {
 
     dispatch(submitComposeRequest());
 
+    // If we're editing a post with media attachments, those have not
+    // necessarily been changed on the server. Do it now in the same
+    // API call.
+    let media_attributes;
+    if (statusId !== null) {
+      media_attributes = media.map(item => {
+        let focus;
+
+        if (item.getIn(['meta', 'focus'])) {
+          focus = `${item.getIn(['meta', 'focus', 'x']).toFixed(2)},${item.getIn(['meta', 'focus', 'y']).toFixed(2)}`;
+        }
+
+        return {
+          id: item.get('id'),
+          description: item.get('description'),
+          focus,
+        };
+      });
+    }
+
     api(getState).request({
       url: statusId === null ? '/api/v1/statuses' : `/api/v1/statuses/${statusId}`,
       method: statusId === null ? 'post' : 'put',
@@ -167,6 +186,7 @@ export function submitCompose(routerHistory) {
         status,
         in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
         media_ids: media.map(item => item.get('id')),
+        media_attributes,
         sensitive: getState().getIn(['compose', 'sensitive']),
         spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '',
         visibility: getState().getIn(['compose', 'privacy']),
@@ -255,46 +275,42 @@ export function uploadCompose(files) {
 
     dispatch(uploadComposeRequest());
 
-    for (const [i, f] of Array.from(files).entries()) {
+    for (const [i, file] of Array.from(files).entries()) {
       if (media.size + i > 3) break;
 
-      resizeImage(f).then(file => {
-        const data = new FormData();
-        data.append('file', file);
-        // Account for disparity in size of original image and resized data
-        total += file.size - f.size;
-
-        return api(getState).post('/api/v2/media', data, {
-          onUploadProgress: function({ loaded }){
-            progress[i] = loaded;
-            dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
-          },
-        }).then(({ status, data }) => {
-          // If server-side processing of the media attachment has not completed yet,
-          // poll the server until it is, before showing the media attachment as uploaded
-
-          if (status === 200) {
-            dispatch(uploadComposeSuccess(data, f));
-          } else if (status === 202) {
-            dispatch(uploadComposeProcessing());
-
-            let tryCount = 1;
-
-            const poll = () => {
-              api(getState).get(`/api/v1/media/${data.id}`).then(response => {
-                if (response.status === 200) {
-                  dispatch(uploadComposeSuccess(response.data, f));
-                } else if (response.status === 206) {
-                  const retryAfter = (Math.log2(tryCount) || 1) * 1000;
-                  tryCount += 1;
-                  setTimeout(() => poll(), retryAfter);
-                }
-              }).catch(error => dispatch(uploadComposeFail(error)));
-            };
-
-            poll();
-          }
-        });
+      const data = new FormData();
+      data.append('file', file);
+
+      api(getState).post('/api/v2/media', data, {
+        onUploadProgress: function({ loaded }){
+          progress[i] = loaded;
+          dispatch(uploadComposeProgress(progress.reduce((a, v) => a + v, 0), total));
+        },
+      }).then(({ status, data }) => {
+        // If server-side processing of the media attachment has not completed yet,
+        // poll the server until it is, before showing the media attachment as uploaded
+
+        if (status === 200) {
+          dispatch(uploadComposeSuccess(data, file));
+        } else if (status === 202) {
+          dispatch(uploadComposeProcessing());
+
+          let tryCount = 1;
+
+          const poll = () => {
+            api(getState).get(`/api/v1/media/${data.id}`).then(response => {
+              if (response.status === 200) {
+                dispatch(uploadComposeSuccess(response.data, file));
+              } else if (response.status === 206) {
+                const retryAfter = (Math.log2(tryCount) || 1) * 1000;
+                tryCount += 1;
+                setTimeout(() => poll(), retryAfter);
+              }
+            }).catch(error => dispatch(uploadComposeFail(error)));
+          };
+
+          poll();
+        }
       }).catch(error => dispatch(uploadComposeFail(error)));
     }
   };
@@ -377,11 +393,31 @@ export function changeUploadCompose(id, params) {
   return (dispatch, getState) => {
     dispatch(changeUploadComposeRequest());
 
-    api(getState).put(`/api/v1/media/${id}`, params).then(response => {
-      dispatch(changeUploadComposeSuccess(response.data));
-    }).catch(error => {
-      dispatch(changeUploadComposeFail(id, error));
-    });
+    let media = getState().getIn(['compose', 'media_attachments']).find((item) => item.get('id') === id);
+
+    // Editing already-attached media is deferred to editing the post itself.
+    // For simplicity's sake, fake an API reply.
+    if (media && !media.get('unattached')) {
+      let { description, focus } = params;
+      const data = media.toJS();
+
+      if (description) {
+        data.description = description;
+      }
+
+      if (focus) {
+        focus = focus.split(',');
+        data.meta = { focus: { x: parseFloat(focus[0]), y: parseFloat(focus[1]) } };
+      }
+
+      dispatch(changeUploadComposeSuccess(data, true));
+    } else {
+      api(getState).put(`/api/v1/media/${id}`, params).then(response => {
+        dispatch(changeUploadComposeSuccess(response.data, false));
+      }).catch(error => {
+        dispatch(changeUploadComposeFail(id, error));
+      });
+    }
   };
 }
 
@@ -392,10 +428,11 @@ export function changeUploadComposeRequest() {
   };
 }
 
-export function changeUploadComposeSuccess(media) {
+export function changeUploadComposeSuccess(media, attached) {
   return {
     type: COMPOSE_UPLOAD_CHANGE_SUCCESS,
     media: media,
+    attached: attached,
     skipLoading: true,
   };
 }
diff --git a/app/javascript/mastodon/actions/markers.js b/app/javascript/mastodon/actions/markers.js
index 16ec7fe77..ca246dce7 100644
--- a/app/javascript/mastodon/actions/markers.js
+++ b/app/javascript/mastodon/actions/markers.js
@@ -55,7 +55,7 @@ export const synchronouslySubmitMarkers = () => (dispatch, getState) => {
     client.open('POST', '/api/v1/markers', false);
     client.setRequestHeader('Content-Type', 'application/json');
     client.setRequestHeader('Authorization', `Bearer ${accessToken}`);
-    client.SUBMIT(JSON.stringify(params));
+    client.send(JSON.stringify(params));
   } catch (e) {
     // Do not make the BeforeUnload handler error out
   }
diff --git a/app/javascript/mastodon/actions/picture_in_picture.js b/app/javascript/mastodon/actions/picture_in_picture.js
index 33d8d57d4..6b9ff7709 100644
--- a/app/javascript/mastodon/actions/picture_in_picture.js
+++ b/app/javascript/mastodon/actions/picture_in_picture.js
@@ -23,6 +23,7 @@ export const PICTURE_IN_PICTURE_REMOVE = 'PICTURE_IN_PICTURE_REMOVE';
  * @return {object}
  */
 export const deployPictureInPicture = (statusId, accountId, playerType, props) => {
+  // @ts-expect-error
   return (dispatch, getState) => {
     // Do not open a player for a toot that does not exist
     if (getState().hasIn(['statuses', statusId])) {
diff --git a/app/javascript/mastodon/actions/push_notifications/registerer.js b/app/javascript/mastodon/actions/push_notifications/registerer.js
index b0f42b6a2..b491f85c2 100644
--- a/app/javascript/mastodon/actions/push_notifications/registerer.js
+++ b/app/javascript/mastodon/actions/push_notifications/registerer.js
@@ -8,7 +8,7 @@ import { me } from '../../initial_state';
 const urlBase64ToUint8Array = (base64String) => {
   const padding = '='.repeat((4 - base64String.length % 4) % 4);
   const base64 = (base64String + padding)
-    .replace(/\-/g, '+')
+    .replace(/-/g, '+')
     .replace(/_/g, '/');
 
   return decodeBase64(base64);
diff --git a/app/javascript/mastodon/actions/search.js b/app/javascript/mastodon/actions/search.js
index 666c6c223..56608f28b 100644
--- a/app/javascript/mastodon/actions/search.js
+++ b/app/javascript/mastodon/actions/search.js
@@ -14,6 +14,9 @@ export const SEARCH_EXPAND_REQUEST = 'SEARCH_EXPAND_REQUEST';
 export const SEARCH_EXPAND_SUCCESS = 'SEARCH_EXPAND_SUCCESS';
 export const SEARCH_EXPAND_FAIL    = 'SEARCH_EXPAND_FAIL';
 
+export const SEARCH_RESULT_CLICK  = 'SEARCH_RESULT_CLICK';
+export const SEARCH_RESULT_FORGET = 'SEARCH_RESULT_FORGET';
+
 export function changeSearch(value) {
   return {
     type: SEARCH_CHANGE,
@@ -27,7 +30,7 @@ export function clearSearch() {
   };
 }
 
-export function submitSearch() {
+export function submitSearch(type) {
   return (dispatch, getState) => {
     const value    = getState().getIn(['search', 'value']);
     const signedIn = !!getState().getIn(['meta', 'me']);
@@ -44,6 +47,7 @@ export function submitSearch() {
         q: value,
         resolve: signedIn,
         limit: 5,
+        type,
       },
     }).then(response => {
       if (response.data.accounts) {
@@ -130,3 +134,42 @@ export const expandSearchFail = error => ({
 export const showSearch = () => ({
   type: SEARCH_SHOW,
 });
+
+export const openURL = routerHistory => (dispatch, getState) => {
+  const value = getState().getIn(['search', 'value']);
+  const signedIn = !!getState().getIn(['meta', 'me']);
+
+  if (!signedIn) {
+    return;
+  }
+
+  dispatch(fetchSearchRequest());
+
+  api(getState).get('/api/v2/search', { params: { q: value, resolve: true } }).then(response => {
+    if (response.data.accounts?.length > 0) {
+      dispatch(importFetchedAccounts(response.data.accounts));
+      routerHistory.push(`/@${response.data.accounts[0].acct}`);
+    } else if (response.data.statuses?.length > 0) {
+      dispatch(importFetchedStatuses(response.data.statuses));
+      routerHistory.push(`/@${response.data.statuses[0].account.acct}/${response.data.statuses[0].id}`);
+    }
+
+    dispatch(fetchSearchSuccess(response.data, value));
+  }).catch(err => {
+    dispatch(fetchSearchFail(err));
+  });
+};
+
+export const clickSearchResult = (q, type) => ({
+  type: SEARCH_RESULT_CLICK,
+
+  result: {
+    type,
+    q,
+  },
+});
+
+export const forgetSearchResult = q => ({
+  type: SEARCH_RESULT_FORGET,
+  q,
+});
diff --git a/app/javascript/mastodon/actions/server.js b/app/javascript/mastodon/actions/server.js
index 31d4aea10..091af0f0f 100644
--- a/app/javascript/mastodon/actions/server.js
+++ b/app/javascript/mastodon/actions/server.js
@@ -5,6 +5,10 @@ export const SERVER_FETCH_REQUEST = 'Server_FETCH_REQUEST';
 export const SERVER_FETCH_SUCCESS = 'Server_FETCH_SUCCESS';
 export const SERVER_FETCH_FAIL    = 'Server_FETCH_FAIL';
 
+export const SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST = 'SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST';
+export const SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS = 'SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS';
+export const SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL    = 'SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL';
+
 export const EXTENDED_DESCRIPTION_REQUEST = 'EXTENDED_DESCRIPTION_REQUEST';
 export const EXTENDED_DESCRIPTION_SUCCESS = 'EXTENDED_DESCRIPTION_SUCCESS';
 export const EXTENDED_DESCRIPTION_FAIL    = 'EXTENDED_DESCRIPTION_FAIL';
@@ -37,6 +41,29 @@ const fetchServerFail = error => ({
   error,
 });
 
+export const fetchServerTranslationLanguages = () => (dispatch, getState) => {
+  dispatch(fetchServerTranslationLanguagesRequest());
+
+  api(getState)
+    .get('/api/v1/instance/translation_languages').then(({ data }) => {
+      dispatch(fetchServerTranslationLanguagesSuccess(data));
+    }).catch(err => dispatch(fetchServerTranslationLanguagesFail(err)));
+};
+
+const fetchServerTranslationLanguagesRequest = () => ({
+  type: SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST,
+});
+
+const fetchServerTranslationLanguagesSuccess = translationLanguages => ({
+  type: SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS,
+  translationLanguages,
+});
+
+const fetchServerTranslationLanguagesFail = error => ({
+  type: SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL,
+  error,
+});
+
 export const fetchExtendedDescription = () => (dispatch, getState) => {
   dispatch(fetchExtendedDescriptionRequest());
 
diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js
index 84709083f..4d4ea83e4 100644
--- a/app/javascript/mastodon/actions/streaming.js
+++ b/app/javascript/mastodon/actions/streaming.js
@@ -46,6 +46,7 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
   connectStream(channelName, params, (dispatch, getState) => {
     const locale = getState().getIn(['meta', 'locale']);
 
+    // @ts-expect-error
     let pollingId;
 
     /**
@@ -61,9 +62,10 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
       onConnect() {
         dispatch(connectTimeline(timelineId));
 
+        // @ts-expect-error
         if (pollingId) {
-          clearTimeout(pollingId);
-          pollingId = null;
+          // @ts-ignore
+          clearTimeout(pollingId); pollingId = null;
         }
 
         if (options.fillGaps) {
@@ -75,31 +77,38 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
         dispatch(disconnectTimeline(timelineId));
 
         if (options.fallback) {
+          // @ts-expect-error
           pollingId = setTimeout(() => useFallback(options.fallback), randomUpTo(40000));
         }
       },
 
-      onReceive (data) {
-        switch(data.event) {
+      onReceive(data) {
+        switch (data.event) {
         case 'update':
+          // @ts-expect-error
           dispatch(updateTimeline(timelineId, JSON.parse(data.payload), options.accept));
           break;
         case 'status.update':
+          // @ts-expect-error
           dispatch(updateStatus(JSON.parse(data.payload)));
           break;
         case 'delete':
           dispatch(deleteFromTimelines(data.payload));
           break;
         case 'notification':
+          // @ts-expect-error
           dispatch(updateNotifications(JSON.parse(data.payload), messages, locale));
           break;
         case 'conversation':
+          // @ts-expect-error
           dispatch(updateConversations(JSON.parse(data.payload)));
           break;
         case 'announcement':
+          // @ts-expect-error
           dispatch(updateAnnouncements(JSON.parse(data.payload)));
           break;
         case 'announcement.reaction':
+          // @ts-expect-error
           dispatch(updateAnnouncementsReaction(JSON.parse(data.payload)));
           break;
         case 'announcement.delete':
@@ -115,7 +124,9 @@ export const connectTimelineStream = (timelineId, channelName, params = {}, opti
  * @param {function(): void} done
  */
 const refreshHomeTimelineAndNotification = (dispatch, done) => {
+  // @ts-expect-error
   dispatch(expandHomeTimeline({}, () =>
+    // @ts-expect-error
     dispatch(expandNotifications({}, () =>
       dispatch(fetchAnnouncements(done))))));
 };
@@ -124,6 +135,7 @@ const refreshHomeTimelineAndNotification = (dispatch, done) => {
  * @return {function(): void}
  */
 export const connectUserStream = () =>
+  // @ts-expect-error
   connectTimelineStream('home', 'user', {}, { fallback: refreshHomeTimelineAndNotification, fillGaps: fillHomeTimelineGaps });
 
 /**
diff --git a/app/javascript/mastodon/actions/tags.js b/app/javascript/mastodon/actions/tags.js
index 37e79d4cb..dda8c924b 100644
--- a/app/javascript/mastodon/actions/tags.js
+++ b/app/javascript/mastodon/actions/tags.js
@@ -1,9 +1,17 @@
-import api from '../api';
+import api, { getLinks } from '../api';
 
 export const HASHTAG_FETCH_REQUEST = 'HASHTAG_FETCH_REQUEST';
 export const HASHTAG_FETCH_SUCCESS = 'HASHTAG_FETCH_SUCCESS';
 export const HASHTAG_FETCH_FAIL    = 'HASHTAG_FETCH_FAIL';
 
+export const FOLLOWED_HASHTAGS_FETCH_REQUEST = 'FOLLOWED_HASHTAGS_FETCH_REQUEST';
+export const FOLLOWED_HASHTAGS_FETCH_SUCCESS = 'FOLLOWED_HASHTAGS_FETCH_SUCCESS';
+export const FOLLOWED_HASHTAGS_FETCH_FAIL    = 'FOLLOWED_HASHTAGS_FETCH_FAIL';
+
+export const FOLLOWED_HASHTAGS_EXPAND_REQUEST = 'FOLLOWED_HASHTAGS_EXPAND_REQUEST';
+export const FOLLOWED_HASHTAGS_EXPAND_SUCCESS = 'FOLLOWED_HASHTAGS_EXPAND_SUCCESS';
+export const FOLLOWED_HASHTAGS_EXPAND_FAIL    = 'FOLLOWED_HASHTAGS_EXPAND_FAIL';
+
 export const HASHTAG_FOLLOW_REQUEST = 'HASHTAG_FOLLOW_REQUEST';
 export const HASHTAG_FOLLOW_SUCCESS = 'HASHTAG_FOLLOW_SUCCESS';
 export const HASHTAG_FOLLOW_FAIL    = 'HASHTAG_FOLLOW_FAIL';
@@ -37,6 +45,78 @@ export const fetchHashtagFail = error => ({
   error,
 });
 
+export const fetchFollowedHashtags = () => (dispatch, getState) => {
+  dispatch(fetchFollowedHashtagsRequest());
+
+  api(getState).get('/api/v1/followed_tags').then(response => {
+    const next = getLinks(response).refs.find(link => link.rel === 'next');
+    dispatch(fetchFollowedHashtagsSuccess(response.data, next ? next.uri : null));
+  }).catch(err => {
+    dispatch(fetchFollowedHashtagsFail(err));
+  });
+};
+
+export function fetchFollowedHashtagsRequest() {
+  return {
+    type: FOLLOWED_HASHTAGS_FETCH_REQUEST,
+  };
+}
+
+export function fetchFollowedHashtagsSuccess(followed_tags, next) {
+  return {
+    type: FOLLOWED_HASHTAGS_FETCH_SUCCESS,
+    followed_tags,
+    next,
+  };
+}
+
+export function fetchFollowedHashtagsFail(error) {
+  return {
+    type: FOLLOWED_HASHTAGS_FETCH_FAIL,
+    error,
+  };
+}
+
+export function expandFollowedHashtags() {
+  return (dispatch, getState) => {
+    const url = getState().getIn(['followed_tags', 'next']);
+
+    if (url === null) {
+      return;
+    }
+
+    dispatch(expandFollowedHashtagsRequest());
+
+    api(getState).get(url).then(response => {
+      const next = getLinks(response).refs.find(link => link.rel === 'next');
+      dispatch(expandFollowedHashtagsSuccess(response.data, next ? next.uri : null));
+    }).catch(error => {
+      dispatch(expandFollowedHashtagsFail(error));
+    });
+  };
+}
+
+export function expandFollowedHashtagsRequest() {
+  return {
+    type: FOLLOWED_HASHTAGS_EXPAND_REQUEST,
+  };
+}
+
+export function expandFollowedHashtagsSuccess(followed_tags, next) {
+  return {
+    type: FOLLOWED_HASHTAGS_EXPAND_SUCCESS,
+    followed_tags,
+    next,
+  };
+}
+
+export function expandFollowedHashtagsFail(error) {
+  return {
+    type: FOLLOWED_HASHTAGS_EXPAND_FAIL,
+    error,
+  };
+}
+
 export const followHashtag = name => (dispatch, getState) => {
   dispatch(followHashtagRequest(name));
 
diff --git a/app/javascript/mastodon/api.js b/app/javascript/mastodon/api.js
index 6bbddbef6..42b64d6cc 100644
--- a/app/javascript/mastodon/api.js
+++ b/app/javascript/mastodon/api.js
@@ -36,7 +36,7 @@ const setCSRFHeader = () => {
 ready(setCSRFHeader);
 
 /**
- * @param {() => import('immutable').Map} getState
+ * @param {() => import('immutable').Map<string,any>} getState
  * @returns {import('axios').RawAxiosRequestHeaders}
  */
 const authorizationHeaderFromState = getState => {
@@ -52,7 +52,7 @@ const authorizationHeaderFromState = getState => {
 };
 
 /**
- * @param {() => import('immutable').Map} getState
+ * @param {() => import('immutable').Map<string,any>} getState
  * @returns {import('axios').AxiosInstance}
  */
 export default function api(getState) {
diff --git a/app/javascript/mastodon/base_polyfills.js b/app/javascript/mastodon/base_polyfills.js
index 12096d902..91bc5d6dc 100644
--- a/app/javascript/mastodon/base_polyfills.js
+++ b/app/javascript/mastodon/base_polyfills.js
@@ -1,17 +1,11 @@
 import 'intl';
 import 'intl/locale-data/jsonp/en';
 import 'es6-symbol/implement';
-import includes from 'array-includes';
 import assign from 'object-assign';
 import values from 'object.values';
-import isNaN from 'is-nan';
 import { decode as decodeBase64 } from './utils/base64';
 import promiseFinally from 'promise.prototype.finally';
 
-if (!Array.prototype.includes) {
-  includes.shim();
-}
-
 if (!Object.assign) {
   Object.assign = assign;
 }
@@ -20,10 +14,6 @@ if (!Object.values) {
   values.shim();
 }
 
-if (!Number.isNaN) {
-  Number.isNaN = isNaN;
-}
-
 promiseFinally.shim();
 
 if (!HTMLCanvasElement.prototype.toBlob) {
diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.jsx.snap
index 1c3727848..1c3727848 100644
--- a/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.js.snap
+++ b/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.jsx.snap
diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.jsx.snap
index 7fbdedeb2..7fbdedeb2 100644
--- a/app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.js.snap
+++ b/app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.jsx.snap
diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.jsx.snap
index f8385357a..f8385357a 100644
--- a/app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.js.snap
+++ b/app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.jsx.snap
diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.jsx.snap
index bfc091d45..bfc091d45 100644
--- a/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.js.snap
+++ b/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.jsx.snap
diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.jsx.snap
index 9c37580d7..9c37580d7 100644
--- a/app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.js.snap
+++ b/app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.jsx.snap
diff --git a/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js b/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.jsx
index 05616e444..05616e444 100644
--- a/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js
+++ b/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.jsx
diff --git a/app/javascript/mastodon/components/__tests__/avatar-test.js b/app/javascript/mastodon/components/__tests__/avatar-test.jsx
index dd3f7b7d2..dd3f7b7d2 100644
--- a/app/javascript/mastodon/components/__tests__/avatar-test.js
+++ b/app/javascript/mastodon/components/__tests__/avatar-test.jsx
diff --git a/app/javascript/mastodon/components/__tests__/avatar_overlay-test.js b/app/javascript/mastodon/components/__tests__/avatar_overlay-test.jsx
index 44addea83..44addea83 100644
--- a/app/javascript/mastodon/components/__tests__/avatar_overlay-test.js
+++ b/app/javascript/mastodon/components/__tests__/avatar_overlay-test.jsx
diff --git a/app/javascript/mastodon/components/__tests__/button-test.js b/app/javascript/mastodon/components/__tests__/button-test.jsx
index f5a649f70..f5a649f70 100644
--- a/app/javascript/mastodon/components/__tests__/button-test.js
+++ b/app/javascript/mastodon/components/__tests__/button-test.jsx
diff --git a/app/javascript/mastodon/components/__tests__/display_name-test.js b/app/javascript/mastodon/components/__tests__/display_name-test.jsx
index 0d040c4cd..0d040c4cd 100644
--- a/app/javascript/mastodon/components/__tests__/display_name-test.js
+++ b/app/javascript/mastodon/components/__tests__/display_name-test.jsx
diff --git a/app/javascript/mastodon/components/account.js b/app/javascript/mastodon/components/account.jsx
index 7aebb124c..a8a47ecac 100644
--- a/app/javascript/mastodon/components/account.js
+++ b/app/javascript/mastodon/components/account.jsx
@@ -1,4 +1,4 @@
-import React, { Fragment } from 'react';
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Avatar from './avatar';
@@ -10,6 +10,10 @@ import { me } from '../initial_state';
 import RelativeTimestamp from './relative_timestamp';
 import Skeleton from 'mastodon/components/skeleton';
 import { Link } from 'react-router-dom';
+import { counterRenderer } from 'mastodon/components/common_counter';
+import ShortNumber from 'mastodon/components/short_number';
+import Icon from 'mastodon/components/icon';
+import classNames from 'classnames';
 
 const messages = defineMessages({
   follow: { id: 'account.follow', defaultMessage: 'Follow' },
@@ -23,7 +27,26 @@ const messages = defineMessages({
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
 });
 
-export default @injectIntl
+class VerifiedBadge extends React.PureComponent {
+
+  static propTypes = {
+    link: PropTypes.string.isRequired,
+    verifiedAt: PropTypes.string.isRequired,
+  };
+
+  render () {
+    const { link } = this.props;
+
+    return (
+      <span className='verified-badge'>
+        <Icon id='check' className='verified-badge__mark' />
+        <span dangerouslySetInnerHTML={{ __html: link }} />
+      </span>
+    );
+  }
+
+}
+
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -35,6 +58,7 @@ class Account extends ImmutablePureComponent {
     onMuteNotifications: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
     hidden: PropTypes.bool,
+    minimal: PropTypes.bool,
     actionIcon: PropTypes.string,
     actionTitle: PropTypes.string,
     defaultAction: PropTypes.string,
@@ -47,38 +71,42 @@ class Account extends ImmutablePureComponent {
 
   handleFollow = () => {
     this.props.onFollow(this.props.account);
-  }
+  };
 
   handleBlock = () => {
     this.props.onBlock(this.props.account);
-  }
+  };
 
   handleMute = () => {
     this.props.onMute(this.props.account);
-  }
+  };
 
   handleMuteNotifications = () => {
     this.props.onMuteNotifications(this.props.account, true);
-  }
+  };
 
   handleUnmuteNotifications = () => {
     this.props.onMuteNotifications(this.props.account, false);
-  }
+  };
 
   handleAction = () => {
     this.props.onActionClick(this.props.account);
-  }
+  };
 
   render () {
-    const { account, intl, hidden, onActionClick, actionIcon, actionTitle, defaultAction, size } = this.props;
+    const { account, intl, hidden, onActionClick, actionIcon, actionTitle, defaultAction, size, minimal } = this.props;
 
     if (!account) {
       return (
-        <div className='account'>
+        <div className={classNames('account', { 'account--minimal': minimal })}>
           <div className='account__wrapper'>
             <div className='account__display-name'>
-              <div className='account__avatar-wrapper'><Skeleton width={36} height={36} /></div>
-              <DisplayName />
+              <div className='account__avatar-wrapper'><Skeleton width={size} height={size} /></div>
+
+              <div>
+                <DisplayName />
+                <Skeleton width='7ch' />
+              </div>
             </div>
           </div>
         </div>
@@ -87,10 +115,10 @@ class Account extends ImmutablePureComponent {
 
     if (hidden) {
       return (
-        <Fragment>
+        <>
           {account.get('display_name')}
           {account.get('username')}
-        </Fragment>
+        </>
       );
     }
 
@@ -118,10 +146,10 @@ class Account extends ImmutablePureComponent {
           hidingNotificationsButton = <IconButton active icon='bell-slash' title={intl.formatMessage(messages.mute_notifications, { name: account.get('username')  })} onClick={this.handleMuteNotifications} />;
         }
         buttons = (
-          <Fragment>
+          <>
             <IconButton active icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />
             {hidingNotificationsButton}
-          </Fragment>
+          </>
         );
       } else if (defaultAction === 'mute') {
         buttons = <IconButton icon='volume-off' title={intl.formatMessage(messages.mute, { name: account.get('username') })} onClick={this.handleMute} />;
@@ -132,26 +160,44 @@ class Account extends ImmutablePureComponent {
       }
     }
 
-    let mute_expires_at;
+    let muteTimeRemaining;
+
     if (account.get('mute_expires_at')) {
-      mute_expires_at =  <div><RelativeTimestamp timestamp={account.get('mute_expires_at')} futureDate /></div>;
+      muteTimeRemaining = <>· <RelativeTimestamp timestamp={account.get('mute_expires_at')} futureDate /></>;
+    }
+
+    let verification;
+
+    const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at'));
+
+    if (firstVerifiedField) {
+      verification = <>· <VerifiedBadge link={firstVerifiedField.get('value')} verifiedAt={firstVerifiedField.get('verified_at')} /></>;
     }
 
     return (
-      <div className='account'>
+      <div className={classNames('account', { 'account--minimal': minimal })}>
         <div className='account__wrapper'>
           <Link key={account.get('id')} className='account__display-name' title={account.get('acct')} to={`/@${account.get('acct')}`}>
-            <div className='account__avatar-wrapper'><Avatar account={account} size={size} /></div>
-            {mute_expires_at}
-            <DisplayName account={account} />
+            <div className='account__avatar-wrapper'>
+              <Avatar account={account} size={size} />
+            </div>
+
+            <div>
+              <DisplayName account={account} />
+              {!minimal && <><ShortNumber value={account.get('followers_count')} renderer={counterRenderer('followers')} /> {verification} {muteTimeRemaining}</>}
+            </div>
           </Link>
 
-          <div className='account__relationship'>
-            {buttons}
-          </div>
+          {!minimal && (
+            <div className='account__relationship'>
+              {buttons}
+            </div>
+          )}
         </div>
       </div>
     );
   }
 
 }
+
+export default injectIntl(Account);
diff --git a/app/javascript/mastodon/components/admin/Counter.js b/app/javascript/mastodon/components/admin/Counter.jsx
index 5a5b2b869..5a5b2b869 100644
--- a/app/javascript/mastodon/components/admin/Counter.js
+++ b/app/javascript/mastodon/components/admin/Counter.jsx
diff --git a/app/javascript/mastodon/components/admin/Dimension.js b/app/javascript/mastodon/components/admin/Dimension.jsx
index 977c8208d..977c8208d 100644
--- a/app/javascript/mastodon/components/admin/Dimension.js
+++ b/app/javascript/mastodon/components/admin/Dimension.jsx
diff --git a/app/javascript/mastodon/components/admin/ReportReasonSelector.js b/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx
index 1f91d2517..cd14dac4e 100644
--- a/app/javascript/mastodon/components/admin/ReportReasonSelector.js
+++ b/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx
@@ -33,7 +33,7 @@ class Category extends React.PureComponent {
     const { id, text, disabled, selected, children } = this.props;
 
     return (
-      <div tabIndex='0' role='button' className={classNames('report-reason-selector__category', { selected, disabled })} onClick={this.handleClick}>
+      <div tabIndex={0} role='button' className={classNames('report-reason-selector__category', { selected, disabled })} onClick={this.handleClick}>
         {selected && <input type='hidden' name='report[category]' value={id} />}
 
         <div className='report-reason-selector__category__label'>
@@ -74,7 +74,7 @@ class Rule extends React.PureComponent {
     const { id, text, disabled, selected } = this.props;
 
     return (
-      <div tabIndex='0' role='button' className={classNames('report-reason-selector__rule', { selected, disabled })} onClick={this.handleClick}>
+      <div tabIndex={0} role='button' className={classNames('report-reason-selector__rule', { selected, disabled })} onClick={this.handleClick}>
         <span className={classNames('poll__input', { checkbox: true, active: selected, disabled })} />
         {selected && <input type='hidden' name='report[rule_ids][]' value={id} />}
         {text}
@@ -84,7 +84,6 @@ class Rule extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class ReportReasonSelector extends React.PureComponent {
 
   static propTypes = {
@@ -157,3 +156,5 @@ class ReportReasonSelector extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ReportReasonSelector);
diff --git a/app/javascript/mastodon/components/admin/Retention.js b/app/javascript/mastodon/components/admin/Retention.jsx
index f312a45eb..f312a45eb 100644
--- a/app/javascript/mastodon/components/admin/Retention.js
+++ b/app/javascript/mastodon/components/admin/Retention.jsx
diff --git a/app/javascript/mastodon/components/admin/Trends.js b/app/javascript/mastodon/components/admin/Trends.jsx
index 9530c2a5b..d01b8437e 100644
--- a/app/javascript/mastodon/components/admin/Trends.js
+++ b/app/javascript/mastodon/components/admin/Trends.jsx
@@ -50,7 +50,7 @@ export default class Trends extends React.PureComponent {
             <Hashtag
               key={hashtag.name}
               name={hashtag.name}
-              to={`/admin/tags/${hashtag.id}`}
+              to={hashtag.id === undefined ? undefined : `/admin/tags/${hashtag.id}`}
               people={hashtag.history[0].accounts * 1 + hashtag.history[1].accounts * 1}
               uses={hashtag.history[0].uses * 1 + hashtag.history[1].uses * 1}
               history={hashtag.history.reverse().map(day => day.uses)}
diff --git a/app/javascript/mastodon/components/animated_number.js b/app/javascript/mastodon/components/animated_number.jsx
index b1aebc73e..ce688f04f 100644
--- a/app/javascript/mastodon/components/animated_number.js
+++ b/app/javascript/mastodon/components/animated_number.jsx
@@ -38,13 +38,13 @@ export default class AnimatedNumber extends React.PureComponent {
     const { direction } = this.state;
 
     return { y: -1 * direction };
-  }
+  };
 
   willLeave = () => {
     const { direction } = this.state;
 
     return { y: spring(1 * direction, { damping: 35, stiffness: 400 }) };
-  }
+  };
 
   render () {
     const { value, obfuscate } = this.props;
diff --git a/app/javascript/mastodon/components/attachment_list.js b/app/javascript/mastodon/components/attachment_list.jsx
index 0e23889de..0e23889de 100644
--- a/app/javascript/mastodon/components/attachment_list.js
+++ b/app/javascript/mastodon/components/attachment_list.jsx
diff --git a/app/javascript/mastodon/components/autosuggest_emoji.js b/app/javascript/mastodon/components/autosuggest_emoji.jsx
index 4937e4d98..4937e4d98 100644
--- a/app/javascript/mastodon/components/autosuggest_emoji.js
+++ b/app/javascript/mastodon/components/autosuggest_emoji.jsx
diff --git a/app/javascript/mastodon/components/autosuggest_hashtag.js b/app/javascript/mastodon/components/autosuggest_hashtag.jsx
index 9e9d888f8..9e9d888f8 100644
--- a/app/javascript/mastodon/components/autosuggest_hashtag.js
+++ b/app/javascript/mastodon/components/autosuggest_hashtag.jsx
diff --git a/app/javascript/mastodon/components/autosuggest_input.js b/app/javascript/mastodon/components/autosuggest_input.jsx
index 12d44b5d0..a68e2a01b 100644
--- a/app/javascript/mastodon/components/autosuggest_input.js
+++ b/app/javascript/mastodon/components/autosuggest_input.jsx
@@ -50,6 +50,8 @@ export default class AutosuggestInput extends ImmutablePureComponent {
     id: PropTypes.string,
     searchTokens: PropTypes.arrayOf(PropTypes.string),
     maxLength: PropTypes.number,
+    lang: PropTypes.string,
+    spellCheck: PropTypes.bool,
   };
 
   static defaultProps = {
@@ -77,7 +79,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
     }
 
     this.props.onChange(e);
-  }
+  };
 
   onKeyDown = (e) => {
     const { suggestions, disabled } = this.props;
@@ -135,22 +137,22 @@ export default class AutosuggestInput extends ImmutablePureComponent {
     }
 
     this.props.onKeyDown(e);
-  }
+  };
 
   onBlur = () => {
     this.setState({ suggestionsHidden: true, focused: false });
-  }
+  };
 
   onFocus = () => {
     this.setState({ focused: true });
-  }
+  };
 
   onSuggestionClick = (e) => {
     const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
     e.preventDefault();
     this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
     this.input.focus();
-  }
+  };
 
   componentWillReceiveProps (nextProps) {
     if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
@@ -160,7 +162,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
 
   setInput = (c) => {
     this.input = c;
-  }
+  };
 
   renderSuggestion = (suggestion, i) => {
     const { selectedSuggestion } = this.state;
@@ -178,14 +180,14 @@ export default class AutosuggestInput extends ImmutablePureComponent {
     }
 
     return (
-      <div role='button' tabIndex='0' key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
+      <div role='button' tabIndex={0} key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
         {inner}
       </div>
     );
-  }
+  };
 
   render () {
-    const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength } = this.props;
+    const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength, lang, spellCheck } = this.props;
     const { suggestionsHidden } = this.state;
 
     return (
@@ -210,6 +212,8 @@ export default class AutosuggestInput extends ImmutablePureComponent {
             id={id}
             className={className}
             maxLength={maxLength}
+            lang={lang}
+            spellCheck={spellCheck}
           />
         </label>
 
diff --git a/app/javascript/mastodon/components/autosuggest_textarea.js b/app/javascript/mastodon/components/autosuggest_textarea.jsx
index 08b9cd80b..a627bc1ec 100644
--- a/app/javascript/mastodon/components/autosuggest_textarea.js
+++ b/app/javascript/mastodon/components/autosuggest_textarea.jsx
@@ -48,6 +48,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
     onKeyDown: PropTypes.func,
     onPaste: PropTypes.func.isRequired,
     autoFocus: PropTypes.bool,
+    lang: PropTypes.string,
   };
 
   static defaultProps = {
@@ -74,7 +75,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
     }
 
     this.props.onChange(e);
-  }
+  };
 
   onKeyDown = (e) => {
     const { suggestions, disabled } = this.props;
@@ -132,25 +133,25 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
     }
 
     this.props.onKeyDown(e);
-  }
+  };
 
   onBlur = () => {
     this.setState({ suggestionsHidden: true, focused: false });
-  }
+  };
 
   onFocus = (e) => {
     this.setState({ focused: true });
     if (this.props.onFocus) {
       this.props.onFocus(e);
     }
-  }
+  };
 
   onSuggestionClick = (e) => {
     const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
     e.preventDefault();
     this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
     this.textarea.focus();
-  }
+  };
 
   componentWillReceiveProps (nextProps) {
     if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
@@ -160,14 +161,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
 
   setTextarea = (c) => {
     this.textarea = c;
-  }
+  };
 
   onPaste = (e) => {
     if (e.clipboardData && e.clipboardData.files.length === 1) {
       this.props.onPaste(e.clipboardData.files);
       e.preventDefault();
     }
-  }
+  };
 
   renderSuggestion = (suggestion, i) => {
     const { selectedSuggestion } = this.state;
@@ -185,14 +186,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
     }
 
     return (
-      <div role='button' tabIndex='0' key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
+      <div role='button' tabIndex={0} key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
         {inner}
       </div>
     );
-  }
+  };
 
   render () {
-    const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props;
+    const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, lang, children } = this.props;
     const { suggestionsHidden } = this.state;
 
     return [
@@ -216,6 +217,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
               onPaste={this.onPaste}
               dir='auto'
               aria-autocomplete='list'
+              lang={lang}
             />
           </label>
         </div>
diff --git a/app/javascript/mastodon/components/avatar.js b/app/javascript/mastodon/components/avatar.js
deleted file mode 100644
index e617c2889..000000000
--- a/app/javascript/mastodon/components/avatar.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import ImmutablePropTypes from 'react-immutable-proptypes';
-import { autoPlayGif } from '../initial_state';
-import classNames from 'classnames';
-
-export default class Avatar extends React.PureComponent {
-
-  static propTypes = {
-    account: ImmutablePropTypes.map,
-    size: PropTypes.number.isRequired,
-    style: PropTypes.object,
-    inline: PropTypes.bool,
-    animate: PropTypes.bool,
-  };
-
-  static defaultProps = {
-    animate: autoPlayGif,
-    size: 20,
-    inline: false,
-  };
-
-  state = {
-    hovering: false,
-  };
-
-  handleMouseEnter = () => {
-    if (this.props.animate) return;
-    this.setState({ hovering: true });
-  }
-
-  handleMouseLeave = () => {
-    if (this.props.animate) return;
-    this.setState({ hovering: false });
-  }
-
-  render () {
-    const { account, size, animate, inline } = this.props;
-    const { hovering } = this.state;
-
-    const style = {
-      ...this.props.style,
-      width: `${size}px`,
-      height: `${size}px`,
-    };
-
-    let src;
-
-    if (hovering || animate) {
-      src = account?.get('avatar');
-    } else {
-      src = account?.get('avatar_static');
-    }
-
-    return (
-      <div className={classNames('account__avatar', { 'account__avatar-inline': inline })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} style={style}>
-        {src && <img src={src} alt={account?.get('acct')} />}
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/mastodon/components/avatar.tsx b/app/javascript/mastodon/components/avatar.tsx
new file mode 100644
index 000000000..e64a8af74
--- /dev/null
+++ b/app/javascript/mastodon/components/avatar.tsx
@@ -0,0 +1,49 @@
+import * as React from 'react';
+import classNames from 'classnames';
+import { autoPlayGif } from '../initial_state';
+import { useHovering } from '../../hooks/useHovering';
+import type { Account } from '../../types/resources';
+
+type Props = {
+  account: Account;
+  size: number;
+  style?: React.CSSProperties;
+  inline?: boolean;
+  animate?: boolean;
+};
+
+export const Avatar: React.FC<Props> = ({
+  account,
+  animate = autoPlayGif,
+  size = 20,
+  inline = false,
+  style: styleFromParent,
+}) => {
+  const { hovering, handleMouseEnter, handleMouseLeave } = useHovering(animate);
+
+  const style = {
+    ...styleFromParent,
+    width: `${size}px`,
+    height: `${size}px`,
+  };
+
+  const src =
+    hovering || animate
+      ? account?.get('avatar')
+      : account?.get('avatar_static');
+
+  return (
+    <div
+      className={classNames('account__avatar', {
+        'account__avatar-inline': inline,
+      })}
+      onMouseEnter={handleMouseEnter}
+      onMouseLeave={handleMouseLeave}
+      style={style}
+    >
+      {src && <img src={src} alt={account?.get('acct')} />}
+    </div>
+  );
+};
+
+export default Avatar;
diff --git a/app/javascript/mastodon/components/avatar_composite.js b/app/javascript/mastodon/components/avatar_composite.jsx
index 220bf5b4f..220bf5b4f 100644
--- a/app/javascript/mastodon/components/avatar_composite.js
+++ b/app/javascript/mastodon/components/avatar_composite.jsx
diff --git a/app/javascript/mastodon/components/avatar_overlay.js b/app/javascript/mastodon/components/avatar_overlay.jsx
index 8d5d44ea5..034e8ba56 100644
--- a/app/javascript/mastodon/components/avatar_overlay.js
+++ b/app/javascript/mastodon/components/avatar_overlay.jsx
@@ -29,12 +29,12 @@ export default class AvatarOverlay extends React.PureComponent {
   handleMouseEnter = () => {
     if (this.props.animate) return;
     this.setState({ hovering: true });
-  }
+  };
 
   handleMouseLeave = () => {
     if (this.props.animate) return;
     this.setState({ hovering: false });
-  }
+  };
 
   render() {
     const { account, friend, animate, size, baseSize, overlaySize } = this.props;
diff --git a/app/javascript/mastodon/components/blurhash.js b/app/javascript/mastodon/components/blurhash.jsx
index 2af5cfc56..07cd31b6c 100644
--- a/app/javascript/mastodon/components/blurhash.js
+++ b/app/javascript/mastodon/components/blurhash.jsx
@@ -44,6 +44,7 @@ function Blurhash({
       const ctx = canvas.getContext('2d');
       const imageData = new ImageData(pixels, width, height);
 
+      // @ts-expect-error
       ctx.putImageData(imageData, 0, 0);
     } catch (err) {
       console.error('Blurhash decoding failure', { err, hash });
diff --git a/app/javascript/mastodon/components/button.js b/app/javascript/mastodon/components/button.jsx
index 42ce01f38..a05a75e89 100644
--- a/app/javascript/mastodon/components/button.js
+++ b/app/javascript/mastodon/components/button.jsx
@@ -24,11 +24,11 @@ export default class Button extends React.PureComponent {
     if (!this.props.disabled && this.props.onClick) {
       this.props.onClick(e);
     }
-  }
+  };
 
   setRef = (c) => {
     this.node = c;
-  }
+  };
 
   focus() {
     this.node.focus();
diff --git a/app/javascript/mastodon/components/check.js b/app/javascript/mastodon/components/check.jsx
index ee2ef1595..ee2ef1595 100644
--- a/app/javascript/mastodon/components/check.js
+++ b/app/javascript/mastodon/components/check.jsx
diff --git a/app/javascript/mastodon/components/column.js b/app/javascript/mastodon/components/column.jsx
index 239824a4f..5780a1397 100644
--- a/app/javascript/mastodon/components/column.js
+++ b/app/javascript/mastodon/components/column.jsx
@@ -27,11 +27,11 @@ export default class Column extends React.PureComponent {
     }
 
     this._interruptScrollAnimation();
-  }
+  };
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   componentDidMount () {
     if (this.props.bindToDocument) {
diff --git a/app/javascript/mastodon/components/column_back_button.js b/app/javascript/mastodon/components/column_back_button.jsx
index d97622705..5c5226b7e 100644
--- a/app/javascript/mastodon/components/column_back_button.js
+++ b/app/javascript/mastodon/components/column_back_button.jsx
@@ -15,12 +15,12 @@ export default class ColumnBackButton extends React.PureComponent {
   };
 
   handleClick = () => {
-    if (window.history && window.history.length === 1) {
-      this.context.router.history.push('/');
-    } else {
+    if (window.history && window.history.state) {
       this.context.router.history.goBack();
+    } else {
+      this.context.router.history.push('/');
     }
-  }
+  };
 
   render () {
     const { multiColumn } = this.props;
diff --git a/app/javascript/mastodon/components/column_back_button_slim.js b/app/javascript/mastodon/components/column_back_button_slim.jsx
index cc8bfb151..46ac23736 100644
--- a/app/javascript/mastodon/components/column_back_button_slim.js
+++ b/app/javascript/mastodon/components/column_back_button_slim.jsx
@@ -8,7 +8,7 @@ export default class ColumnBackButtonSlim extends ColumnBackButton {
   render () {
     return (
       <div className='column-back-button--slim'>
-        <div role='button' tabIndex='0' onClick={this.handleClick} className='column-back-button column-back-button--slim-button'>
+        <div role='button' tabIndex={0} onClick={this.handleClick} className='column-back-button column-back-button--slim-button'>
           <Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
           <FormattedMessage id='column_back_button.label' defaultMessage='Back' />
         </div>
diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.jsx
index 7850a93ec..afc526f27 100644
--- a/app/javascript/mastodon/components/column_header.js
+++ b/app/javascript/mastodon/components/column_header.jsx
@@ -12,7 +12,6 @@ const messages = defineMessages({
   moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
 });
 
-export default @injectIntl
 class ColumnHeader extends React.PureComponent {
 
   static contextTypes = {
@@ -43,38 +42,34 @@ class ColumnHeader extends React.PureComponent {
     animating: false,
   };
 
-  historyBack = () => {
-    if (window.history && window.history.length === 1) {
-      this.context.router.history.push('/');
-    } else {
-      this.context.router.history.goBack();
-    }
-  }
-
   handleToggleClick = (e) => {
     e.stopPropagation();
     this.setState({ collapsed: !this.state.collapsed, animating: true });
-  }
+  };
 
   handleTitleClick = () => {
     this.props.onClick?.();
-  }
+  };
 
   handleMoveLeft = () => {
     this.props.onMove(-1);
-  }
+  };
 
   handleMoveRight = () => {
     this.props.onMove(1);
-  }
+  };
 
   handleBackClick = () => {
-    this.historyBack();
-  }
+    if (window.history && window.history.state) {
+      this.context.router.history.goBack();
+    } else {
+      this.context.router.history.push('/');
+    }
+  };
 
   handleTransitionEnd = () => {
     this.setState({ animating: false });
-  }
+  };
 
   handlePin = () => {
     if (!this.props.pinned) {
@@ -82,7 +77,7 @@ class ColumnHeader extends React.PureComponent {
     }
 
     this.props.onPin();
-  }
+  };
 
   render () {
     const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues } = this.props;
@@ -213,3 +208,5 @@ class ColumnHeader extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnHeader);
diff --git a/app/javascript/mastodon/components/common_counter.js b/app/javascript/mastodon/components/common_counter.jsx
index dd9b62de9..dd9b62de9 100644
--- a/app/javascript/mastodon/components/common_counter.js
+++ b/app/javascript/mastodon/components/common_counter.jsx
diff --git a/app/javascript/mastodon/components/dismissable_banner.js b/app/javascript/mastodon/components/dismissable_banner.jsx
index 1ee032056..242021e76 100644
--- a/app/javascript/mastodon/components/dismissable_banner.js
+++ b/app/javascript/mastodon/components/dismissable_banner.jsx
@@ -8,7 +8,6 @@ const messages = defineMessages({
   dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
 });
 
-export default @injectIntl
 class DismissableBanner extends React.PureComponent {
 
   static propTypes = {
@@ -24,7 +23,7 @@ class DismissableBanner extends React.PureComponent {
   handleDismiss = () => {
     const { id } = this.props;
     this.setState({ visible: false }, () => bannerSettings.set(id, true));
-  }
+  };
 
   render () {
     const { visible } = this.state;
@@ -49,3 +48,5 @@ class DismissableBanner extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(DismissableBanner);
diff --git a/app/javascript/mastodon/components/display_name.js b/app/javascript/mastodon/components/display_name.jsx
index e9139ab0f..1dd9fb1d6 100644
--- a/app/javascript/mastodon/components/display_name.js
+++ b/app/javascript/mastodon/components/display_name.jsx
@@ -23,7 +23,7 @@ export default class DisplayName extends React.PureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -36,7 +36,7 @@ export default class DisplayName extends React.PureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   render () {
     const { others, localDomain } = this.props;
diff --git a/app/javascript/flavours/glitch/components/domain.js b/app/javascript/mastodon/components/domain.jsx
index 697065d87..85ebdbde9 100644
--- a/app/javascript/flavours/glitch/components/domain.js
+++ b/app/javascript/mastodon/components/domain.jsx
@@ -8,7 +8,6 @@ const messages = defineMessages({
   unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' },
 });
 
-export default @injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -19,7 +18,7 @@ class Account extends ImmutablePureComponent {
 
   handleDomainUnblock = () => {
     this.props.onUnblockDomain(this.props.domain);
-  }
+  };
 
   render () {
     const { domain, intl } = this.props;
@@ -40,3 +39,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Account);
diff --git a/app/javascript/mastodon/components/dropdown_menu.js b/app/javascript/mastodon/components/dropdown_menu.jsx
index 5897aada8..eaaa72fd8 100644
--- a/app/javascript/mastodon/components/dropdown_menu.js
+++ b/app/javascript/mastodon/components/dropdown_menu.jsx
@@ -36,7 +36,7 @@ class DropdownMenu extends React.PureComponent {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
     }
-  }
+  };
 
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
@@ -56,11 +56,11 @@ class DropdownMenu extends React.PureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   setFocusRef = c => {
     this.focusedItem = c;
-  }
+  };
 
   handleKeyDown = e => {
     const items = Array.from(this.node.querySelectorAll('a, button'));
@@ -97,18 +97,18 @@ class DropdownMenu extends React.PureComponent {
       e.preventDefault();
       e.stopPropagation();
     }
-  }
+  };
 
   handleItemKeyPress = e => {
     if (e.key === 'Enter' || e.key === ' ') {
       this.handleClick(e);
     }
-  }
+  };
 
   handleClick = e => {
     const { onItemClick } = this.props;
     onItemClick(e);
-  }
+  };
 
   renderItem = (option, i) => {
     if (option === null) {
@@ -119,12 +119,12 @@ class DropdownMenu extends React.PureComponent {
 
     return (
       <li className='dropdown-menu__item' key={`${text}-${i}`}>
-        <a href={href} target={target} data-method={method} rel='noopener noreferrer' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}>
+        <a href={href} target={target} data-method={method} rel='noopener noreferrer' role='button' tabIndex={0} ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}>
           {text}
         </a>
       </li>
     );
-  }
+  };
 
   render () {
     const { items, scrollable, renderHeader, loading } = this.props;
@@ -194,7 +194,7 @@ export default class Dropdown extends React.PureComponent {
     } else {
       this.props.onOpen(this.state.id, this.handleItemClick, type !== 'click');
     }
-  }
+  };
 
   handleClose = () => {
     if (this.activeElement) {
@@ -202,13 +202,13 @@ export default class Dropdown extends React.PureComponent {
       this.activeElement = null;
     }
     this.props.onClose(this.state.id);
-  }
+  };
 
   handleMouseDown = () => {
     if (!this.state.open) {
       this.activeElement = document.activeElement;
     }
-  }
+  };
 
   handleButtonKeyDown = (e) => {
     switch(e.key) {
@@ -217,7 +217,7 @@ export default class Dropdown extends React.PureComponent {
       this.handleMouseDown();
       break;
     }
-  }
+  };
 
   handleKeyPress = (e) => {
     switch(e.key) {
@@ -228,7 +228,7 @@ export default class Dropdown extends React.PureComponent {
       e.preventDefault();
       break;
     }
-  }
+  };
 
   handleItemClick = e => {
     const { onItemClick } = this.props;
@@ -247,25 +247,25 @@ export default class Dropdown extends React.PureComponent {
       e.preventDefault();
       this.context.router.history.push(item.to);
     }
-  }
+  };
 
   setTargetRef = c => {
     this.target = c;
-  }
+  };
 
   findTarget = () => {
     return this.target;
-  }
+  };
 
   componentWillUnmount = () => {
     if (this.state.id === this.props.openDropdownId) {
       this.handleClose();
     }
-  }
+  };
 
   close = () => {
     this.handleClose();
-  }
+  };
 
   render () {
     const {
diff --git a/app/javascript/mastodon/components/edited_timestamp/index.js b/app/javascript/mastodon/components/edited_timestamp/index.jsx
index bebf93886..1513f9361 100644
--- a/app/javascript/mastodon/components/edited_timestamp/index.js
+++ b/app/javascript/mastodon/components/edited_timestamp/index.jsx
@@ -16,8 +16,6 @@ const mapDispatchToProps = (dispatch, { statusId }) => ({
 
 });
 
-export default @connect(null, mapDispatchToProps)
-@injectIntl
 class EditedTimestamp extends React.PureComponent {
 
   static propTypes = {
@@ -36,7 +34,7 @@ class EditedTimestamp extends React.PureComponent {
     return (
       <FormattedMessage id='status.edited_x_times' defaultMessage='Edited {count, plural, one {{count} time} other {{count} times}}' values={{ count: items.size - 1 }} />
     );
-  }
+  };
 
   renderItem = (item, index, { onClick, onKeyPress }) => {
     const formattedDate = <RelativeTimestamp timestamp={item.get('created_at')} short={false} />;
@@ -53,7 +51,7 @@ class EditedTimestamp extends React.PureComponent {
         <button data-index={index} onClick={onClick} onKeyPress={onKeyPress}>{label}</button>
       </li>
     );
-  }
+  };
 
   render () {
     const { timestamp, intl, statusId } = this.props;
@@ -68,3 +66,5 @@ class EditedTimestamp extends React.PureComponent {
   }
 
 }
+
+export default connect(null, mapDispatchToProps)(injectIntl(EditedTimestamp));
diff --git a/app/javascript/mastodon/components/error_boundary.js b/app/javascript/mastodon/components/error_boundary.jsx
index 02d5616d6..b711f1e46 100644
--- a/app/javascript/mastodon/components/error_boundary.js
+++ b/app/javascript/mastodon/components/error_boundary.jsx
@@ -64,7 +64,7 @@ export default class ErrorBoundary extends React.PureComponent {
 
     this.setState({ copied: true });
     setTimeout(() => this.setState({ copied: false }), 700);
-  }
+  };
 
   render() {
     const { hasError, copied, errorMessage } = this.state;
diff --git a/app/javascript/mastodon/components/gifv.js b/app/javascript/mastodon/components/gifv.jsx
index b775e5200..1ce7e7c29 100644
--- a/app/javascript/mastodon/components/gifv.js
+++ b/app/javascript/mastodon/components/gifv.jsx
@@ -6,6 +6,7 @@ export default class GIFV extends React.PureComponent {
   static propTypes = {
     src: PropTypes.string.isRequired,
     alt: PropTypes.string,
+    lang: PropTypes.string,
     width: PropTypes.number,
     height: PropTypes.number,
     onClick: PropTypes.func,
@@ -17,7 +18,7 @@ export default class GIFV extends React.PureComponent {
 
   handleLoadedData = () => {
     this.setState({ loading: false });
-  }
+  };
 
   componentWillReceiveProps (nextProps) {
     if (nextProps.src !== this.props.src) {
@@ -32,10 +33,10 @@ export default class GIFV extends React.PureComponent {
       e.stopPropagation();
       onClick();
     }
-  }
+  };
 
   render () {
-    const { src, width, height, alt } = this.props;
+    const { src, width, height, alt, lang } = this.props;
     const { loading } = this.state;
 
     return (
@@ -45,9 +46,10 @@ export default class GIFV extends React.PureComponent {
             width={width}
             height={height}
             role='button'
-            tabIndex='0'
+            tabIndex={0}
             aria-label={alt}
             title={alt}
+            lang={lang}
             onClick={this.handleClick}
           />
         )}
@@ -55,9 +57,10 @@ export default class GIFV extends React.PureComponent {
         <video
           src={src}
           role='button'
-          tabIndex='0'
+          tabIndex={0}
           aria-label={alt}
           title={alt}
+          lang={lang}
           muted
           loop
           autoPlay
diff --git a/app/javascript/mastodon/components/hashtag.js b/app/javascript/mastodon/components/hashtag.jsx
index e516fc086..94c61b654 100644
--- a/app/javascript/mastodon/components/hashtag.js
+++ b/app/javascript/mastodon/components/hashtag.jsx
@@ -5,7 +5,9 @@ import { FormattedMessage } from 'react-intl';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { Link } from 'react-router-dom';
+// @ts-expect-error
 import ShortNumber from 'mastodon/components/short_number';
+// @ts-expect-error
 import Skeleton from 'mastodon/components/skeleton';
 import classNames from 'classnames';
 
@@ -19,11 +21,11 @@ class SilentErrorBoundary extends React.Component {
     error: false,
   };
 
-  componentDidCatch () {
+  componentDidCatch() {
     this.setState({ error: true });
   }
 
-  render () {
+  render() {
     if (this.state.error) {
       return null;
     }
@@ -50,11 +52,13 @@ export const accountsCountRenderer = (displayNumber, pluralReady) => (
   />
 );
 
+// @ts-expect-error
 export const ImmutableHashtag = ({ hashtag }) => (
   <Hashtag
     name={hashtag.get('name')}
     to={`/tags/${hashtag.get('name')}`}
     people={hashtag.getIn(['history', 0, 'accounts']) * 1 + hashtag.getIn(['history', 1, 'accounts']) * 1}
+    // @ts-expect-error
     history={hashtag.get('history').reverse().map((day) => day.get('uses')).toArray()}
   />
 );
@@ -63,6 +67,7 @@ ImmutableHashtag.propTypes = {
   hashtag: ImmutablePropTypes.map.isRequired,
 };
 
+// @ts-expect-error
 const Hashtag = ({ name, to, people, uses, history, className, description, withGraph }) => (
   <div className={classNames('trends__item', className)}>
     <div className='trends__item__name'>
diff --git a/app/javascript/mastodon/components/icon.js b/app/javascript/mastodon/components/icon.jsx
index d3d7c591d..d3d7c591d 100644
--- a/app/javascript/mastodon/components/icon.js
+++ b/app/javascript/mastodon/components/icon.jsx
diff --git a/app/javascript/mastodon/components/icon_button.js b/app/javascript/mastodon/components/icon_button.jsx
index b7daf82a4..989cae440 100644
--- a/app/javascript/mastodon/components/icon_button.js
+++ b/app/javascript/mastodon/components/icon_button.jsx
@@ -23,7 +23,7 @@ export default class IconButton extends React.PureComponent {
     inverted: PropTypes.bool,
     animate: PropTypes.bool,
     overlay: PropTypes.bool,
-    tabIndex: PropTypes.string,
+    tabIndex: PropTypes.number,
     counter: PropTypes.number,
     obfuscateCount: PropTypes.bool,
     href: PropTypes.string,
@@ -36,14 +36,14 @@ export default class IconButton extends React.PureComponent {
     disabled: false,
     animate: false,
     overlay: false,
-    tabIndex: '0',
+    tabIndex: 0,
     ariaHidden: false,
   };
 
   state = {
     activate: false,
     deactivate: false,
-  }
+  };
 
   componentWillReceiveProps (nextProps) {
     if (!nextProps.animate) return;
@@ -61,25 +61,25 @@ export default class IconButton extends React.PureComponent {
     if (!this.props.disabled) {
       this.props.onClick(e);
     }
-  }
+  };
 
   handleKeyPress = (e) => {
     if (this.props.onKeyPress && !this.props.disabled) {
       this.props.onKeyPress(e);
     }
-  }
+  };
 
   handleMouseDown = (e) => {
     if (!this.props.disabled && this.props.onMouseDown) {
       this.props.onMouseDown(e);
     }
-  }
+  };
 
   handleKeyDown = (e) => {
     if (!this.props.disabled && this.props.onKeyDown) {
       this.props.onKeyDown(e);
     }
-  }
+  };
 
   render () {
     const style = {
diff --git a/app/javascript/mastodon/components/icon_with_badge.js b/app/javascript/mastodon/components/icon_with_badge.jsx
index 4214eccfd..4214eccfd 100644
--- a/app/javascript/mastodon/components/icon_with_badge.js
+++ b/app/javascript/mastodon/components/icon_with_badge.jsx
diff --git a/app/javascript/mastodon/components/image.js b/app/javascript/mastodon/components/image.jsx
index 6e81ddf08..6e81ddf08 100644
--- a/app/javascript/mastodon/components/image.js
+++ b/app/javascript/mastodon/components/image.jsx
diff --git a/app/javascript/mastodon/components/inline_account.js b/app/javascript/mastodon/components/inline_account.jsx
index a1b495590..31dc63f93 100644
--- a/app/javascript/mastodon/components/inline_account.js
+++ b/app/javascript/mastodon/components/inline_account.jsx
@@ -14,7 +14,6 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-export default @connect(makeMapStateToProps)
 class InlineAccount extends React.PureComponent {
 
   static propTypes = {
@@ -32,3 +31,5 @@ class InlineAccount extends React.PureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(InlineAccount);
diff --git a/app/javascript/mastodon/components/intersection_observer_article.js b/app/javascript/mastodon/components/intersection_observer_article.jsx
index 26f85fa40..77957a21d 100644
--- a/app/javascript/mastodon/components/intersection_observer_article.js
+++ b/app/javascript/mastodon/components/intersection_observer_article.jsx
@@ -21,7 +21,7 @@ export default class IntersectionObserverArticle extends React.Component {
 
   state = {
     isHidden: false, // set to true in requestIdleCallback to trigger un-render
-  }
+  };
 
   shouldComponentUpdate (nextProps, nextState) {
     const isUnrendered = !this.state.isIntersecting && (this.state.isHidden || this.props.cachedHeight);
@@ -62,7 +62,7 @@ export default class IntersectionObserverArticle extends React.Component {
 
     scheduleIdleTask(this.calculateHeight);
     this.setState(this.updateStateAfterIntersection);
-  }
+  };
 
   updateStateAfterIntersection = (prevState) => {
     if (prevState.isIntersecting !== false && !this.entry.isIntersecting) {
@@ -72,7 +72,7 @@ export default class IntersectionObserverArticle extends React.Component {
       isIntersecting: this.entry.isIntersecting,
       isHidden: false,
     };
-  }
+  };
 
   calculateHeight = () => {
     const { onHeightChange, saveHeightKey, id } = this.props;
@@ -83,7 +83,7 @@ export default class IntersectionObserverArticle extends React.Component {
     if (onHeightChange && saveHeightKey) {
       onHeightChange(saveHeightKey, id, this.height);
     }
-  }
+  };
 
   hideIfNotIntersecting = () => {
     if (!this.componentMounted) {
@@ -95,11 +95,11 @@ export default class IntersectionObserverArticle extends React.Component {
     // this is to save DOM nodes and avoid using up too much memory.
     // See: https://github.com/mastodon/mastodon/issues/2900
     this.setState((prevState) => ({ isHidden: !prevState.isIntersecting }));
-  }
+  };
 
   handleRef = (node) => {
     this.node = node;
-  }
+  };
 
   render () {
     const { children, id, index, listLength, cachedHeight } = this.props;
@@ -113,7 +113,7 @@ export default class IntersectionObserverArticle extends React.Component {
           aria-setsize={listLength}
           style={{ height: `${this.height || cachedHeight}px`, opacity: 0, overflow: 'hidden' }}
           data-id={id}
-          tabIndex='0'
+          tabIndex={0}
         >
           {children && React.cloneElement(children, { hidden: true })}
         </article>
@@ -121,7 +121,7 @@ export default class IntersectionObserverArticle extends React.Component {
     }
 
     return (
-      <article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex='0'>
+      <article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex={0}>
         {children && React.cloneElement(children, { hidden: false })}
       </article>
     );
diff --git a/app/javascript/mastodon/components/load_gap.js b/app/javascript/mastodon/components/load_gap.jsx
index a44d55d09..2c91d37be 100644
--- a/app/javascript/mastodon/components/load_gap.js
+++ b/app/javascript/mastodon/components/load_gap.jsx
@@ -7,7 +7,6 @@ const messages = defineMessages({
   load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
 });
 
-export default @injectIntl
 class LoadGap extends React.PureComponent {
 
   static propTypes = {
@@ -19,7 +18,7 @@ class LoadGap extends React.PureComponent {
 
   handleClick = () => {
     this.props.onClick(this.props.maxId);
-  }
+  };
 
   render () {
     const { disabled, intl } = this.props;
@@ -32,3 +31,5 @@ class LoadGap extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(LoadGap);
diff --git a/app/javascript/mastodon/components/load_more.js b/app/javascript/mastodon/components/load_more.jsx
index 00e023ca2..150525214 100644
--- a/app/javascript/mastodon/components/load_more.js
+++ b/app/javascript/mastodon/components/load_more.jsx
@@ -8,11 +8,11 @@ export default class LoadMore extends React.PureComponent {
     onClick: PropTypes.func,
     disabled: PropTypes.bool,
     visible: PropTypes.bool,
-  }
+  };
 
   static defaultProps = {
     visible: true,
-  }
+  };
 
   render() {
     const { disabled, visible } = this.props;
diff --git a/app/javascript/mastodon/components/load_pending.js b/app/javascript/mastodon/components/load_pending.jsx
index 7e2702403..a75259146 100644
--- a/app/javascript/mastodon/components/load_pending.js
+++ b/app/javascript/mastodon/components/load_pending.jsx
@@ -7,7 +7,7 @@ export default class LoadPending extends React.PureComponent {
   static propTypes = {
     onClick: PropTypes.func,
     count: PropTypes.number,
-  }
+  };
 
   render() {
     const { count } = this.props;
diff --git a/app/javascript/mastodon/components/loading_indicator.js b/app/javascript/mastodon/components/loading_indicator.jsx
index 33c59d94c..33c59d94c 100644
--- a/app/javascript/mastodon/components/loading_indicator.js
+++ b/app/javascript/mastodon/components/loading_indicator.jsx
diff --git a/app/javascript/mastodon/components/logo.js b/app/javascript/mastodon/components/logo.jsx
index ee5c22496..ee5c22496 100644
--- a/app/javascript/mastodon/components/logo.js
+++ b/app/javascript/mastodon/components/logo.jsx
diff --git a/app/javascript/mastodon/components/media_attachments.js b/app/javascript/mastodon/components/media_attachments.jsx
index d27720de4..0e25e5973 100644
--- a/app/javascript/mastodon/components/media_attachments.js
+++ b/app/javascript/mastodon/components/media_attachments.jsx
@@ -10,6 +10,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
 
   static propTypes = {
     status: ImmutablePropTypes.map.isRequired,
+    lang: PropTypes.string,
     height: PropTypes.number,
     width: PropTypes.number,
   };
@@ -29,7 +30,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
     return (
       <div className='media-gallery' style={{ height, width }} />
     );
-  }
+  };
 
   renderLoadingVideoPlayer = () => {
     const { height, width } = this.props;
@@ -37,7 +38,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
     return (
       <div className='video-player' style={{ height, width }} />
     );
-  }
+  };
 
   renderLoadingAudioPlayer = () => {
     const { height, width } = this.props;
@@ -45,10 +46,10 @@ export default class MediaAttachments extends ImmutablePureComponent {
     return (
       <div className='audio-player' style={{ height, width }} />
     );
-  }
+  };
 
   render () {
-    const { status, width, height } = this.props;
+    const { status, lang, width, height } = this.props;
     const mediaAttachments = status.get('media_attachments');
 
     if (mediaAttachments.size === 0) {
@@ -64,6 +65,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
             <Component
               src={audio.get('url')}
               alt={audio.get('description')}
+              lang={lang || status.get('language')}
               width={width}
               height={height}
               poster={audio.get('preview_url') || status.getIn(['account', 'avatar_static'])}
@@ -87,6 +89,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
               blurhash={video.get('blurhash')}
               src={video.get('url')}
               alt={video.get('description')}
+              lang={lang || status.get('language')}
               width={width}
               height={height}
               inline
@@ -102,6 +105,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
           {Component => (
             <Component
               media={mediaAttachments}
+              lang={lang || status.get('language')}
               sensitive={status.get('sensitive')}
               defaultWidth={width}
               height={height}
diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.jsx
index e4a8be338..5be0070a3 100644
--- a/app/javascript/mastodon/components/media_gallery.js
+++ b/app/javascript/mastodon/components/media_gallery.jsx
@@ -17,6 +17,7 @@ class Item extends React.PureComponent {
 
   static propTypes = {
     attachment: ImmutablePropTypes.map.isRequired,
+    lang: PropTypes.string,
     standalone: PropTypes.bool,
     index: PropTypes.number.isRequired,
     size: PropTypes.number.isRequired,
@@ -40,14 +41,14 @@ class Item extends React.PureComponent {
     if (this.hoverToPlay()) {
       e.target.play();
     }
-  }
+  };
 
   handleMouseLeave = (e) => {
     if (this.hoverToPlay()) {
       e.target.pause();
       e.target.currentTime = 0;
     }
-  }
+  };
 
   getAutoPlay() {
     return this.props.autoplay || autoPlayGif;
@@ -71,14 +72,14 @@ class Item extends React.PureComponent {
     }
 
     e.stopPropagation();
-  }
+  };
 
   handleImageLoad = () => {
     this.setState({ loaded: true });
-  }
+  };
 
   render () {
-    const { attachment, index, size, standalone, displayWidth, visible } = this.props;
+    const { attachment, lang, index, size, standalone, displayWidth, visible } = this.props;
 
     let width  = 50;
     let height = 100;
@@ -134,7 +135,7 @@ class Item extends React.PureComponent {
     if (attachment.get('type') === 'unknown') {
       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}%` }}>
-          <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={attachment.get('description')} target='_blank' rel='noopener noreferrer'>
+          <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={attachment.get('description')} lang={lang} target='_blank' rel='noopener noreferrer'>
             <Blurhash
               hash={attachment.get('blurhash')}
               className='media-gallery__preview'
@@ -174,6 +175,7 @@ class Item extends React.PureComponent {
             sizes={sizes}
             alt={attachment.get('description')}
             title={attachment.get('description')}
+            lang={lang}
             style={{ objectPosition: `${x}% ${y}%` }}
             onLoad={this.handleImageLoad}
           />
@@ -188,6 +190,7 @@ class Item extends React.PureComponent {
             className='media-gallery__item-gifv-thumbnail'
             aria-label={attachment.get('description')}
             title={attachment.get('description')}
+            lang={lang}
             role='application'
             src={attachment.get('url')}
             onClick={this.handleClick}
@@ -220,13 +223,13 @@ class Item extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class MediaGallery extends React.PureComponent {
 
   static propTypes = {
     sensitive: PropTypes.bool,
     standalone: PropTypes.bool,
     media: ImmutablePropTypes.list.isRequired,
+    lang: PropTypes.string,
     size: PropTypes.object,
     height: PropTypes.number.isRequired,
     onOpenMedia: PropTypes.func.isRequired,
@@ -277,11 +280,11 @@ class MediaGallery extends React.PureComponent {
     } else {
       this.setState({ visible: !this.state.visible });
     }
-  }
+  };
 
   handleClick = (index) => {
     this.props.onOpenMedia(this.props.media, index);
-  }
+  };
 
   handleRef = c => {
     this.node = c;
@@ -289,7 +292,7 @@ class MediaGallery extends React.PureComponent {
     if (this.node) {
       this._setDimensions();
     }
-  }
+  };
 
   _setDimensions () {
     const width = this.node.offsetWidth;
@@ -310,9 +313,8 @@ class MediaGallery extends React.PureComponent {
   }
 
   render () {
-    const { media, intl, sensitive, height, defaultWidth, standalone, autoplay } = this.props;
+    const { media, lang, intl, sensitive, height, defaultWidth, standalone, autoplay } = this.props;
     const { visible } = this.state;
-
     const width = this.state.width || defaultWidth;
 
     let children, spoilerButton;
@@ -333,9 +335,9 @@ class MediaGallery extends React.PureComponent {
     const uncached = media.every(attachment => attachment.get('type') === 'unknown');
 
     if (standalone && this.isFullSizeEligible()) {
-      children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
+      children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} lang={lang} displayWidth={width} visible={visible} />;
     } else {
-      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} size={size} displayWidth={width} visible={visible || uncached} />);
+      children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} lang={lang} size={size} displayWidth={width} visible={visible || uncached} />);
     }
 
     if (uncached) {
@@ -366,3 +368,5 @@ class MediaGallery extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(MediaGallery);
diff --git a/app/javascript/mastodon/components/missing_indicator.js b/app/javascript/mastodon/components/missing_indicator.jsx
index 05e0d653d..05e0d653d 100644
--- a/app/javascript/mastodon/components/missing_indicator.js
+++ b/app/javascript/mastodon/components/missing_indicator.jsx
diff --git a/app/javascript/mastodon/components/modal_root.js b/app/javascript/mastodon/components/modal_root.jsx
index b894aeaf9..c0525c221 100644
--- a/app/javascript/mastodon/components/modal_root.js
+++ b/app/javascript/mastodon/components/modal_root.jsx
@@ -28,7 +28,7 @@ export default class ModalRoot extends React.PureComponent {
          && !!this.props.children) {
       this.props.onClose();
     }
-  }
+  };
 
   handleKeyDown = (e) => {
     if (e.key === 'Tab') {
@@ -49,7 +49,7 @@ export default class ModalRoot extends React.PureComponent {
         e.preventDefault();
       }
     }
-  }
+  };
 
   componentDidMount () {
     window.addEventListener('keyup', this.handleKeyUp, false);
@@ -122,11 +122,11 @@ export default class ModalRoot extends React.PureComponent {
 
   getSiblings = () => {
     return Array(...this.node.parentElement.childNodes).filter(node => node !== this.node);
-  }
+  };
 
   setRef = ref => {
     this.node = ref;
-  }
+  };
 
   render () {
     const { children, onClose } = this.props;
diff --git a/app/javascript/mastodon/components/navigation_portal.js b/app/javascript/mastodon/components/navigation_portal.jsx
index 45407be43..a100dc04a 100644
--- a/app/javascript/mastodon/components/navigation_portal.js
+++ b/app/javascript/mastodon/components/navigation_portal.jsx
@@ -15,7 +15,6 @@ const DefaultNavigation = () => (
   </>
 );
 
-export default @withRouter
 class NavigationPortal extends React.PureComponent {
 
   render () {
@@ -33,3 +32,4 @@ class NavigationPortal extends React.PureComponent {
   }
 
 }
+export default withRouter(NavigationPortal);
diff --git a/app/javascript/mastodon/components/not_signed_in_indicator.js b/app/javascript/mastodon/components/not_signed_in_indicator.jsx
index b440c6be2..b440c6be2 100644
--- a/app/javascript/mastodon/components/not_signed_in_indicator.js
+++ b/app/javascript/mastodon/components/not_signed_in_indicator.jsx
diff --git a/app/javascript/mastodon/components/picture_in_picture_placeholder.js b/app/javascript/mastodon/components/picture_in_picture_placeholder.jsx
index 19d15c18b..6322b1c66 100644
--- a/app/javascript/mastodon/components/picture_in_picture_placeholder.js
+++ b/app/javascript/mastodon/components/picture_in_picture_placeholder.jsx
@@ -6,7 +6,6 @@ import { connect } from 'react-redux';
 import { debounce } from 'lodash';
 import { FormattedMessage } from 'react-intl';
 
-export default @connect()
 class PictureInPicturePlaceholder extends React.PureComponent {
 
   static propTypes = {
@@ -22,7 +21,7 @@ class PictureInPicturePlaceholder extends React.PureComponent {
   handleClick = () => {
     const { dispatch } = this.props;
     dispatch(removePictureInPicture());
-  }
+  };
 
   setRef = c => {
     this.node = c;
@@ -30,7 +29,7 @@ class PictureInPicturePlaceholder extends React.PureComponent {
     if (this.node) {
       this._setDimensions();
     }
-  }
+  };
 
   _setDimensions () {
     const width  = this.node.offsetWidth;
@@ -59,7 +58,7 @@ class PictureInPicturePlaceholder extends React.PureComponent {
     const { height } = this.state;
 
     return (
-      <div ref={this.setRef} className='picture-in-picture-placeholder' style={{ height }} role='button' tabIndex='0' onClick={this.handleClick}>
+      <div ref={this.setRef} className='picture-in-picture-placeholder' style={{ height }} role='button' tabIndex={0} onClick={this.handleClick}>
         <Icon id='window-restore' />
         <FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' />
       </div>
@@ -67,3 +66,5 @@ class PictureInPicturePlaceholder extends React.PureComponent {
   }
 
 }
+
+export default connect()(PictureInPicturePlaceholder);
diff --git a/app/javascript/mastodon/components/poll.js b/app/javascript/mastodon/components/poll.jsx
index 3e643168e..b9b96a700 100644
--- a/app/javascript/mastodon/components/poll.js
+++ b/app/javascript/mastodon/components/poll.jsx
@@ -31,7 +31,6 @@ const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
   return obj;
 }, {});
 
-export default @injectIntl
 class Poll extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -40,6 +39,7 @@ class Poll extends ImmutablePureComponent {
 
   static propTypes = {
     poll: ImmutablePropTypes.map,
+    lang: PropTypes.string,
     intl: PropTypes.object.isRequired,
     disabled: PropTypes.bool,
     refresh: PropTypes.func,
@@ -95,7 +95,7 @@ class Poll extends ImmutablePureComponent {
       tmp[value] = true;
       this.setState({ selected: tmp });
     }
-  }
+  };
 
   handleOptionChange = ({ target: { value } }) => {
     this._toggleOption(value);
@@ -107,7 +107,7 @@ class Poll extends ImmutablePureComponent {
       e.stopPropagation();
       e.preventDefault();
     }
-  }
+  };
 
   handleVote = () => {
     if (this.props.disabled) {
@@ -126,7 +126,7 @@ class Poll extends ImmutablePureComponent {
   };
 
   renderOption (option, optionIndex, showResults) {
-    const { poll, disabled, intl } = this.props;
+    const { poll, lang, disabled, intl } = this.props;
     const pollVotesCount  = poll.get('voters_count') || poll.get('votes_count');
     const percent         = pollVotesCount === 0 ? 0 : (option.get('votes_count') / pollVotesCount) * 100;
     const leading         = poll.get('options').filterNot(other => other.get('title') === option.get('title')).every(other => option.get('votes_count') >= other.get('votes_count'));
@@ -154,11 +154,12 @@ class Poll extends ImmutablePureComponent {
           {!showResults && (
             <span
               className={classNames('poll__input', { checkbox: poll.get('multiple'), active })}
-              tabIndex='0'
+              tabIndex={0}
               role={poll.get('multiple') ? 'checkbox' : 'radio'}
               onKeyPress={this.handleOptionKeyPress}
               aria-checked={active}
               aria-label={option.get('title')}
+              lang={lang}
               data-index={optionIndex}
             />
           )}
@@ -175,6 +176,7 @@ class Poll extends ImmutablePureComponent {
 
           <span
             className='poll__option__text translate'
+            lang={lang}
             dangerouslySetInnerHTML={{ __html: titleEmojified }}
           />
 
@@ -231,3 +233,5 @@ class Poll extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Poll);
diff --git a/app/javascript/mastodon/components/radio_button.js b/app/javascript/mastodon/components/radio_button.jsx
index 0496fa286..0496fa286 100644
--- a/app/javascript/mastodon/components/radio_button.js
+++ b/app/javascript/mastodon/components/radio_button.jsx
diff --git a/app/javascript/mastodon/components/regeneration_indicator.js b/app/javascript/mastodon/components/regeneration_indicator.jsx
index 52696a4a7..52696a4a7 100644
--- a/app/javascript/mastodon/components/regeneration_indicator.js
+++ b/app/javascript/mastodon/components/regeneration_indicator.jsx
diff --git a/app/javascript/flavours/glitch/components/relative_timestamp.js b/app/javascript/mastodon/components/relative_timestamp.jsx
index 512480339..e6c3e0880 100644
--- a/app/javascript/flavours/glitch/components/relative_timestamp.js
+++ b/app/javascript/mastodon/components/relative_timestamp.jsx
@@ -121,7 +121,6 @@ const timeRemainingString = (intl, date, now, timeGiven = true) => {
   return relativeTime;
 };
 
-export default @injectIntl
 class RelativeTimestamp extends React.Component {
 
   static propTypes = {
@@ -197,3 +196,5 @@ class RelativeTimestamp extends React.Component {
   }
 
 }
+
+export default injectIntl(RelativeTimestamp);
diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.jsx
index 91d04bf4d..57bc88121 100644
--- a/app/javascript/mastodon/components/scrollable_list.js
+++ b/app/javascript/mastodon/components/scrollable_list.jsx
@@ -20,7 +20,6 @@ const mapStateToProps = (state, { scrollKey }) => {
   };
 };
 
-export default @connect(mapStateToProps, null, null, { forwardRef: true })
 class ScrollableList extends PureComponent {
 
   static contextTypes = {
@@ -97,7 +96,7 @@ class ScrollableList extends PureComponent {
     } else {
       return this.node;
     }
-  }
+  };
 
   setScrollTop = newScrollTop => {
     if (this.getScrollTop() !== newScrollTop) {
@@ -143,7 +142,7 @@ class ScrollableList extends PureComponent {
 
     this.mouseMovedRecently = false;
     this.scrollToTopOnMouseIdle = false;
-  }
+  };
 
   componentDidMount () {
     this.attachScrollListener();
@@ -161,25 +160,25 @@ class ScrollableList extends PureComponent {
     } else {
       return null;
     }
-  }
+  };
 
   getScrollTop = () => {
     return this._getScrollingElement().scrollTop;
-  }
+  };
 
   getScrollHeight = () => {
     return this._getScrollingElement().scrollHeight;
-  }
+  };
 
   getClientHeight = () => {
     return this._getScrollingElement().clientHeight;
-  }
+  };
 
   updateScrollBottom = (snapshot) => {
     const newScrollTop = this.getScrollHeight() - snapshot;
 
     this.setScrollTop(newScrollTop);
-  }
+  };
 
   getSnapshotBeforeUpdate (prevProps) {
     const someItemInserted = React.Children.count(prevProps.children) > 0 &&
@@ -206,7 +205,7 @@ class ScrollableList extends PureComponent {
     if (width && this.state.cachedMediaWidth !== width) {
       this.setState({ cachedMediaWidth: width });
     }
-  }
+  };
 
   componentWillUnmount () {
     this.clearMouseIdleTimer();
@@ -218,7 +217,7 @@ class ScrollableList extends PureComponent {
 
   onFullScreenChange = () => {
     this.setState({ fullscreen: isFullscreen() });
-  }
+  };
 
   attachIntersectionObserver () {
     let nodeOptions = {
@@ -269,12 +268,12 @@ class ScrollableList extends PureComponent {
 
   setRef = (c) => {
     this.node = c;
-  }
+  };
 
   handleLoadMore = e => {
     e.preventDefault();
     this.props.onLoadMore();
-  }
+  };
 
   handleLoadPending = e => {
     e.preventDefault();
@@ -286,7 +285,7 @@ class ScrollableList extends PureComponent {
     this.clearMouseIdleTimer();
     this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY);
     this.mouseMovedRecently = true;
-  }
+  };
 
   render () {
     const { children, scrollKey, trackScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props;
@@ -365,3 +364,5 @@ class ScrollableList extends PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(ScrollableList);
diff --git a/app/javascript/mastodon/components/server_banner.js b/app/javascript/mastodon/components/server_banner.jsx
index 617fdecdf..e5f5aa8ee 100644
--- a/app/javascript/mastodon/components/server_banner.js
+++ b/app/javascript/mastodon/components/server_banner.jsx
@@ -18,8 +18,6 @@ const mapStateToProps = state => ({
   server: state.getIn(['server', 'server']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class ServerBanner extends React.PureComponent {
 
   static propTypes = {
@@ -61,7 +59,7 @@ class ServerBanner extends React.PureComponent {
           <div className='server-banner__meta__column'>
             <h4><FormattedMessage id='server_banner.administered_by' defaultMessage='Administered by:' /></h4>
 
-            <Account id={server.getIn(['contact', 'account', 'id'])} size={36} />
+            <Account id={server.getIn(['contact', 'account', 'id'])} size={36} minimal />
           </div>
 
           <div className='server-banner__meta__column'>
@@ -91,3 +89,5 @@ class ServerBanner extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(ServerBanner));
diff --git a/app/javascript/mastodon/components/short_number.js b/app/javascript/mastodon/components/short_number.jsx
index 535c17727..535c17727 100644
--- a/app/javascript/mastodon/components/short_number.js
+++ b/app/javascript/mastodon/components/short_number.jsx
diff --git a/app/javascript/mastodon/components/skeleton.js b/app/javascript/mastodon/components/skeleton.jsx
index 6a17ffb26..6a17ffb26 100644
--- a/app/javascript/mastodon/components/skeleton.js
+++ b/app/javascript/mastodon/components/skeleton.jsx
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.jsx
index a1384ba58..923dc892d 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.jsx
@@ -59,7 +59,6 @@ const messages = defineMessages({
   edited: { id: 'status.edited', defaultMessage: 'Edited {date}' },
 });
 
-export default @injectIntl
 class Status extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -135,7 +134,7 @@ class Status extends ImmutablePureComponent {
 
   handleToggleMediaVisibility = () => {
     this.setState({ showMedia: !this.state.showMedia });
-  }
+  };
 
   handleClick = e => {
     if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) {
@@ -147,11 +146,11 @@ class Status extends ImmutablePureComponent {
     }
 
     this.handleHotkeyOpen();
-  }
+  };
 
   handlePrependAccountClick = e => {
     this.handleAccountClick(e, false);
-  }
+  };
 
   handleAccountClick = (e, proper = true) => {
     if (e && (e.button !== 0 || e.ctrlKey || e.metaKey))  {
@@ -160,22 +159,23 @@ class Status extends ImmutablePureComponent {
 
     if (e) {
       e.preventDefault();
+      e.stopPropagation();
     }
 
     this._openProfile(proper);
-  }
+  };
 
   handleExpandedToggle = () => {
     this.props.onToggleHidden(this._properStatus());
-  }
+  };
 
   handleCollapsedToggle = isCollapsed => {
     this.props.onToggleCollapsed(this._properStatus(), isCollapsed);
-  }
+  };
 
   handleTranslate = () => {
     this.props.onTranslate(this._properStatus());
-  }
+  };
 
   renderLoadingMediaGallery () {
     return <div className='media-gallery' style={{ height: '110px' }} />;
@@ -192,11 +192,11 @@ class Status extends ImmutablePureComponent {
   handleOpenVideo = (options) => {
     const status = this._properStatus();
     this.props.onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), options);
-  }
+  };
 
   handleOpenMedia = (media, index) => {
     this.props.onOpenMedia(this._properStatus().get('id'), media, index);
-  }
+  };
 
   handleHotkeyOpenMedia = e => {
     const { onOpenMedia, onOpenVideo } = this.props;
@@ -211,32 +211,32 @@ class Status extends ImmutablePureComponent {
         onOpenMedia(status.get('id'), status.get('media_attachments'), 0);
       }
     }
-  }
+  };
 
   handleDeployPictureInPicture = (type, mediaProps) => {
     const { deployPictureInPicture } = this.props;
     const status = this._properStatus();
 
     deployPictureInPicture(status, type, mediaProps);
-  }
+  };
 
   handleHotkeyReply = e => {
     e.preventDefault();
     this.props.onReply(this._properStatus(), this.context.router.history);
-  }
+  };
 
   handleHotkeyFavourite = () => {
     this.props.onFavourite(this._properStatus());
-  }
+  };
 
   handleHotkeyBoost = e => {
     this.props.onReblog(this._properStatus(), e);
-  }
+  };
 
   handleHotkeyMention = e => {
     e.preventDefault();
     this.props.onMention(this._properStatus().get('account'), this.context.router.history);
-  }
+  };
 
   handleHotkeyOpen = () => {
     if (this.props.onClick) {
@@ -252,11 +252,11 @@ class Status extends ImmutablePureComponent {
     }
 
     router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
-  }
+  };
 
   handleHotkeyOpenProfile = () => {
     this._openProfile();
-  }
+  };
 
   _openProfile = (proper = true) => {
     const { router } = this.context;
@@ -267,32 +267,32 @@ class Status extends ImmutablePureComponent {
     }
 
     router.history.push(`/@${status.getIn(['account', 'acct'])}`);
-  }
+  };
 
   handleHotkeyMoveUp = e => {
     this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured'));
-  }
+  };
 
   handleHotkeyMoveDown = e => {
     this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured'));
-  }
+  };
 
   handleHotkeyToggleHidden = () => {
     this.props.onToggleHidden(this._properStatus());
-  }
+  };
 
   handleHotkeyToggleSensitive = () => {
     this.handleToggleMediaVisibility();
-  }
+  };
 
   handleUnfilterClick = e => {
     this.setState({ forceFilter: false });
     e.preventDefault();
-  }
+  };
 
   handleFilterClick = () => {
     this.setState({ forceFilter: true });
-  }
+  };
 
   _properStatus () {
     const { status } = this.props;
@@ -306,7 +306,7 @@ class Status extends ImmutablePureComponent {
 
   handleRef = c => {
     this.node = c;
-  }
+  };
 
   render () {
     let media = null;
@@ -337,7 +337,7 @@ class Status extends ImmutablePureComponent {
     if (hidden) {
       return (
         <HotKeys handlers={handlers}>
-          <div ref={this.handleRef} className={classNames('status__wrapper', { focusable: !this.props.muted })} tabIndex='0'>
+          <div ref={this.handleRef} className={classNames('status__wrapper', { focusable: !this.props.muted })} tabIndex={0}>
             <span>{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}</span>
             <span>{status.get('content')}</span>
           </div>
@@ -354,7 +354,7 @@ class Status extends ImmutablePureComponent {
 
       return (
         <HotKeys handlers={minHandlers}>
-          <div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0' ref={this.handleRef}>
+          <div className='status__wrapper status__wrapper--filtered focusable' tabIndex={0} ref={this.handleRef}>
             <FormattedMessage id='status.filtered' defaultMessage='Filtered' />: {matchedFilters.join(', ')}.
             {' '}
             <button className='status__wrapper--filtered__button' onClick={this.handleUnfilterClick}>
@@ -386,6 +386,13 @@ class Status extends ImmutablePureComponent {
 
       account = status.get('account');
       status  = status.get('reblog');
+    } else if (status.get('visibility') === 'direct') {
+      prepend = (
+        <div className='status__prepend'>
+          <div className='status__prepend-icon-wrapper'><Icon id='at' className='status__prepend-icon' fixedWidth /></div>
+          <FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' />
+        </div>
+      );
     } else if (showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id'])) {
       const display_name_html = { __html: status.getIn(['account', 'display_name_html']) };
 
@@ -416,6 +423,7 @@ class Status extends ImmutablePureComponent {
               <Component
                 src={attachment.get('url')}
                 alt={attachment.get('description')}
+                lang={status.get('language')}
                 poster={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
                 backgroundColor={attachment.getIn(['meta', 'colors', 'background'])}
                 foregroundColor={attachment.getIn(['meta', 'colors', 'foreground'])}
@@ -445,6 +453,7 @@ class Status extends ImmutablePureComponent {
                 blurhash={attachment.get('blurhash')}
                 src={attachment.get('url')}
                 alt={attachment.get('description')}
+                lang={status.get('language')}
                 width={this.props.cachedMediaWidth}
                 height={110}
                 inline
@@ -464,6 +473,7 @@ class Status extends ImmutablePureComponent {
             {Component => (
               <Component
                 media={status.get('media_attachments')}
+                lang={status.get('language')}
                 sensitive={status.get('sensitive')}
                 height={110}
                 onOpenMedia={this.handleOpenMedia}
@@ -510,8 +520,8 @@ class Status extends ImmutablePureComponent {
           {prepend}
 
           <div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted })} data-id={status.get('id')}>
-            <div className='status__info'>
-              <a onClick={this.handleClick} href={`/@${status.getIn(['account', 'acct'])}\/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'>
+            <div onClick={this.handleClick} className='status__info'>
+              <a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'>
                 <span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span>
                 <RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
               </a>
@@ -545,3 +555,5 @@ class Status extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Status);
diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.jsx
index 00fc94358..7b4031b68 100644
--- a/app/javascript/mastodon/components/status_action_bar.js
+++ b/app/javascript/mastodon/components/status_action_bar.jsx
@@ -14,7 +14,7 @@ const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
   redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
   edit: { id: 'status.edit', defaultMessage: 'Edit' },
-  direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
+  direct: { id: 'status.direct', defaultMessage: 'Privately mention @{name}' },
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
@@ -53,8 +53,6 @@ const mapStateToProps = (state, { status }) => ({
   relationship: state.getIn(['relationships', status.getIn(['account', 'id'])]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class StatusActionBar extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -97,7 +95,7 @@ class StatusActionBar extends ImmutablePureComponent {
     'status',
     'relationship',
     'withDismiss',
-  ]
+  ];
 
   handleReplyClick = () => {
     const { signedIn } = this.context.identity;
@@ -107,7 +105,7 @@ class StatusActionBar extends ImmutablePureComponent {
     } else {
       this.props.onInteractionModal('reply', this.props.status);
     }
-  }
+  };
 
   handleShareClick = () => {
     navigator.share({
@@ -116,7 +114,7 @@ class StatusActionBar extends ImmutablePureComponent {
     }).catch((e) => {
       if (e.name !== 'AbortError') console.error(e);
     });
-  }
+  };
 
   handleFavouriteClick = () => {
     const { signedIn } = this.context.identity;
@@ -126,7 +124,7 @@ class StatusActionBar extends ImmutablePureComponent {
     } else {
       this.props.onInteractionModal('favourite', this.props.status);
     }
-  }
+  };
 
   handleReblogClick = e => {
     const { signedIn } = this.context.identity;
@@ -136,35 +134,35 @@ class StatusActionBar extends ImmutablePureComponent {
     } else {
       this.props.onInteractionModal('reblog', this.props.status);
     }
-  }
+  };
 
   handleBookmarkClick = () => {
     this.props.onBookmark(this.props.status);
-  }
+  };
 
   handleDeleteClick = () => {
     this.props.onDelete(this.props.status, this.context.router.history);
-  }
+  };
 
   handleRedraftClick = () => {
     this.props.onDelete(this.props.status, this.context.router.history, true);
-  }
+  };
 
   handleEditClick = () => {
     this.props.onEdit(this.props.status, this.context.router.history);
-  }
+  };
 
   handlePinClick = () => {
     this.props.onPin(this.props.status);
-  }
+  };
 
   handleMentionClick = () => {
     this.props.onMention(this.props.status.get('account'), this.context.router.history);
-  }
+  };
 
   handleDirectClick = () => {
     this.props.onDirect(this.props.status.get('account'), this.context.router.history);
-  }
+  };
 
   handleMuteClick = () => {
     const { status, relationship, onMute, onUnmute } = this.props;
@@ -175,7 +173,7 @@ class StatusActionBar extends ImmutablePureComponent {
     } else {
       onMute(account);
     }
-  }
+  };
 
   handleBlockClick = () => {
     const { status, relationship, onBlock, onUnblock } = this.props;
@@ -186,50 +184,50 @@ class StatusActionBar extends ImmutablePureComponent {
     } else {
       onBlock(status);
     }
-  }
+  };
 
   handleBlockDomain = () => {
     const { status, onBlockDomain } = this.props;
     const account = status.get('account');
 
     onBlockDomain(account.get('acct').split('@')[1]);
-  }
+  };
 
   handleUnblockDomain = () => {
     const { status, onUnblockDomain } = this.props;
     const account = status.get('account');
 
     onUnblockDomain(account.get('acct').split('@')[1]);
-  }
+  };
 
   handleOpen = () => {
     this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}`);
-  }
+  };
 
   handleEmbed = () => {
     this.props.onEmbed(this.props.status);
-  }
+  };
 
   handleReport = () => {
     this.props.onReport(this.props.status);
-  }
+  };
 
   handleConversationMuteClick = () => {
     this.props.onMuteConversation(this.props.status);
-  }
+  };
 
   handleFilterClick = () => {
     this.props.onAddFilter(this.props.status);
-  }
+  };
 
   handleCopy = () => {
     const url = this.props.status.get('url');
     navigator.clipboard.writeText(url);
-  }
+  };
 
   handleHideClick = () => {
     this.props.onFilter();
-  }
+  };
 
   render () {
     const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props;
@@ -385,3 +383,5 @@ class StatusActionBar extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(StatusActionBar));
diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.jsx
index 6f3093d63..fb953b9dd 100644
--- a/app/javascript/mastodon/components/status_content.js
+++ b/app/javascript/mastodon/components/status_content.jsx
@@ -3,10 +3,11 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import { FormattedMessage, injectIntl } from 'react-intl';
 import { Link } from 'react-router-dom';
+import { connect } from 'react-redux';
 import classnames from 'classnames';
 import PollContainer from 'mastodon/containers/poll_container';
 import Icon from 'mastodon/components/icon';
-import { autoPlayGif, languages as preloadedLanguages, translationEnabled } from 'mastodon/initial_state';
+import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
 
 const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
 
@@ -47,7 +48,10 @@ class TranslateButton extends React.PureComponent {
 
 }
 
-export default @injectIntl
+const mapStateToProps = state => ({
+  languages: state.getIn(['server', 'translationLanguages', 'items']),
+});
+
 class StatusContent extends React.PureComponent {
 
   static contextTypes = {
@@ -63,6 +67,7 @@ class StatusContent extends React.PureComponent {
     onClick: PropTypes.func,
     collapsable: PropTypes.bool,
     onCollapsedToggle: PropTypes.func,
+    languages: ImmutablePropTypes.map,
     intl: PropTypes.object,
   };
 
@@ -130,7 +135,7 @@ class StatusContent extends React.PureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -143,7 +148,7 @@ class StatusContent extends React.PureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   componentDidMount () {
     this._updateStatusLinks();
@@ -158,7 +163,7 @@ class StatusContent extends React.PureComponent {
       e.preventDefault();
       this.context.router.history.push(`/@${mention.get('acct')}`);
     }
-  }
+  };
 
   onHashtagClick = (hashtag, e) => {
     hashtag = hashtag.replace(/^#/, '');
@@ -167,11 +172,11 @@ class StatusContent extends React.PureComponent {
       e.preventDefault();
       this.context.router.history.push(`/tags/${hashtag}`);
     }
-  }
+  };
 
   handleMouseDown = (e) => {
     this.startXY = [e.clientX, e.clientY];
-  }
+  };
 
   handleMouseUp = (e) => {
     if (!this.startXY) {
@@ -194,7 +199,7 @@ class StatusContent extends React.PureComponent {
     }
 
     this.startXY = null;
-  }
+  };
 
   handleSpoilerClick = (e) => {
     e.preventDefault();
@@ -205,22 +210,24 @@ class StatusContent extends React.PureComponent {
     } else {
       this.setState({ hidden: !this.state.hidden });
     }
-  }
+  };
 
   handleTranslate = () => {
     this.props.onTranslate();
-  }
+  };
 
   setRef = (c) => {
     this.node = c;
-  }
+  };
 
   render () {
     const { status, intl } = this.props;
 
     const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
     const renderReadMore = this.props.onClick && status.get('collapsed');
-    const renderTranslate = translationEnabled && this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && status.get('language') !== null && intl.locale !== status.get('language');
+    const contentLocale = intl.locale.replace(/[_-].*/, '');
+    const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
+    const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && targetLanguages?.includes(contentLocale);
 
     const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') };
     const spoilerContent = { __html: status.get('spoilerHtml') };
@@ -242,7 +249,7 @@ class StatusContent extends React.PureComponent {
     );
 
     const poll = !!status.get('poll') && (
-      <PollContainer pollId={status.get('poll')} />
+      <PollContainer pollId={status.get('poll')} lang={status.get('language')} />
     );
 
     if (status.get('spoiler_text').length > 0) {
@@ -261,7 +268,7 @@ class StatusContent extends React.PureComponent {
       }
 
       return (
-        <div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
+        <div className={classNames} ref={this.setRef} tabIndex={0} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
           <p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}>
             <span dangerouslySetInnerHTML={spoilerContent} className='translate' lang={lang} />
             {' '}
@@ -279,7 +286,7 @@ class StatusContent extends React.PureComponent {
     } else if (this.props.onClick) {
       return (
         <>
-          <div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
+          <div className={classNames} ref={this.setRef} tabIndex={0} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
             <div className='status__content__text status__content__text--visible translate' lang={lang} dangerouslySetInnerHTML={content} />
 
             {poll}
@@ -291,7 +298,7 @@ class StatusContent extends React.PureComponent {
       );
     } else {
       return (
-        <div className={classNames} ref={this.setRef} tabIndex='0' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
+        <div className={classNames} ref={this.setRef} tabIndex={0} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
           <div className='status__content__text status__content__text--visible translate' lang={lang} dangerouslySetInnerHTML={content} />
 
           {poll}
@@ -302,3 +309,5 @@ class StatusContent extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(StatusContent));
diff --git a/app/javascript/mastodon/components/status_list.js b/app/javascript/mastodon/components/status_list.jsx
index 35e5749a3..3d513bbf8 100644
--- a/app/javascript/mastodon/components/status_list.js
+++ b/app/javascript/mastodon/components/status_list.jsx
@@ -34,7 +34,7 @@ export default class StatusList extends ImmutablePureComponent {
 
   getFeaturedStatusCount = () => {
     return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0;
-  }
+  };
 
   getCurrentStatusIndex = (id, featured) => {
     if (featured) {
@@ -42,21 +42,21 @@ export default class StatusList extends ImmutablePureComponent {
     } else {
       return this.props.statusIds.indexOf(id) + this.getFeaturedStatusCount();
     }
-  }
+  };
 
   handleMoveUp = (id, featured) => {
     const elementIndex = this.getCurrentStatusIndex(id, featured) - 1;
     this._selectChild(elementIndex, true);
-  }
+  };
 
   handleMoveDown = (id, featured) => {
     const elementIndex = this.getCurrentStatusIndex(id, featured) + 1;
     this._selectChild(elementIndex, false);
-  }
+  };
 
   handleLoadOlder = debounce(() => {
     this.props.onLoadMore(this.props.statusIds.size > 0 ? this.props.statusIds.last() : undefined);
-  }, 300, { leading: true })
+  }, 300, { leading: true });
 
   _selectChild (index, align_top) {
     const container = this.node.node;
@@ -74,7 +74,7 @@ export default class StatusList extends ImmutablePureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   render () {
     const { statusIds, featuredStatusIds, onLoadMore, timelineId, ...other }  = this.props;
diff --git a/app/javascript/mastodon/components/timeline_hint.js b/app/javascript/mastodon/components/timeline_hint.jsx
index ac9a79dcc..ac9a79dcc 100644
--- a/app/javascript/mastodon/components/timeline_hint.js
+++ b/app/javascript/mastodon/components/timeline_hint.jsx
diff --git a/app/javascript/mastodon/containers/account_container.js b/app/javascript/mastodon/containers/account_container.jsx
index 5a5136dd1..5a5136dd1 100644
--- a/app/javascript/mastodon/containers/account_container.js
+++ b/app/javascript/mastodon/containers/account_container.jsx
diff --git a/app/javascript/mastodon/containers/admin_component.js b/app/javascript/mastodon/containers/admin_component.jsx
index 816b44bd1..816b44bd1 100644
--- a/app/javascript/mastodon/containers/admin_component.js
+++ b/app/javascript/mastodon/containers/admin_component.jsx
diff --git a/app/javascript/mastodon/containers/compose_container.js b/app/javascript/mastodon/containers/compose_container.jsx
index 7bc7bbaa4..7bc7bbaa4 100644
--- a/app/javascript/mastodon/containers/compose_container.js
+++ b/app/javascript/mastodon/containers/compose_container.jsx
diff --git a/app/javascript/mastodon/containers/domain_container.js b/app/javascript/mastodon/containers/domain_container.jsx
index 8a8ba1df1..8a8ba1df1 100644
--- a/app/javascript/mastodon/containers/domain_container.js
+++ b/app/javascript/mastodon/containers/domain_container.jsx
diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.jsx
index 002b71e93..002b71e93 100644
--- a/app/javascript/mastodon/containers/mastodon.js
+++ b/app/javascript/mastodon/containers/mastodon.jsx
diff --git a/app/javascript/mastodon/containers/media_container.js b/app/javascript/mastodon/containers/media_container.jsx
index 6ee1f0bd8..f9244f8dd 100644
--- a/app/javascript/mastodon/containers/media_container.js
+++ b/app/javascript/mastodon/containers/media_container.jsx
@@ -39,7 +39,7 @@ export default class MediaContainer extends PureComponent {
     document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
 
     this.setState({ media, index });
-  }
+  };
 
   handleOpenVideo = (options) => {
     const { components } = this.props;
@@ -50,11 +50,11 @@ export default class MediaContainer extends PureComponent {
     document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
 
     this.setState({ media: mediaList, options });
-  }
+  };
 
   handleCloseMedia = () => {
     document.body.classList.remove('with-modals--active');
-    document.documentElement.style.marginRight = 0;
+    document.documentElement.style.marginRight = '0';
 
     this.setState({
       media: null,
@@ -63,11 +63,11 @@ export default class MediaContainer extends PureComponent {
       backgroundColor: null,
       options: null,
     });
-  }
+  };
 
   setBackgroundColor = color => {
     this.setState({ backgroundColor: color });
-  }
+  };
 
   render () {
     const { locale, components } = this.props;
diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.jsx
index 294105f25..580f409e9 100644
--- a/app/javascript/mastodon/containers/status_container.js
+++ b/app/javascript/mastodon/containers/status_container.jsx
@@ -56,6 +56,8 @@ const messages = defineMessages({
   redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: '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.' },
   replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
   replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
+  editConfirm: { id: 'confirmations.edit.confirm', defaultMessage: 'Edit' },
+  editMessage: { id: 'confirmations.edit.message', defaultMessage: 'Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
   blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
 });
 
@@ -149,7 +151,18 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
   },
 
   onEdit (status, history) {
-    dispatch(editStatus(status.get('id'), history));
+    dispatch((_, getState) => {
+      let state = getState();
+      if (state.getIn(['compose', 'text']).trim().length !== 0) {
+        dispatch(openModal('CONFIRM', {
+          message: intl.formatMessage(messages.editMessage),
+          confirm: intl.formatMessage(messages.editConfirm),
+          onConfirm: () => dispatch(editStatus(status.get('id'), history)),
+        }));
+      } else {
+        dispatch(editStatus(status.get('id'), history));
+      }
+    });
   },
 
   onTranslate (status) {
diff --git a/app/javascript/mastodon/extra_polyfills.js b/app/javascript/mastodon/extra_polyfills.js
index 6e8004f07..e6c69de8b 100644
--- a/app/javascript/mastodon/extra_polyfills.js
+++ b/app/javascript/mastodon/extra_polyfills.js
@@ -1,3 +1,2 @@
 import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
-import 'intersection-observer';
 import 'requestidlecallback';
diff --git a/app/javascript/mastodon/features/about/index.js b/app/javascript/mastodon/features/about/index.jsx
index e59f73738..bb35f3203 100644
--- a/app/javascript/mastodon/features/about/index.js
+++ b/app/javascript/mastodon/features/about/index.jsx
@@ -59,7 +59,7 @@ class Section extends React.PureComponent {
     const { collapsed } = this.state;
 
     this.setState({ collapsed: !collapsed }, () => onOpen && onOpen());
-  }
+  };
 
   render () {
     const { title, children } = this.props;
@@ -67,7 +67,7 @@ class Section extends React.PureComponent {
 
     return (
       <div className={classNames('about__section', { active: !collapsed })}>
-        <div className='about__section__title' role='button' tabIndex='0' onClick={this.handleClick}>
+        <div className='about__section__title' role='button' tabIndex={0} onClick={this.handleClick}>
           <Icon id={collapsed ? 'chevron-right' : 'chevron-down'} fixedWidth /> {title}
         </div>
 
@@ -80,8 +80,6 @@ class Section extends React.PureComponent {
 
 }
 
-export default @connect(mapStateToProps)
-@injectIntl
 class About extends React.PureComponent {
 
   static propTypes = {
@@ -106,7 +104,7 @@ class About extends React.PureComponent {
   handleDomainBlocksOpen = () => {
     const { dispatch } = this.props;
     dispatch(fetchDomainBlocks());
-  }
+  };
 
   render () {
     const { multiColumn, intl, server, extendedDescription, domainBlocks } = this.props;
@@ -125,7 +123,7 @@ class About extends React.PureComponent {
             <div className='about__meta__column'>
               <h4><FormattedMessage id='server_banner.administered_by' defaultMessage='Administered by:' /></h4>
 
-              <Account id={server.getIn(['contact', 'account', 'id'])} size={36} />
+              <Account id={server.getIn(['contact', 'account', 'id'])} size={36} minimal />
             </div>
 
             <hr className='about__meta__divider' />
@@ -217,3 +215,5 @@ class About extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(About));
diff --git a/app/javascript/mastodon/features/account/components/account_note.js b/app/javascript/mastodon/features/account/components/account_note.jsx
index 1787ce1ab..5201ebd4d 100644
--- a/app/javascript/mastodon/features/account/components/account_note.js
+++ b/app/javascript/mastodon/features/account/components/account_note.jsx
@@ -43,7 +43,6 @@ class InlineAlert extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class AccountNote extends ImmutablePureComponent {
 
   static propTypes = {
@@ -90,7 +89,7 @@ class AccountNote extends ImmutablePureComponent {
 
   setTextareaRef = c => {
     this.textarea = c;
-  }
+  };
 
   handleChange = e => {
     this.setState({ value: e.target.value, saving: false });
@@ -114,13 +113,13 @@ class AccountNote extends ImmutablePureComponent {
         }
       });
     }
-  }
+  };
 
   handleBlur = () => {
     if (this._isDirty()) {
       this._save();
     }
-  }
+  };
 
   _save (showMessage = true) {
     this.setState({ saving: true }, () => this.props.onSave(this.state.value));
@@ -168,3 +167,5 @@ class AccountNote extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(AccountNote);
diff --git a/app/javascript/mastodon/features/account/components/featured_tags.js b/app/javascript/mastodon/features/account/components/featured_tags.jsx
index 24a3f2171..52aa232ca 100644
--- a/app/javascript/mastodon/features/account/components/featured_tags.js
+++ b/app/javascript/mastodon/features/account/components/featured_tags.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   empty: { id: 'account.featured_tags.last_status_never', defaultMessage: 'No posts' },
 });
 
-export default @injectIntl
 class FeaturedTags extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -50,3 +49,5 @@ class FeaturedTags extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(FeaturedTags);
diff --git a/app/javascript/mastodon/features/account/components/follow_request_note.js b/app/javascript/mastodon/features/account/components/follow_request_note.jsx
index 300ae4266..300ae4266 100644
--- a/app/javascript/mastodon/features/account/components/follow_request_note.js
+++ b/app/javascript/mastodon/features/account/components/follow_request_note.jsx
diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.jsx
index 2481e4783..72eb7e6b6 100644
--- a/app/javascript/mastodon/features/account/components/header.js
+++ b/app/javascript/mastodon/features/account/components/header.jsx
@@ -28,7 +28,7 @@ const messages = defineMessages({
   linkVerifiedOn: { id: 'account.link_verified_on', defaultMessage: 'Ownership of this link was checked on {date}' },
   account_locked: { id: 'account.locked_info', defaultMessage: 'This account privacy status is set to locked. The owner manually reviews who can follow them.' },
   mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' },
-  direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' },
+  direct: { id: 'account.direct', defaultMessage: 'Privately mention @{name}' },
   unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
   mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
@@ -46,6 +46,7 @@ const messages = defineMessages({
   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
+  followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
   blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
   domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Blocked domains' },
   mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
@@ -75,7 +76,6 @@ const dateFormatOptions = {
   minute: '2-digit',
 };
 
-export default @injectIntl
 class Header extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -108,7 +108,7 @@ class Header extends ImmutablePureComponent {
 
   openEditProfile = () => {
     window.open('/settings/profile', '_blank');
-  }
+  };
 
   isStatusesPageActive = (match, location) => {
     if (!match) {
@@ -116,7 +116,7 @@ class Header extends ImmutablePureComponent {
     }
 
     return !location.pathname.match(/\/(followers|following)\/?$/);
-  }
+  };
 
   handleMouseEnter = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -129,7 +129,7 @@ class Header extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -142,14 +142,14 @@ class Header extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   handleAvatarClick = e => {
     if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
       e.preventDefault();
       this.props.onOpenAvatar();
     }
-  }
+  };
 
   handleShare = () => {
     const { account } = this.props;
@@ -160,7 +160,7 @@ class Header extends ImmutablePureComponent {
     }).catch((e) => {
       if (e.name !== 'AbortError') console.error(e);
     });
-  }
+  };
 
   render () {
     const { account, hidden, intl, domain } = this.props;
@@ -193,7 +193,7 @@ class Header extends ImmutablePureComponent {
     }
 
     if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) {
-      bellBtn = <IconButton icon='bell-o' size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
+      bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
     }
 
     if (me !== account.get('id')) {
@@ -242,6 +242,7 @@ class Header extends ImmutablePureComponent {
       menu.push({ text: intl.formatMessage(messages.follow_requests), to: '/follow_requests' });
       menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
       menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
+      menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
       menu.push(null);
       menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
       menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
@@ -294,9 +295,9 @@ class Header extends ImmutablePureComponent {
       if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
         menu.push({ text: intl.formatMessage(messages.admin_account, { name: account.get('username') }), href: `/admin/accounts/${account.get('id')}` });
       }
-        if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
-          menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: remoteDomain }), href: `/admin/instances/${remoteDomain}` });
-        }
+      if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
+        menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: remoteDomain }), href: `/admin/instances/${remoteDomain}` });
+      }
     }
 
     const content         = { __html: account.get('note_emojified') };
@@ -417,3 +418,5 @@ class Header extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Header);
diff --git a/app/javascript/mastodon/features/account/navigation.js b/app/javascript/mastodon/features/account/navigation.jsx
index eb9ff9a95..07dc3757c 100644
--- a/app/javascript/mastodon/features/account/navigation.js
+++ b/app/javascript/mastodon/features/account/navigation.jsx
@@ -19,7 +19,6 @@ const mapStateToProps = (state, { match: { params: { acct } } }) => {
   };
 };
 
-export default @connect(mapStateToProps)
 class AccountNavigation extends React.PureComponent {
 
   static propTypes = {
@@ -50,3 +49,5 @@ class AccountNavigation extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AccountNavigation);
diff --git a/app/javascript/mastodon/features/account_gallery/components/media_item.js b/app/javascript/mastodon/features/account_gallery/components/media_item.jsx
index 13fd7fe03..00526016d 100644
--- a/app/javascript/mastodon/features/account_gallery/components/media_item.js
+++ b/app/javascript/mastodon/features/account_gallery/components/media_item.jsx
@@ -22,20 +22,20 @@ export default class MediaItem extends ImmutablePureComponent {
 
   handleImageLoad = () => {
     this.setState({ loaded: true });
-  }
+  };
 
   handleMouseEnter = e => {
     if (this.hoverToPlay()) {
       e.target.play();
     }
-  }
+  };
 
   handleMouseLeave = e => {
     if (this.hoverToPlay()) {
       e.target.pause();
       e.target.currentTime = 0;
     }
-  }
+  };
 
   hoverToPlay () {
     return !autoPlayGif && ['gifv', 'video'].indexOf(this.props.attachment.get('type')) !== -1;
@@ -51,7 +51,7 @@ export default class MediaItem extends ImmutablePureComponent {
         this.setState({ visible: true });
       }
     }
-  }
+  };
 
   render () {
     const { attachment, displayWidth } = this.props;
@@ -74,8 +74,9 @@ export default class MediaItem extends ImmutablePureComponent {
       if (['audio', 'video'].includes(attachment.get('type'))) {
         content = (
           <img
-            src={attachment.get('preview_url') || attachment.getIn(['account', 'avatar_static'])}
+            src={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             onLoad={this.handleImageLoad}
           />
         );
@@ -95,6 +96,7 @@ export default class MediaItem extends ImmutablePureComponent {
           <img
             src={attachment.get('preview_url')}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             style={{ objectPosition: `${x}% ${y}%` }}
             onLoad={this.handleImageLoad}
           />
@@ -105,6 +107,7 @@ export default class MediaItem extends ImmutablePureComponent {
             className='media-gallery__item-gifv-thumbnail'
             aria-label={attachment.get('description')}
             title={attachment.get('description')}
+            lang={status.get('language')}
             role='application'
             src={attachment.get('url')}
             onMouseEnter={this.handleMouseEnter}
@@ -130,7 +133,7 @@ export default class MediaItem extends ImmutablePureComponent {
 
     return (
       <div className='account-gallery__item' style={{ width, height }}>
-        <a className='media-gallery__item-thumbnail' href={`/@${status.getIn(['account', 'acct'])}\/${status.get('id')}`} onClick={this.handleClick} title={title} target='_blank' rel='noopener noreferrer'>
+        <a className='media-gallery__item-thumbnail' href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} onClick={this.handleClick} title={title} target='_blank' rel='noopener noreferrer'>
           <Blurhash
             hash={attachment.get('blurhash')}
             className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && loaded })}
diff --git a/app/javascript/mastodon/features/account_gallery/index.js b/app/javascript/mastodon/features/account_gallery/index.jsx
index dc7556a9a..db76507db 100644
--- a/app/javascript/mastodon/features/account_gallery/index.js
+++ b/app/javascript/mastodon/features/account_gallery/index.jsx
@@ -47,7 +47,7 @@ class LoadMoreMedia extends ImmutablePureComponent {
 
   handleLoadMore = () => {
     this.props.onLoadMore(this.props.maxId);
-  }
+  };
 
   render () {
     return (
@@ -60,7 +60,6 @@ class LoadMoreMedia extends ImmutablePureComponent {
 
 }
 
-export default @connect(mapStateToProps)
 class AccountGallery extends ImmutablePureComponent {
 
   static propTypes = {
@@ -114,7 +113,7 @@ class AccountGallery extends ImmutablePureComponent {
     if (this.props.hasMore) {
       this.handleLoadMore(this.props.attachments.size > 0 ? this.props.attachments.last().getIn(['status', 'id']) : undefined);
     }
-  }
+  };
 
   handleScroll = e => {
     const { scrollTop, scrollHeight, clientHeight } = e.target;
@@ -123,7 +122,7 @@ class AccountGallery extends ImmutablePureComponent {
     if (150 > offset && !this.props.isLoading) {
       this.handleScrollToBottom();
     }
-  }
+  };
 
   handleLoadMore = maxId => {
     this.props.dispatch(expandAccountMediaTimeline(this.props.accountId, { maxId }));
@@ -132,7 +131,7 @@ class AccountGallery extends ImmutablePureComponent {
   handleLoadOlder = e => {
     e.preventDefault();
     this.handleScrollToBottom();
-  }
+  };
 
   handleOpenMedia = attachment => {
     const { dispatch } = this.props;
@@ -148,13 +147,13 @@ class AccountGallery extends ImmutablePureComponent {
 
       dispatch(openModal('MEDIA', { media, index, statusId }));
     }
-  }
+  };
 
   handleRef = c => {
     if (c) {
       this.setState({ width: c.offsetWidth });
     }
-  }
+  };
 
   render () {
     const { attachments, isLoading, hasMore, isAccount, multiColumn, blockedBy, suspended } = this.props;
@@ -226,3 +225,5 @@ class AccountGallery extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AccountGallery);
diff --git a/app/javascript/mastodon/features/account_timeline/components/header.js b/app/javascript/mastodon/features/account_timeline/components/header.jsx
index d67e307ff..bffa5554b 100644
--- a/app/javascript/mastodon/features/account_timeline/components/header.js
+++ b/app/javascript/mastodon/features/account_timeline/components/header.jsx
@@ -36,35 +36,35 @@ export default class Header extends ImmutablePureComponent {
 
   handleFollow = () => {
     this.props.onFollow(this.props.account);
-  }
+  };
 
   handleBlock = () => {
     this.props.onBlock(this.props.account);
-  }
+  };
 
   handleMention = () => {
     this.props.onMention(this.props.account, this.context.router.history);
-  }
+  };
 
   handleDirect = () => {
     this.props.onDirect(this.props.account, this.context.router.history);
-  }
+  };
 
   handleReport = () => {
     this.props.onReport(this.props.account);
-  }
+  };
 
   handleReblogToggle = () => {
     this.props.onReblogToggle(this.props.account);
-  }
+  };
 
   handleNotifyToggle = () => {
     this.props.onNotifyToggle(this.props.account);
-  }
+  };
 
   handleMute = () => {
     this.props.onMute(this.props.account);
-  }
+  };
 
   handleBlockDomain = () => {
     const domain = this.props.account.get('acct').split('@')[1];
@@ -72,7 +72,7 @@ export default class Header extends ImmutablePureComponent {
     if (!domain) return;
 
     this.props.onBlockDomain(domain);
-  }
+  };
 
   handleUnblockDomain = () => {
     const domain = this.props.account.get('acct').split('@')[1];
@@ -80,31 +80,31 @@ export default class Header extends ImmutablePureComponent {
     if (!domain) return;
 
     this.props.onUnblockDomain(domain);
-  }
+  };
 
   handleEndorseToggle = () => {
     this.props.onEndorseToggle(this.props.account);
-  }
+  };
 
   handleAddToList = () => {
     this.props.onAddToList(this.props.account);
-  }
+  };
 
   handleEditAccountNote = () => {
     this.props.onEditAccountNote(this.props.account);
-  }
+  };
 
   handleChangeLanguages = () => {
     this.props.onChangeLanguages(this.props.account);
-  }
+  };
 
   handleInteractionModal = () => {
     this.props.onInteractionModal(this.props.account);
-  }
+  };
 
   handleOpenAvatar = () => {
     this.props.onOpenAvatar(this.props.account);
-  }
+  };
 
   render () {
     const { account, hidden, hideTabs } = this.props;
diff --git a/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.js b/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.jsx
index 80b967642..e6a111470 100644
--- a/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.js
+++ b/app/javascript/mastodon/features/account_timeline/components/limited_account_hint.jsx
@@ -14,13 +14,12 @@ const mapDispatchToProps = (dispatch, { accountId }) => ({
 
 });
 
-export default @connect(() => {}, mapDispatchToProps)
 class LimitedAccountHint extends React.PureComponent {
 
   static propTypes = {
     accountId: PropTypes.string.isRequired,
     reveal: PropTypes.func,
-  }
+  };
 
   render () {
     const { reveal } = this.props;
@@ -34,3 +33,5 @@ class LimitedAccountHint extends React.PureComponent {
   }
 
 }
+
+export default connect(() => {}, mapDispatchToProps)(LimitedAccountHint);
diff --git a/app/javascript/mastodon/features/account_timeline/components/moved_note.js b/app/javascript/mastodon/features/account_timeline/components/moved_note.jsx
index daff47a9d..daff47a9d 100644
--- a/app/javascript/mastodon/features/account_timeline/components/moved_note.js
+++ b/app/javascript/mastodon/features/account_timeline/components/moved_note.jsx
diff --git a/app/javascript/mastodon/features/account_timeline/containers/header_container.js b/app/javascript/mastodon/features/account_timeline/containers/header_container.jsx
index f53cd2480..f53cd2480 100644
--- a/app/javascript/mastodon/features/account_timeline/containers/header_container.js
+++ b/app/javascript/mastodon/features/account_timeline/containers/header_container.jsx
diff --git a/app/javascript/mastodon/features/account_timeline/index.js b/app/javascript/mastodon/features/account_timeline/index.jsx
index bdb430a33..d77563684 100644
--- a/app/javascript/mastodon/features/account_timeline/index.js
+++ b/app/javascript/mastodon/features/account_timeline/index.jsx
@@ -64,7 +64,6 @@ RemoteHint.propTypes = {
   url: PropTypes.string.isRequired,
 };
 
-export default @connect(mapStateToProps)
 class AccountTimeline extends ImmutablePureComponent {
 
   static propTypes = {
@@ -145,7 +144,7 @@ class AccountTimeline extends ImmutablePureComponent {
 
   handleLoadMore = maxId => {
     this.props.dispatch(expandAccountTimeline(this.props.accountId, { maxId, withReplies: this.props.withReplies, tagged: this.props.params.tagged }));
-  }
+  };
 
   render () {
     const { accountId, statusIds, featuredStatusIds, isLoading, hasMore, blockedBy, suspended, isAccount, hidden, multiColumn, remote, remoteUrl } = this.props;
@@ -206,3 +205,5 @@ class AccountTimeline extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AccountTimeline);
diff --git a/app/javascript/mastodon/features/audio/index.js b/app/javascript/mastodon/features/audio/index.jsx
index 00854d0e4..df40b48f0 100644
--- a/app/javascript/mastodon/features/audio/index.js
+++ b/app/javascript/mastodon/features/audio/index.jsx
@@ -1,12 +1,10 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
-import { formatTime } from 'mastodon/features/video';
+import { formatTime, getPointerPosition, fileNameFromURL } from 'mastodon/features/video';
 import Icon from 'mastodon/components/icon';
 import classNames from 'classnames';
-import { throttle } from 'lodash';
-import { getPointerPosition, fileNameFromURL } from 'mastodon/features/video';
-import { debounce } from 'lodash';
+import { throttle, debounce } from 'lodash';
 import Visualizer from './visualizer';
 import { displayMedia, useBlurhash } from '../../initial_state';
 import Blurhash from '../../components/blurhash';
@@ -24,12 +22,12 @@ const messages = defineMessages({
 const TICK_SIZE = 10;
 const PADDING   = 180;
 
-export default @injectIntl
 class Audio extends React.PureComponent {
 
   static propTypes = {
     src: PropTypes.string.isRequired,
     alt: PropTypes.string,
+    lang: PropTypes.string,
     poster: PropTypes.string,
     duration: PropTypes.number,
     width: PropTypes.number,
@@ -59,7 +57,7 @@ class Audio extends React.PureComponent {
     duration: null,
     paused: true,
     muted: false,
-    volume: 0.5,
+    volume: 1,
     dragging: false,
     revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
   };
@@ -75,13 +73,13 @@ class Audio extends React.PureComponent {
     if (this.player) {
       this._setDimensions();
     }
-  }
+  };
 
   _pack() {
     return {
       src: this.props.src,
-      volume: this.audio.volume,
-      muted: this.audio.muted,
+      volume: this.state.volume,
+      muted: this.state.muted,
       currentTime: this.audio.currentTime,
       poster: this.props.poster,
       backgroundColor: this.props.backgroundColor,
@@ -105,25 +103,26 @@ class Audio extends React.PureComponent {
 
   setSeekRef = c => {
     this.seek = c;
-  }
+  };
 
   setVolumeRef = c => {
     this.volume = c;
-  }
+  };
 
   setAudioRef = c => {
     this.audio = c;
 
     if (this.audio) {
-      this.setState({ volume: this.audio.volume, muted: this.audio.muted });
+      this.audio.volume = 1;
+      this.audio.muted = false;
     }
-  }
+  };
 
   setCanvasRef = c => {
     this.canvas = c;
 
     this.visualizer.setCanvas(c);
-  }
+  };
 
   componentDidMount () {
     window.addEventListener('scroll', this.handleScroll);
@@ -162,7 +161,7 @@ class Audio extends React.PureComponent {
     } else {
       this.setState({ paused: true }, () => this.audio.pause());
     }
-  }
+  };
 
   handleResize = debounce(() => {
     if (this.player) {
@@ -180,7 +179,7 @@ class Audio extends React.PureComponent {
     }
 
     this._renderCanvas();
-  }
+  };
 
   handlePause = () => {
     this.setState({ paused: true });
@@ -188,7 +187,7 @@ class Audio extends React.PureComponent {
     if (this.audioContext) {
       this.audioContext.suspend();
     }
-  }
+  };
 
   handleProgress = () => {
     const lastTimeRange = this.audio.buffered.length - 1;
@@ -196,15 +195,17 @@ class Audio extends React.PureComponent {
     if (lastTimeRange > -1) {
       this.setState({ buffer: Math.ceil(this.audio.buffered.end(lastTimeRange) / this.audio.duration * 100) });
     }
-  }
+  };
 
   toggleMute = () => {
     const muted = !this.state.muted;
 
     this.setState({ muted }, () => {
-      this.audio.muted = muted;
+      if (this.gainNode) {
+        this.gainNode.gain.value = muted ? 0 : this.state.volume;
+      }
     });
-  }
+  };
 
   toggleReveal = () => {
     if (this.props.onToggleVisibility) {
@@ -212,7 +213,7 @@ class Audio extends React.PureComponent {
     } else {
       this.setState({ revealed: !this.state.revealed });
     }
-  }
+  };
 
   handleVolumeMouseDown = e => {
     document.addEventListener('mousemove', this.handleMouseVolSlide, true);
@@ -224,14 +225,14 @@ class Audio extends React.PureComponent {
 
     e.preventDefault();
     e.stopPropagation();
-  }
+  };
 
   handleVolumeMouseUp = () => {
     document.removeEventListener('mousemove', this.handleMouseVolSlide, true);
     document.removeEventListener('mouseup', this.handleVolumeMouseUp, true);
     document.removeEventListener('touchmove', this.handleMouseVolSlide, true);
     document.removeEventListener('touchend', this.handleVolumeMouseUp, true);
-  }
+  };
 
   handleMouseDown = e => {
     document.addEventListener('mousemove', this.handleMouseMove, true);
@@ -245,7 +246,7 @@ class Audio extends React.PureComponent {
 
     e.preventDefault();
     e.stopPropagation();
-  }
+  };
 
   handleMouseUp = () => {
     document.removeEventListener('mousemove', this.handleMouseMove, true);
@@ -255,7 +256,7 @@ class Audio extends React.PureComponent {
 
     this.setState({ dragging: false });
     this.audio.play();
-  }
+  };
 
   handleMouseMove = throttle(e => {
     const { x } = getPointerPosition(this.seek, e);
@@ -273,14 +274,16 @@ class Audio extends React.PureComponent {
       currentTime: this.audio.currentTime,
       duration: this.audio.duration,
     });
-  }
+  };
 
   handleMouseVolSlide = throttle(e => {
     const { x } = getPointerPosition(this.volume, e);
 
     if(!isNaN(x)) {
       this.setState({ volume: x }, () => {
-        this.audio.volume = x;
+        if (this.gainNode) {
+          this.gainNode.gain.value = this.state.muted ? 0 : x;
+        }
       });
     }
   }, 15);
@@ -306,41 +309,38 @@ class Audio extends React.PureComponent {
 
   handleMouseEnter = () => {
     this.setState({ hovered: true });
-  }
+  };
 
   handleMouseLeave = () => {
     this.setState({ hovered: false });
-  }
+  };
 
   handleLoadedData = () => {
-    const { autoPlay, currentTime, volume, muted } = this.props;
+    const { autoPlay, currentTime } = this.props;
 
     if (currentTime) {
       this.audio.currentTime = currentTime;
     }
 
-    if (volume !== undefined) {
-      this.audio.volume = volume;
-    }
-
-    if (muted !== undefined) {
-      this.audio.muted = muted;
-    }
-
     if (autoPlay) {
       this.togglePlay();
     }
-  }
+  };
 
   _initAudioContext () {
     const AudioContext = window.AudioContext || window.webkitAudioContext;
     const context      = new AudioContext();
     const source       = context.createMediaElementSource(this.audio);
+    const gainNode     = context.createGain();
+
+    gainNode.gain.value = this.state.muted ? 0 : this.state.volume;
 
     this.visualizer.setAudioContext(context, source);
-    source.connect(context.destination);
+    source.connect(gainNode);
+    gainNode.connect(context.destination);
 
     this.audioContext = context;
+    this.gainNode = gainNode;
   }
 
   handleDownload = () => {
@@ -359,7 +359,7 @@ class Audio extends React.PureComponent {
     }).catch(err => {
       console.error(err);
     });
-  }
+  };
 
   _renderCanvas () {
     requestAnimationFrame(() => {
@@ -430,7 +430,7 @@ class Audio extends React.PureComponent {
       e.stopPropagation();
       this.togglePlay();
     }
-  }
+  };
 
   handleKeyDown = e => {
     switch(e.key) {
@@ -455,10 +455,10 @@ class Audio extends React.PureComponent {
       this.seekBy(10);
       break;
     }
-  }
+  };
 
   render () {
-    const { src, intl, alt, editable, autoPlay, sensitive, blurhash } = this.props;
+    const { src, intl, alt, lang, editable, autoPlay, sensitive, blurhash } = this.props;
     const { paused, muted, volume, currentTime, duration, buffer, dragging, revealed } = this.state;
     const progress = Math.min((currentTime / duration) * 100, 100);
 
@@ -470,7 +470,7 @@ class Audio extends React.PureComponent {
     }
 
     return (
-      <div className={classNames('audio-player', { editable, inactive: !revealed })} ref={this.setPlayerRef} style={{ backgroundColor: this._getBackgroundColor(), color: this._getForegroundColor(), width: '100%', height: this.props.fullscreen ? '100%' : (this.state.height || this.props.height) }} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} tabIndex='0' onKeyDown={this.handleKeyDown}>
+      <div className={classNames('audio-player', { editable, inactive: !revealed })} ref={this.setPlayerRef} style={{ backgroundColor: this._getBackgroundColor(), color: this._getForegroundColor(), width: '100%', height: this.props.fullscreen ? '100%' : (this.state.height || this.props.height) }} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} tabIndex={0} onKeyDown={this.handleKeyDown}>
 
         <Blurhash
           hash={blurhash}
@@ -493,7 +493,7 @@ class Audio extends React.PureComponent {
 
         <canvas
           role='button'
-          tabIndex='0'
+          tabIndex={0}
           className='audio-player__canvas'
           width={this.state.width}
           height={this.state.height}
@@ -503,6 +503,7 @@ class Audio extends React.PureComponent {
           onKeyDown={this.handleAudioKeyDown}
           title={alt}
           aria-label={alt}
+          lang={lang}
         />
 
         <div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
@@ -525,7 +526,7 @@ class Audio extends React.PureComponent {
 
           <span
             className={classNames('video-player__seek__handle', { active: dragging })}
-            tabIndex='0'
+            tabIndex={0}
             style={{ left: `${progress}%`, backgroundColor: this._getAccentColor() }}
             onKeyDown={this.handleAudioKeyDown}
           />
@@ -542,7 +543,7 @@ class Audio extends React.PureComponent {
 
                 <span
                   className='video-player__volume__handle'
-                  tabIndex='0'
+                  tabIndex={0}
                   style={{ left: `${volume * 100}%`, backgroundColor: this._getAccentColor() }}
                 />
               </div>
@@ -567,3 +568,5 @@ class Audio extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(Audio);
diff --git a/app/javascript/mastodon/features/blocks/index.js b/app/javascript/mastodon/features/blocks/index.jsx
index e00f2b60e..38616ba84 100644
--- a/app/javascript/mastodon/features/blocks/index.js
+++ b/app/javascript/mastodon/features/blocks/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['user_lists', 'blocks', 'isLoading'], true),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Blocks extends ImmutablePureComponent {
 
   static propTypes = {
@@ -77,3 +75,5 @@ class Blocks extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Blocks));
diff --git a/app/javascript/mastodon/features/bookmarked_statuses/index.js b/app/javascript/mastodon/features/bookmarked_statuses/index.jsx
index 097be17c9..46fff856e 100644
--- a/app/javascript/mastodon/features/bookmarked_statuses/index.js
+++ b/app/javascript/mastodon/features/bookmarked_statuses/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'bookmarks', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Bookmarks extends ImmutablePureComponent {
 
   static propTypes = {
@@ -48,24 +46,24 @@ class Bookmarks extends ImmutablePureComponent {
     } else {
       dispatch(addColumn('BOOKMARKS', {}));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = debounce(() => {
     this.props.dispatch(expandBookmarkedStatuses());
-  }, 300, { leading: true })
+  }, 300, { leading: true });
 
   render () {
     const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
@@ -106,3 +104,5 @@ class Bookmarks extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Bookmarks));
diff --git a/app/javascript/mastodon/features/closed_registrations_modal/index.js b/app/javascript/mastodon/features/closed_registrations_modal/index.jsx
index e6540e825..40df804f2 100644
--- a/app/javascript/mastodon/features/closed_registrations_modal/index.js
+++ b/app/javascript/mastodon/features/closed_registrations_modal/index.jsx
@@ -9,7 +9,6 @@ const mapStateToProps = state => ({
   message: state.getIn(['server', 'server', 'registrations', 'message']),
 });
 
-export default @connect(mapStateToProps)
 class ClosedRegistrationsModal extends ImmutablePureComponent {
 
   componentDidMount () {
@@ -73,3 +72,5 @@ class ClosedRegistrationsModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(ClosedRegistrationsModal);
diff --git a/app/javascript/mastodon/features/community_timeline/components/column_settings.js b/app/javascript/mastodon/features/community_timeline/components/column_settings.jsx
index 0cb6db883..d6181919a 100644
--- a/app/javascript/mastodon/features/community_timeline/components/column_settings.js
+++ b/app/javascript/mastodon/features/community_timeline/components/column_settings.jsx
@@ -4,7 +4,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import SettingToggle from '../../notifications/components/setting_toggle';
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -27,3 +26,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/mastodon/features/community_timeline/index.js b/app/javascript/mastodon/features/community_timeline/index.jsx
index 7b3f8845f..e41d11bb5 100644
--- a/app/javascript/mastodon/features/community_timeline/index.js
+++ b/app/javascript/mastodon/features/community_timeline/index.jsx
@@ -30,8 +30,6 @@ const mapStateToProps = (state, { columnId }) => {
   };
 };
 
-export default @connect(mapStateToProps)
-@injectIntl
 class CommunityTimeline extends React.PureComponent {
 
   static contextTypes = {
@@ -60,16 +58,16 @@ class CommunityTimeline extends React.PureComponent {
     } else {
       dispatch(addColumn('COMMUNITY', { other: { onlyMedia } }));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   componentDidMount () {
     const { dispatch, onlyMedia } = this.props;
@@ -109,13 +107,13 @@ class CommunityTimeline extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = maxId => {
     const { dispatch, onlyMedia } = this.props;
 
     dispatch(expandCommunityTimeline({ maxId, onlyMedia }));
-  }
+  };
 
   render () {
     const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
@@ -158,3 +156,5 @@ class CommunityTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(CommunityTimeline));
diff --git a/app/javascript/mastodon/features/compose/components/action_bar.js b/app/javascript/mastodon/features/compose/components/action_bar.jsx
index ceed928bf..13760582e 100644
--- a/app/javascript/mastodon/features/compose/components/action_bar.js
+++ b/app/javascript/mastodon/features/compose/components/action_bar.jsx
@@ -11,6 +11,7 @@ const messages = defineMessages({
   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
+  followed_tags: { id: 'navigation_bar.followed_tags', defaultMessage: 'Followed hashtags' },
   blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
   domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' },
   mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
@@ -19,7 +20,6 @@ const messages = defineMessages({
   bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
 });
 
-export default @injectIntl
 class ActionBar extends React.PureComponent {
 
   static propTypes = {
@@ -30,7 +30,7 @@ class ActionBar extends React.PureComponent {
 
   handleLogout = () => {
     this.props.onLogout();
-  }
+  };
 
   render () {
     const { intl } = this.props;
@@ -45,6 +45,7 @@ class ActionBar extends React.PureComponent {
     menu.push({ text: intl.formatMessage(messages.favourites), to: '/favourites' });
     menu.push({ text: intl.formatMessage(messages.bookmarks), to: '/bookmarks' });
     menu.push({ text: intl.formatMessage(messages.lists), to: '/lists' });
+    menu.push({ text: intl.formatMessage(messages.followed_tags), to: '/followed_tags' });
     menu.push(null);
     menu.push({ text: intl.formatMessage(messages.mutes), to: '/mutes' });
     menu.push({ text: intl.formatMessage(messages.blocks), to: '/blocks' });
@@ -63,3 +64,5 @@ class ActionBar extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ActionBar);
diff --git a/app/javascript/mastodon/features/compose/components/autosuggest_account.js b/app/javascript/mastodon/features/compose/components/autosuggest_account.jsx
index 1451be0e6..1451be0e6 100644
--- a/app/javascript/mastodon/features/compose/components/autosuggest_account.js
+++ b/app/javascript/mastodon/features/compose/components/autosuggest_account.jsx
diff --git a/app/javascript/mastodon/features/compose/components/character_counter.js b/app/javascript/mastodon/features/compose/components/character_counter.jsx
index 0ecfc9141..0ecfc9141 100644
--- a/app/javascript/mastodon/features/compose/components/character_counter.js
+++ b/app/javascript/mastodon/features/compose/components/character_counter.jsx
diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.jsx
index ebdd55d33..a40eb87e3 100644
--- a/app/javascript/mastodon/features/compose/components/compose_form.js
+++ b/app/javascript/mastodon/features/compose/components/compose_form.jsx
@@ -32,7 +32,6 @@ const messages = defineMessages({
   saveChanges: { id: 'compose_form.save_changes', defaultMessage: 'Save changes' },
 });
 
-export default @injectIntl
 class ComposeForm extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -65,6 +64,7 @@ class ComposeForm extends ImmutablePureComponent {
     anyMedia: PropTypes.bool,
     isInReply: PropTypes.bool,
     singleColumn: PropTypes.bool,
+    lang: PropTypes.string,
   };
 
   static defaultProps = {
@@ -73,17 +73,17 @@ class ComposeForm extends ImmutablePureComponent {
 
   handleChange = (e) => {
     this.props.onChange(e.target.value);
-  }
+  };
 
   handleKeyDown = (e) => {
     if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
       this.handleSubmit();
     }
-  }
+  };
 
   getFulltextForCharacterCounting = () => {
     return [this.props.spoiler? this.props.spoilerText: '', countableText(this.props.text)].join('');
-  }
+  };
 
   canSubmit = () => {
     const { isSubmitting, isChangingUpload, isUploading, anyMedia } = this.props;
@@ -91,7 +91,7 @@ class ComposeForm extends ImmutablePureComponent {
     const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0;
 
     return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > maxChars || (isOnlyWhitespace && !anyMedia));
-  }
+  };
 
   handleSubmit = (e) => {
     if (this.props.text !== this.autosuggestTextarea.textarea.value) {
@@ -109,27 +109,27 @@ class ComposeForm extends ImmutablePureComponent {
     if (e) {
       e.preventDefault();
     }
-  }
+  };
 
   onSuggestionsClearRequested = () => {
     this.props.onClearSuggestions();
-  }
+  };
 
   onSuggestionsFetchRequested = (token) => {
     this.props.onFetchSuggestions(token);
-  }
+  };
 
   onSuggestionSelected = (tokenStart, token, value) => {
     this.props.onSuggestionSelected(tokenStart, token, value, ['text']);
-  }
+  };
 
   onSpoilerSuggestionSelected = (tokenStart, token, value) => {
     this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']);
-  }
+  };
 
   handleChangeSpoilerText = (e) => {
     this.props.onChangeSpoilerText(e.target.value);
-  }
+  };
 
   handleFocus = () => {
     if (this.composeForm && !this.props.singleColumn) {
@@ -138,7 +138,7 @@ class ComposeForm extends ImmutablePureComponent {
         this.composeForm.scrollIntoView();
       }
     }
-  }
+  };
 
   componentDidMount () {
     this._updateFocusAndSelection({ });
@@ -184,15 +184,15 @@ class ComposeForm extends ImmutablePureComponent {
         this.autosuggestTextarea.textarea.focus();
       }
     }
-  }
+  };
 
   setAutosuggestTextarea = (c) => {
     this.autosuggestTextarea = c;
-  }
+  };
 
   setSpoilerText = (c) => {
     this.spoilerText = c;
-  }
+  };
 
   setRef = c => {
     this.composeForm = c;
@@ -204,7 +204,7 @@ class ComposeForm extends ImmutablePureComponent {
     const needsSpace   = data.custom && position > 0 && !allowedAroundShortCode.includes(text[position - 1]);
 
     this.props.onPickEmoji(position, data, needsSpace);
-  }
+  };
 
   render () {
     const { intl, onPaste, autoFocus } = this.props;
@@ -241,6 +241,8 @@ class ComposeForm extends ImmutablePureComponent {
             searchTokens={[':']}
             id='cw-spoiler-input'
             className='spoiler-input__input'
+            lang={this.props.lang}
+            spellCheck
           />
         </div>
 
@@ -258,6 +260,7 @@ class ComposeForm extends ImmutablePureComponent {
           onSuggestionSelected={this.onSuggestionSelected}
           onPaste={onPaste}
           autoFocus={autoFocus}
+          lang={this.props.lang}
         >
           <EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
 
@@ -296,3 +299,5 @@ class ComposeForm extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(ComposeForm);
diff --git a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx
index 76c9cda81..4fb131b47 100644
--- a/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/emoji_picker_dropdown.jsx
@@ -57,7 +57,7 @@ class ModifierPickerMenu extends React.PureComponent {
 
   handleClick = e => {
     this.props.onSelect(e.currentTarget.getAttribute('data-index') * 1);
-  }
+  };
 
   componentWillReceiveProps (nextProps) {
     if (nextProps.active) {
@@ -75,7 +75,7 @@ class ModifierPickerMenu extends React.PureComponent {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
     }
-  }
+  };
 
   attachListeners () {
     document.addEventListener('click', this.handleDocumentClick, false);
@@ -89,7 +89,7 @@ class ModifierPickerMenu extends React.PureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   render () {
     const { active } = this.props;
@@ -124,12 +124,12 @@ class ModifierPicker extends React.PureComponent {
     } else {
       this.props.onOpen();
     }
-  }
+  };
 
   handleSelect = modifier => {
     this.props.onChange(modifier);
     this.props.onClose();
-  }
+  };
 
   render () {
     const { active, modifier } = this.props;
@@ -144,8 +144,7 @@ class ModifierPicker extends React.PureComponent {
 
 }
 
-@injectIntl
-class EmojiPickerMenu extends React.PureComponent {
+class EmojiPickerMenuImpl extends React.PureComponent {
 
   static propTypes = {
     custom_emojis: ImmutablePropTypes.list,
@@ -174,7 +173,7 @@ class EmojiPickerMenu extends React.PureComponent {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
     }
-  }
+  };
 
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
@@ -198,7 +197,7 @@ class EmojiPickerMenu extends React.PureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   getI18n = () => {
     const { intl } = this.props;
@@ -219,7 +218,7 @@ class EmojiPickerMenu extends React.PureComponent {
         custom: intl.formatMessage(messages.custom),
       },
     };
-  }
+  };
 
   handleClick = (emoji, event) => {
     if (!emoji.native) {
@@ -229,19 +228,19 @@ class EmojiPickerMenu extends React.PureComponent {
       this.props.onClose();
     }
     this.props.onPick(emoji);
-  }
+  };
 
   handleModifierOpen = () => {
     this.setState({ modifierOpen: true });
-  }
+  };
 
   handleModifierClose = () => {
     this.setState({ modifierOpen: false });
-  }
+  };
 
   handleModifierChange = modifier => {
     this.props.onSkinTone(modifier);
-  }
+  };
 
   render () {
     const { loading, style, intl, custom_emojis, skinTone, frequentlyUsedEmojis } = this.props;
@@ -305,7 +304,8 @@ class EmojiPickerMenu extends React.PureComponent {
 
 }
 
-export default @injectIntl
+const EmojiPickerMenu = injectIntl(EmojiPickerMenuImpl);
+
 class EmojiPickerDropdown extends React.PureComponent {
 
   static propTypes = {
@@ -325,7 +325,7 @@ class EmojiPickerDropdown extends React.PureComponent {
 
   setRef = (c) => {
     this.dropdown = c;
-  }
+  };
 
   onShowDropdown = () => {
     this.setState({ active: true });
@@ -342,11 +342,11 @@ class EmojiPickerDropdown extends React.PureComponent {
         this.setState({ loading: false, active: false });
       });
     }
-  }
+  };
 
   onHideDropdown = () => {
     this.setState({ active: false });
-  }
+  };
 
   onToggle = (e) => {
     if (!this.state.loading && (!e.key || e.key === 'Enter')) {
@@ -356,21 +356,21 @@ class EmojiPickerDropdown extends React.PureComponent {
         this.onShowDropdown(e);
       }
     }
-  }
+  };
 
   handleKeyDown = e => {
     if (e.key === 'Escape') {
       this.onHideDropdown();
     }
-  }
+  };
 
   setTargetRef = c => {
     this.target = c;
-  }
+  };
 
   findTarget = () => {
     return this.target;
-  }
+  };
 
   render () {
     const { intl, onPickEmoji, onSkinTone, skinTone, frequentlyUsedEmojis, button } = this.props;
@@ -409,3 +409,5 @@ class EmojiPickerDropdown extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(EmojiPickerDropdown);
diff --git a/app/javascript/mastodon/features/compose/components/language_dropdown.js b/app/javascript/mastodon/features/compose/components/language_dropdown.jsx
index 2dd406b4b..08542e3a9 100644
--- a/app/javascript/mastodon/features/compose/components/language_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/language_dropdown.jsx
@@ -40,7 +40,7 @@ class LanguageDropdownMenu extends React.PureComponent {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
     }
-  }
+  };
 
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
@@ -63,15 +63,15 @@ class LanguageDropdownMenu extends React.PureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   setListRef = c => {
     this.listNode = c;
-  }
+  };
 
   handleSearchChange = ({ target }) => {
     this.setState({ searchValue: target.value });
-  }
+  };
 
   search () {
     const { languages, value, frequentlyUsedLanguages } = this.props;
@@ -122,7 +122,7 @@ class LanguageDropdownMenu extends React.PureComponent {
 
     this.props.onClose();
     this.props.onChange(value);
-  }
+  };
 
   handleKeyDown = e => {
     const { onClose } = this.props;
@@ -163,7 +163,7 @@ class LanguageDropdownMenu extends React.PureComponent {
       e.preventDefault();
       e.stopPropagation();
     }
-  }
+  };
 
   handleSearchKeyDown = e => {
     const { onChange, onClose } = this.props;
@@ -199,21 +199,21 @@ class LanguageDropdownMenu extends React.PureComponent {
 
       break;
     }
-  }
+  };
 
   handleClear = () => {
     this.setState({ searchValue: '' });
-  }
+  };
 
   renderItem = lang => {
     const { value } = this.props;
 
     return (
-      <div key={lang[0]} role='option' tabIndex='0' data-index={lang[0]} className={classNames('language-dropdown__dropdown__results__item', { active: lang[0] === value })} aria-selected={lang[0] === value} onClick={this.handleClick} onKeyDown={this.handleKeyDown}>
-        <span className='language-dropdown__dropdown__results__item__native-name'>{lang[2]}</span> <span className='language-dropdown__dropdown__results__item__common-name'>({lang[1]})</span>
+      <div key={lang[0]} role='option' tabIndex={0} data-index={lang[0]} className={classNames('language-dropdown__dropdown__results__item', { active: lang[0] === value })} aria-selected={lang[0] === value} onClick={this.handleClick} onKeyDown={this.handleKeyDown}>
+        <span className='language-dropdown__dropdown__results__item__native-name' lang={lang[0]}>{lang[2]}</span> <span className='language-dropdown__dropdown__results__item__common-name'>({lang[1]})</span>
       </div>
     );
-  }
+  };
 
   render () {
     const { intl } = this.props;
@@ -237,7 +237,6 @@ class LanguageDropdownMenu extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class LanguageDropdown extends React.PureComponent {
 
   static propTypes = {
@@ -259,7 +258,7 @@ class LanguageDropdown extends React.PureComponent {
     }
 
     this.setState({ open: !this.state.open });
-  }
+  };
 
   handleClose = () => {
     const { value, onClose } = this.props;
@@ -270,24 +269,24 @@ class LanguageDropdown extends React.PureComponent {
 
     this.setState({ open: false });
     onClose(value);
-  }
+  };
 
   handleChange = value => {
     const { onChange } = this.props;
     onChange(value);
-  }
+  };
 
   setTargetRef = c => {
     this.target = c;
-  }
+  };
 
   findTarget = () => {
     return this.target;
-  }
+  };
 
   handleOverlayEnter = (state) => {
     this.setState({ placement: state.placement });
-  }
+  };
 
   render () {
     const { value, intl, frequentlyUsedLanguages } = this.props;
@@ -325,3 +324,5 @@ class LanguageDropdown extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(LanguageDropdown);
diff --git a/app/javascript/mastodon/features/compose/components/navigation_bar.js b/app/javascript/mastodon/features/compose/components/navigation_bar.jsx
index be979af50..be979af50 100644
--- a/app/javascript/mastodon/features/compose/components/navigation_bar.js
+++ b/app/javascript/mastodon/features/compose/components/navigation_bar.jsx
diff --git a/app/javascript/mastodon/features/compose/components/poll_button.js b/app/javascript/mastodon/features/compose/components/poll_button.jsx
index 76f96bfa4..6ad689ccc 100644
--- a/app/javascript/mastodon/features/compose/components/poll_button.js
+++ b/app/javascript/mastodon/features/compose/components/poll_button.jsx
@@ -13,8 +13,6 @@ const iconStyle = {
   lineHeight: '27px',
 };
 
-export default
-@injectIntl
 class PollButton extends React.PureComponent {
 
   static propTypes = {
@@ -27,7 +25,7 @@ class PollButton extends React.PureComponent {
 
   handleClick = () => {
     this.props.onClick();
-  }
+  };
 
   render () {
     const { intl, active, unavailable, disabled } = this.props;
@@ -53,3 +51,5 @@ class PollButton extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(PollButton);
diff --git a/app/javascript/mastodon/features/compose/components/poll_form.js b/app/javascript/mastodon/features/compose/components/poll_form.jsx
index 3aa527161..f81d7355a 100644
--- a/app/javascript/mastodon/features/compose/components/poll_form.js
+++ b/app/javascript/mastodon/features/compose/components/poll_form.jsx
@@ -20,11 +20,11 @@ const messages = defineMessages({
   days: { id: 'intervals.full.days', defaultMessage: '{number, plural, one {# day} other {# days}}' },
 });
 
-@injectIntl
-class Option extends React.PureComponent {
+class OptionIntl extends React.PureComponent {
 
   static propTypes = {
     title: PropTypes.string.isRequired,
+    lang: PropTypes.string,
     index: PropTypes.number.isRequired,
     isPollMultiple: PropTypes.bool,
     autoFocus: PropTypes.bool,
@@ -57,22 +57,22 @@ class Option extends React.PureComponent {
     if (e.key === 'Enter' || e.key === ' ') {
       this.handleToggleMultiple(e);
     }
-  }
+  };
 
   onSuggestionsClearRequested = () => {
     this.props.onClearSuggestions();
-  }
+  };
 
   onSuggestionsFetchRequested = (token) => {
     this.props.onFetchSuggestions(token);
-  }
+  };
 
   onSuggestionSelected = (tokenStart, token, value) => {
     this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
-  }
+  };
 
   render () {
-    const { isPollMultiple, title, index, autoFocus, intl } = this.props;
+    const { isPollMultiple, title, lang, index, autoFocus, intl } = this.props;
 
     return (
       <li>
@@ -82,7 +82,7 @@ class Option extends React.PureComponent {
             onClick={this.handleToggleMultiple}
             onKeyPress={this.handleCheckboxKeypress}
             role='button'
-            tabIndex='0'
+            tabIndex={0}
             title={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
             aria-label={intl.formatMessage(isPollMultiple ? messages.switchToSingle : messages.switchToMultiple)}
           />
@@ -91,6 +91,8 @@ class Option extends React.PureComponent {
             placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })}
             maxLength={100}
             value={title}
+            lang={lang}
+            spellCheck
             onChange={this.handleOptionTitleChange}
             suggestions={this.props.suggestions}
             onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
@@ -110,12 +112,13 @@ class Option extends React.PureComponent {
 
 }
 
-export default
-@injectIntl
+const Option = injectIntl(OptionIntl);
+
 class PollForm extends ImmutablePureComponent {
 
   static propTypes = {
     options: ImmutablePropTypes.list,
+    lang: PropTypes.string,
     expiresIn: PropTypes.number,
     isMultiple: PropTypes.bool,
     onChangeOption: PropTypes.func.isRequired,
@@ -142,7 +145,7 @@ class PollForm extends ImmutablePureComponent {
   };
 
   render () {
-    const { options, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl, ...other } = this.props;
+    const { options, lang, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl, ...other } = this.props;
 
     if (!options) {
       return null;
@@ -153,7 +156,7 @@ class PollForm extends ImmutablePureComponent {
     return (
       <div className='compose-form__poll-wrapper'>
         <ul>
-          {options.map((title, i) => <Option title={title} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} autoFocus={i === autoFocusIndex} {...other} />)}
+          {options.map((title, i) => <Option title={title} lang={lang} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} autoFocus={i === autoFocusIndex} {...other} />)}
         </ul>
 
         <div className='poll__footer'>
@@ -176,3 +179,5 @@ class PollForm extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(PollForm);
diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js b/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx
index 545b67eda..e65c9cb72 100644
--- a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js
+++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.jsx
@@ -35,7 +35,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
     }
-  }
+  };
 
   handleKeyDown = e => {
     const { items } = this.props;
@@ -79,7 +79,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
       e.preventDefault();
       e.stopPropagation();
     }
-  }
+  };
 
   handleClick = e => {
     const value = e.currentTarget.getAttribute('data-index');
@@ -88,7 +88,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
 
     this.props.onClose();
     this.props.onChange(value);
-  }
+  };
 
   componentDidMount () {
     document.addEventListener('click', this.handleDocumentClick, false);
@@ -103,11 +103,11 @@ class PrivacyDropdownMenu extends React.PureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   setFocusRef = c => {
     this.focusedItem = c;
-  }
+  };
 
   render () {
     const { style, items, value } = this.props;
@@ -115,7 +115,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
     return (
       <div style={{ ...style }} role='listbox' ref={this.setRef}>
         {items.map(item => (
-          <div role='option' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
+          <div role='option' tabIndex={0} key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
             <div className='privacy-dropdown__option__icon'>
               <Icon id={item.icon} fixedWidth />
             </div>
@@ -132,7 +132,6 @@ class PrivacyDropdownMenu extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class PrivacyDropdown extends React.PureComponent {
 
   static propTypes = {
@@ -168,7 +167,7 @@ class PrivacyDropdown extends React.PureComponent {
       }
       this.setState({ open: !this.state.open });
     }
-  }
+  };
 
   handleModalActionClick = (e) => {
     e.preventDefault();
@@ -177,7 +176,7 @@ class PrivacyDropdown extends React.PureComponent {
 
     this.props.onModalClose();
     this.props.onChange(value);
-  }
+  };
 
   handleKeyDown = e => {
     switch(e.key) {
@@ -185,13 +184,13 @@ class PrivacyDropdown extends React.PureComponent {
       this.handleClose();
       break;
     }
-  }
+  };
 
   handleMouseDown = () => {
     if (!this.state.open) {
       this.activeElement = document.activeElement;
     }
-  }
+  };
 
   handleButtonKeyDown = (e) => {
     switch(e.key) {
@@ -200,18 +199,18 @@ class PrivacyDropdown extends React.PureComponent {
       this.handleMouseDown();
       break;
     }
-  }
+  };
 
   handleClose = () => {
     if (this.state.open && this.activeElement) {
       this.activeElement.focus({ preventScroll: true });
     }
     this.setState({ open: false });
-  }
+  };
 
   handleChange = value => {
     this.props.onChange(value);
-  }
+  };
 
   componentWillMount () {
     const { intl: { formatMessage } } = this.props;
@@ -231,15 +230,15 @@ class PrivacyDropdown extends React.PureComponent {
 
   setTargetRef = c => {
     this.target = c;
-  }
+  };
 
   findTarget = () => {
     return this.target;
-  }
+  };
 
   handleOverlayEnter = (state) => {
     this.setState({ placement: state.placement });
-  }
+  };
 
   render () {
     const { value, container, disabled, intl } = this.props;
@@ -285,3 +284,5 @@ class PrivacyDropdown extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(PrivacyDropdown);
diff --git a/app/javascript/mastodon/features/compose/components/reply_indicator.js b/app/javascript/mastodon/features/compose/components/reply_indicator.jsx
index fc236882a..81de4ea76 100644
--- a/app/javascript/mastodon/features/compose/components/reply_indicator.js
+++ b/app/javascript/mastodon/features/compose/components/reply_indicator.jsx
@@ -12,7 +12,6 @@ const messages = defineMessages({
   cancel: { id: 'reply_indicator.cancel', defaultMessage: 'Cancel' },
 });
 
-export default @injectIntl
 class ReplyIndicator extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -27,14 +26,14 @@ class ReplyIndicator extends ImmutablePureComponent {
 
   handleClick = () => {
     this.props.onCancel();
-  }
+  };
 
   handleAccountClick = (e) => {
     if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
       e.preventDefault();
       this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
     }
-  }
+  };
 
   render () {
     const { status, intl } = this.props;
@@ -69,3 +68,5 @@ class ReplyIndicator extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(ReplyIndicator);
diff --git a/app/javascript/mastodon/features/compose/components/search.js b/app/javascript/mastodon/features/compose/components/search.js
deleted file mode 100644
index 5820f8ca2..000000000
--- a/app/javascript/mastodon/features/compose/components/search.js
+++ /dev/null
@@ -1,147 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
-import Overlay from 'react-overlays/Overlay';
-import { searchEnabled } from '../../../initial_state';
-import Icon from 'mastodon/components/icon';
-
-const messages = defineMessages({
-  placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
-  placeholderSignedIn: { id: 'search.search_or_paste', defaultMessage: 'Search or paste URL' },
-});
-
-class SearchPopout extends React.PureComponent {
-
-  render () {
-    const extraInformation = searchEnabled ? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' /> : <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />;
-    return (
-      <div className='search-popout'>
-        <h4><FormattedMessage id='search_popout.search_format' defaultMessage='Advanced search format' /></h4>
-
-        <ul>
-          <li><em>#example</em> <FormattedMessage id='search_popout.tips.hashtag' defaultMessage='hashtag' /></li>
-          <li><em>@username@domain</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
-          <li><em>URL</em> <FormattedMessage id='search_popout.tips.user' defaultMessage='user' /></li>
-          <li><em>URL</em> <FormattedMessage id='search_popout.tips.status' defaultMessage='status' /></li>
-        </ul>
-
-        {extraInformation}
-      </div>
-    );
-  }
-
-}
-
-export default @injectIntl
-class Search extends React.PureComponent {
-
-  static contextTypes = {
-    router: PropTypes.object.isRequired,
-    identity: PropTypes.object.isRequired,
-  };
-
-  static propTypes = {
-    value: PropTypes.string.isRequired,
-    submitted: PropTypes.bool,
-    onChange: PropTypes.func.isRequired,
-    onSubmit: PropTypes.func.isRequired,
-    onClear: PropTypes.func.isRequired,
-    onShow: PropTypes.func.isRequired,
-    openInRoute: PropTypes.bool,
-    intl: PropTypes.object.isRequired,
-    singleColumn: PropTypes.bool,
-  };
-
-  state = {
-    expanded: false,
-  };
-
-  setRef = c => {
-    this.searchForm = c;
-  }
-
-  handleChange = (e) => {
-    this.props.onChange(e.target.value);
-  }
-
-  handleClear = (e) => {
-    e.preventDefault();
-
-    if (this.props.value.length > 0 || this.props.submitted) {
-      this.props.onClear();
-    }
-  }
-
-  handleKeyUp = (e) => {
-    if (e.key === 'Enter') {
-      e.preventDefault();
-
-      this.props.onSubmit();
-
-      if (this.props.openInRoute) {
-        this.context.router.history.push('/search');
-      }
-    } else if (e.key === 'Escape') {
-      document.querySelector('.ui').parentElement.focus();
-    }
-  }
-
-  handleFocus = () => {
-    this.setState({ expanded: true });
-    this.props.onShow();
-
-    if (this.searchForm && !this.props.singleColumn) {
-      const { left, right } = this.searchForm.getBoundingClientRect();
-      if (left < 0 || right > (window.innerWidth || document.documentElement.clientWidth)) {
-        this.searchForm.scrollIntoView();
-      }
-    }
-  }
-
-  handleBlur = () => {
-    this.setState({ expanded: false });
-  }
-
-  findTarget = () => {
-    return this.searchForm;
-  }
-
-  render () {
-    const { intl, value, submitted } = this.props;
-    const { expanded } = this.state;
-    const { signedIn } = this.context.identity;
-    const hasValue = value.length > 0 || submitted;
-
-    return (
-      <div className='search'>
-        <input
-          ref={this.setRef}
-          className='search__input'
-          type='text'
-          placeholder={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
-          aria-label={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
-          value={value}
-          onChange={this.handleChange}
-          onKeyUp={this.handleKeyUp}
-          onFocus={this.handleFocus}
-          onBlur={this.handleBlur}
-        />
-
-        <div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
-          <Icon id='search' className={hasValue ? '' : 'active'} />
-          <Icon id='times-circle' className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} />
-        </div>
-        <Overlay show={expanded && !hasValue} placement='bottom' target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
-          {({ props, placement }) => (
-            <div {...props} style={{ ...props.style, width: 285, zIndex: 2 }}>
-              <div className={`dropdown-animation ${placement}`}>
-                <SearchPopout />
-              </div>
-            </div>
-          )}
-        </Overlay>
-      </div>
-    );
-  }
-
-}
diff --git a/app/javascript/mastodon/features/compose/components/search.jsx b/app/javascript/mastodon/features/compose/components/search.jsx
new file mode 100644
index 000000000..46723f5cc
--- /dev/null
+++ b/app/javascript/mastodon/features/compose/components/search.jsx
@@ -0,0 +1,335 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
+import { searchEnabled } from 'mastodon/initial_state';
+import Icon from 'mastodon/components/icon';
+import classNames from 'classnames';
+import { HASHTAG_REGEX } from 'mastodon/utils/hashtags';
+
+const messages = defineMessages({
+  placeholder: { id: 'search.placeholder', defaultMessage: 'Search' },
+  placeholderSignedIn: { id: 'search.search_or_paste', defaultMessage: 'Search or paste URL' },
+});
+
+class Search extends React.PureComponent {
+
+  static contextTypes = {
+    router: PropTypes.object.isRequired,
+    identity: PropTypes.object.isRequired,
+  };
+
+  static propTypes = {
+    value: PropTypes.string.isRequired,
+    recent: ImmutablePropTypes.orderedSet,
+    submitted: PropTypes.bool,
+    onChange: PropTypes.func.isRequired,
+    onSubmit: PropTypes.func.isRequired,
+    onOpenURL: PropTypes.func.isRequired,
+    onClickSearchResult: PropTypes.func.isRequired,
+    onForgetSearchResult: PropTypes.func.isRequired,
+    onClear: PropTypes.func.isRequired,
+    onShow: PropTypes.func.isRequired,
+    openInRoute: PropTypes.bool,
+    intl: PropTypes.object.isRequired,
+    singleColumn: PropTypes.bool,
+  };
+
+  state = {
+    expanded: false,
+    selectedOption: -1,
+    options: [],
+  };
+
+  setRef = c => {
+    this.searchForm = c;
+  };
+
+  handleChange = ({ target }) => {
+    const { onChange } = this.props;
+
+    onChange(target.value);
+
+    this._calculateOptions(target.value);
+  };
+
+  handleClear = e => {
+    const { value, submitted, onClear } = this.props;
+
+    e.preventDefault();
+
+    if (value.length > 0 || submitted) {
+      onClear();
+      this.setState({ options: [], selectedOption: -1 });
+    }
+  };
+
+  handleKeyDown = (e) => {
+    const { selectedOption } = this.state;
+    const options = this._getOptions();
+
+    switch(e.key) {
+    case 'Escape':
+      e.preventDefault();
+      this._unfocus();
+
+      break;
+    case 'ArrowDown':
+      e.preventDefault();
+
+      if (options.length > 0) {
+        this.setState({ selectedOption: Math.min(selectedOption + 1, options.length - 1) });
+      }
+
+      break;
+    case 'ArrowUp':
+      e.preventDefault();
+
+      if (options.length > 0) {
+        this.setState({ selectedOption: Math.max(selectedOption - 1, -1) });
+      }
+
+      break;
+    case 'Enter':
+      e.preventDefault();
+
+      if (selectedOption === -1) {
+        this._submit();
+      } else if (options.length > 0) {
+        options[selectedOption].action();
+      }
+
+      this._unfocus();
+
+      break;
+    case 'Delete':
+      if (selectedOption > -1 && options.length > 0) {
+        const search = options[selectedOption];
+
+        if (typeof search.forget === 'function') {
+          e.preventDefault();
+          search.forget(e);
+        }
+      }
+
+      break;
+    }
+  };
+
+  handleFocus = () => {
+    const { onShow, singleColumn } = this.props;
+
+    this.setState({ expanded: true, selectedOption: -1 });
+    onShow();
+
+    if (this.searchForm && !singleColumn) {
+      const { left, right } = this.searchForm.getBoundingClientRect();
+
+      if (left < 0 || right > (window.innerWidth || document.documentElement.clientWidth)) {
+        this.searchForm.scrollIntoView();
+      }
+    }
+  };
+
+  handleBlur = () => {
+    this.setState({ expanded: false, selectedOption: -1 });
+  };
+
+  findTarget = () => {
+    return this.searchForm;
+  };
+
+  handleHashtagClick = () => {
+    const { router } = this.context;
+    const { value, onClickSearchResult } = this.props;
+
+    const query = value.trim().replace(/^#/, '');
+
+    router.history.push(`/tags/${query}`);
+    onClickSearchResult(query, 'hashtag');
+  };
+
+  handleAccountClick = () => {
+    const { router } = this.context;
+    const { value, onClickSearchResult } = this.props;
+
+    const query = value.trim().replace(/^@/, '');
+
+    router.history.push(`/@${query}`);
+    onClickSearchResult(query, 'account');
+  };
+
+  handleURLClick = () => {
+    const { router } = this.context;
+    const { onOpenURL } = this.props;
+
+    onOpenURL(router.history);
+  };
+
+  handleStatusSearch = () => {
+    this._submit('statuses');
+  };
+
+  handleAccountSearch = () => {
+    this._submit('accounts');
+  };
+
+  handleRecentSearchClick = search => {
+    const { router } = this.context;
+
+    if (search.get('type') === 'account') {
+      router.history.push(`/@${search.get('q')}`);
+    } else if (search.get('type') === 'hashtag') {
+      router.history.push(`/tags/${search.get('q')}`);
+    }
+  };
+
+  handleForgetRecentSearchClick = search => {
+    const { onForgetSearchResult } = this.props;
+
+    onForgetSearchResult(search.get('q'));
+  };
+
+  _unfocus () {
+    document.querySelector('.ui').parentElement.focus();
+  }
+
+  _submit (type) {
+    const { onSubmit, openInRoute } = this.props;
+    const { router } = this.context;
+
+    onSubmit(type);
+
+    if (openInRoute) {
+      router.history.push('/search');
+    }
+  }
+
+  _getOptions () {
+    const { options } = this.state;
+
+    if (options.length > 0) {
+      return options;
+    }
+
+    const { recent } = this.props;
+
+    return recent.toArray().map(search => ({
+      label: search.get('type') === 'account' ? `@${search.get('q')}` : `#${search.get('q')}`,
+
+      action: () => this.handleRecentSearchClick(search),
+
+      forget: e => {
+        e.stopPropagation();
+        this.handleForgetRecentSearchClick(search);
+      },
+    }));
+  }
+
+  _calculateOptions (value) {
+    const trimmedValue = value.trim();
+    const options = [];
+
+    if (trimmedValue.length > 0) {
+      const couldBeURL = trimmedValue.startsWith('https://') && !trimmedValue.includes(' ');
+
+      if (couldBeURL) {
+        options.push({ key: 'open-url', label: <FormattedMessage id='search.quick_action.open_url' defaultMessage='Open URL in Mastodon' />, action: this.handleURLClick });
+      }
+
+      const couldBeHashtag = (trimmedValue.startsWith('#') && trimmedValue.length > 1) || trimmedValue.match(HASHTAG_REGEX);
+
+      if (couldBeHashtag) {
+        options.push({ key: 'go-to-hashtag', label: <FormattedMessage id='search.quick_action.go_to_hashtag' defaultMessage='Go to hashtag {x}' values={{ x: <mark>#{trimmedValue.replace(/^#/, '')}</mark> }} />, action: this.handleHashtagClick });
+      }
+
+      const couldBeUsername = trimmedValue.match(/^@?[a-z0-9_-]+(@[^\s]+)?$/i);
+
+      if (couldBeUsername) {
+        options.push({ key: 'go-to-account', label: <FormattedMessage id='search.quick_action.go_to_account' defaultMessage='Go to profile {x}' values={{ x: <mark>@{trimmedValue.replace(/^@/, '')}</mark> }} />, action: this.handleAccountClick });
+      }
+
+      const couldBeStatusSearch = searchEnabled;
+
+      if (couldBeStatusSearch) {
+        options.push({ key: 'status-search', label: <FormattedMessage id='search.quick_action.status_search' defaultMessage='Posts matching {x}' values={{ x: <mark>{trimmedValue}</mark> }} />, action: this.handleStatusSearch });
+      }
+
+      const couldBeUserSearch = true;
+
+      if (couldBeUserSearch) {
+        options.push({ key: 'account-search', label: <FormattedMessage id='search.quick_action.account_search' defaultMessage='Profiles matching {x}' values={{ x: <mark>{trimmedValue}</mark> }} />, action: this.handleAccountSearch });
+      }
+    }
+
+    this.setState({ options });
+  }
+
+  render () {
+    const { intl, value, submitted, recent } = this.props;
+    const { expanded, options, selectedOption } = this.state;
+    const { signedIn } = this.context.identity;
+
+    const hasValue = value.length > 0 || submitted;
+
+    return (
+      <div className={classNames('search', { active: expanded })}>
+        <input
+          ref={this.setRef}
+          className='search__input'
+          type='text'
+          placeholder={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
+          aria-label={intl.formatMessage(signedIn ? messages.placeholderSignedIn : messages.placeholder)}
+          value={value}
+          onChange={this.handleChange}
+          onKeyDown={this.handleKeyDown}
+          onFocus={this.handleFocus}
+          onBlur={this.handleBlur}
+        />
+
+        <div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
+          <Icon id='search' className={hasValue ? '' : 'active'} />
+          <Icon id='times-circle' className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} />
+        </div>
+
+        <div className='search__popout'>
+          {options.length === 0 && (
+            <>
+              <h4><FormattedMessage id='search_popout.recent' defaultMessage='Recent searches' /></h4>
+
+              <div className='search__popout__menu'>
+                {recent.size > 0 ? this._getOptions().map(({ label, action, forget }, i) => (
+                  <button key={label} onMouseDown={action} className={classNames('search__popout__menu__item search__popout__menu__item--flex', { selected: selectedOption === i })}>
+                    <span>{label}</span>
+                    <button className='icon-button' onMouseDown={forget}><Icon id='times' /></button>
+                  </button>
+                )) : (
+                  <div className='search__popout__menu__message'>
+                    <FormattedMessage id='search.no_recent_searches' defaultMessage='No recent searches' />
+                  </div>
+                )}
+              </div>
+            </>
+          )}
+
+          {options.length > 0 && (
+            <>
+              <h4><FormattedMessage id='search_popout.quick_actions' defaultMessage='Quick actions' /></h4>
+
+              <div className='search__popout__menu'>
+                {options.map(({ key, label, action }, i) => (
+                  <button key={key} onMouseDown={action} className={classNames('search__popout__menu__item', { selected: selectedOption === i })}>
+                    {label}
+                  </button>
+                ))}
+              </div>
+            </>
+          )}
+        </div>
+      </div>
+    );
+  }
+
+}
+
+export default injectIntl(Search);
diff --git a/app/javascript/mastodon/features/compose/components/search_results.js b/app/javascript/mastodon/features/compose/components/search_results.jsx
index 44ab43638..1dccd950c 100644
--- a/app/javascript/mastodon/features/compose/components/search_results.js
+++ b/app/javascript/mastodon/features/compose/components/search_results.jsx
@@ -14,7 +14,6 @@ const messages = defineMessages({
   dismissSuggestion: { id: 'suggestions.dismiss', defaultMessage: 'Dismiss suggestion' },
 });
 
-export default @injectIntl
 class SearchResults extends ImmutablePureComponent {
 
   static propTypes = {
@@ -78,7 +77,7 @@ class SearchResults extends ImmutablePureComponent {
       count   += results.get('accounts').size;
       accounts = (
         <div className='search-results__section'>
-          <h5><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='People' /></h5>
+          <h5><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></h5>
 
           {results.get('accounts').map(accountId => <AccountContainer key={accountId} id={accountId} />)}
 
@@ -138,3 +137,5 @@ class SearchResults extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(SearchResults);
diff --git a/app/javascript/mastodon/features/compose/components/text_icon_button.js b/app/javascript/mastodon/features/compose/components/text_icon_button.jsx
index 73da32ad5..73da32ad5 100644
--- a/app/javascript/mastodon/features/compose/components/text_icon_button.js
+++ b/app/javascript/mastodon/features/compose/components/text_icon_button.jsx
diff --git a/app/javascript/mastodon/features/compose/components/upload.js b/app/javascript/mastodon/features/compose/components/upload.jsx
index b08307ade..e3651c229 100644
--- a/app/javascript/mastodon/features/compose/components/upload.js
+++ b/app/javascript/mastodon/features/compose/components/upload.jsx
@@ -22,31 +22,36 @@ export default class Upload extends ImmutablePureComponent {
   handleUndoClick = e => {
     e.stopPropagation();
     this.props.onUndo(this.props.media.get('id'));
-  }
+  };
 
   handleFocalPointClick = e => {
     e.stopPropagation();
     this.props.onOpenFocalPoint(this.props.media.get('id'));
-  }
+  };
 
   render () {
     const { media } = this.props;
+
+    if (!media) {
+      return null;
+    }
+
     const focusX = media.getIn(['meta', 'focus', 'x']);
     const focusY = media.getIn(['meta', 'focus', 'y']);
     const x = ((focusX /  2) + .5) * 100;
     const y = ((focusY / -2) + .5) * 100;
 
     return (
-      <div className='compose-form__upload' tabIndex='0' role='button'>
+      <div className='compose-form__upload' tabIndex={0} role='button'>
         <Motion defaultStyle={{ scale: 0.8 }} style={{ scale: spring(1, { stiffness: 180, damping: 12 }) }}>
           {({ scale }) => (
             <div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}>
               <div className='compose-form__upload__actions'>
                 <button type='button' className='icon-button' onClick={this.handleUndoClick}><Icon id='times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button>
-                {!!media.get('unattached') && (<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>)}
+                <button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>
               </div>
 
-              {(media.get('description') || '').length === 0 && !!media.get('unattached') && (
+              {(media.get('description') || '').length === 0 && (
                 <div className='compose-form__upload__warning'>
                   <button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button>
                 </div>
diff --git a/app/javascript/mastodon/features/compose/components/upload_button.js b/app/javascript/mastodon/features/compose/components/upload_button.jsx
index 9cb36167a..f2e6ff85c 100644
--- a/app/javascript/mastodon/features/compose/components/upload_button.js
+++ b/app/javascript/mastodon/features/compose/components/upload_button.jsx
@@ -23,8 +23,6 @@ const iconStyle = {
   lineHeight: '27px',
 };
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class UploadButton extends ImmutablePureComponent {
 
   static propTypes = {
@@ -41,15 +39,15 @@ class UploadButton extends ImmutablePureComponent {
     if (e.target.files.length > 0) {
       this.props.onSelectFile(e.target.files);
     }
-  }
+  };
 
   handleClick = () => {
     this.fileElement.click();
-  }
+  };
 
   setRef = (c) => {
     this.fileElement = c;
-  }
+  };
 
   render () {
     const { intl, resetFileKey, unavailable, disabled, acceptContentTypes } = this.props;
@@ -81,3 +79,5 @@ class UploadButton extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(UploadButton));
diff --git a/app/javascript/mastodon/features/compose/components/upload_form.js b/app/javascript/mastodon/features/compose/components/upload_form.jsx
index 9ff2aa0fa..9ff2aa0fa 100644
--- a/app/javascript/mastodon/features/compose/components/upload_form.js
+++ b/app/javascript/mastodon/features/compose/components/upload_form.jsx
diff --git a/app/javascript/mastodon/features/compose/components/upload_progress.js b/app/javascript/mastodon/features/compose/components/upload_progress.jsx
index cabf520fd..cabf520fd 100644
--- a/app/javascript/mastodon/features/compose/components/upload_progress.js
+++ b/app/javascript/mastodon/features/compose/components/upload_progress.jsx
diff --git a/app/javascript/mastodon/features/compose/components/warning.js b/app/javascript/mastodon/features/compose/components/warning.jsx
index 803b7f86a..803b7f86a 100644
--- a/app/javascript/mastodon/features/compose/components/warning.js
+++ b/app/javascript/mastodon/features/compose/components/warning.jsx
diff --git a/app/javascript/mastodon/features/compose/containers/compose_form_container.js b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
index 14cf9230b..2b7642237 100644
--- a/app/javascript/mastodon/features/compose/containers/compose_form_container.js
+++ b/app/javascript/mastodon/features/compose/containers/compose_form_container.js
@@ -26,6 +26,7 @@ const mapStateToProps = state => ({
   isUploading: state.getIn(['compose', 'is_uploading']),
   anyMedia: state.getIn(['compose', 'media_attachments']).size > 0,
   isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
+  lang: state.getIn(['compose', 'language']),
 });
 
 const mapDispatchToProps = (dispatch) => ({
diff --git a/app/javascript/mastodon/features/compose/containers/poll_form_container.js b/app/javascript/mastodon/features/compose/containers/poll_form_container.js
index 1401371d0..479117e91 100644
--- a/app/javascript/mastodon/features/compose/containers/poll_form_container.js
+++ b/app/javascript/mastodon/features/compose/containers/poll_form_container.js
@@ -1,7 +1,10 @@
 import { connect } from 'react-redux';
 import PollForm from '../components/poll_form';
-import { addPollOption, removePollOption, changePollOption, changePollSettings } from '../../../actions/compose';
 import {
+  addPollOption,
+  removePollOption,
+  changePollOption,
+  changePollSettings,
   clearComposeSuggestions,
   fetchComposeSuggestions,
   selectComposeSuggestion,
@@ -10,6 +13,7 @@ import {
 const mapStateToProps = state => ({
   suggestions: state.getIn(['compose', 'suggestions']),
   options: state.getIn(['compose', 'poll', 'options']),
+  lang: state.getIn(['compose', 'language']),
   expiresIn: state.getIn(['compose', 'poll', 'expires_in']),
   isMultiple: state.getIn(['compose', 'poll', 'multiple']),
 });
diff --git a/app/javascript/mastodon/features/compose/containers/search_container.js b/app/javascript/mastodon/features/compose/containers/search_container.js
index 392bd0f56..3ee55fae5 100644
--- a/app/javascript/mastodon/features/compose/containers/search_container.js
+++ b/app/javascript/mastodon/features/compose/containers/search_container.js
@@ -4,12 +4,16 @@ import {
   clearSearch,
   submitSearch,
   showSearch,
-} from '../../../actions/search';
+  openURL,
+  clickSearchResult,
+  forgetSearchResult,
+} from 'mastodon/actions/search';
 import Search from '../components/search';
 
 const mapStateToProps = state => ({
   value: state.getIn(['search', 'value']),
   submitted: state.getIn(['search', 'submitted']),
+  recent: state.getIn(['search', 'recent']),
 });
 
 const mapDispatchToProps = dispatch => ({
@@ -22,14 +26,26 @@ const mapDispatchToProps = dispatch => ({
     dispatch(clearSearch());
   },
 
-  onSubmit () {
-    dispatch(submitSearch());
+  onSubmit (type) {
+    dispatch(submitSearch(type));
   },
 
   onShow () {
     dispatch(showSearch());
   },
 
+  onOpenURL (routerHistory) {
+    dispatch(openURL(routerHistory));
+  },
+
+  onClickSearchResult (q, type) {
+    dispatch(clickSearchResult(q, type));
+  },
+
+  onForgetSearchResult (q) {
+    dispatch(forgetSearchResult(q));
+  },
+
 });
 
 export default connect(mapStateToProps, mapDispatchToProps)(Search);
diff --git a/app/javascript/mastodon/features/compose/containers/sensitive_button_container.js b/app/javascript/mastodon/features/compose/containers/sensitive_button_container.jsx
index 1bcce5731..03f831d28 100644
--- a/app/javascript/mastodon/features/compose/containers/sensitive_button_container.js
+++ b/app/javascript/mastodon/features/compose/containers/sensitive_button_container.jsx
@@ -54,8 +54,6 @@ class SensitiveButton extends React.PureComponent {
             disabled={disabled}
           />
 
-          <span className={classNames('checkbox', { active })} />
-
           <FormattedMessage
             id='compose_form.sensitive.hide'
             defaultMessage='{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}'
diff --git a/app/javascript/mastodon/features/compose/containers/upload_container.js b/app/javascript/mastodon/features/compose/containers/upload_container.js
index 05cd2ecc1..5a8a64931 100644
--- a/app/javascript/mastodon/features/compose/containers/upload_container.js
+++ b/app/javascript/mastodon/features/compose/containers/upload_container.js
@@ -1,7 +1,6 @@
 import { connect } from 'react-redux';
 import Upload from '../components/upload';
-import { undoUploadCompose, initMediaEditModal } from '../../../actions/compose';
-import { submitCompose } from '../../../actions/compose';
+import { undoUploadCompose, initMediaEditModal, submitCompose } from '../../../actions/compose';
 
 const mapStateToProps = (state, { id }) => ({
   media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
diff --git a/app/javascript/mastodon/features/compose/containers/warning_container.js b/app/javascript/mastodon/features/compose/containers/warning_container.jsx
index 571d1d838..e99f5dacd 100644
--- a/app/javascript/mastodon/features/compose/containers/warning_container.js
+++ b/app/javascript/mastodon/features/compose/containers/warning_container.jsx
@@ -3,36 +3,12 @@ import { connect } from 'react-redux';
 import Warning from '../components/warning';
 import PropTypes from 'prop-types';
 import { FormattedMessage } from 'react-intl';
-import { me } from '../../../initial_state';
-
-const buildHashtagRE = () => {
-  try {
-    const HASHTAG_SEPARATORS = '_\\u00b7\\u200c';
-    const ALPHA = '\\p{L}\\p{M}';
-    const WORD = '\\p{L}\\p{M}\\p{N}\\p{Pc}';
-    return new RegExp(
-      '(?:^|[^\\/\\)\\w])#((' +
-      '[' + WORD + '_]' +
-      '[' + WORD + HASHTAG_SEPARATORS + ']*' +
-      '[' + ALPHA + HASHTAG_SEPARATORS + ']' +
-      '[' + WORD + HASHTAG_SEPARATORS +']*' +
-      '[' + WORD + '_]' +
-      ')|(' +
-      '[' + WORD + '_]*' +
-      '[' + ALPHA + ']' +
-      '[' + WORD + '_]*' +
-      '))', 'iu',
-    );
-  } catch {
-    return /(?:^|[^\/\)\w])#(\w*[a-zA-Z·]\w*)/i;
-  }
-};
-
-const APPROX_HASHTAG_RE = buildHashtagRE();
+import { me } from 'mastodon/initial_state';
+import { HASHTAG_PATTERN_REGEX } from 'mastodon/utils/hashtags';
 
 const mapStateToProps = state => ({
   needsLockWarning: state.getIn(['compose', 'privacy']) === 'private' && !state.getIn(['accounts', me, 'locked']),
-  hashtagWarning: state.getIn(['compose', 'privacy']) !== 'public' && APPROX_HASHTAG_RE.test(state.getIn(['compose', 'text'])),
+  hashtagWarning: state.getIn(['compose', 'privacy']) !== 'public' && HASHTAG_PATTERN_REGEX.test(state.getIn(['compose', 'text'])),
   directMessageWarning: state.getIn(['compose', 'privacy']) === 'direct',
 });
 
diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.jsx
index aead7776a..6cba1e9ad 100644
--- a/app/javascript/mastodon/features/compose/index.js
+++ b/app/javascript/mastodon/features/compose/index.jsx
@@ -38,8 +38,6 @@ const mapStateToProps = (state, ownProps) => ({
   showSearch: ownProps.multiColumn ? state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']) : false,
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Compose extends React.PureComponent {
 
   static propTypes = {
@@ -74,15 +72,15 @@ class Compose extends React.PureComponent {
     }));
 
     return false;
-  }
+  };
 
   onFocus = () => {
     this.props.dispatch(changeComposing(true));
-  }
+  };
 
   onBlur = () => {
     this.props.dispatch(changeComposing(false));
-  }
+  };
 
   render () {
     const { multiColumn, showSearch, intl } = this.props;
@@ -148,3 +146,5 @@ class Compose extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Compose));
diff --git a/app/javascript/mastodon/features/compose/util/counter.js b/app/javascript/mastodon/features/compose/util/counter.js
index 5a68bad99..ec2431096 100644
--- a/app/javascript/mastodon/features/compose/util/counter.js
+++ b/app/javascript/mastodon/features/compose/util/counter.js
@@ -5,5 +5,5 @@ const urlPlaceholder = '$2xxxxxxxxxxxxxxxxxxxxxxx';
 export function countableText(inputText) {
   return inputText
     .replace(urlRegex, urlPlaceholder)
-    .replace(/(^|[^\/\w])@(([a-z0-9_]+)@[a-z0-9\.\-]+[a-z0-9]+)/ig, '$1@$3');
+    .replace(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, '$1@$3');
 }
diff --git a/app/javascript/mastodon/features/direct_timeline/components/conversation.js b/app/javascript/mastodon/features/direct_timeline/components/conversation.jsx
index 4a770970d..d0dbffe65 100644
--- a/app/javascript/mastodon/features/direct_timeline/components/conversation.js
+++ b/app/javascript/mastodon/features/direct_timeline/components/conversation.jsx
@@ -24,7 +24,6 @@ const messages = defineMessages({
   unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
 });
 
-export default @injectIntl
 class Conversation extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -55,7 +54,7 @@ class Conversation extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -68,7 +67,7 @@ class Conversation extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   handleClick = () => {
     if (!this.context.router) {
@@ -82,35 +81,35 @@ class Conversation extends ImmutablePureComponent {
     }
 
     this.context.router.history.push(`/@${lastStatus.getIn(['account', 'acct'])}/${lastStatus.get('id')}`);
-  }
+  };
 
   handleMarkAsRead = () => {
     this.props.markRead();
-  }
+  };
 
   handleReply = () => {
     this.props.reply(this.props.lastStatus, this.context.router.history);
-  }
+  };
 
   handleDelete = () => {
     this.props.delete();
-  }
+  };
 
   handleHotkeyMoveUp = () => {
     this.props.onMoveUp(this.props.conversationId);
-  }
+  };
 
   handleHotkeyMoveDown = () => {
     this.props.onMoveDown(this.props.conversationId);
-  }
+  };
 
   handleConversationMute = () => {
     this.props.onMute(this.props.lastStatus);
-  }
+  };
 
   handleShowMore = () => {
     this.props.onToggleHidden(this.props.lastStatus);
-  }
+  };
 
   render () {
     const { accounts, lastStatus, unread, scrollKey, intl } = this.props;
@@ -145,7 +144,7 @@ class Conversation extends ImmutablePureComponent {
 
     return (
       <HotKeys handlers={handlers}>
-        <div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex='0'>
+        <div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex={0}>
           <div className='conversation__avatar' onClick={this.handleClick} role='presentation'>
             <AvatarComposite accounts={accounts} size={48} />
           </div>
@@ -198,3 +197,5 @@ class Conversation extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Conversation);
diff --git a/app/javascript/mastodon/features/direct_timeline/components/conversations_list.js b/app/javascript/mastodon/features/direct_timeline/components/conversations_list.jsx
index fd1df7256..04c404e1d 100644
--- a/app/javascript/mastodon/features/direct_timeline/components/conversations_list.js
+++ b/app/javascript/mastodon/features/direct_timeline/components/conversations_list.jsx
@@ -16,17 +16,17 @@ export default class ConversationsList extends ImmutablePureComponent {
     onLoadMore: PropTypes.func,
   };
 
-  getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id)
+  getCurrentIndex = id => this.props.conversations.findIndex(x => x.get('id') === id);
 
   handleMoveUp = id => {
     const elementIndex = this.getCurrentIndex(id) - 1;
     this._selectChild(elementIndex, true);
-  }
+  };
 
   handleMoveDown = id => {
     const elementIndex = this.getCurrentIndex(id) + 1;
     this._selectChild(elementIndex, false);
-  }
+  };
 
   _selectChild (index, align_top) {
     const container = this.node.node;
@@ -44,7 +44,7 @@ export default class ConversationsList extends ImmutablePureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   handleLoadOlder = debounce(() => {
     const last = this.props.conversations.last();
@@ -52,13 +52,13 @@ export default class ConversationsList extends ImmutablePureComponent {
     if (last && last.get('last_status')) {
       this.props.onLoadMore(last.get('last_status'));
     }
-  }, 300, { leading: true })
+  }, 300, { leading: true });
 
   render () {
-    const { conversations, onLoadMore, ...other } = this.props;
+    const { conversations, isLoading, onLoadMore, ...other } = this.props;
 
     return (
-      <ScrollableList {...other} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
+      <ScrollableList {...other} isLoading={isLoading} showLoading={isLoading && conversations.isEmpty()} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
         {conversations.map(item => (
           <ConversationContainer
             key={item.get('id')}
diff --git a/app/javascript/mastodon/features/direct_timeline/index.js b/app/javascript/mastodon/features/direct_timeline/index.jsx
index 8dcc43e28..fc45ea69a 100644
--- a/app/javascript/mastodon/features/direct_timeline/index.js
+++ b/app/javascript/mastodon/features/direct_timeline/index.jsx
@@ -11,11 +11,9 @@ import ColumnHeader from 'mastodon/components/column_header';
 import ConversationsListContainer from './containers/conversations_list_container';
 
 const messages = defineMessages({
-  title: { id: 'column.direct', defaultMessage: 'Direct messages' },
+  title: { id: 'column.direct', defaultMessage: 'Private mentions' },
 });
 
-export default @connect()
-@injectIntl
 class DirectTimeline extends React.PureComponent {
 
   static propTypes = {
@@ -34,16 +32,16 @@ class DirectTimeline extends React.PureComponent {
     } else {
       dispatch(addColumn('DIRECT', {}));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   componentDidMount () {
     const { dispatch } = this.props;
@@ -64,11 +62,11 @@ class DirectTimeline extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = maxId => {
     this.props.dispatch(expandConversations({ maxId }));
-  }
+  };
 
   render () {
     const { intl, hasUnread, columnId, multiColumn } = this.props;
@@ -91,9 +89,11 @@ class DirectTimeline extends React.PureComponent {
           trackScroll={!pinned}
           scrollKey={`direct_timeline-${columnId}`}
           timelineId='direct'
+          bindToDocument={!multiColumn}
           onLoadMore={this.handleLoadMore}
           prepend={<div className='follow_requests-unlocked_explanation'><span><FormattedMessage id='compose_form.encryption_warning' defaultMessage='Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.' /> <a href='/terms' target='_blank'><FormattedMessage id='compose_form.direct_message_warning_learn_more' defaultMessage='Learn more' /></a></span></div>}
-          emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any direct messages yet. When you send or receive one, it will show up here." />}
+          alwaysPrepend
+          emptyMessage={<FormattedMessage id='empty_column.direct' defaultMessage="You don't have any private mentions yet. When you send or receive one, it will show up here." />}
         />
 
         <Helmet>
@@ -105,3 +105,5 @@ class DirectTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect()(injectIntl(DirectTimeline));
diff --git a/app/javascript/mastodon/features/directory/components/account_card.js b/app/javascript/mastodon/features/directory/components/account_card.jsx
index 977f0c32c..1cee3a4e4 100644
--- a/app/javascript/mastodon/features/directory/components/account_card.js
+++ b/app/javascript/mastodon/features/directory/components/account_card.jsx
@@ -91,9 +91,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
 
 });
 
-export default
-@injectIntl
-@connect(makeMapStateToProps, mapDispatchToProps)
 class AccountCard extends ImmutablePureComponent {
 
   static propTypes = {
@@ -115,7 +112,7 @@ class AccountCard extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -128,7 +125,7 @@ class AccountCard extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   handleFollow = () => {
     this.props.onFollow(this.props.account);
@@ -140,11 +137,11 @@ class AccountCard extends ImmutablePureComponent {
 
   handleMute = () => {
     this.props.onMute(this.props.account);
-  }
+  };
 
   handleEditProfile = () => {
     window.open('/settings/profile', '_blank');
-  }
+  };
 
   render() {
     const { account, intl } = this.props;
@@ -233,3 +230,5 @@ class AccountCard extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(AccountCard));
diff --git a/app/javascript/mastodon/features/directory/index.js b/app/javascript/mastodon/features/directory/index.jsx
index b45faa049..afaf39b77 100644
--- a/app/javascript/mastodon/features/directory/index.js
+++ b/app/javascript/mastodon/features/directory/index.jsx
@@ -29,8 +29,6 @@ const mapStateToProps = state => ({
   domain: state.getIn(['meta', 'domain']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Directory extends React.PureComponent {
 
   static contextTypes = {
@@ -64,7 +62,7 @@ class Directory extends React.PureComponent {
     } else {
       dispatch(addColumn('DIRECTORY', this.getParams(this.props, this.state)));
     }
-  }
+  };
 
   getParams = (props, state) => ({
     order: state.order === null ? (props.params.order || 'active') : state.order,
@@ -74,11 +72,11 @@ class Directory extends React.PureComponent {
   handleMove = dir => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   componentDidMount () {
     const { dispatch } = this.props;
@@ -97,7 +95,7 @@ class Directory extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleChangeOrder = e => {
     const { dispatch, columnId } = this.props;
@@ -107,7 +105,7 @@ class Directory extends React.PureComponent {
     } else {
       this.setState({ order: e.target.value });
     }
-  }
+  };
 
   handleChangeLocal = e => {
     const { dispatch, columnId } = this.props;
@@ -117,12 +115,12 @@ class Directory extends React.PureComponent {
     } else {
       this.setState({ local: e.target.value === '1' });
     }
-  }
+  };
 
   handleLoadMore = () => {
     const { dispatch } = this.props;
     dispatch(expandDirectory(this.getParams(this.props, this.state)));
-  }
+  };
 
   render () {
     const { isLoading, accountIds, intl, columnId, multiColumn, domain } = this.props;
@@ -176,3 +174,5 @@ class Directory extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Directory));
diff --git a/app/javascript/mastodon/features/domain_blocks/index.js b/app/javascript/mastodon/features/domain_blocks/index.jsx
index 43b275c2d..0c22aa239 100644
--- a/app/javascript/mastodon/features/domain_blocks/index.js
+++ b/app/javascript/mastodon/features/domain_blocks/index.jsx
@@ -23,8 +23,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['domain_lists', 'blocks', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Blocks extends ImmutablePureComponent {
 
   static propTypes = {
@@ -81,3 +79,5 @@ class Blocks extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Blocks));
diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js
index bc3dd8c60..6ae406624 100644
--- a/app/javascript/mastodon/features/emoji/emoji.js
+++ b/app/javascript/mastodon/features/emoji/emoji.js
@@ -50,7 +50,7 @@ const emojifyTextNode = (node, customEmojis) => {
         if (shortname in customEmojis) {
           const filename = autoPlayGif ? customEmojis[shortname].url : customEmojis[shortname].static_url;
           replacement = document.createElement('img');
-          replacement.setAttribute('draggable', false);
+          replacement.setAttribute('draggable', 'false');
           replacement.setAttribute('class', 'emojione custom-emoji');
           replacement.setAttribute('alt', shortname);
           replacement.setAttribute('title', shortname);
@@ -65,7 +65,7 @@ const emojifyTextNode = (node, customEmojis) => {
       const { filename, shortCode } = unicodeMapping[match];
       const title = shortCode ? `:${shortCode}:` : '';
       replacement = document.createElement('img');
-      replacement.setAttribute('draggable', false);
+      replacement.setAttribute('draggable', 'false');
       replacement.setAttribute('class', 'emojione');
       replacement.setAttribute('alt', match);
       replacement.setAttribute('title', title);
diff --git a/app/javascript/mastodon/features/emoji/emoji_mart_data_light.js b/app/javascript/mastodon/features/emoji/emoji_mart_data_light.js
index 45086fc4c..49813537d 100644
--- a/app/javascript/mastodon/features/emoji/emoji_mart_data_light.js
+++ b/app/javascript/mastodon/features/emoji/emoji_mart_data_light.js
@@ -9,7 +9,7 @@ const emojis = {};
 // decompress
 Object.keys(shortCodesToEmojiData).forEach((shortCode) => {
   let [
-    filenameData, // eslint-disable-line no-unused-vars
+    filenameData, // eslint-disable-line @typescript-eslint/no-unused-vars
     searchData,
   ] = shortCodesToEmojiData[shortCode];
   let [
diff --git a/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.js b/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.js
index 918684c31..1a38fde23 100644
--- a/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.js
+++ b/app/javascript/mastodon/features/emoji/emoji_unicode_mapping_light.js
@@ -4,9 +4,9 @@
 
 const [
   shortCodesToEmojiData,
-  skins, // eslint-disable-line no-unused-vars
-  categories, // eslint-disable-line no-unused-vars
-  short_names, // eslint-disable-line no-unused-vars
+  skins, // eslint-disable-line @typescript-eslint/no-unused-vars
+  categories, // eslint-disable-line @typescript-eslint/no-unused-vars
+  short_names, // eslint-disable-line @typescript-eslint/no-unused-vars
   emojisWithoutShortCodes,
 ] = require('./emoji_compressed');
 const { unicodeToFilename } = require('./unicode_to_filename');
diff --git a/app/javascript/mastodon/features/emoji/emoji_utils.js b/app/javascript/mastodon/features/emoji/emoji_utils.js
index dbf725c1f..be793526d 100644
--- a/app/javascript/mastodon/features/emoji/emoji_utils.js
+++ b/app/javascript/mastodon/features/emoji/emoji_utils.js
@@ -73,7 +73,7 @@ const stringFromCodePoint = _String.fromCodePoint || function () {
 
 const _JSON = JSON;
 
-const COLONS_REGEX = /^(?:\:([^\:]+)\:)(?:\:skin-tone-(\d)\:)?$/;
+const COLONS_REGEX = /^(?::([^:]+):)(?::skin-tone-(\d):)?$/;
 const SKINS = [
   '1F3FA', '1F3FB', '1F3FC',
   '1F3FD', '1F3FE', '1F3FF',
@@ -135,19 +135,19 @@ function getData(emoji, skin, set) {
       }
     }
 
-    if (data.short_names.hasOwnProperty(emoji)) {
+    if (Object.prototype.hasOwnProperty.call(data.short_names, emoji)) {
       emoji = data.short_names[emoji];
     }
 
-    if (data.emojis.hasOwnProperty(emoji)) {
+    if (Object.prototype.hasOwnProperty.call(data.emojis, emoji)) {
       emojiData = data.emojis[emoji];
     }
   } else if (emoji.id) {
-    if (data.short_names.hasOwnProperty(emoji.id)) {
+    if (Object.prototype.hasOwnProperty.call(data.short_names, emoji.id)) {
       emoji.id = data.short_names[emoji.id];
     }
 
-    if (data.emojis.hasOwnProperty(emoji.id)) {
+    if (Object.prototype.hasOwnProperty.call(data.emojis, emoji.id)) {
       emojiData = data.emojis[emoji.id];
       skin = skin || emoji.skin;
     }
@@ -216,7 +216,7 @@ function deepMerge(a, b) {
     let originalValue = a[key],
       value = originalValue;
 
-    if (b.hasOwnProperty(key)) {
+    if (Object.prototype.hasOwnProperty.call(b, key)) {
       value = b[key];
     }
 
diff --git a/app/javascript/mastodon/features/explore/components/story.js b/app/javascript/mastodon/features/explore/components/story.jsx
index 563128029..563128029 100644
--- a/app/javascript/mastodon/features/explore/components/story.js
+++ b/app/javascript/mastodon/features/explore/components/story.jsx
diff --git a/app/javascript/mastodon/features/explore/index.js b/app/javascript/mastodon/features/explore/index.jsx
index 1ae249f45..939550d83 100644
--- a/app/javascript/mastodon/features/explore/index.js
+++ b/app/javascript/mastodon/features/explore/index.jsx
@@ -24,8 +24,6 @@ const mapStateToProps = state => ({
   isSearching: state.getIn(['search', 'submitted']) || !showTrends,
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Explore extends React.PureComponent {
 
   static contextTypes = {
@@ -41,11 +39,11 @@ class Explore extends React.PureComponent {
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   render() {
     const { intl, multiColumn, isSearching } = this.props;
@@ -90,7 +88,9 @@ class Explore extends React.PureComponent {
                 <Route path='/explore/tags' component={Tags} />
                 <Route path='/explore/links' component={Links} />
                 <Route path='/explore/suggestions' component={Suggestions} />
-                <Route exact path={['/explore', '/explore/posts', '/search']} component={Statuses} componentParams={{ multiColumn }} />
+                <Route exact path={['/explore', '/explore/posts', '/search']}>
+                  <Statuses multiColumn={multiColumn} />
+                </Route>
               </Switch>
 
               <Helmet>
@@ -105,3 +105,5 @@ class Explore extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Explore));
diff --git a/app/javascript/mastodon/features/explore/links.js b/app/javascript/mastodon/features/explore/links.jsx
index b47fc8fcf..193739916 100644
--- a/app/javascript/mastodon/features/explore/links.js
+++ b/app/javascript/mastodon/features/explore/links.jsx
@@ -13,7 +13,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['trends', 'links', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Links extends React.PureComponent {
 
   static propTypes = {
@@ -68,3 +67,5 @@ class Links extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Links);
diff --git a/app/javascript/mastodon/features/explore/results.js b/app/javascript/mastodon/features/explore/results.jsx
index b2f6c72b7..9725cf35c 100644
--- a/app/javascript/mastodon/features/explore/results.js
+++ b/app/javascript/mastodon/features/explore/results.jsx
@@ -42,8 +42,6 @@ const renderStatuses = (results, onLoadMore) => appendLoadMore('statuses', resul
   <Status key={`status-${item}`} id={item} />
 )), onLoadMore);
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Results extends React.PureComponent {
 
   static propTypes = {
@@ -107,7 +105,7 @@ class Results extends React.PureComponent {
       <React.Fragment>
         <div className='account__section-headline'>
           <button onClick={this.handleSelectAll} className={type === 'all' && 'active'}><FormattedMessage id='search_results.all' defaultMessage='All' /></button>
-          <button onClick={this.handleSelectAccounts} className={type === 'accounts' && 'active'}><FormattedMessage id='search_results.accounts' defaultMessage='People' /></button>
+          <button onClick={this.handleSelectAccounts} className={type === 'accounts' && 'active'}><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></button>
           <button onClick={this.handleSelectHashtags} className={type === 'hashtags' && 'active'}><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></button>
           <button onClick={this.handleSelectStatuses} className={type === 'statuses' && 'active'}><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></button>
         </div>
@@ -124,3 +122,5 @@ class Results extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Results));
diff --git a/app/javascript/mastodon/features/explore/statuses.js b/app/javascript/mastodon/features/explore/statuses.jsx
index 791f11b9f..a98a6d046 100644
--- a/app/javascript/mastodon/features/explore/statuses.js
+++ b/app/javascript/mastodon/features/explore/statuses.jsx
@@ -14,7 +14,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'trending', 'next']),
 });
 
-export default @connect(mapStateToProps)
 class Statuses extends React.PureComponent {
 
   static propTypes = {
@@ -33,7 +32,7 @@ class Statuses extends React.PureComponent {
   handleLoadMore = debounce(() => {
     const { dispatch } = this.props;
     dispatch(expandTrendingStatuses());
-  }, 300, { leading: true })
+  }, 300, { leading: true });
 
   render () {
     const { isLoading, hasMore, statusIds, multiColumn } = this.props;
@@ -62,3 +61,5 @@ class Statuses extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Statuses);
diff --git a/app/javascript/mastodon/features/explore/suggestions.js b/app/javascript/mastodon/features/explore/suggestions.jsx
index e6ad09974..53eb7ba4b 100644
--- a/app/javascript/mastodon/features/explore/suggestions.js
+++ b/app/javascript/mastodon/features/explore/suggestions.jsx
@@ -12,7 +12,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['suggestions', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Suggestions extends React.PureComponent {
 
   static propTypes = {
@@ -49,3 +48,5 @@ class Suggestions extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Suggestions);
diff --git a/app/javascript/mastodon/features/explore/tags.js b/app/javascript/mastodon/features/explore/tags.jsx
index 258dc392f..3ba813c3f 100644
--- a/app/javascript/mastodon/features/explore/tags.js
+++ b/app/javascript/mastodon/features/explore/tags.jsx
@@ -13,7 +13,6 @@ const mapStateToProps = state => ({
   isLoadingHashtags: state.getIn(['trends', 'tags', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Tags extends React.PureComponent {
 
   static propTypes = {
@@ -60,3 +59,5 @@ class Tags extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Tags);
diff --git a/app/javascript/mastodon/features/favourited_statuses/index.js b/app/javascript/mastodon/features/favourited_statuses/index.jsx
index 3741f68f6..2cbf00291 100644
--- a/app/javascript/mastodon/features/favourited_statuses/index.js
+++ b/app/javascript/mastodon/features/favourited_statuses/index.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Favourites extends ImmutablePureComponent {
 
   static propTypes = {
@@ -48,24 +46,24 @@ class Favourites extends ImmutablePureComponent {
     } else {
       dispatch(addColumn('FAVOURITES', {}));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = debounce(() => {
     this.props.dispatch(expandFavouritedStatuses());
-  }, 300, { leading: true })
+  }, 300, { leading: true });
 
   render () {
     const { intl, statusIds, columnId, multiColumn, hasMore, isLoading } = this.props;
@@ -106,3 +104,5 @@ class Favourites extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Favourites));
diff --git a/app/javascript/mastodon/features/favourites/index.js b/app/javascript/mastodon/features/favourites/index.jsx
index 66ec6a31b..b1e81debd 100644
--- a/app/javascript/mastodon/features/favourites/index.js
+++ b/app/javascript/mastodon/features/favourites/index.jsx
@@ -21,8 +21,6 @@ const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'favourited_by', props.params.statusId]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Favourites extends ImmutablePureComponent {
 
   static propTypes = {
@@ -47,7 +45,7 @@ class Favourites extends ImmutablePureComponent {
 
   handleRefresh = () => {
     this.props.dispatch(fetchFavourites(this.props.params.statusId));
-  }
+  };
 
   render () {
     const { intl, accountIds, multiColumn } = this.props;
@@ -90,3 +88,5 @@ class Favourites extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Favourites));
diff --git a/app/javascript/mastodon/features/filters/added_to_filter.js b/app/javascript/mastodon/features/filters/added_to_filter.jsx
index 3785eb3c5..d935d96e2 100644
--- a/app/javascript/mastodon/features/filters/added_to_filter.js
+++ b/app/javascript/mastodon/features/filters/added_to_filter.jsx
@@ -10,7 +10,6 @@ const mapStateToProps = (state, { filterId }) => ({
   filter: state.getIn(['filters', filterId]),
 });
 
-export default @connect(mapStateToProps)
 class AddedToFilter extends React.PureComponent {
 
   static propTypes = {
@@ -100,3 +99,5 @@ class AddedToFilter extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(AddedToFilter);
diff --git a/app/javascript/mastodon/features/filters/select_filter.js b/app/javascript/mastodon/features/filters/select_filter.jsx
index b68a5de6c..5778eec8e 100644
--- a/app/javascript/mastodon/features/filters/select_filter.js
+++ b/app/javascript/mastodon/features/filters/select_filter.jsx
@@ -22,8 +22,6 @@ const mapStateToProps = (state, { contextType }) => ({
   ]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class SelectFilter extends React.PureComponent {
 
   static propTypes = {
@@ -67,15 +65,15 @@ class SelectFilter extends React.PureComponent {
     }
 
     return (
-      <div key={filter[0]} role='button' tabIndex='0' data-index={filter[0]} className='language-dropdown__dropdown__results__item' onClick={this.handleItemClick} onKeyDown={this.handleKeyDown}>
+      <div key={filter[0]} role='button' tabIndex={0} data-index={filter[0]} className='language-dropdown__dropdown__results__item' onClick={this.handleItemClick} onKeyDown={this.handleKeyDown}>
         <span className='language-dropdown__dropdown__results__item__native-name'>{filter[1]}</span> {warning}
       </div>
     );
-  }
+  };
 
   renderCreateNew (name) {
     return (
-      <div key='add-new-filter' role='button' tabIndex='0' className='language-dropdown__dropdown__results__item' onClick={this.handleNewFilterClick} onKeyDown={this.handleKeyDown}>
+      <div key='add-new-filter' role='button' tabIndex={0} className='language-dropdown__dropdown__results__item' onClick={this.handleNewFilterClick} onKeyDown={this.handleKeyDown}>
         <Icon id='plus' fixedWidth /> <FormattedMessage id='filter_modal.select_filter.prompt_new' defaultMessage='New category: {name}' values={{ name }} />
       </div>
     );
@@ -83,11 +81,11 @@ class SelectFilter extends React.PureComponent {
 
   handleSearchChange = ({ target }) => {
     this.setState({ searchValue: target.value });
-  }
+  };
 
   setListRef = c => {
     this.listNode = c;
-  }
+  };
 
   handleKeyDown = e => {
     const index = Array.from(this.listNode.childNodes).findIndex(node => node === e.currentTarget);
@@ -125,7 +123,7 @@ class SelectFilter extends React.PureComponent {
       e.preventDefault();
       e.stopPropagation();
     }
-  }
+  };
 
   handleSearchKeyDown = e => {
     let element = null;
@@ -143,11 +141,11 @@ class SelectFilter extends React.PureComponent {
 
       break;
     }
-  }
+  };
 
   handleClear = () => {
     this.setState({ searchValue: '' });
-  }
+  };
 
   handleItemClick = e => {
     const value = e.currentTarget.getAttribute('data-index');
@@ -155,7 +153,7 @@ class SelectFilter extends React.PureComponent {
     e.preventDefault();
 
     this.props.onSelectFilter(value);
-  }
+  };
 
   handleNewFilterClick = e => {
     e.preventDefault();
@@ -190,3 +188,5 @@ class SelectFilter extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(SelectFilter));
diff --git a/app/javascript/mastodon/features/follow_recommendations/components/account.js b/app/javascript/mastodon/features/follow_recommendations/components/account.jsx
index 14f4e7e1b..9cb26fe64 100644
--- a/app/javascript/mastodon/features/follow_recommendations/components/account.js
+++ b/app/javascript/mastodon/features/follow_recommendations/components/account.jsx
@@ -27,13 +27,11 @@ const makeMapStateToProps = () => {
 };
 
 const getFirstSentence = str => {
-  const arr = str.split(/(([\.\?!]+\s)|[.。?!\n•])/);
+  const arr = str.split(/(([.?!]+\s)|[.。?!\n•])/);
 
   return arr[0];
 };
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -50,7 +48,7 @@ class Account extends ImmutablePureComponent {
     } else {
       dispatch(followAccount(account.get('id')));
     }
-  }
+  };
 
   render () {
     const { account, intl } = this.props;
@@ -83,3 +81,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(Account));
diff --git a/app/javascript/mastodon/features/follow_recommendations/index.js b/app/javascript/mastodon/features/follow_recommendations/index.jsx
index 5f7baa64c..7ba34b51f 100644
--- a/app/javascript/mastodon/features/follow_recommendations/index.js
+++ b/app/javascript/mastodon/features/follow_recommendations/index.jsx
@@ -19,7 +19,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['suggestions', 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class FollowRecommendations extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -69,7 +68,7 @@ class FollowRecommendations extends ImmutablePureComponent {
     }));
 
     router.history.push('/home');
-  }
+  };
 
   render () {
     const { suggestions, isLoading } = this.props;
@@ -114,3 +113,5 @@ class FollowRecommendations extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(FollowRecommendations);
diff --git a/app/javascript/mastodon/features/follow_requests/components/account_authorize.js b/app/javascript/mastodon/features/follow_requests/components/account_authorize.jsx
index d41f331e5..0bd45592c 100644
--- a/app/javascript/mastodon/features/follow_requests/components/account_authorize.js
+++ b/app/javascript/mastodon/features/follow_requests/components/account_authorize.jsx
@@ -13,7 +13,6 @@ const messages = defineMessages({
   reject: { id: 'follow_request.reject', defaultMessage: 'Reject' },
 });
 
-export default @injectIntl
 class AccountAuthorize extends ImmutablePureComponent {
 
   static propTypes = {
@@ -47,3 +46,5 @@ class AccountAuthorize extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(AccountAuthorize);
diff --git a/app/javascript/mastodon/features/follow_requests/index.js b/app/javascript/mastodon/features/follow_requests/index.jsx
index d16aa7737..a8875bbd3 100644
--- a/app/javascript/mastodon/features/follow_requests/index.js
+++ b/app/javascript/mastodon/features/follow_requests/index.jsx
@@ -5,7 +5,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { debounce } from 'lodash';
-import LoadingIndicator from '../../components/loading_indicator';
 import Column from '../ui/components/column';
 import ColumnBackButtonSlim from '../../components/column_back_button_slim';
 import AccountAuthorizeContainer from './containers/account_authorize_container';
@@ -26,8 +25,6 @@ const mapStateToProps = state => ({
   domain: state.getIn(['meta', 'domain']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class FollowRequests extends ImmutablePureComponent {
 
   static propTypes = {
@@ -53,16 +50,8 @@ class FollowRequests extends ImmutablePureComponent {
   render () {
     const { intl, accountIds, hasMore, multiColumn, locked, domain, isLoading } = this.props;
 
-    if (!accountIds) {
-      return (
-        <Column>
-          <LoadingIndicator />
-        </Column>
-      );
-    }
-
     const emptyMessage = <FormattedMessage id='empty_column.follow_requests' defaultMessage="You don't have any follow requests yet. When you receive one, it will show up here." />;
-    const unlockedPrependMessage = locked ? null : (
+    const unlockedPrependMessage = !locked && accountIds.size > 0 && (
       <div className='follow_requests-unlocked_explanation'>
         <FormattedMessage
           id='follow_requests.unlocked_explanation'
@@ -80,6 +69,7 @@ class FollowRequests extends ImmutablePureComponent {
           onLoadMore={this.handleLoadMore}
           hasMore={hasMore}
           isLoading={isLoading}
+          showLoading={isLoading && accountIds.size === 0}
           emptyMessage={emptyMessage}
           bindToDocument={!multiColumn}
           prepend={unlockedPrependMessage}
@@ -97,3 +87,5 @@ class FollowRequests extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(FollowRequests));
diff --git a/app/javascript/mastodon/features/followed_tags/index.jsx b/app/javascript/mastodon/features/followed_tags/index.jsx
new file mode 100644
index 000000000..7c53542c2
--- /dev/null
+++ b/app/javascript/mastodon/features/followed_tags/index.jsx
@@ -0,0 +1,89 @@
+import { debounce } from 'lodash';
+import PropTypes from 'prop-types';
+import React from 'react';
+import ImmutablePureComponent from 'react-immutable-pure-component';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
+import { connect } from 'react-redux';
+import ColumnHeader from 'mastodon/components/column_header';
+import ScrollableList from 'mastodon/components/scrollable_list';
+import Column from 'mastodon/features/ui/components/column';
+import { Helmet } from 'react-helmet';
+import Hashtag from 'mastodon/components/hashtag';
+import { expandFollowedHashtags, fetchFollowedHashtags } from 'mastodon/actions/tags';
+
+const messages = defineMessages({
+  heading: { id: 'followed_tags', defaultMessage: 'Followed hashtags' },
+});
+
+const mapStateToProps = state => ({
+  hashtags: state.getIn(['followed_tags', 'items']),
+  isLoading: state.getIn(['followed_tags', 'isLoading'], true),
+  hasMore: !!state.getIn(['followed_tags', 'next']),
+});
+
+class FollowedTags extends ImmutablePureComponent {
+
+  static propTypes = {
+    params: PropTypes.object.isRequired,
+    dispatch: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+    hashtags: ImmutablePropTypes.list,
+    isLoading: PropTypes.bool,
+    hasMore: PropTypes.bool,
+    multiColumn: PropTypes.bool,
+  };
+
+  componentDidMount() {
+    this.props.dispatch(fetchFollowedHashtags());
+  }
+
+  handleLoadMore = debounce(() => {
+    this.props.dispatch(expandFollowedHashtags());
+  }, 300, { leading: true });
+
+  render () {
+    const { intl, hashtags, isLoading, hasMore, multiColumn } = this.props;
+
+    const emptyMessage = <FormattedMessage id='empty_column.followed_tags' defaultMessage='You have not followed any hashtags yet. When you do, they will show up here.' />;
+
+    return (
+      <Column bindToDocument={!multiColumn}>
+        <ColumnHeader
+          icon='hashtag'
+          title={intl.formatMessage(messages.heading)}
+          showBackButton
+          multiColumn={multiColumn}
+        />
+
+        <ScrollableList
+          scrollKey='followed_tags'
+          emptyMessage={emptyMessage}
+          hasMore={hasMore}
+          isLoading={isLoading}
+          onLoadMore={this.handleLoadMore}
+          bindToDocument={!multiColumn}
+        >
+          {hashtags.map((hashtag) => (
+            <Hashtag
+              key={hashtag.get('name')}
+              name={hashtag.get('name')}
+              to={`/tags/${hashtag.get('name')}`}
+              withGraph={false}
+              // Taken from ImmutableHashtag. Should maybe refactor ImmutableHashtag to accept more options?
+              people={hashtag.getIn(['history', 0, 'accounts']) * 1 + hashtag.getIn(['history', 1, 'accounts']) * 1}
+              history={hashtag.get('history').reverse().map((day) => day.get('uses')).toArray()}
+            />
+          ))}
+        </ScrollableList>
+
+        <Helmet>
+          <meta name='robots' content='noindex' />
+        </Helmet>
+      </Column>
+    );
+  }
+
+}
+
+export default connect(mapStateToProps)(injectIntl(FollowedTags));
diff --git a/app/javascript/mastodon/features/followers/index.js b/app/javascript/mastodon/features/followers/index.jsx
index 277eb702f..fe86ebb3b 100644
--- a/app/javascript/mastodon/features/followers/index.js
+++ b/app/javascript/mastodon/features/followers/index.jsx
@@ -54,7 +54,6 @@ RemoteHint.propTypes = {
   url: PropTypes.string.isRequired,
 };
 
-export default @connect(mapStateToProps)
 class Followers extends ImmutablePureComponent {
 
   static propTypes = {
@@ -168,3 +167,5 @@ class Followers extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Followers);
diff --git a/app/javascript/mastodon/features/following/index.js b/app/javascript/mastodon/features/following/index.jsx
index e23d9b35c..8095d73a3 100644
--- a/app/javascript/mastodon/features/following/index.js
+++ b/app/javascript/mastodon/features/following/index.jsx
@@ -54,7 +54,6 @@ RemoteHint.propTypes = {
   url: PropTypes.string.isRequired,
 };
 
-export default @connect(mapStateToProps)
 class Following extends ImmutablePureComponent {
 
   static propTypes = {
@@ -168,3 +167,5 @@ class Following extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Following);
diff --git a/app/javascript/mastodon/features/generic_not_found/index.js b/app/javascript/mastodon/features/generic_not_found/index.jsx
index 41cd61a5f..41cd61a5f 100644
--- a/app/javascript/mastodon/features/generic_not_found/index.js
+++ b/app/javascript/mastodon/features/generic_not_found/index.jsx
diff --git a/app/javascript/mastodon/features/getting_started/components/announcements.js b/app/javascript/mastodon/features/getting_started/components/announcements.jsx
index 24db8cede..5f993d2c5 100644
--- a/app/javascript/mastodon/features/getting_started/components/announcements.js
+++ b/app/javascript/mastodon/features/getting_started/components/announcements.jsx
@@ -6,9 +6,8 @@ import PropTypes from 'prop-types';
 import IconButton from 'mastodon/components/icon_button';
 import Icon from 'mastodon/components/icon';
 import { defineMessages, injectIntl, FormattedMessage, FormattedDate } from 'react-intl';
-import { autoPlayGif, reduceMotion, disableSwiping } from 'mastodon/initial_state';
+import { autoPlayGif, reduceMotion, disableSwiping, mascot } from 'mastodon/initial_state';
 import elephantUIPlane from 'mastodon/../images/elephant_ui_plane.svg';
-import { mascot } from 'mastodon/initial_state';
 import unicodeMapping from 'mastodon/features/emoji/emoji_unicode_mapping_light';
 import classNames from 'classnames';
 import EmojiPickerDropdown from 'mastodon/features/compose/containers/emoji_picker_dropdown_container';
@@ -35,7 +34,7 @@ class Content extends ImmutablePureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   componentDidMount () {
     this._updateLinks();
@@ -89,7 +88,7 @@ class Content extends ImmutablePureComponent {
       e.preventDefault();
       this.context.router.history.push(`/@${mention.get('acct')}`);
     }
-  }
+  };
 
   onHashtagClick = (hashtag, e) => {
     hashtag = hashtag.replace(/^#/, '');
@@ -98,14 +97,14 @@ class Content extends ImmutablePureComponent {
       e.preventDefault();
       this.context.router.history.push(`/tags/${hashtag}`);
     }
-  }
+  };
 
   onStatusClick = (status, e) => {
     if (this.context.router && e.button === 0 && !(e.ctrlKey || e.metaKey)) {
       e.preventDefault();
       this.context.router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
     }
-  }
+  };
 
   handleMouseEnter = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -118,7 +117,7 @@ class Content extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-original');
     }
-  }
+  };
 
   handleMouseLeave = ({ currentTarget }) => {
     if (autoPlayGif) {
@@ -131,7 +130,7 @@ class Content extends ImmutablePureComponent {
       let emoji = emojis[i];
       emoji.src = emoji.getAttribute('data-static');
     }
-  }
+  };
 
   render () {
     const { announcement } = this.props;
@@ -216,11 +215,11 @@ class Reaction extends ImmutablePureComponent {
     } else {
       addReaction(announcementId, reaction.get('name'));
     }
-  }
+  };
 
-  handleMouseEnter = () => this.setState({ hovered: true })
+  handleMouseEnter = () => this.setState({ hovered: true });
 
-  handleMouseLeave = () => this.setState({ hovered: false })
+  handleMouseLeave = () => this.setState({ hovered: false });
 
   render () {
     const { reaction } = this.props;
@@ -254,7 +253,7 @@ class ReactionsBar extends ImmutablePureComponent {
   handleEmojiPick = data => {
     const { addReaction, announcementId } = this.props;
     addReaction(announcementId, data.native.replace(/:/g, ''));
-  }
+  };
 
   willEnter () {
     return { scale: reduceMotion ? 1 : 0 };
@@ -356,7 +355,6 @@ class Announcement extends ImmutablePureComponent {
 
 }
 
-export default @injectIntl
 class Announcements extends ImmutablePureComponent {
 
   static propTypes = {
@@ -397,15 +395,15 @@ class Announcements extends ImmutablePureComponent {
 
   handleChangeIndex = index => {
     this.setState({ index: index % this.props.announcements.size });
-  }
+  };
 
   handleNextClick = () => {
     this.setState({ index: (this.state.index + 1) % this.props.announcements.size });
-  }
+  };
 
   handlePrevClick = () => {
     this.setState({ index: (this.props.announcements.size + this.state.index - 1) % this.props.announcements.size });
-  }
+  };
 
   render () {
     const { announcements, intl } = this.props;
@@ -420,7 +418,7 @@ class Announcements extends ImmutablePureComponent {
         <img className='announcements__mastodon' alt='' draggable='false' src={mascot || elephantUIPlane} />
 
         <div className='announcements__container'>
-          <ReactSwipeableViews animateHeight={!reduceMotion} adjustHeight={reduceMotion} index={index} onChangeIndex={this.handleChangeIndex}>
+          <ReactSwipeableViews animateHeight animateTransitions={!reduceMotion} index={index} onChangeIndex={this.handleChangeIndex}>
             {announcements.map((announcement, idx) => (
               <Announcement
                 key={announcement.get('id')}
@@ -448,3 +446,5 @@ class Announcements extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Announcements);
diff --git a/app/javascript/mastodon/features/getting_started/components/trends.js b/app/javascript/mastodon/features/getting_started/components/trends.jsx
index 8dcdb4f61..8dcdb4f61 100644
--- a/app/javascript/mastodon/features/getting_started/components/trends.js
+++ b/app/javascript/mastodon/features/getting_started/components/trends.jsx
diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.jsx
index fc91070d1..29659acc7 100644
--- a/app/javascript/mastodon/features/getting_started/index.js
+++ b/app/javascript/mastodon/features/getting_started/index.jsx
@@ -23,7 +23,7 @@ const messages = defineMessages({
   settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
   community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
   explore: { id: 'navigation_bar.explore', defaultMessage: 'Explore' },
-  direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
+  direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
   bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
   preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
   follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
@@ -58,8 +58,6 @@ const badgeDisplay = (number, limit) => {
   }
 };
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class GettingStarted extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -153,3 +151,5 @@ class GettingStarted extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(GettingStarted));
diff --git a/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js b/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.jsx
index ede8907e5..f140f2d01 100644
--- a/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js
+++ b/app/javascript/mastodon/features/hashtag_timeline/components/column_settings.jsx
@@ -12,7 +12,6 @@ const messages = defineMessages({
   noOptions: { id: 'hashtag.column_settings.select.no_options_message', defaultMessage: 'No suggestions found' },
 });
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -38,7 +37,7 @@ class ColumnSettings extends React.PureComponent {
     } else {
       return tags;
     }
-  };
+  }
 
   onSelect = mode => value => {
     const oldValue = this.tags(mode);
@@ -98,7 +97,7 @@ class ColumnSettings extends React.PureComponent {
     default:
       return '';
     }
-  };
+  }
 
   render () {
     const { settings, onChange } = this.props;
@@ -131,3 +130,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/mastodon/features/hashtag_timeline/index.js b/app/javascript/mastodon/features/hashtag_timeline/index.jsx
index 733f54ff3..a244dbdb2 100644
--- a/app/javascript/mastodon/features/hashtag_timeline/index.js
+++ b/app/javascript/mastodon/features/hashtag_timeline/index.jsx
@@ -26,8 +26,6 @@ const mapStateToProps = (state, props) => ({
   tag: state.getIn(['tags', props.params.id]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class HashtagTimeline extends React.PureComponent {
 
   disconnects = [];
@@ -54,7 +52,7 @@ class HashtagTimeline extends React.PureComponent {
     } else {
       dispatch(addColumn('HASHTAG', { id: this.props.params.id }));
     }
-  }
+  };
 
   title = () => {
     const { id } = this.props.params;
@@ -73,7 +71,7 @@ class HashtagTimeline extends React.PureComponent {
     }
 
     return title;
-  }
+  };
 
   additionalFor = (mode) => {
     const { tags } = this.props.params;
@@ -83,16 +81,16 @@ class HashtagTimeline extends React.PureComponent {
     } else {
       return '';
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   _subscribe (dispatch, id, tags = {}, local) {
     const { signedIn } = this.context.identity;
@@ -157,14 +155,14 @@ class HashtagTimeline extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = maxId => {
     const { dispatch, params } = this.props;
     const { id, tags, local }  = params;
 
     dispatch(expandHashtagTimeline(id, { maxId, tags, local }));
-  }
+  };
 
   handleFollow = () => {
     const { dispatch, params, tag } = this.props;
@@ -180,7 +178,7 @@ class HashtagTimeline extends React.PureComponent {
     } else {
       dispatch(followHashtag(id));
     }
-  }
+  };
 
   render () {
     const { hasUnread, columnId, multiColumn, tag, intl } = this.props;
@@ -193,8 +191,12 @@ class HashtagTimeline extends React.PureComponent {
     if (tag) {
       const following = tag.get('following');
 
+      const classes = classNames('column-header__button', {
+        active: following,
+      });
+
       followButton = (
-        <button className={classNames('column-header__button')} onClick={this.handleFollow} disabled={!signedIn} active={following} title={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-label={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)}>
+        <button className={classes} onClick={this.handleFollow} disabled={!signedIn} title={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)} aria-label={intl.formatMessage(following ? messages.unfollowHashtag : messages.followHashtag)}>
           <Icon id={following ? 'user-times' : 'user-plus'} fixedWidth className='column-header__icon' />
         </button>
       );
@@ -235,3 +237,5 @@ class HashtagTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(HashtagTimeline));
diff --git a/app/javascript/mastodon/features/home_timeline/components/column_settings.js b/app/javascript/mastodon/features/home_timeline/components/column_settings.jsx
index 455e21881..bd15390c0 100644
--- a/app/javascript/mastodon/features/home_timeline/components/column_settings.js
+++ b/app/javascript/mastodon/features/home_timeline/components/column_settings.jsx
@@ -4,7 +4,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import SettingToggle from '../../notifications/components/setting_toggle';
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -32,3 +31,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/mastodon/features/home_timeline/index.js b/app/javascript/mastodon/features/home_timeline/index.jsx
index ae11ccbe4..9aa4c6d82 100644
--- a/app/javascript/mastodon/features/home_timeline/index.js
+++ b/app/javascript/mastodon/features/home_timeline/index.jsx
@@ -30,8 +30,6 @@ const mapStateToProps = state => ({
   showAnnouncements: state.getIn(['announcements', 'show']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class HomeTimeline extends React.PureComponent {
 
   static contextTypes = {
@@ -58,24 +56,24 @@ class HomeTimeline extends React.PureComponent {
     } else {
       dispatch(addColumn('HOME', {}));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = maxId => {
     this.props.dispatch(expandHomeTimeline({ maxId }));
-  }
+  };
 
   componentDidMount () {
     setTimeout(() => this.props.dispatch(fetchAnnouncements()), 700);
@@ -114,7 +112,7 @@ class HomeTimeline extends React.PureComponent {
   handleToggleAnnouncementsClick = (e) => {
     e.stopPropagation();
     this.props.dispatch(toggleShowAnnouncements());
-  }
+  };
 
   render () {
     const { intl, hasUnread, columnId, multiColumn, hasAnnouncements, unreadAnnouncements, showAnnouncements } = this.props;
@@ -174,3 +172,5 @@ class HomeTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(HomeTimeline));
diff --git a/app/javascript/mastodon/features/interaction_modal/index.js b/app/javascript/mastodon/features/interaction_modal/index.jsx
index d4535378f..5742f1104 100644
--- a/app/javascript/mastodon/features/interaction_modal/index.js
+++ b/app/javascript/mastodon/features/interaction_modal/index.jsx
@@ -30,14 +30,14 @@ class Copypaste extends React.PureComponent {
 
   setRef = c => {
     this.input = c;
-  }
+  };
 
   handleInputClick = () => {
     this.setState({ copied: false });
     this.input.focus();
     this.input.select();
     this.input.setSelectionRange(0, this.input.value.length);
-  }
+  };
 
   handleButtonClick = () => {
     const { value } = this.props;
@@ -45,7 +45,7 @@ class Copypaste extends React.PureComponent {
     this.input.blur();
     this.setState({ copied: true });
     this.timeout = setTimeout(() => this.setState({ copied: false }), 700);
-  }
+  };
 
   componentWillUnmount () {
     if (this.timeout) clearTimeout(this.timeout);
@@ -74,7 +74,6 @@ class Copypaste extends React.PureComponent {
 
 }
 
-export default @connect(mapStateToProps, mapDispatchToProps)
 class InteractionModal extends React.PureComponent {
 
   static propTypes = {
@@ -86,7 +85,7 @@ class InteractionModal extends React.PureComponent {
 
   handleSignupClick = () => {
     this.props.onSignupClick();
-  }
+  };
 
   render () {
     const { url, type, displayNameHtml } = this.props;
@@ -159,3 +158,5 @@ class InteractionModal extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(InteractionModal);
diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.js b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx
index 9a870478d..70f019712 100644
--- a/app/javascript/mastodon/features/keyboard_shortcuts/index.js
+++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   heading: { id: 'keyboard_shortcuts.heading', defaultMessage: 'Keyboard Shortcuts' },
 });
 
-export default @injectIntl
 class KeyboardShortcuts extends ImmutablePureComponent {
 
   static propTypes = {
@@ -174,3 +173,5 @@ class KeyboardShortcuts extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(KeyboardShortcuts);
diff --git a/app/javascript/mastodon/features/list_adder/components/account.js b/app/javascript/mastodon/features/list_adder/components/account.jsx
index 1369aac07..786af3bb1 100644
--- a/app/javascript/mastodon/features/list_adder/components/account.js
+++ b/app/javascript/mastodon/features/list_adder/components/account.jsx
@@ -17,9 +17,6 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-
-export default @connect(makeMapStateToProps)
-@injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -41,3 +38,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(Account));
diff --git a/app/javascript/mastodon/features/list_adder/components/list.js b/app/javascript/mastodon/features/list_adder/components/list.jsx
index 60c8958a7..34ccf8451 100644
--- a/app/javascript/mastodon/features/list_adder/components/list.js
+++ b/app/javascript/mastodon/features/list_adder/components/list.jsx
@@ -23,8 +23,6 @@ const mapDispatchToProps = (dispatch, { listId }) => ({
   onAdd: () => dispatch(addToListAdder(listId)),
 });
 
-export default @connect(MapStateToProps, mapDispatchToProps)
-@injectIntl
 class List extends ImmutablePureComponent {
 
   static propTypes = {
@@ -67,3 +65,5 @@ class List extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(MapStateToProps, mapDispatchToProps)(injectIntl(List));
diff --git a/app/javascript/mastodon/features/list_adder/index.js b/app/javascript/mastodon/features/list_adder/index.jsx
index cb8a15e8c..45d5589f9 100644
--- a/app/javascript/mastodon/features/list_adder/index.js
+++ b/app/javascript/mastodon/features/list_adder/index.jsx
@@ -28,8 +28,6 @@ const mapDispatchToProps = dispatch => ({
   onReset: () => dispatch(resetListAdder()),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class ListAdder extends ImmutablePureComponent {
 
   static propTypes = {
@@ -71,3 +69,5 @@ class ListAdder extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ListAdder));
diff --git a/app/javascript/mastodon/features/list_editor/components/account.js b/app/javascript/mastodon/features/list_editor/components/account.jsx
index 48085af43..86209bb3c 100644
--- a/app/javascript/mastodon/features/list_editor/components/account.js
+++ b/app/javascript/mastodon/features/list_editor/components/account.jsx
@@ -31,8 +31,6 @@ const mapDispatchToProps = (dispatch, { accountId }) => ({
   onAdd: () => dispatch(addToListEditor(accountId)),
 });
 
-export default @connect(makeMapStateToProps, mapDispatchToProps)
-@injectIntl
 class Account extends ImmutablePureComponent {
 
   static propTypes = {
@@ -75,3 +73,5 @@ class Account extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(Account));
diff --git a/app/javascript/mastodon/features/list_editor/components/edit_list_form.js b/app/javascript/mastodon/features/list_editor/components/edit_list_form.jsx
index 3ccab12a8..9c1c244cb 100644
--- a/app/javascript/mastodon/features/list_editor/components/edit_list_form.js
+++ b/app/javascript/mastodon/features/list_editor/components/edit_list_form.jsx
@@ -19,8 +19,6 @@ const mapDispatchToProps = dispatch => ({
   onSubmit: () => dispatch(submitListEditor(false)),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class ListForm extends React.PureComponent {
 
   static propTypes = {
@@ -33,16 +31,16 @@ class ListForm extends React.PureComponent {
 
   handleChange = e => {
     this.props.onChange(e.target.value);
-  }
+  };
 
   handleSubmit = e => {
     e.preventDefault();
     this.props.onSubmit();
-  }
+  };
 
   handleClick = () => {
     this.props.onSubmit();
-  }
+  };
 
   render () {
     const { value, disabled, intl } = this.props;
@@ -68,3 +66,5 @@ class ListForm extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ListForm));
diff --git a/app/javascript/mastodon/features/list_editor/components/search.js b/app/javascript/mastodon/features/list_editor/components/search.jsx
index e3f069bb8..f70e272f7 100644
--- a/app/javascript/mastodon/features/list_editor/components/search.js
+++ b/app/javascript/mastodon/features/list_editor/components/search.jsx
@@ -20,8 +20,6 @@ const mapDispatchToProps = dispatch => ({
   onChange: value => dispatch(changeListSuggestions(value)),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class Search extends React.PureComponent {
 
   static propTypes = {
@@ -34,17 +32,17 @@ class Search extends React.PureComponent {
 
   handleChange = e => {
     this.props.onChange(e.target.value);
-  }
+  };
 
   handleKeyUp = e => {
     if (e.keyCode === 13) {
       this.props.onSubmit(this.props.value);
     }
-  }
+  };
 
   handleClear = () => {
     this.props.onClear();
-  }
+  };
 
   render () {
     const { value, intl } = this.props;
@@ -65,7 +63,7 @@ class Search extends React.PureComponent {
           />
         </label>
 
-        <div role='button' tabIndex='0' className='search__icon' onClick={this.handleClear}>
+        <div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
           <Icon id='search' className={classNames({ active: !hasValue })} />
           <Icon id='times-circle' aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
         </div>
@@ -74,3 +72,5 @@ class Search extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(Search));
diff --git a/app/javascript/mastodon/features/list_editor/index.js b/app/javascript/mastodon/features/list_editor/index.jsx
index 48466604a..ed9d09132 100644
--- a/app/javascript/mastodon/features/list_editor/index.js
+++ b/app/javascript/mastodon/features/list_editor/index.jsx
@@ -22,8 +22,6 @@ const mapDispatchToProps = dispatch => ({
   onReset: () => dispatch(resetListEditor()),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class ListEditor extends ImmutablePureComponent {
 
   static propTypes = {
@@ -62,7 +60,7 @@ class ListEditor extends ImmutablePureComponent {
             {accountIds.map(accountId => <Account key={accountId} accountId={accountId} added />)}
           </div>
 
-          {showSearch && <div role='button' tabIndex='-1' className='drawer__backdrop' onClick={onClear} />}
+          {showSearch && <div role='button' tabIndex={-1} className='drawer__backdrop' onClick={onClear} />}
 
           <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
             {({ x }) => (
@@ -77,3 +75,5 @@ class ListEditor extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(ListEditor));
diff --git a/app/javascript/mastodon/features/list_timeline/index.js b/app/javascript/mastodon/features/list_timeline/index.jsx
index c2e72e2e9..c0b9a62ff 100644
--- a/app/javascript/mastodon/features/list_timeline/index.js
+++ b/app/javascript/mastodon/features/list_timeline/index.jsx
@@ -31,8 +31,6 @@ const mapStateToProps = (state, props) => ({
   hasUnread: state.getIn(['timelines', `list:${props.params.id}`, 'unread']) > 0,
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class ListTimeline extends React.PureComponent {
 
   static contextTypes = {
@@ -58,16 +56,16 @@ class ListTimeline extends React.PureComponent {
       dispatch(addColumn('LIST', { id: this.props.params.id }));
       this.context.router.history.push('/');
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   componentDidMount () {
     const { dispatch } = this.props;
@@ -105,16 +103,16 @@ class ListTimeline extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = maxId => {
     const { id } = this.props.params;
     this.props.dispatch(expandListTimeline(id, { maxId }));
-  }
+  };
 
   handleEditClick = () => {
     this.props.dispatch(openModal('LIST_EDITOR', { listId: this.props.params.id }));
-  }
+  };
 
   handleDeleteClick = () => {
     const { dispatch, columnId, intl } = this.props;
@@ -133,13 +131,13 @@ class ListTimeline extends React.PureComponent {
         }
       },
     }));
-  }
+  };
 
   handleRepliesPolicyChange = ({ target }) => {
     const { dispatch } = this.props;
     const { id } = this.props.params;
     dispatch(updateList(id, undefined, false, target.value));
-  }
+  };
 
   render () {
     const { hasUnread, columnId, multiColumn, list, intl } = this.props;
@@ -178,11 +176,11 @@ class ListTimeline extends React.PureComponent {
           multiColumn={multiColumn}
         >
           <div className='column-settings__row column-header__links'>
-            <button type='button' className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.handleEditClick}>
+            <button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}>
               <Icon id='pencil' /> <FormattedMessage id='lists.edit' defaultMessage='Edit list' />
             </button>
 
-            <button type='button' className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.handleDeleteClick}>
+            <button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
               <Icon id='trash' /> <FormattedMessage id='lists.delete' defaultMessage='Delete list' />
             </button>
           </div>
@@ -219,3 +217,5 @@ class ListTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(ListTimeline));
diff --git a/app/javascript/mastodon/features/lists/components/new_list_form.js b/app/javascript/mastodon/features/lists/components/new_list_form.jsx
index f790ccbe6..50b52518e 100644
--- a/app/javascript/mastodon/features/lists/components/new_list_form.js
+++ b/app/javascript/mastodon/features/lists/components/new_list_form.jsx
@@ -20,8 +20,6 @@ const mapDispatchToProps = dispatch => ({
   onSubmit: () => dispatch(submitListEditor(true)),
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class NewListForm extends React.PureComponent {
 
   static propTypes = {
@@ -34,16 +32,16 @@ class NewListForm extends React.PureComponent {
 
   handleChange = e => {
     this.props.onChange(e.target.value);
-  }
+  };
 
   handleSubmit = e => {
     e.preventDefault();
     this.props.onSubmit();
-  }
+  };
 
   handleClick = () => {
     this.props.onSubmit();
-  }
+  };
 
   render () {
     const { value, disabled, intl } = this.props;
@@ -75,3 +73,5 @@ class NewListForm extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(NewListForm));
diff --git a/app/javascript/mastodon/features/lists/index.js b/app/javascript/mastodon/features/lists/index.jsx
index 3a0b1373a..afd645a30 100644
--- a/app/javascript/mastodon/features/lists/index.js
+++ b/app/javascript/mastodon/features/lists/index.jsx
@@ -32,8 +32,6 @@ const mapStateToProps = state => ({
   lists: getOrderedLists(state),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Lists extends ImmutablePureComponent {
 
   static propTypes = {
@@ -87,3 +85,5 @@ class Lists extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Lists));
diff --git a/app/javascript/mastodon/features/mutes/index.js b/app/javascript/mastodon/features/mutes/index.jsx
index 65df6149f..5c05d2f70 100644
--- a/app/javascript/mastodon/features/mutes/index.js
+++ b/app/javascript/mastodon/features/mutes/index.jsx
@@ -23,8 +23,6 @@ const mapStateToProps = state => ({
   isLoading: state.getIn(['user_lists', 'mutes', 'isLoading'], true),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Mutes extends ImmutablePureComponent {
 
   static propTypes = {
@@ -82,3 +80,5 @@ class Mutes extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Mutes));
diff --git a/app/javascript/mastodon/features/notifications/components/clear_column_button.js b/app/javascript/mastodon/features/notifications/components/clear_column_button.jsx
index b82fd092f..9a076ce5e 100644
--- a/app/javascript/mastodon/features/notifications/components/clear_column_button.js
+++ b/app/javascript/mastodon/features/notifications/components/clear_column_button.jsx
@@ -11,7 +11,7 @@ export default class ClearColumnButton extends React.PureComponent {
 
   render () {
     return (
-      <button className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.props.onClick}><Icon id='eraser' /> <FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' /></button>
+      <button className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.props.onClick}><Icon id='eraser' /> <FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' /></button>
     );
   }
 
diff --git a/app/javascript/mastodon/features/notifications/components/column_settings.js b/app/javascript/mastodon/features/notifications/components/column_settings.jsx
index a38f8d3c2..9251847ba 100644
--- a/app/javascript/mastodon/features/notifications/components/column_settings.js
+++ b/app/javascript/mastodon/features/notifications/components/column_settings.jsx
@@ -26,7 +26,7 @@ export default class ColumnSettings extends React.PureComponent {
 
   onPushChange = (path, checked) => {
     this.props.onChange(['push', ...path], checked);
-  }
+  };
 
   render () {
     const { settings, pushSettings, onChange, onClear, alertsEnabled, browserSupport, browserPermission, onRequestNotificationPermission } = this.props;
diff --git a/app/javascript/mastodon/features/notifications/components/filter_bar.js b/app/javascript/mastodon/features/notifications/components/filter_bar.jsx
index 368eb0b7e..88e95cf48 100644
--- a/app/javascript/mastodon/features/notifications/components/filter_bar.js
+++ b/app/javascript/mastodon/features/notifications/components/filter_bar.jsx
@@ -12,7 +12,6 @@ const tooltips = defineMessages({
   statuses: { id: 'notifications.filter.statuses', defaultMessage: 'Updates from people you follow' },
 });
 
-export default @injectIntl
 class FilterBar extends React.PureComponent {
 
   static propTypes = {
@@ -108,3 +107,5 @@ class FilterBar extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(FilterBar);
diff --git a/app/javascript/mastodon/features/notifications/components/follow_request.js b/app/javascript/mastodon/features/notifications/components/follow_request.jsx
index 08de875e3..01bf67065 100644
--- a/app/javascript/mastodon/features/notifications/components/follow_request.js
+++ b/app/javascript/mastodon/features/notifications/components/follow_request.jsx
@@ -1,4 +1,4 @@
-import React, { Fragment } from 'react';
+import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Avatar from 'mastodon/components/avatar';
@@ -13,7 +13,6 @@ const messages = defineMessages({
   reject: { id: 'follow_request.reject', defaultMessage: 'Reject' },
 });
 
-export default @injectIntl
 class FollowRequest extends ImmutablePureComponent {
 
   static propTypes = {
@@ -32,10 +31,10 @@ class FollowRequest extends ImmutablePureComponent {
 
     if (hidden) {
       return (
-        <Fragment>
+        <React.Fragment>
           {account.get('display_name')}
           {account.get('username')}
-        </Fragment>
+        </React.Fragment>
       );
     }
 
@@ -57,3 +56,5 @@ class FollowRequest extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(FollowRequest);
diff --git a/app/javascript/mastodon/features/notifications/components/grant_permission_button.js b/app/javascript/mastodon/features/notifications/components/grant_permission_button.jsx
index 798e4c787..5b2db48fd 100644
--- a/app/javascript/mastodon/features/notifications/components/grant_permission_button.js
+++ b/app/javascript/mastodon/features/notifications/components/grant_permission_button.jsx
@@ -10,7 +10,7 @@ export default class GrantPermissionButton extends React.PureComponent {
 
   render () {
     return (
-      <button className='text-btn column-header__permission-btn' tabIndex='0' onClick={this.props.onClick}>
+      <button className='text-btn column-header__permission-btn' tabIndex={0} onClick={this.props.onClick}>
         <FormattedMessage id='notifications.grant_permission' defaultMessage='Grant permission.' />
       </button>
     );
diff --git a/app/javascript/mastodon/features/notifications/components/notification.js b/app/javascript/mastodon/features/notifications/components/notification.jsx
index ea2c9c0a4..f3104cee0 100644
--- a/app/javascript/mastodon/features/notifications/components/notification.js
+++ b/app/javascript/mastodon/features/notifications/components/notification.jsx
@@ -33,7 +33,6 @@ const notificationForScreenReader = (intl, message, timestamp) => {
   return output.join(', ');
 };
 
-export default @injectIntl
 class Notification extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -61,12 +60,12 @@ class Notification extends ImmutablePureComponent {
   handleMoveUp = () => {
     const { notification, onMoveUp } = this.props;
     onMoveUp(notification.get('id'));
-  }
+  };
 
   handleMoveDown = () => {
     const { notification, onMoveDown } = this.props;
     onMoveDown(notification.get('id'));
-  }
+  };
 
   handleOpen = () => {
     const { notification } = this.props;
@@ -76,34 +75,34 @@ class Notification extends ImmutablePureComponent {
     } else {
       this.handleOpenProfile();
     }
-  }
+  };
 
   handleOpenProfile = () => {
     const { notification } = this.props;
     this.context.router.history.push(`/@${notification.getIn(['account', 'acct'])}`);
-  }
+  };
 
   handleMention = e => {
     e.preventDefault();
 
     const { notification, onMention } = this.props;
     onMention(notification.get('account'), this.context.router.history);
-  }
+  };
 
   handleHotkeyFavourite = () => {
     const { status } = this.props;
     if (status) this.props.onFavourite(status);
-  }
+  };
 
   handleHotkeyBoost = e => {
     const { status } = this.props;
     if (status) this.props.onReblog(status, e);
-  }
+  };
 
   handleHotkeyToggleHidden = () => {
     const { status } = this.props;
     if (status) this.props.onToggleHidden(status);
-  }
+  };
 
   getHandlers () {
     return {
@@ -124,7 +123,7 @@ class Notification extends ImmutablePureComponent {
 
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-follow focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.follow, { name: account.get('acct') }), notification.get('created_at'))}>
+        <div className={classNames('notification notification-follow focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.follow, { name: account.get('acct') }), notification.get('created_at'))}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon id='user-plus' fixedWidth />
@@ -146,7 +145,7 @@ class Notification extends ImmutablePureComponent {
 
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-follow-request focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.follow_request', defaultMessage: '{name} has requested to follow you' }, { name: account.get('acct') }), notification.get('created_at'))}>
+        <div className={classNames('notification notification-follow-request focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.follow_request', defaultMessage: '{name} has requested to follow you' }, { name: account.get('acct') }), notification.get('created_at'))}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon id='user' fixedWidth />
@@ -186,7 +185,7 @@ class Notification extends ImmutablePureComponent {
 
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-favourite focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.favourite, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
+        <div className={classNames('notification notification-favourite focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.favourite, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon id='star' className='star-icon' fixedWidth />
@@ -218,7 +217,7 @@ class Notification extends ImmutablePureComponent {
 
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-reblog focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.reblog, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
+        <div className={classNames('notification notification-reblog focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.reblog, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon id='retweet' fixedWidth />
@@ -246,11 +245,15 @@ class Notification extends ImmutablePureComponent {
   }
 
   renderStatus (notification, link) {
-    const { intl, unread } = this.props;
+    const { intl, unread, status } = this.props;
+
+    if (!status) {
+      return null;
+    }
 
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-status focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.status, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
+        <div className={classNames('notification notification-status focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.status, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon id='home' fixedWidth />
@@ -264,6 +267,7 @@ class Notification extends ImmutablePureComponent {
           <StatusContainer
             id={notification.get('status')}
             account={notification.get('account')}
+            contextType='notifications'
             muted
             withDismiss
             hidden={this.props.hidden}
@@ -278,11 +282,15 @@ class Notification extends ImmutablePureComponent {
   }
 
   renderUpdate (notification, link) {
-    const { intl, unread } = this.props;
+    const { intl, unread, status } = this.props;
+
+    if (!status) {
+      return null;
+    }
 
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-update focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.update, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
+        <div className={classNames('notification notification-update focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.update, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon id='pencil' fixedWidth />
@@ -296,6 +304,7 @@ class Notification extends ImmutablePureComponent {
           <StatusContainer
             id={notification.get('status')}
             account={notification.get('account')}
+            contextType='notifications'
             muted
             withDismiss
             hidden={this.props.hidden}
@@ -310,13 +319,17 @@ class Notification extends ImmutablePureComponent {
   }
 
   renderPoll (notification, account) {
-    const { intl, unread } = this.props;
+    const { intl, unread, status } = this.props;
     const ownPoll  = me === account.get('id');
     const message  = ownPoll ? intl.formatMessage(messages.ownPoll) : intl.formatMessage(messages.poll);
 
+    if (!status) {
+      return null;
+    }
+
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-poll focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, message, notification.get('created_at'))}>
+        <div className={classNames('notification notification-poll focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, message, notification.get('created_at'))}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon id='tasks' fixedWidth />
@@ -334,6 +347,7 @@ class Notification extends ImmutablePureComponent {
           <StatusContainer
             id={notification.get('status')}
             account={account}
+            contextType='notifications'
             muted
             withDismiss
             hidden={this.props.hidden}
@@ -352,7 +366,7 @@ class Notification extends ImmutablePureComponent {
 
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-admin-sign-up focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.adminSignUp, { name: account.get('acct') }), notification.get('created_at'))}>
+        <div className={classNames('notification notification-admin-sign-up focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.adminSignUp, { name: account.get('acct') }), notification.get('created_at'))}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon id='user-plus' fixedWidth />
@@ -382,7 +396,7 @@ class Notification extends ImmutablePureComponent {
 
     return (
       <HotKeys handlers={this.getHandlers()}>
-        <div className={classNames('notification notification-admin-report focusable', { unread })} tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.adminReport, { name: account.get('acct'), target: notification.getIn(['report', 'target_account', 'acct']) }), notification.get('created_at'))}>
+        <div className={classNames('notification notification-admin-report focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.adminReport, { name: account.get('acct'), target: notification.getIn(['report', 'target_account', 'acct']) }), notification.get('created_at'))}>
           <div className='notification__message'>
             <div className='notification__favourite-icon-wrapper'>
               <Icon id='flag' fixedWidth />
@@ -432,3 +446,5 @@ class Notification extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Notification);
diff --git a/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js b/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.jsx
index df9b7fb1b..c54137e60 100644
--- a/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js
+++ b/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.jsx
@@ -12,8 +12,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @connect()
-@injectIntl
 class NotificationsPermissionBanner extends React.PureComponent {
 
   static propTypes = {
@@ -23,11 +21,11 @@ class NotificationsPermissionBanner extends React.PureComponent {
 
   handleClick = () => {
     this.props.dispatch(requestBrowserPermission());
-  }
+  };
 
   handleClose = () => {
     this.props.dispatch(changeSetting(['notifications', 'dismissPermissionBanner'], true));
-  }
+  };
 
   render () {
     const { intl } = this.props;
@@ -46,3 +44,5 @@ class NotificationsPermissionBanner extends React.PureComponent {
   }
 
 }
+
+export default connect()(injectIntl(NotificationsPermissionBanner));
diff --git a/app/javascript/mastodon/features/notifications/components/report.js b/app/javascript/mastodon/features/notifications/components/report.jsx
index 3ce3eb9d3..4663b2359 100644
--- a/app/javascript/mastodon/features/notifications/components/report.js
+++ b/app/javascript/mastodon/features/notifications/components/report.jsx
@@ -13,7 +13,6 @@ const messages = defineMessages({
   violation: { id: 'report_notification.categories.violation', defaultMessage: 'Rule violation' },
 });
 
-export default @injectIntl
 class Report extends ImmutablePureComponent {
 
   static propTypes = {
@@ -60,3 +59,5 @@ class Report extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(Report);
diff --git a/app/javascript/mastodon/features/notifications/components/setting_toggle.js b/app/javascript/mastodon/features/notifications/components/setting_toggle.jsx
index c4c8bffbe..c979e4383 100644
--- a/app/javascript/mastodon/features/notifications/components/setting_toggle.js
+++ b/app/javascript/mastodon/features/notifications/components/setting_toggle.jsx
@@ -13,11 +13,11 @@ export default class SettingToggle extends React.PureComponent {
     onChange: PropTypes.func.isRequired,
     defaultValue: PropTypes.bool,
     disabled: PropTypes.bool,
-  }
+  };
 
   onChange = ({ target }) => {
     this.props.onChange(this.props.settingPath, target.checked);
-  }
+  };
 
   render () {
     const { prefix, settings, settingPath, label, defaultValue, disabled } = this.props;
diff --git a/app/javascript/mastodon/features/notifications/containers/column_settings_container.js b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js
index 9a70bd4f3..515afaca9 100644
--- a/app/javascript/mastodon/features/notifications/containers/column_settings_container.js
+++ b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js
@@ -2,8 +2,7 @@ import { connect } from 'react-redux';
 import { defineMessages, injectIntl } from 'react-intl';
 import ColumnSettings from '../components/column_settings';
 import { changeSetting } from '../../../actions/settings';
-import { setFilter } from '../../../actions/notifications';
-import { clearNotifications, requestBrowserPermission } from '../../../actions/notifications';
+import { setFilter, clearNotifications, requestBrowserPermission } from '../../../actions/notifications';
 import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications';
 import { openModal } from '../../../actions/modal';
 import { showAlert } from '../../../actions/alerts';
diff --git a/app/javascript/mastodon/features/notifications/containers/notification_container.js b/app/javascript/mastodon/features/notifications/containers/notification_container.js
index 8bd5b3d78..8c5688acb 100644
--- a/app/javascript/mastodon/features/notifications/containers/notification_container.js
+++ b/app/javascript/mastodon/features/notifications/containers/notification_container.js
@@ -24,7 +24,7 @@ const makeMapStateToProps = () => {
     const notification = getNotification(state, props.notification, props.accountId);
     return {
       notification: notification,
-      status: notification.get('status') ? getStatus(state, { id: notification.get('status') }) : null,
+      status: notification.get('status') ? getStatus(state, { id: notification.get('status'), contextType: 'notifications' }) : null,
       report: notification.get('report') ? getReport(state, notification.get('report'), notification.getIn(['report', 'target_account', 'id'])) : null,
     };
   };
diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.jsx
index 826a7e9ad..bb8852abf 100644
--- a/app/javascript/mastodon/features/notifications/index.js
+++ b/app/javascript/mastodon/features/notifications/index.jsx
@@ -67,8 +67,6 @@ const mapStateToProps = state => ({
   needsNotificationPermission: state.getIn(['settings', 'notifications', 'alerts']).includes(true) && state.getIn(['notifications', 'browserSupport']) && state.getIn(['notifications', 'browserPermission']) === 'default' && !state.getIn(['settings', 'notifications', 'dismissPermissionBanner']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Notifications extends React.PureComponent {
 
   static contextTypes = {
@@ -136,30 +134,30 @@ class Notifications extends React.PureComponent {
     } else {
       dispatch(addColumn('NOTIFICATIONS', {}));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setColumnRef = c => {
     this.column = c;
-  }
+  };
 
   handleMoveUp = id => {
     const elementIndex = this.props.notifications.findIndex(item => item !== null && item.get('id') === id) - 1;
     this._selectChild(elementIndex, true);
-  }
+  };
 
   handleMoveDown = id => {
     const elementIndex = this.props.notifications.findIndex(item => item !== null && item.get('id') === id) + 1;
     this._selectChild(elementIndex, false);
-  }
+  };
 
   _selectChild (index, align_top) {
     const container = this.column.node;
@@ -288,3 +286,5 @@ class Notifications extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Notifications));
diff --git a/app/javascript/mastodon/features/picture_in_picture/components/footer.js b/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx
index 0dff834c3..66124f331 100644
--- a/app/javascript/mastodon/features/picture_in_picture/components/footer.js
+++ b/app/javascript/mastodon/features/picture_in_picture/components/footer.jsx
@@ -37,8 +37,6 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class Footer extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -112,7 +110,7 @@ class Footer extends ImmutablePureComponent {
   _performReblog = (status, privacy) => {
     const { dispatch } = this.props;
     dispatch(reblog(status, privacy));
-  }
+  };
 
   handleReblogClick = e => {
     const { dispatch, status } = this.props;
@@ -149,7 +147,7 @@ class Footer extends ImmutablePureComponent {
     }
 
     router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
-  }
+  };
 
   render () {
     const { status, intl, withOpenButton } = this.props;
@@ -184,9 +182,11 @@ class Footer extends ImmutablePureComponent {
         <IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} counter={status.get('replies_count')} obfuscateCount />
         <IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate}  active={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
         <IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} />
-        {withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' onClick={this.handleOpenClick} href={`/@${status.getIn(['account', 'acct'])}\/${status.get('id')}`} />}
+        {withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' onClick={this.handleOpenClick} href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} />}
       </div>
     );
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(Footer));
diff --git a/app/javascript/mastodon/features/picture_in_picture/components/header.js b/app/javascript/mastodon/features/picture_in_picture/components/header.jsx
index e05d8c62e..100f9db7a 100644
--- a/app/javascript/mastodon/features/picture_in_picture/components/header.js
+++ b/app/javascript/mastodon/features/picture_in_picture/components/header.jsx
@@ -17,8 +17,6 @@ const mapStateToProps = (state, { accountId }) => ({
   account: state.getIn(['accounts', accountId]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Header extends ImmutablePureComponent {
 
   static propTypes = {
@@ -45,3 +43,5 @@ class Header extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Header));
diff --git a/app/javascript/mastodon/features/picture_in_picture/index.js b/app/javascript/mastodon/features/picture_in_picture/index.jsx
index 1e59fbcd3..ae48a1b4e 100644
--- a/app/javascript/mastodon/features/picture_in_picture/index.js
+++ b/app/javascript/mastodon/features/picture_in_picture/index.jsx
@@ -11,7 +11,6 @@ const mapStateToProps = state => ({
   ...state.get('picture_in_picture'),
 });
 
-export default @connect(mapStateToProps)
 class PictureInPicture extends React.Component {
 
   static propTypes = {
@@ -32,7 +31,7 @@ class PictureInPicture extends React.Component {
   handleClose = () => {
     const { dispatch } = this.props;
     dispatch(removePictureInPicture());
-  }
+  };
 
   render () {
     const { type, src, currentTime, accountId, statusId } = this.props;
@@ -83,3 +82,5 @@ class PictureInPicture extends React.Component {
   }
 
 }
+
+export default connect(mapStateToProps)(PictureInPicture);
diff --git a/app/javascript/mastodon/features/pinned_statuses/index.js b/app/javascript/mastodon/features/pinned_statuses/index.jsx
index c6790ea06..24a0b6d2e 100644
--- a/app/javascript/mastodon/features/pinned_statuses/index.js
+++ b/app/javascript/mastodon/features/pinned_statuses/index.jsx
@@ -19,8 +19,6 @@ const mapStateToProps = state => ({
   hasMore: !!state.getIn(['status_lists', 'pins', 'next']),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class PinnedStatuses extends ImmutablePureComponent {
 
   static propTypes = {
@@ -37,11 +35,11 @@ class PinnedStatuses extends ImmutablePureComponent {
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   render () {
     const { intl, statusIds, hasMore, multiColumn } = this.props;
@@ -63,3 +61,5 @@ class PinnedStatuses extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(PinnedStatuses));
diff --git a/app/javascript/mastodon/features/privacy_policy/index.js b/app/javascript/mastodon/features/privacy_policy/index.jsx
index 3df487e8f..d5bbda6a3 100644
--- a/app/javascript/mastodon/features/privacy_policy/index.js
+++ b/app/javascript/mastodon/features/privacy_policy/index.jsx
@@ -10,7 +10,6 @@ const messages = defineMessages({
   title: { id: 'privacy_policy.title', defaultMessage: 'Privacy Policy' },
 });
 
-export default @injectIntl
 class PrivacyPolicy extends React.PureComponent {
 
   static propTypes = {
@@ -59,3 +58,5 @@ class PrivacyPolicy extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(PrivacyPolicy);
diff --git a/app/javascript/mastodon/features/public_timeline/components/column_settings.js b/app/javascript/mastodon/features/public_timeline/components/column_settings.jsx
index 756b6fe06..bf8a8323e 100644
--- a/app/javascript/mastodon/features/public_timeline/components/column_settings.js
+++ b/app/javascript/mastodon/features/public_timeline/components/column_settings.jsx
@@ -4,7 +4,6 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import SettingToggle from '../../notifications/components/setting_toggle';
 
-export default @injectIntl
 class ColumnSettings extends React.PureComponent {
 
   static propTypes = {
@@ -28,3 +27,5 @@ class ColumnSettings extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ColumnSettings);
diff --git a/app/javascript/mastodon/features/public_timeline/index.js b/app/javascript/mastodon/features/public_timeline/index.jsx
index a41be07e1..f89caa2c9 100644
--- a/app/javascript/mastodon/features/public_timeline/index.js
+++ b/app/javascript/mastodon/features/public_timeline/index.jsx
@@ -31,8 +31,6 @@ const mapStateToProps = (state, { columnId }) => {
   };
 };
 
-export default @connect(mapStateToProps)
-@injectIntl
 class PublicTimeline extends React.PureComponent {
 
   static contextTypes = {
@@ -62,16 +60,16 @@ class PublicTimeline extends React.PureComponent {
     } else {
       dispatch(addColumn(onlyRemote ? 'REMOTE' : 'PUBLIC', { other: { onlyMedia, onlyRemote } }));
     }
-  }
+  };
 
   handleMove = (dir) => {
     const { columnId, dispatch } = this.props;
     dispatch(moveColumn(columnId, dir));
-  }
+  };
 
   handleHeaderClick = () => {
     this.column.scrollTop();
-  }
+  };
 
   componentDidMount () {
     const { dispatch, onlyMedia, onlyRemote } = this.props;
@@ -111,13 +109,13 @@ class PublicTimeline extends React.PureComponent {
 
   setRef = c => {
     this.column = c;
-  }
+  };
 
   handleLoadMore = maxId => {
     const { dispatch, onlyMedia, onlyRemote } = this.props;
 
     dispatch(expandPublicTimeline({ maxId, onlyMedia, onlyRemote }));
-  }
+  };
 
   render () {
     const { intl, columnId, hasUnread, multiColumn, onlyMedia, onlyRemote } = this.props;
@@ -160,3 +158,5 @@ class PublicTimeline extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(PublicTimeline));
diff --git a/app/javascript/mastodon/features/reblogs/index.js b/app/javascript/mastodon/features/reblogs/index.jsx
index 36ca11d1a..35edb0d6a 100644
--- a/app/javascript/mastodon/features/reblogs/index.js
+++ b/app/javascript/mastodon/features/reblogs/index.jsx
@@ -21,8 +21,6 @@ const mapStateToProps = (state, props) => ({
   accountIds: state.getIn(['user_lists', 'reblogged_by', props.params.statusId]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Reblogs extends ImmutablePureComponent {
 
   static propTypes = {
@@ -47,7 +45,7 @@ class Reblogs extends ImmutablePureComponent {
 
   handleRefresh = () => {
     this.props.dispatch(fetchReblogs(this.props.params.statusId));
-  }
+  };
 
   render () {
     const { intl, accountIds, multiColumn } = this.props;
@@ -90,3 +88,5 @@ class Reblogs extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Reblogs));
diff --git a/app/javascript/mastodon/features/report/category.js b/app/javascript/mastodon/features/report/category.jsx
index c6c0a506f..492a533f2 100644
--- a/app/javascript/mastodon/features/report/category.js
+++ b/app/javascript/mastodon/features/report/category.jsx
@@ -24,8 +24,6 @@ const mapStateToProps = state => ({
   rules: state.getIn(['server', 'server', 'rules'], ImmutableList()),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class Category extends React.PureComponent {
 
   static propTypes = {
@@ -104,3 +102,5 @@ class Category extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(Category));
diff --git a/app/javascript/mastodon/features/report/comment.js b/app/javascript/mastodon/features/report/comment.jsx
index cde156415..ab29f6c22 100644
--- a/app/javascript/mastodon/features/report/comment.js
+++ b/app/javascript/mastodon/features/report/comment.jsx
@@ -8,7 +8,6 @@ const messages = defineMessages({
   placeholder: { id: 'report.placeholder', defaultMessage: 'Type or paste additional comments' },
 });
 
-export default @injectIntl
 class Comment extends React.PureComponent {
 
   static propTypes = {
@@ -81,3 +80,5 @@ class Comment extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(Comment);
diff --git a/app/javascript/mastodon/features/report/components/option.js b/app/javascript/mastodon/features/report/components/option.jsx
index 744d85268..342204e22 100644
--- a/app/javascript/mastodon/features/report/components/option.js
+++ b/app/javascript/mastodon/features/report/components/option.jsx
@@ -24,12 +24,12 @@ export default class Option extends React.PureComponent {
       e.preventDefault();
       onToggle(value, !checked);
     }
-  }
+  };
 
   handleChange = e => {
     const { value, onToggle } = this.props;
     onToggle(value, e.target.checked);
-  }
+  };
 
   render () {
     const { name, value, checked, label, labelComponent, description, multiple } = this.props;
@@ -40,7 +40,7 @@ export default class Option extends React.PureComponent {
 
         <span
           className={classNames('poll__input', { active: checked, checkbox: multiple })}
-          tabIndex='0'
+          tabIndex={0}
           role='radio'
           onKeyPress={this.handleKeyPress}
           aria-checked={checked}
diff --git a/app/javascript/mastodon/features/report/components/status_check_box.js b/app/javascript/mastodon/features/report/components/status_check_box.jsx
index 5366da90b..28d572e89 100644
--- a/app/javascript/mastodon/features/report/components/status_check_box.js
+++ b/app/javascript/mastodon/features/report/components/status_check_box.jsx
@@ -17,7 +17,6 @@ const messages = defineMessages({
   direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
 });
 
-export default @injectIntl
 class StatusCheckBox extends React.PureComponent {
 
   static propTypes = {
@@ -80,3 +79,5 @@ class StatusCheckBox extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(StatusCheckBox);
diff --git a/app/javascript/mastodon/features/report/rules.js b/app/javascript/mastodon/features/report/rules.jsx
index 920da68d6..b500c0503 100644
--- a/app/javascript/mastodon/features/report/rules.js
+++ b/app/javascript/mastodon/features/report/rules.jsx
@@ -10,7 +10,6 @@ const mapStateToProps = state => ({
   rules: state.getIn(['server', 'server', 'rules']),
 });
 
-export default @connect(mapStateToProps)
 class Rules extends React.PureComponent {
 
   static propTypes = {
@@ -62,3 +61,5 @@ class Rules extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Rules);
diff --git a/app/javascript/mastodon/features/report/statuses.js b/app/javascript/mastodon/features/report/statuses.jsx
index d5d86034f..87163aeb8 100644
--- a/app/javascript/mastodon/features/report/statuses.js
+++ b/app/javascript/mastodon/features/report/statuses.jsx
@@ -13,7 +13,6 @@ const mapStateToProps = (state, { accountId }) => ({
   isLoading: state.getIn(['timelines', `account:${accountId}:with_replies`, 'isLoading']),
 });
 
-export default @connect(mapStateToProps)
 class Statuses extends React.PureComponent {
 
   static propTypes = {
@@ -59,3 +58,5 @@ class Statuses extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Statuses);
diff --git a/app/javascript/mastodon/features/report/thanks.js b/app/javascript/mastodon/features/report/thanks.jsx
index d169b1e32..4e5b6e864 100644
--- a/app/javascript/mastodon/features/report/thanks.js
+++ b/app/javascript/mastodon/features/report/thanks.jsx
@@ -12,7 +12,6 @@ import {
 
 const mapStateToProps = () => ({});
 
-export default @connect(mapStateToProps)
 class Thanks extends React.PureComponent {
 
   static propTypes = {
@@ -82,3 +81,5 @@ class Thanks extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(Thanks);
diff --git a/app/javascript/mastodon/features/standalone/compose/index.js b/app/javascript/mastodon/features/standalone/compose/index.jsx
index 0d764575f..fbadef6f4 100644
--- a/app/javascript/mastodon/features/standalone/compose/index.js
+++ b/app/javascript/mastodon/features/standalone/compose/index.jsx
@@ -9,7 +9,7 @@ export default class Compose extends React.PureComponent {
   render () {
     return (
       <div>
-        <ComposeFormContainer />
+        <ComposeFormContainer autoFocus />
         <NotificationsContainer />
         <ModalContainer />
         <LoadingBarContainer className='loading-bar' />
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.jsx
index 46ee9f6c1..0c74c4cc4 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.js
+++ b/app/javascript/mastodon/features/status/components/action_bar.jsx
@@ -13,7 +13,7 @@ const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
   redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
   edit: { id: 'status.edit', defaultMessage: 'Edit' },
-  direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
+  direct: { id: 'status.direct', defaultMessage: 'Privately mention @{name}' },
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   reply: { id: 'status.reply', defaultMessage: 'Reply' },
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
@@ -47,8 +47,6 @@ const mapStateToProps = (state, { status }) => ({
   relationship: state.getIn(['relationships', status.getIn(['account', 'id'])]),
 });
 
-export default @connect(mapStateToProps)
-@injectIntl
 class ActionBar extends React.PureComponent {
 
   static contextTypes = {
@@ -82,39 +80,39 @@ class ActionBar extends React.PureComponent {
 
   handleReplyClick = () => {
     this.props.onReply(this.props.status);
-  }
+  };
 
   handleReblogClick = (e) => {
     this.props.onReblog(this.props.status, e);
-  }
+  };
 
   handleFavouriteClick = () => {
     this.props.onFavourite(this.props.status);
-  }
+  };
 
   handleBookmarkClick = (e) => {
     this.props.onBookmark(this.props.status, e);
-  }
+  };
 
   handleDeleteClick = () => {
     this.props.onDelete(this.props.status, this.context.router.history);
-  }
+  };
 
   handleRedraftClick = () => {
     this.props.onDelete(this.props.status, this.context.router.history, true);
-  }
+  };
 
   handleEditClick = () => {
     this.props.onEdit(this.props.status, this.context.router.history);
-  }
+  };
 
   handleDirectClick = () => {
     this.props.onDirect(this.props.status.get('account'), this.context.router.history);
-  }
+  };
 
   handleMentionClick = () => {
     this.props.onMention(this.props.status.get('account'), this.context.router.history);
-  }
+  };
 
   handleMuteClick = () => {
     const { status, relationship, onMute, onUnmute } = this.props;
@@ -125,7 +123,7 @@ class ActionBar extends React.PureComponent {
     } else {
       onMute(account);
     }
-  }
+  };
 
   handleBlockClick = () => {
     const { status, relationship, onBlock, onUnblock } = this.props;
@@ -136,49 +134,49 @@ class ActionBar extends React.PureComponent {
     } else {
       onBlock(status);
     }
-  }
+  };
 
   handleBlockDomain = () => {
     const { status, onBlockDomain } = this.props;
     const account = status.get('account');
 
     onBlockDomain(account.get('acct').split('@')[1]);
-  }
+  };
 
   handleUnblockDomain = () => {
     const { status, onUnblockDomain } = this.props;
     const account = status.get('account');
 
     onUnblockDomain(account.get('acct').split('@')[1]);
-  }
+  };
 
   handleConversationMuteClick = () => {
     this.props.onMuteConversation(this.props.status);
-  }
+  };
 
   handleReport = () => {
     this.props.onReport(this.props.status);
-  }
+  };
 
   handlePinClick = () => {
     this.props.onPin(this.props.status);
-  }
+  };
 
   handleShare = () => {
     navigator.share({
       text: this.props.status.get('search_index'),
       url: this.props.status.get('url'),
     });
-  }
+  };
 
   handleEmbed = () => {
     this.props.onEmbed(this.props.status);
-  }
+  };
 
   handleCopy = () => {
     const url = this.props.status.get('url');
     navigator.clipboard.writeText(url);
-  }
+  };
 
   render () {
     const { status, relationship, intl } = this.props;
@@ -298,3 +296,5 @@ class ActionBar extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(ActionBar));
diff --git a/app/javascript/mastodon/features/status/components/card.js b/app/javascript/mastodon/features/status/components/card.jsx
index 82537dd5d..b588c83e4 100644
--- a/app/javascript/mastodon/features/status/components/card.js
+++ b/app/javascript/mastodon/features/status/components/card.jsx
@@ -146,7 +146,7 @@ export default class Card extends React.PureComponent {
     } else {
       this.setState({ embedded: true });
     }
-  }
+  };
 
   setRef = c => {
     this.node = c;
@@ -154,17 +154,17 @@ export default class Card extends React.PureComponent {
     if (this.node) {
       this._setDimensions();
     }
-  }
+  };
 
   handleImageLoad = () => {
     this.setState({ previewLoaded: true });
-  }
+  };
 
   handleReveal = e => {
     e.preventDefault();
     e.stopPropagation();
     this.setState({ revealed: true });
-  }
+  };
 
   renderVideo () {
     const { card }  = this.props;
@@ -196,11 +196,12 @@ export default class Card extends React.PureComponent {
     const interactive = card.get('type') !== 'link';
     const className   = classnames('status-card', { horizontal, compact, interactive });
     const title       = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener noreferrer' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
+    const language    = card.get('language') || '';
     const ratio       = card.get('width') / card.get('height');
     const height      = (compact && !embedded) ? (width / (16 / 9)) : (width / ratio);
 
     const description = (
-      <div className='status-card__content'>
+      <div className='status-card__content' lang={language}>
         {title}
         {!(horizontal || compact) && <p className='status-card__description'>{trim(card.get('description') || '', maxDescription)}</p>}
         <span className='status-card__host'>{provider}</span>
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.js b/app/javascript/mastodon/features/status/components/detailed_status.jsx
index c62910e0e..e4e572026 100644
--- a/app/javascript/mastodon/features/status/components/detailed_status.js
+++ b/app/javascript/mastodon/features/status/components/detailed_status.jsx
@@ -6,7 +6,7 @@ import DisplayName from '../../../components/display_name';
 import StatusContent from '../../../components/status_content';
 import MediaGallery from '../../../components/media_gallery';
 import { Link } from 'react-router-dom';
-import { injectIntl, defineMessages, FormattedDate } from 'react-intl';
+import { injectIntl, defineMessages, FormattedDate, FormattedMessage } from 'react-intl';
 import Card from './card';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import Video from '../../video';
@@ -25,7 +25,6 @@ const messages = defineMessages({
   direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
 });
 
-export default  @injectIntl
 class DetailedStatus extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -61,15 +60,15 @@ class DetailedStatus extends ImmutablePureComponent {
     }
 
     e.stopPropagation();
-  }
+  };
 
   handleOpenVideo = (options) => {
     this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), options);
-  }
+  };
 
   handleExpandedToggle = () => {
     this.props.onToggleHidden(this.props.status);
-  }
+  };
 
   _measureHeight (heightJustChanged) {
     if (this.props.measureHeight && this.node) {
@@ -84,7 +83,7 @@ class DetailedStatus extends ImmutablePureComponent {
   setRef = c => {
     this.node = c;
     this._measureHeight();
-  }
+  };
 
   componentDidUpdate (prevProps, prevState) {
     this._measureHeight(prevState.height !== this.state.height);
@@ -102,12 +101,12 @@ class DetailedStatus extends ImmutablePureComponent {
     }
 
     window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
-  }
+  };
 
   handleTranslate = () => {
     const { onTranslate, status } = this.props;
     onTranslate(status);
-  }
+  };
 
   render () {
     const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
@@ -139,6 +138,7 @@ class DetailedStatus extends ImmutablePureComponent {
           <Audio
             src={attachment.get('url')}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             duration={attachment.getIn(['meta', 'original', 'duration'], 0)}
             poster={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
             backgroundColor={attachment.getIn(['meta', 'colors', 'background'])}
@@ -161,6 +161,7 @@ class DetailedStatus extends ImmutablePureComponent {
             blurhash={attachment.get('blurhash')}
             src={attachment.get('url')}
             alt={attachment.get('description')}
+            lang={status.get('language')}
             width={300}
             height={150}
             inline
@@ -176,6 +177,7 @@ class DetailedStatus extends ImmutablePureComponent {
             standalone
             sensitive={status.get('sensitive')}
             media={status.get('media_attachments')}
+            lang={status.get('language')}
             height={300}
             onOpenMedia={this.props.onOpenMedia}
             visible={this.props.showMedia}
@@ -260,7 +262,13 @@ class DetailedStatus extends ImmutablePureComponent {
 
     return (
       <div style={outerStyle}>
-        <div ref={this.setRef} className={classNames('detailed-status', `detailed-status-${status.get('visibility')}`, { compact })}>
+        <div ref={this.setRef} className={classNames('detailed-status', { compact })}>
+          {status.get('visibility') === 'direct' && (
+            <div className='status__prepend'>
+              <div className='status__prepend-icon-wrapper'><Icon id='at' className='status__prepend-icon' fixedWidth /></div>
+              <FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' />
+            </div>
+          )}
           <a href={`/@${status.getIn(['account', 'acct'])}`} onClick={this.handleAccountClick} className='detailed-status__display-name'>
             <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={46} /></div>
             <DisplayName account={status.get('account')} localDomain={this.props.domain} />
@@ -276,7 +284,7 @@ class DetailedStatus extends ImmutablePureComponent {
           {media}
 
           <div className='detailed-status__meta'>
-            <a className='detailed-status__datetime' href={`/@${status.getIn(['account', 'acct'])}\/${status.get('id')}`} target='_blank' rel='noopener noreferrer'>
+            <a className='detailed-status__datetime' href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} target='_blank' rel='noopener noreferrer'>
               <FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />
             </a>{edited}{visibilityLink}{applicationLink}{reblogLink} · {favouriteLink}
           </div>
@@ -286,3 +294,5 @@ class DetailedStatus extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(DetailedStatus);
diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.jsx
index 8a63cced2..2d18c6e9d 100644
--- a/app/javascript/mastodon/features/status/index.js
+++ b/app/javascript/mastodon/features/status/index.jsx
@@ -5,7 +5,17 @@ import PropTypes from 'prop-types';
 import classNames from 'classnames';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { createSelector } from 'reselect';
-import { fetchStatus } from '../../actions/statuses';
+import {
+  fetchStatus,
+  muteStatus,
+  unmuteStatus,
+  deleteStatus,
+  editStatus,
+  hideStatus,
+  revealStatus,
+  translateStatus,
+  undoStatusTranslation,
+} from '../../actions/statuses';
 import MissingIndicator from '../../components/missing_indicator';
 import LoadingIndicator from 'mastodon/components/loading_indicator';
 import DetailedStatus from './components/detailed_status';
@@ -27,16 +37,6 @@ import {
   directCompose,
 } from '../../actions/compose';
 import {
-  muteStatus,
-  unmuteStatus,
-  deleteStatus,
-  editStatus,
-  hideStatus,
-  revealStatus,
-  translateStatus,
-  undoStatusTranslation,
-} from '../../actions/statuses';
-import {
   unblockAccount,
   unmuteAccount,
 } from '../../actions/accounts';
@@ -176,8 +176,6 @@ const titleFromStatus = status => {
   return `${prefix}: "${truncate(text, 30)}"`;
 };
 
-export default @injectIntl
-@connect(makeMapStateToProps)
 class Status extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -233,7 +231,7 @@ class Status extends ImmutablePureComponent {
 
   handleToggleMediaVisibility = () => {
     this.setState({ showMedia: !this.state.showMedia });
-  }
+  };
 
   handleFavouriteClick = (status) => {
     const { dispatch } = this.props;
@@ -252,7 +250,7 @@ class Status extends ImmutablePureComponent {
         url: status.get('url'),
       }));
     }
-  }
+  };
 
   handlePin = (status) => {
     if (status.get('pinned')) {
@@ -260,7 +258,7 @@ class Status extends ImmutablePureComponent {
     } else {
       this.props.dispatch(pin(status));
     }
-  }
+  };
 
   handleReplyClick = (status) => {
     const { askReplyConfirmation, dispatch, intl } = this.props;
@@ -283,11 +281,11 @@ class Status extends ImmutablePureComponent {
         url: status.get('url'),
       }));
     }
-  }
+  };
 
   handleModalReblog = (status, privacy) => {
     this.props.dispatch(reblog(status, privacy));
-  }
+  };
 
   handleReblogClick = (status, e) => {
     const { dispatch } = this.props;
@@ -310,7 +308,7 @@ class Status extends ImmutablePureComponent {
         url: status.get('url'),
       }));
     }
-  }
+  };
 
   handleBookmarkClick = (status) => {
     if (status.get('bookmarked')) {
@@ -318,7 +316,7 @@ class Status extends ImmutablePureComponent {
     } else {
       this.props.dispatch(bookmark(status));
     }
-  }
+  };
 
   handleDeleteClick = (status, history, withRedraft = false) => {
     const { dispatch, intl } = this.props;
@@ -332,27 +330,27 @@ class Status extends ImmutablePureComponent {
         onConfirm: () => dispatch(deleteStatus(status.get('id'), history, withRedraft)),
       }));
     }
-  }
+  };
 
   handleEditClick = (status, history) => {
     this.props.dispatch(editStatus(status.get('id'), history));
-  }
+  };
 
   handleDirectClick = (account, router) => {
     this.props.dispatch(directCompose(account, router));
-  }
+  };
 
   handleMentionClick = (account, router) => {
     this.props.dispatch(mentionCompose(account, router));
-  }
+  };
 
   handleOpenMedia = (media, index) => {
     this.props.dispatch(openModal('MEDIA', { statusId: this.props.status.get('id'), media, index }));
-  }
+  };
 
   handleOpenVideo = (media, options) => {
     this.props.dispatch(openModal('VIDEO', { statusId: this.props.status.get('id'), media, options }));
-  }
+  };
 
   handleHotkeyOpenMedia = e => {
     const { status } = this.props;
@@ -366,11 +364,11 @@ class Status extends ImmutablePureComponent {
         this.handleOpenMedia(status.get('media_attachments'), 0);
       }
     }
-  }
+  };
 
   handleMuteClick = (account) => {
     this.props.dispatch(initMuteModal(account));
-  }
+  };
 
   handleConversationMuteClick = (status) => {
     if (status.get('muted')) {
@@ -378,7 +376,7 @@ class Status extends ImmutablePureComponent {
     } else {
       this.props.dispatch(muteStatus(status.get('id')));
     }
-  }
+  };
 
   handleToggleHidden = (status) => {
     if (status.get('hidden')) {
@@ -386,7 +384,7 @@ class Status extends ImmutablePureComponent {
     } else {
       this.props.dispatch(hideStatus(status.get('id')));
     }
-  }
+  };
 
   handleToggleAll = () => {
     const { status, ancestorsIds, descendantsIds } = this.props;
@@ -397,7 +395,7 @@ class Status extends ImmutablePureComponent {
     } else {
       this.props.dispatch(hideStatus(statusIds));
     }
-  }
+  };
 
   handleTranslate = status => {
     const { dispatch } = this.props;
@@ -407,29 +405,29 @@ class Status extends ImmutablePureComponent {
     } else {
       dispatch(translateStatus(status.get('id')));
     }
-  }
+  };
 
   handleBlockClick = (status) => {
     const { dispatch } = this.props;
     const account = status.get('account');
     dispatch(initBlockModal(account));
-  }
+  };
 
   handleReport = (status) => {
     this.props.dispatch(initReport(status.get('account'), status));
-  }
+  };
 
   handleEmbed = (status) => {
     this.props.dispatch(openModal('EMBED', { url: status.get('url') }));
-  }
+  };
 
   handleUnmuteClick = account => {
     this.props.dispatch(unmuteAccount(account.get('id')));
-  }
+  };
 
   handleUnblockClick = account => {
     this.props.dispatch(unblockAccount(account.get('id')));
-  }
+  };
 
   handleBlockDomainClick = domain => {
     this.props.dispatch(openModal('CONFIRM', {
@@ -437,50 +435,50 @@ class Status extends ImmutablePureComponent {
       confirm: this.props.intl.formatMessage(messages.blockDomainConfirm),
       onConfirm: () => this.props.dispatch(blockDomain(domain)),
     }));
-  }
+  };
 
   handleUnblockDomainClick = domain => {
     this.props.dispatch(unblockDomain(domain));
-  }
+  };
 
 
   handleHotkeyMoveUp = () => {
     this.handleMoveUp(this.props.status.get('id'));
-  }
+  };
 
   handleHotkeyMoveDown = () => {
     this.handleMoveDown(this.props.status.get('id'));
-  }
+  };
 
   handleHotkeyReply = e => {
     e.preventDefault();
     this.handleReplyClick(this.props.status);
-  }
+  };
 
   handleHotkeyFavourite = () => {
     this.handleFavouriteClick(this.props.status);
-  }
+  };
 
   handleHotkeyBoost = () => {
     this.handleReblogClick(this.props.status);
-  }
+  };
 
   handleHotkeyMention = e => {
     e.preventDefault();
     this.handleMentionClick(this.props.status.get('account'));
-  }
+  };
 
   handleHotkeyOpenProfile = () => {
     this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
-  }
+  };
 
   handleHotkeyToggleHidden = () => {
     this.handleToggleHidden(this.props.status);
-  }
+  };
 
   handleHotkeyToggleSensitive = () => {
     this.handleToggleMediaVisibility();
-  }
+  };
 
   handleMoveUp = id => {
     const { status, ancestorsIds, descendantsIds } = this.props;
@@ -497,7 +495,7 @@ class Status extends ImmutablePureComponent {
         this._selectChild(index - 1, true);
       }
     }
-  }
+  };
 
   handleMoveDown = id => {
     const { status, ancestorsIds, descendantsIds } = this.props;
@@ -514,7 +512,7 @@ class Status extends ImmutablePureComponent {
         this._selectChild(index + 1, false);
       }
     }
-  }
+  };
 
   _selectChild (index, align_top) {
     const container = this.node;
@@ -544,7 +542,7 @@ class Status extends ImmutablePureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   componentDidUpdate () {
     if (this._scrolledIntoView) {
@@ -569,7 +567,7 @@ class Status extends ImmutablePureComponent {
 
   onFullScreenChange = () => {
     this.setState({ fullscreen: isFullscreen() });
-  }
+  };
 
   render () {
     let ancestors, descendants;
@@ -632,7 +630,7 @@ class Status extends ImmutablePureComponent {
             {ancestors}
 
             <HotKeys handlers={handlers}>
-              <div className={classNames('focusable', 'detailed-status__wrapper')} tabIndex='0' aria-label={textForScreenReader(intl, status, false)}>
+              <div className={classNames('focusable', 'detailed-status__wrapper', `detailed-status__wrapper-${status.get('visibility')}`)} tabIndex={0} aria-label={textForScreenReader(intl, status, false)}>
                 <DetailedStatus
                   key={`details-${status.get('id')}`}
                   status={status}
@@ -684,3 +682,5 @@ class Status extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(connect(makeMapStateToProps)(Status));
diff --git a/app/javascript/mastodon/features/subscribed_languages_modal/index.js b/app/javascript/mastodon/features/subscribed_languages_modal/index.jsx
index a519ceabc..04fe31b5c 100644
--- a/app/javascript/mastodon/features/subscribed_languages_modal/index.js
+++ b/app/javascript/mastodon/features/subscribed_languages_modal/index.jsx
@@ -36,8 +36,6 @@ const mapDispatchToProps = (dispatch, { accountId }) => ({
 
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class SubscribedLanguagesModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -72,7 +70,7 @@ class SubscribedLanguagesModal extends ImmutablePureComponent {
   handleSubmit = () => {
     this.props.onSubmit(this.state.selectedLanguages.toArray());
     this.props.onClose();
-  }
+  };
 
   renderItem (value) {
     const language = this.props.languages.find(language => language[0] === value);
@@ -123,3 +121,5 @@ class SubscribedLanguagesModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(SubscribedLanguagesModal));
diff --git a/app/javascript/mastodon/features/ui/components/__tests__/column-test.js b/app/javascript/mastodon/features/ui/components/__tests__/column-test.jsx
index a56859be0..a56859be0 100644
--- a/app/javascript/mastodon/features/ui/components/__tests__/column-test.js
+++ b/app/javascript/mastodon/features/ui/components/__tests__/column-test.jsx
diff --git a/app/javascript/mastodon/features/ui/components/actions_modal.js b/app/javascript/mastodon/features/ui/components/actions_modal.jsx
index 67be69d43..35090e242 100644
--- a/app/javascript/mastodon/features/ui/components/actions_modal.js
+++ b/app/javascript/mastodon/features/ui/components/actions_modal.jsx
@@ -23,7 +23,7 @@ export default class ActionsModal extends ImmutablePureComponent {
     return (
       <li key={`${text}-${i}`}>
         <a href={href} target='_blank' rel='noopener noreferrer' onClick={this.props.onClick} data-index={i} className={classNames({ active })}>
-          {icon && <IconButton title={text} icon={icon} role='presentation' tabIndex='-1' inverted />}
+          {icon && <IconButton title={text} icon={icon} role='presentation' tabIndex={-1} inverted />}
           <div>
             <div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
             <div>{meta}</div>
@@ -31,7 +31,7 @@ export default class ActionsModal extends ImmutablePureComponent {
         </a>
       </li>
     );
-  }
+  };
 
   render () {
     return (
diff --git a/app/javascript/mastodon/features/ui/components/audio_modal.js b/app/javascript/mastodon/features/ui/components/audio_modal.jsx
index c46fefce8..c0ac12ba7 100644
--- a/app/javascript/mastodon/features/ui/components/audio_modal.js
+++ b/app/javascript/mastodon/features/ui/components/audio_modal.jsx
@@ -7,15 +7,16 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 import Footer from 'mastodon/features/picture_in_picture/components/footer';
 
 const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
   accountStaticAvatar: state.getIn(['accounts', state.getIn(['statuses', statusId, 'account']), 'avatar_static']),
 });
 
-export default @connect(mapStateToProps)
 class AudioModal extends ImmutablePureComponent {
 
   static propTypes = {
     media: ImmutablePropTypes.map.isRequired,
     statusId: PropTypes.string.isRequired,
+    language: PropTypes.string,
     accountStaticAvatar: PropTypes.string.isRequired,
     options: PropTypes.shape({
       autoPlay: PropTypes.bool,
@@ -25,7 +26,7 @@ class AudioModal extends ImmutablePureComponent {
   };
 
   render () {
-    const { media, accountStaticAvatar, statusId, onClose } = this.props;
+    const { media, language, accountStaticAvatar, statusId, onClose } = this.props;
     const options = this.props.options || {};
 
     return (
@@ -34,6 +35,7 @@ class AudioModal extends ImmutablePureComponent {
           <Audio
             src={media.get('url')}
             alt={media.get('description')}
+            lang={language}
             duration={media.getIn(['meta', 'original', 'duration'], 0)}
             height={150}
             poster={media.get('preview_url') || accountStaticAvatar}
@@ -52,3 +54,5 @@ class AudioModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(AudioModal);
diff --git a/app/javascript/mastodon/features/ui/components/block_modal.js b/app/javascript/mastodon/features/ui/components/block_modal.jsx
index a07baeaa6..a9506aa69 100644
--- a/app/javascript/mastodon/features/ui/components/block_modal.js
+++ b/app/javascript/mastodon/features/ui/components/block_modal.jsx
@@ -36,8 +36,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-export default @connect(makeMapStateToProps, mapDispatchToProps)
-@injectIntl
 class BlockModal extends React.PureComponent {
 
   static propTypes = {
@@ -55,20 +53,20 @@ class BlockModal extends React.PureComponent {
   handleClick = () => {
     this.props.onClose();
     this.props.onConfirm(this.props.account);
-  }
+  };
 
   handleSecondary = () => {
     this.props.onClose();
     this.props.onBlockAndReport(this.props.account);
-  }
+  };
 
   handleCancel = () => {
     this.props.onClose();
-  }
+  };
 
   setRef = (c) => {
     this.button = c;
-  }
+  };
 
   render () {
     const { account } = this.props;
@@ -101,3 +99,5 @@ class BlockModal extends React.PureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps, mapDispatchToProps)(injectIntl(BlockModal));
diff --git a/app/javascript/mastodon/features/ui/components/boost_modal.js b/app/javascript/mastodon/features/ui/components/boost_modal.jsx
index 077ce7b35..fe55c963a 100644
--- a/app/javascript/mastodon/features/ui/components/boost_modal.js
+++ b/app/javascript/mastodon/features/ui/components/boost_modal.jsx
@@ -38,8 +38,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class BoostModal extends ImmutablePureComponent {
 
   static contextTypes = {
@@ -62,7 +60,7 @@ class BoostModal extends ImmutablePureComponent {
   handleReblog = () => {
     this.props.onReblog(this.props.status, this.props.privacy);
     this.props.onClose();
-  }
+  };
 
   handleAccountClick = (e) => {
     if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
@@ -70,7 +68,7 @@ class BoostModal extends ImmutablePureComponent {
       this.props.onClose();
       this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
     }
-  }
+  };
 
   _findContainer = () => {
     return document.getElementsByClassName('modal-root__container')[0];
@@ -78,7 +76,7 @@ class BoostModal extends ImmutablePureComponent {
 
   setRef = (c) => {
     this.button = c;
-  }
+  };
 
   render () {
     const { status, privacy, intl } = this.props;
@@ -98,7 +96,7 @@ class BoostModal extends ImmutablePureComponent {
         <div className='boost-modal__container'>
           <div className={classNames('status', `status-${status.get('visibility')}`, 'light')}>
             <div className='status__info'>
-              <a href={`/@${status.getIn(['account', 'acct'])}\/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'>
+              <a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'>
                 <span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span>
                 <RelativeTimestamp timestamp={status.get('created_at')} />
               </a>
@@ -140,3 +138,5 @@ class BoostModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(BoostModal));
diff --git a/app/javascript/mastodon/features/ui/components/bundle.js b/app/javascript/mastodon/features/ui/components/bundle.jsx
index a60ace35b..1b10a218b 100644
--- a/app/javascript/mastodon/features/ui/components/bundle.js
+++ b/app/javascript/mastodon/features/ui/components/bundle.jsx
@@ -15,7 +15,7 @@ class Bundle extends React.PureComponent {
     onFetch: PropTypes.func,
     onFetchSuccess: PropTypes.func,
     onFetchFail: PropTypes.func,
-  }
+  };
 
   static defaultProps = {
     loading: emptyComponent,
@@ -24,14 +24,14 @@ class Bundle extends React.PureComponent {
     onFetch: noop,
     onFetchSuccess: noop,
     onFetchFail: noop,
-  }
+  };
 
-  static cache = new Map
+  static cache = new Map;
 
   state = {
     mod: undefined,
     forceRender: false,
-  }
+  };
 
   componentWillMount() {
     this.load(this.props);
@@ -83,7 +83,7 @@ class Bundle extends React.PureComponent {
         this.setState({ mod: null });
         onFetchFail(error);
       });
-  }
+  };
 
   render() {
     const { loading: Loading, error: Error, children, renderDelay } = this.props;
diff --git a/app/javascript/mastodon/features/ui/components/bundle_column_error.js b/app/javascript/mastodon/features/ui/components/bundle_column_error.jsx
index dfe970ad0..dc9b0e539 100644
--- a/app/javascript/mastodon/features/ui/components/bundle_column_error.js
+++ b/app/javascript/mastodon/features/ui/components/bundle_column_error.jsx
@@ -31,7 +31,7 @@ class GIF extends React.PureComponent {
     if (!animate) {
       this.setState({ hovering: true });
     }
-  }
+  };
 
   handleMouseLeave = () => {
     const { animate } = this.props;
@@ -39,7 +39,7 @@ class GIF extends React.PureComponent {
     if (!animate) {
       this.setState({ hovering: false });
     }
-  }
+  };
 
   render () {
     const { src, staticSrc, className, animate } = this.props;
@@ -75,7 +75,7 @@ class CopyButton extends React.PureComponent {
     navigator.clipboard.writeText(value);
     this.setState({ copied: true });
     this.timeout = setTimeout(() => this.setState({ copied: false }), 700);
-  }
+  };
 
   componentWillUnmount () {
     if (this.timeout) clearTimeout(this.timeout);
@@ -92,7 +92,6 @@ class CopyButton extends React.PureComponent {
 
 }
 
-export default @injectIntl
 class BundleColumnError extends React.PureComponent {
 
   static propTypes = {
@@ -113,7 +112,7 @@ class BundleColumnError extends React.PureComponent {
     if (onRetry) {
       onRetry();
     }
-  }
+  };
 
   render () {
     const { errorType, multiColumn, stacktrace } = this.props;
@@ -160,3 +159,5 @@ class BundleColumnError extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(BundleColumnError);
diff --git a/app/javascript/mastodon/features/ui/components/bundle_modal_error.js b/app/javascript/mastodon/features/ui/components/bundle_modal_error.jsx
index f9365b95b..d79d0ca4a 100644
--- a/app/javascript/mastodon/features/ui/components/bundle_modal_error.js
+++ b/app/javascript/mastodon/features/ui/components/bundle_modal_error.jsx
@@ -16,11 +16,11 @@ class BundleModalError extends React.PureComponent {
     onRetry: PropTypes.func.isRequired,
     onClose: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
-  }
+  };
 
   handleRetry = () => {
     this.props.onRetry();
-  }
+  };
 
   render () {
     const { onClose, intl: { formatMessage } } = this.props;
diff --git a/app/javascript/mastodon/features/ui/components/column.js b/app/javascript/mastodon/features/ui/components/column.jsx
index 15538ea38..7bc2f7e00 100644
--- a/app/javascript/mastodon/features/ui/components/column.js
+++ b/app/javascript/mastodon/features/ui/components/column.jsx
@@ -23,7 +23,7 @@ export default class Column extends React.PureComponent {
     }
 
     this._interruptScrollAnimation = scrollTop(scrollable);
-  }
+  };
 
   scrollTop () {
     const scrollable = this.node.querySelector('.scrollable');
@@ -40,11 +40,11 @@ export default class Column extends React.PureComponent {
     if (typeof this._interruptScrollAnimation !== 'undefined') {
       this._interruptScrollAnimation();
     }
-  }, 200)
+  }, 200);
 
   setRef = (c) => {
     this.node = c;
-  }
+  };
 
   render () {
     const { heading, icon, children, active, hideHeadingOnMobile } = this.props;
diff --git a/app/javascript/mastodon/features/ui/components/column_header.js b/app/javascript/mastodon/features/ui/components/column_header.jsx
index b1a36e173..4ceef5957 100644
--- a/app/javascript/mastodon/features/ui/components/column_header.js
+++ b/app/javascript/mastodon/features/ui/components/column_header.jsx
@@ -15,7 +15,7 @@ export default class ColumnHeader extends React.PureComponent {
 
   handleClick = () => {
     this.props.onClick();
-  }
+  };
 
   render () {
     const { icon, type, active, columnHeaderId } = this.props;
diff --git a/app/javascript/mastodon/features/ui/components/column_link.js b/app/javascript/mastodon/features/ui/components/column_link.jsx
index 8eebbf526..8eebbf526 100644
--- a/app/javascript/mastodon/features/ui/components/column_link.js
+++ b/app/javascript/mastodon/features/ui/components/column_link.jsx
diff --git a/app/javascript/mastodon/features/ui/components/column_loading.js b/app/javascript/mastodon/features/ui/components/column_loading.jsx
index e5ed22584..e5ed22584 100644
--- a/app/javascript/mastodon/features/ui/components/column_loading.js
+++ b/app/javascript/mastodon/features/ui/components/column_loading.jsx
diff --git a/app/javascript/mastodon/features/ui/components/column_subheading.js b/app/javascript/mastodon/features/ui/components/column_subheading.jsx
index 8160c4aa3..8160c4aa3 100644
--- a/app/javascript/mastodon/features/ui/components/column_subheading.js
+++ b/app/javascript/mastodon/features/ui/components/column_subheading.jsx
diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.jsx
index e7def800e..1dd6e34e8 100644
--- a/app/javascript/mastodon/features/ui/components/columns_area.js
+++ b/app/javascript/mastodon/features/ui/components/columns_area.jsx
@@ -57,7 +57,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
 
   state = {
     renderComposePanel: !(this.mediaQuery && this.mediaQuery.matches),
-  }
+  };
 
   componentDidMount() {
     if (!this.props.singleColumn) {
@@ -111,7 +111,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
 
   handleLayoutChange = (e) => {
     this.setState({ renderComposePanel: !e.matches });
-  }
+  };
 
   handleWheel = () => {
     if (typeof this._interruptScrollAnimation !== 'function') {
@@ -119,19 +119,19 @@ export default class ColumnsArea extends ImmutablePureComponent {
     }
 
     this._interruptScrollAnimation();
-  }
+  };
 
   setRef = (node) => {
     this.node = node;
-  }
+  };
 
   renderLoading = columnId => () => {
     return columnId === 'COMPOSE' ? <DrawerLoading /> : <ColumnLoading multiColumn />;
-  }
+  };
 
   renderError = (props) => {
     return <BundleColumnError multiColumn errorType='network' {...props} />;
-  }
+  };
 
   render () {
     const { columns, children, singleColumn, isModalOpen } = this.props;
diff --git a/app/javascript/mastodon/features/ui/components/compare_history_modal.js b/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx
index ecccc8f7d..1802c167c 100644
--- a/app/javascript/mastodon/features/ui/components/compare_history_modal.js
+++ b/app/javascript/mastodon/features/ui/components/compare_history_modal.jsx
@@ -12,6 +12,7 @@ import RelativeTimestamp from 'mastodon/components/relative_timestamp';
 import MediaAttachments from 'mastodon/components/media_attachments';
 
 const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
   versions: state.getIn(['history', statusId, 'items']),
 });
 
@@ -23,18 +24,18 @@ const mapDispatchToProps = dispatch => ({
 
 });
 
-export default @connect(mapStateToProps, mapDispatchToProps)
 class CompareHistoryModal extends React.PureComponent {
 
   static propTypes = {
     onClose: PropTypes.func.isRequired,
     index: PropTypes.number.isRequired,
     statusId: PropTypes.string.isRequired,
+    language: PropTypes.string.isRequired,
     versions: ImmutablePropTypes.list.isRequired,
   };
 
   render () {
-    const { index, versions, onClose } = this.props;
+    const { index, versions, language, onClose } = this.props;
     const currentVersion = versions.get(index);
 
     const emojiMap = currentVersion.get('emojis').reduce((obj, emoji) => {
@@ -65,12 +66,12 @@ class CompareHistoryModal extends React.PureComponent {
           <div className='status__content'>
             {currentVersion.get('spoiler_text').length > 0 && (
               <React.Fragment>
-                <div className='translate' dangerouslySetInnerHTML={spoilerContent} />
+                <div className='translate' dangerouslySetInnerHTML={spoilerContent} lang={language} />
                 <hr />
               </React.Fragment>
             )}
 
-            <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
+            <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} lang={language} />
 
             {!!currentVersion.get('poll') && (
               <div className='poll'>
@@ -82,6 +83,7 @@ class CompareHistoryModal extends React.PureComponent {
                       <span
                         className='poll__option__text translate'
                         dangerouslySetInnerHTML={{ __html: emojify(escapeTextContentForBrowser(option.get('title')), emojiMap) }}
+                        lang={language}
                       />
                     </li>
                   ))}
@@ -89,7 +91,7 @@ class CompareHistoryModal extends React.PureComponent {
               </div>
             )}
 
-            <MediaAttachments status={currentVersion} />
+            <MediaAttachments status={currentVersion} lang={language} />
           </div>
         </div>
       </div>
@@ -97,3 +99,5 @@ class CompareHistoryModal extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(CompareHistoryModal);
diff --git a/app/javascript/mastodon/features/ui/components/compose_panel.js b/app/javascript/mastodon/features/ui/components/compose_panel.jsx
index 92d16b5b3..1c6f80ad5 100644
--- a/app/javascript/mastodon/features/ui/components/compose_panel.js
+++ b/app/javascript/mastodon/features/ui/components/compose_panel.jsx
@@ -8,7 +8,6 @@ import LinkFooter from './link_footer';
 import ServerBanner from 'mastodon/components/server_banner';
 import { changeComposing, mountCompose, unmountCompose } from 'mastodon/actions/compose';
 
-export default @connect()
 class ComposePanel extends React.PureComponent {
 
   static contextTypes = {
@@ -22,12 +21,12 @@ class ComposePanel extends React.PureComponent {
   onFocus = () => {
     const { dispatch } = this.props;
     dispatch(changeComposing(true));
-  }
+  };
 
   onBlur = () => {
     const { dispatch } = this.props;
     dispatch(changeComposing(false));
-  }
+  };
 
   componentDidMount () {
     const { dispatch } = this.props;
@@ -66,3 +65,5 @@ class ComposePanel extends React.PureComponent {
   }
 
 }
+
+export default connect()(ComposePanel);
diff --git a/app/javascript/mastodon/features/ui/components/confirmation_modal.js b/app/javascript/mastodon/features/ui/components/confirmation_modal.jsx
index 65d97ca16..4437567a1 100644
--- a/app/javascript/mastodon/features/ui/components/confirmation_modal.js
+++ b/app/javascript/mastodon/features/ui/components/confirmation_modal.jsx
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
 import { injectIntl, FormattedMessage } from 'react-intl';
 import Button from '../../../components/button';
 
-export default @injectIntl
 class ConfirmationModal extends React.PureComponent {
 
   static propTypes = {
@@ -30,20 +29,20 @@ class ConfirmationModal extends React.PureComponent {
       this.props.onClose();
     }
     this.props.onConfirm();
-  }
+  };
 
   handleSecondary = () => {
     this.props.onClose();
     this.props.onSecondary();
-  }
+  };
 
   handleCancel = () => {
     this.props.onClose();
-  }
+  };
 
   setRef = (c) => {
     this.button = c;
-  }
+  };
 
   render () {
     const { message, confirm, secondary } = this.props;
@@ -68,3 +67,5 @@ class ConfirmationModal extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ConfirmationModal);
diff --git a/app/javascript/mastodon/features/ui/components/disabled_account_banner.js b/app/javascript/mastodon/features/ui/components/disabled_account_banner.jsx
index 038cc3553..ea11cea44 100644
--- a/app/javascript/mastodon/features/ui/components/disabled_account_banner.js
+++ b/app/javascript/mastodon/features/ui/components/disabled_account_banner.jsx
@@ -28,8 +28,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
   },
 });
 
-export default @injectIntl
-@connect(mapStateToProps, mapDispatchToProps)
 class DisabledAccountBanner extends React.PureComponent {
 
   static propTypes = {
@@ -46,7 +44,7 @@ class DisabledAccountBanner extends React.PureComponent {
     this.props.onLogout();
 
     return false;
-  }
+  };
 
   render () {
     const { disabledAcct, movedToAcct } = this.props;
@@ -90,3 +88,5 @@ class DisabledAccountBanner extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(DisabledAccountBanner));
diff --git a/app/javascript/mastodon/features/ui/components/drawer_loading.js b/app/javascript/mastodon/features/ui/components/drawer_loading.jsx
index 08b0d2347..08b0d2347 100644
--- a/app/javascript/mastodon/features/ui/components/drawer_loading.js
+++ b/app/javascript/mastodon/features/ui/components/drawer_loading.jsx
diff --git a/app/javascript/mastodon/features/ui/components/embed_modal.js b/app/javascript/mastodon/features/ui/components/embed_modal.jsx
index 4679c9650..baf6be411 100644
--- a/app/javascript/mastodon/features/ui/components/embed_modal.js
+++ b/app/javascript/mastodon/features/ui/components/embed_modal.jsx
@@ -9,7 +9,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @injectIntl
 class EmbedModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -17,7 +16,7 @@ class EmbedModal extends ImmutablePureComponent {
     onClose: PropTypes.func.isRequired,
     onError: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
-  }
+  };
 
   state = {
     loading: false,
@@ -48,11 +47,11 @@ class EmbedModal extends ImmutablePureComponent {
 
   setIframeRef = c =>  {
     this.iframe = c;
-  }
+  };
 
   handleTextareaClick = (e) => {
     e.target.select();
-  }
+  };
 
   render () {
     const { intl, onClose } = this.props;
@@ -95,3 +94,5 @@ class EmbedModal extends ImmutablePureComponent {
   }
 
 }
+
+export default injectIntl(EmbedModal);
diff --git a/app/javascript/mastodon/features/ui/components/filter_modal.js b/app/javascript/mastodon/features/ui/components/filter_modal.jsx
index 376db961d..32ebaf7b7 100644
--- a/app/javascript/mastodon/features/ui/components/filter_modal.js
+++ b/app/javascript/mastodon/features/ui/components/filter_modal.jsx
@@ -13,8 +13,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @connect(undefined)
-@injectIntl
 class FilterModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -132,3 +130,5 @@ class FilterModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(injectIntl(FilterModal));
diff --git a/app/javascript/mastodon/features/ui/components/focal_point_modal.js b/app/javascript/mastodon/features/ui/components/focal_point_modal.jsx
index 479f4abd2..11c4c5237 100644
--- a/app/javascript/mastodon/features/ui/components/focal_point_modal.js
+++ b/app/javascript/mastodon/features/ui/components/focal_point_modal.jsx
@@ -39,6 +39,7 @@ const mapStateToProps = (state, { id }) => ({
   account: state.getIn(['accounts', me]),
   isUploadingThumbnail: state.getIn(['compose', 'isUploadingThumbnail']),
   description: state.getIn(['compose', 'media_modal', 'description']),
+  lang: state.getIn(['compose', 'language']),
   focusX: state.getIn(['compose', 'media_modal', 'focusX']),
   focusY: state.getIn(['compose', 'media_modal', 'focusY']),
   dirty: state.getIn(['compose', 'media_modal', 'dirty']),
@@ -99,8 +100,6 @@ class ImageLoader extends React.PureComponent {
 
 }
 
-export default @connect(mapStateToProps, mapDispatchToProps, null, { forwardRef: true })
-@(component => injectIntl(component, { withRef: true }))
 class FocalPointModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -134,7 +133,7 @@ class FocalPointModal extends ImmutablePureComponent {
 
     this.updatePosition(e);
     this.setState({ dragging: true });
-  }
+  };
 
   handleTouchStart = e => {
     document.addEventListener('touchmove', this.handleMouseMove);
@@ -142,25 +141,25 @@ class FocalPointModal extends ImmutablePureComponent {
 
     this.updatePosition(e);
     this.setState({ dragging: true });
-  }
+  };
 
   handleMouseMove = e => {
     this.updatePosition(e);
-  }
+  };
 
   handleMouseUp = () => {
     document.removeEventListener('mousemove', this.handleMouseMove);
     document.removeEventListener('mouseup', this.handleMouseUp);
 
     this.setState({ dragging: false });
-  }
+  };
 
   handleTouchEnd = () => {
     document.removeEventListener('touchmove', this.handleMouseMove);
     document.removeEventListener('touchend', this.handleTouchEnd);
 
     this.setState({ dragging: false });
-  }
+  };
 
   updatePosition = e => {
     const { x, y } = getPointerPosition(this.node, e);
@@ -168,24 +167,24 @@ class FocalPointModal extends ImmutablePureComponent {
     const focusY   = (y - .5) * -2;
 
     this.props.onChangeFocus(focusX, focusY);
-  }
+  };
 
   handleChange = e => {
     this.props.onChangeDescription(e.target.value);
-  }
+  };
 
   handleKeyDown = (e) => {
     if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
       this.props.onChangeDescription(e.target.value);
       this.handleSubmit(e);
     }
-  }
+  };
 
   handleSubmit = (e) => {
     e.preventDefault();
     e.stopPropagation();
     this.props.onSave(this.props.description, this.props.focusX, this.props.focusY);
-  }
+  };
 
   getCloseConfirmationMessage = () => {
     const { intl, dirty } = this.props;
@@ -198,15 +197,15 @@ class FocalPointModal extends ImmutablePureComponent {
     } else {
       return null;
     }
-  }
+  };
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   handleTextDetection = () => {
     this._detectText();
-  }
+  };
 
   _detectText = (refreshCache = false) => {
     const { media } = this.props;
@@ -257,24 +256,24 @@ class FocalPointModal extends ImmutablePureComponent {
       console.error(e);
       this.setState({ detecting: false });
     });
-  }
+  };
 
   handleThumbnailChange = e => {
     if (e.target.files.length > 0) {
       this.props.onSelectThumbnail(e.target.files);
     }
-  }
+  };
 
   setFileInputRef = c => {
     this.fileInput = c;
-  }
+  };
 
   handleFileInputClick = () => {
     this.fileInput.click();
-  }
+  };
 
   render () {
-    const { media, intl, account, onClose, isUploadingThumbnail, description, focusX, focusY, dirty, is_changing_upload } = this.props;
+    const { media, intl, account, onClose, isUploadingThumbnail, description, lang, focusX, focusY, dirty, is_changing_upload } = this.props;
     const { dragging, detecting, progress, ocrStatus } = this.state;
     const x = (focusX /  2) + .5;
     const y = (focusY / -2) + .5;
@@ -320,7 +319,7 @@ class FocalPointModal extends ImmutablePureComponent {
               <React.Fragment>
                 <label className='setting-text-label' htmlFor='upload-modal__thumbnail'><FormattedMessage id='upload_form.thumbnail' defaultMessage='Change thumbnail' /></label>
 
-                <Button disabled={isUploadingThumbnail} text={intl.formatMessage(messages.chooseImage)} onClick={this.handleFileInputClick} />
+                <Button disabled={isUploadingThumbnail || !media.get('unattached')} text={intl.formatMessage(messages.chooseImage)} onClick={this.handleFileInputClick} />
 
                 <label>
                   <span style={{ display: 'none' }}>{intl.formatMessage(messages.chooseImage)}</span>
@@ -349,6 +348,7 @@ class FocalPointModal extends ImmutablePureComponent {
                 id='upload-modal__description'
                 className='setting-text light'
                 value={detecting ? '…' : description}
+                lang={lang}
                 onChange={this.handleChange}
                 onKeyDown={this.handleKeyDown}
                 disabled={detecting || is_changing_upload}
@@ -426,3 +426,7 @@ class FocalPointModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps, null, {
+  forwardRef: true,
+})(injectIntl(FocalPointModal, { withRef: true }));
diff --git a/app/javascript/mastodon/features/ui/components/follow_requests_column_link.js b/app/javascript/mastodon/features/ui/components/follow_requests_column_link.jsx
index d04d58011..e6ffbdb84 100644
--- a/app/javascript/mastodon/features/ui/components/follow_requests_column_link.js
+++ b/app/javascript/mastodon/features/ui/components/follow_requests_column_link.jsx
@@ -15,8 +15,6 @@ const mapStateToProps = state => ({
   count: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
 });
 
-export default @injectIntl
-@connect(mapStateToProps)
 class FollowRequestsColumnLink extends React.Component {
 
   static propTypes = {
@@ -45,3 +43,5 @@ class FollowRequestsColumnLink extends React.Component {
   }
 
 }
+
+export default injectIntl(connect(mapStateToProps)(FollowRequestsColumnLink));
diff --git a/app/javascript/mastodon/features/ui/components/header.js b/app/javascript/mastodon/features/ui/components/header.jsx
index 1384bebda..c14c6faa7 100644
--- a/app/javascript/mastodon/features/ui/components/header.js
+++ b/app/javascript/mastodon/features/ui/components/header.jsx
@@ -22,8 +22,6 @@ const mapDispatchToProps = (dispatch) => ({
   },
 });
 
-export default @connect(null, mapDispatchToProps)
-@withRouter
 class Header extends React.PureComponent {
 
   static contextTypes = {
@@ -85,3 +83,5 @@ class Header extends React.PureComponent {
   }
 
 }
+
+export default withRouter(connect(null, mapDispatchToProps)(Header));
diff --git a/app/javascript/flavours/glitch/features/ui/components/image_loader.js b/app/javascript/mastodon/features/ui/components/image_loader.jsx
index dfa0efe49..9093eab28 100644
--- a/app/javascript/flavours/glitch/features/ui/components/image_loader.js
+++ b/app/javascript/mastodon/features/ui/components/image_loader.jsx
@@ -8,16 +8,18 @@ export default class ImageLoader extends PureComponent {
 
   static propTypes = {
     alt: PropTypes.string,
+    lang: PropTypes.string,
     src: PropTypes.string.isRequired,
     previewSrc: PropTypes.string,
     width: PropTypes.number,
     height: PropTypes.number,
     onClick: PropTypes.func,
     zoomButtonHidden: PropTypes.bool,
-  }
+  };
 
   static defaultProps = {
     alt: '',
+    lang: '',
     width: null,
     height: null,
   };
@@ -26,7 +28,7 @@ export default class ImageLoader extends PureComponent {
     loading: true,
     error: false,
     width: null,
-  }
+  };
 
   removers = [];
   canvas = null;
@@ -86,7 +88,7 @@ export default class ImageLoader extends PureComponent {
     image.addEventListener('load', handleLoad);
     image.src = previewSrc;
     this.removers.push(removeEventListeners);
-  })
+  });
 
   clearPreviewCanvas () {
     const { width, height } = this.canvas;
@@ -126,10 +128,10 @@ export default class ImageLoader extends PureComponent {
   setCanvasRef = c => {
     this.canvas = c;
     if (c) this.setState({ width: c.offsetWidth });
-  }
+  };
 
   render () {
-    const { alt, src, width, height, onClick } = this.props;
+    const { alt, lang, src, width, height, onClick } = this.props;
     const { loading } = this.state;
 
     const className = classNames('image-loader', {
@@ -154,6 +156,7 @@ export default class ImageLoader extends PureComponent {
         ) : (
           <ZoomableImage
             alt={alt}
+            lang={lang}
             src={src}
             onClick={onClick}
             width={width}
diff --git a/app/javascript/mastodon/features/ui/components/image_modal.js b/app/javascript/mastodon/features/ui/components/image_modal.jsx
index 7522c3da5..ca93d7b4e 100644
--- a/app/javascript/mastodon/features/ui/components/image_modal.js
+++ b/app/javascript/mastodon/features/ui/components/image_modal.jsx
@@ -9,7 +9,6 @@ const messages = defineMessages({
   close: { id: 'lightbox.close', defaultMessage: 'Close' },
 });
 
-export default @injectIntl
 class ImageModal extends React.PureComponent {
 
   static propTypes = {
@@ -57,3 +56,5 @@ class ImageModal extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ImageModal);
diff --git a/app/javascript/mastodon/features/ui/components/link_footer.js b/app/javascript/mastodon/features/ui/components/link_footer.jsx
index 3664a05bf..68ef015ab 100644
--- a/app/javascript/mastodon/features/ui/components/link_footer.js
+++ b/app/javascript/mastodon/features/ui/components/link_footer.jsx
@@ -3,7 +3,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
 import { Link } from 'react-router-dom';
-import { domain, version, source_url, profile_directory as profileDirectory } from 'mastodon/initial_state';
+import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'mastodon/initial_state';
 import { logOut } from 'mastodon/utils/log_out';
 import { openModal } from 'mastodon/actions/modal';
 import { PERMISSION_INVITE_USERS } from 'mastodon/permissions';
@@ -24,8 +24,6 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
   },
 });
 
-export default @injectIntl
-@connect(null, mapDispatchToProps)
 class LinkFooter extends React.PureComponent {
 
   static contextTypes = {
@@ -44,7 +42,7 @@ class LinkFooter extends React.PureComponent {
     this.props.onLogout();
 
     return false;
-  }
+  };
 
   render () {
     const { signedIn, permissions } = this.context.identity;
@@ -59,21 +57,27 @@ class LinkFooter extends React.PureComponent {
         <p>
           <strong>{domain}</strong>:
           {' '}
-          <Link key='about' to='/about'><FormattedMessage id='footer.about' defaultMessage='About' /></Link>
+          <Link to='/about'><FormattedMessage id='footer.about' defaultMessage='About' /></Link>
+          {statusPageUrl && (
+            <>
+              {DividingCircle}
+              <a href={statusPageUrl} target='_blank' rel='noopener'><FormattedMessage id='footer.status' defaultMessage='Status' /></a>
+            </>
+          )}
           {canInvite && (
             <>
               {DividingCircle}
-              <a key='invites' href='/invites' target='_blank'><FormattedMessage id='footer.invite' defaultMessage='Invite people' /></a>
+              <a href='/invites' target='_blank'><FormattedMessage id='footer.invite' defaultMessage='Invite people' /></a>
             </>
           )}
           {canProfileDirectory && (
             <>
               {DividingCircle}
-              <Link key='directory' to='/directory'><FormattedMessage id='footer.directory' defaultMessage='Profiles directory' /></Link>
+              <Link to='/directory'><FormattedMessage id='footer.directory' defaultMessage='Profiles directory' /></Link>
             </>
           )}
           {DividingCircle}
-          <Link key='privacy-policy' to='/privacy-policy'><FormattedMessage id='footer.privacy_policy' defaultMessage='Privacy policy' /></Link>
+          <Link to='/privacy-policy'><FormattedMessage id='footer.privacy_policy' defaultMessage='Privacy policy' /></Link>
         </p>
 
         <p>
@@ -94,3 +98,5 @@ class LinkFooter extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(connect(null, mapDispatchToProps)(LinkFooter));
diff --git a/app/javascript/mastodon/features/ui/components/list_panel.js b/app/javascript/mastodon/features/ui/components/list_panel.jsx
index 2f92a9254..fcff4e37d 100644
--- a/app/javascript/mastodon/features/ui/components/list_panel.js
+++ b/app/javascript/mastodon/features/ui/components/list_panel.jsx
@@ -20,8 +20,6 @@ const mapStateToProps = state => ({
   lists: getOrderedLists(state),
 });
 
-export default @withRouter
-@connect(mapStateToProps)
 class ListPanel extends ImmutablePureComponent {
 
   static propTypes = {
@@ -53,3 +51,5 @@ class ListPanel extends ImmutablePureComponent {
   }
 
 }
+
+export default withRouter(connect(mapStateToProps)(ListPanel));
diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.jsx
index 7f50572e7..e8005e67a 100644
--- a/app/javascript/mastodon/features/ui/components/media_modal.js
+++ b/app/javascript/mastodon/features/ui/components/media_modal.jsx
@@ -3,6 +3,7 @@ import ReactSwipeableViews from 'react-swipeable-views';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Video from 'mastodon/features/video';
+import { connect } from 'react-redux';
 import classNames from 'classnames';
 import { defineMessages, injectIntl } from 'react-intl';
 import IconButton from 'mastodon/components/icon_button';
@@ -20,7 +21,10 @@ const messages = defineMessages({
   next: { id: 'lightbox.next', defaultMessage: 'Next' },
 });
 
-export default @injectIntl
+const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
+});
+
 class MediaModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -43,27 +47,27 @@ class MediaModal extends ImmutablePureComponent {
 
   handleSwipe = (index) => {
     this.setState({ index: index % this.props.media.size });
-  }
+  };
 
   handleTransitionEnd = () => {
     this.setState({
       zoomButtonHidden: false,
     });
-  }
+  };
 
   handleNextClick = () => {
     this.setState({
       index: (this.getIndex() + 1) % this.props.media.size,
       zoomButtonHidden: true,
     });
-  }
+  };
 
   handlePrevClick = () => {
     this.setState({
       index: (this.props.media.size + this.getIndex() - 1) % this.props.media.size,
       zoomButtonHidden: true,
     });
-  }
+  };
 
   handleChangeIndex = (e) => {
     const index = Number(e.currentTarget.getAttribute('data-index'));
@@ -72,7 +76,7 @@ class MediaModal extends ImmutablePureComponent {
       index: index % this.props.media.size,
       zoomButtonHidden: true,
     });
-  }
+  };
 
   handleKeyDown = (e) => {
     switch(e.key) {
@@ -87,7 +91,7 @@ class MediaModal extends ImmutablePureComponent {
       e.stopPropagation();
       break;
     }
-  }
+  };
 
   componentDidMount () {
     window.addEventListener('keydown', this.handleKeyDown, false);
@@ -110,13 +114,13 @@ class MediaModal extends ImmutablePureComponent {
   };
 
   render () {
-    const { media, statusId, intl, onClose } = this.props;
+    const { media, language, statusId, intl, onClose } = this.props;
     const { navigationHidden } = this.state;
 
     const index = this.getIndex();
 
-    const leftNav  = media.size > 1 && <button tabIndex='0' className='media-modal__nav media-modal__nav--left' onClick={this.handlePrevClick} aria-label={intl.formatMessage(messages.previous)}><Icon id='chevron-left' fixedWidth /></button>;
-    const rightNav = media.size > 1 && <button tabIndex='0' className='media-modal__nav  media-modal__nav--right' onClick={this.handleNextClick} aria-label={intl.formatMessage(messages.next)}><Icon id='chevron-right' fixedWidth /></button>;
+    const leftNav  = media.size > 1 && <button tabIndex={0} className='media-modal__nav media-modal__nav--left' onClick={this.handlePrevClick} aria-label={intl.formatMessage(messages.previous)}><Icon id='chevron-left' fixedWidth /></button>;
+    const rightNav = media.size > 1 && <button tabIndex={0} className='media-modal__nav  media-modal__nav--right' onClick={this.handleNextClick} aria-label={intl.formatMessage(messages.next)}><Icon id='chevron-right' fixedWidth /></button>;
 
     const content = media.map((image) => {
       const width  = image.getIn(['meta', 'original', 'width']) || null;
@@ -130,6 +134,7 @@ class MediaModal extends ImmutablePureComponent {
             width={width}
             height={height}
             alt={image.get('description')}
+            lang={language}
             key={image.get('url')}
             onClick={this.toggleNavigation}
             zoomButtonHidden={this.state.zoomButtonHidden}
@@ -152,6 +157,7 @@ class MediaModal extends ImmutablePureComponent {
             onCloseVideo={onClose}
             detailed
             alt={image.get('description')}
+            lang={language}
             key={image.get('url')}
           />
         );
@@ -163,6 +169,7 @@ class MediaModal extends ImmutablePureComponent {
             height={height}
             key={image.get('preview_url')}
             alt={image.get('description')}
+            lang={language}
             onClick={this.toggleNavigation}
           />
         );
@@ -229,3 +236,5 @@ class MediaModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(injectIntl(MediaModal));
diff --git a/app/javascript/mastodon/features/ui/components/modal_loading.js b/app/javascript/mastodon/features/ui/components/modal_loading.jsx
index f403ca4c9..f403ca4c9 100644
--- a/app/javascript/mastodon/features/ui/components/modal_loading.js
+++ b/app/javascript/mastodon/features/ui/components/modal_loading.jsx
diff --git a/app/javascript/mastodon/features/ui/components/modal_root.js b/app/javascript/mastodon/features/ui/components/modal_root.jsx
index 6c4aabae5..c252e6caa 100644
--- a/app/javascript/mastodon/features/ui/components/modal_root.js
+++ b/app/javascript/mastodon/features/ui/components/modal_root.jsx
@@ -73,23 +73,23 @@ export default class ModalRoot extends React.PureComponent {
       document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
     } else {
       document.body.classList.remove('with-modals--active');
-      document.documentElement.style.marginRight = 0;
+      document.documentElement.style.marginRight = '0';
     }
   }
 
   setBackgroundColor = color => {
     this.setState({ backgroundColor: color });
-  }
+  };
 
   renderLoading = modalId => () => {
     return ['MEDIA', 'VIDEO', 'BOOST', 'CONFIRM', 'ACTIONS'].indexOf(modalId) === -1 ? <ModalLoading /> : null;
-  }
+  };
 
   renderError = (props) => {
     const { onClose } = this.props;
 
     return <BundleModalError {...props} onClose={onClose} />;
-  }
+  };
 
   handleClose = (ignoreFocus = false) => {
     const { onClose } = this.props;
@@ -102,11 +102,11 @@ export default class ModalRoot extends React.PureComponent {
       // This would be much smoother with react-intl 3+ and `forwardRef`.
     }
     onClose(message, ignoreFocus);
-  }
+  };
 
   setModalRef = (c) => {
     this._modal = c;
-  }
+  };
 
   render () {
     const { type, props, ignoreFocus } = this.props;
diff --git a/app/javascript/mastodon/features/ui/components/mute_modal.js b/app/javascript/mastodon/features/ui/components/mute_modal.jsx
index d8d8e68c3..fa64bf0f4 100644
--- a/app/javascript/mastodon/features/ui/components/mute_modal.js
+++ b/app/javascript/mastodon/features/ui/components/mute_modal.jsx
@@ -43,8 +43,6 @@ const mapDispatchToProps = dispatch => {
   };
 };
 
-export default @connect(mapStateToProps, mapDispatchToProps)
-@injectIntl
 class MuteModal extends React.PureComponent {
 
   static propTypes = {
@@ -65,23 +63,23 @@ class MuteModal extends React.PureComponent {
   handleClick = () => {
     this.props.onClose();
     this.props.onConfirm(this.props.account, this.props.notifications, this.props.muteDuration);
-  }
+  };
 
   handleCancel = () => {
     this.props.onClose();
-  }
+  };
 
   setRef = (c) => {
     this.button = c;
-  }
+  };
 
   toggleNotifications = () => {
     this.props.onToggleNotifications();
-  }
+  };
 
   changeMuteDuration = (e) => {
     this.props.onChangeMuteDuration(e);
-  }
+  };
 
   render () {
     const { account, notifications, muteDuration, intl } = this.props;
@@ -138,3 +136,5 @@ class MuteModal extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, mapDispatchToProps)(injectIntl(MuteModal));
diff --git a/app/javascript/mastodon/features/ui/components/navigation_panel.js b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
index 9a9309be0..ee1a83cc6 100644
--- a/app/javascript/mastodon/features/ui/components/navigation_panel.js
+++ b/app/javascript/mastodon/features/ui/components/navigation_panel.jsx
@@ -18,7 +18,7 @@ const messages = defineMessages({
   explore: { id: 'explore.title', defaultMessage: 'Explore' },
   local: { id: 'tabs_bar.local_timeline', defaultMessage: 'Local' },
   federated: { id: 'tabs_bar.federated_timeline', defaultMessage: 'Federated' },
-  direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' },
+  direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
   favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
   bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
   lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
@@ -28,7 +28,6 @@ const messages = defineMessages({
   search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
 });
 
-export default @injectIntl
 class NavigationPanel extends React.Component {
 
   static contextTypes = {
@@ -82,8 +81,8 @@ class NavigationPanel extends React.Component {
         {signedIn && (
           <React.Fragment>
             <ColumnLink transparent to='/conversations' icon='at' text={intl.formatMessage(messages.direct)} />
-            <ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
             <ColumnLink transparent to='/bookmarks' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} />
+            <ColumnLink transparent to='/favourites' icon='star' text={intl.formatMessage(messages.favourites)} />
             <ColumnLink transparent to='/lists' icon='list-ul' text={intl.formatMessage(messages.lists)} />
 
             <ListPanel />
@@ -105,3 +104,5 @@ class NavigationPanel extends React.Component {
   }
 
 }
+
+export default injectIntl(NavigationPanel);
diff --git a/app/javascript/mastodon/features/ui/components/report_modal.js b/app/javascript/mastodon/features/ui/components/report_modal.jsx
index 264da07ce..8b505b8bd 100644
--- a/app/javascript/mastodon/features/ui/components/report_modal.js
+++ b/app/javascript/mastodon/features/ui/components/report_modal.jsx
@@ -30,8 +30,6 @@ const makeMapStateToProps = () => {
   return mapStateToProps;
 };
 
-export default @connect(makeMapStateToProps)
-@injectIntl
 class ReportModal extends ImmutablePureComponent {
 
   static propTypes = {
@@ -95,7 +93,7 @@ class ReportModal extends ImmutablePureComponent {
     } else {
       this.setState({ selectedRuleIds: selectedRuleIds.remove(ruleId) });
     }
-  }
+  };
 
   handleChangeCategory = category => {
     this.setState({ category });
@@ -217,3 +215,5 @@ class ReportModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(makeMapStateToProps)(injectIntl(ReportModal));
diff --git a/app/javascript/mastodon/features/ui/components/sign_in_banner.js b/app/javascript/mastodon/features/ui/components/sign_in_banner.jsx
index 8bd32edf9..86fcc11b5 100644
--- a/app/javascript/mastodon/features/ui/components/sign_in_banner.js
+++ b/app/javascript/mastodon/features/ui/components/sign_in_banner.jsx
@@ -30,7 +30,7 @@ const SignInBanner = () => {
 
   return (
     <div className='sign-in-banner'>
-      <p><FormattedMessage id='sign_in_banner.text' defaultMessage='Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.' /></p>
+      <p><FormattedMessage id='sign_in_banner.text' defaultMessage='Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.' /></p>
       <a href='/auth/sign_in' className='button button--block'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Sign in' /></a>
       {signupButton}
     </div>
diff --git a/app/javascript/mastodon/features/ui/components/upload_area.js b/app/javascript/mastodon/features/ui/components/upload_area.jsx
index 6c423b2c1..035fe7a26 100644
--- a/app/javascript/mastodon/features/ui/components/upload_area.js
+++ b/app/javascript/mastodon/features/ui/components/upload_area.jsx
@@ -22,7 +22,7 @@ export default class UploadArea extends React.PureComponent {
         break;
       }
     }
-  }
+  };
 
   componentDidMount () {
     window.addEventListener('keyup', this.handleKeyUp, false);
diff --git a/app/javascript/mastodon/features/ui/components/video_modal.js b/app/javascript/mastodon/features/ui/components/video_modal.jsx
index a1533eba0..0e754ccc7 100644
--- a/app/javascript/mastodon/features/ui/components/video_modal.js
+++ b/app/javascript/mastodon/features/ui/components/video_modal.jsx
@@ -2,15 +2,21 @@ import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Video from 'mastodon/features/video';
+import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import Footer from 'mastodon/features/picture_in_picture/components/footer';
 import { getAverageFromBlurhash } from 'mastodon/blurhash';
 
-export default class VideoModal extends ImmutablePureComponent {
+const mapStateToProps = (state, { statusId }) => ({
+  language: state.getIn(['statuses', statusId, 'language']),
+});
+
+class VideoModal extends ImmutablePureComponent {
 
   static propTypes = {
     media: ImmutablePropTypes.map.isRequired,
     statusId: PropTypes.string,
+    language: PropTypes.string,
     options: PropTypes.shape({
       startTime: PropTypes.number,
       autoPlay: PropTypes.bool,
@@ -25,7 +31,7 @@ export default class VideoModal extends ImmutablePureComponent {
   }
 
   render () {
-    const { media, statusId, onClose } = this.props;
+    const { media, statusId, language, onClose } = this.props;
     const options = this.props.options || {};
 
     return (
@@ -43,6 +49,7 @@ export default class VideoModal extends ImmutablePureComponent {
             autoFocus
             detailed
             alt={media.get('description')}
+            lang={language}
           />
         </div>
 
@@ -54,3 +61,5 @@ export default class VideoModal extends ImmutablePureComponent {
   }
 
 }
+
+export default connect(mapStateToProps, null, null, { forwardRef: true })(VideoModal);
diff --git a/app/javascript/mastodon/features/ui/components/zoomable_image.js b/app/javascript/mastodon/features/ui/components/zoomable_image.jsx
index 1cf263cb9..b41d0fe31 100644
--- a/app/javascript/mastodon/features/ui/components/zoomable_image.js
+++ b/app/javascript/mastodon/features/ui/components/zoomable_image.jsx
@@ -91,21 +91,22 @@ const normalizeWheel = event => {
   };
 };
 
-export default @injectIntl
 class ZoomableImage extends React.PureComponent {
 
   static propTypes = {
     alt: PropTypes.string,
+    lang: PropTypes.string,
     src: PropTypes.string.isRequired,
     width: PropTypes.number,
     height: PropTypes.number,
     onClick: PropTypes.func,
     zoomButtonHidden: PropTypes.bool,
     intl: PropTypes.object.isRequired,
-  }
+  };
 
   static defaultProps = {
     alt: '',
+    lang: '',
     width: null,
     height: null,
   };
@@ -132,7 +133,7 @@ class ZoomableImage extends React.PureComponent {
     dragged: false,
     lockScroll: { x: 0, y: 0 },
     lockTranslate: { x: 0, y: 0 },
-  }
+  };
 
   removers = [];
   container = null;
@@ -212,7 +213,7 @@ class ZoomableImage extends React.PureComponent {
 
     // lock horizontal scroll
     this.container.scrollLeft = Math.max(this.container.scrollLeft + event.pixelX, this.state.lockScroll.x);
-  }
+  };
 
   mouseDownHandler = e => {
     this.container.style.cursor = 'grabbing';
@@ -228,7 +229,7 @@ class ZoomableImage extends React.PureComponent {
 
     this.image.addEventListener('mousemove', this.mouseMoveHandler);
     this.image.addEventListener('mouseup', this.mouseUpHandler);
-  }
+  };
 
   mouseMoveHandler = e => {
     const dx = e.clientX - this.state.dragPosition.x;
@@ -238,7 +239,7 @@ class ZoomableImage extends React.PureComponent {
     this.container.scrollTop = Math.max(this.state.dragPosition.top - dy, this.state.lockScroll.y);
 
     this.setState({ dragged: true });
-  }
+  };
 
   mouseUpHandler = () => {
     this.container.style.cursor = 'grab';
@@ -246,13 +247,13 @@ class ZoomableImage extends React.PureComponent {
 
     this.image.removeEventListener('mousemove', this.mouseMoveHandler);
     this.image.removeEventListener('mouseup', this.mouseUpHandler);
-  }
+  };
 
   handleTouchStart = e => {
     if (e.touches.length !== 2) return;
 
     this.lastDistance = getDistance(...e.touches);
-  }
+  };
 
   handleTouchMove = e => {
     const { scrollTop, scrollHeight, clientHeight } = this.container;
@@ -275,7 +276,7 @@ class ZoomableImage extends React.PureComponent {
 
     this.lastMidpoint = midpoint;
     this.lastDistance = distance;
-  }
+  };
 
   zoom(nextScale, midpoint) {
     const { scale, zoomMatrix } = this.state;
@@ -314,11 +315,11 @@ class ZoomableImage extends React.PureComponent {
     const handler = this.props.onClick;
     if (handler) handler();
     this.setState({ navigationHidden: !this.state.navigationHidden });
-  }
+  };
 
   handleMouseDown = e => {
     e.preventDefault();
-  }
+  };
 
   initZoomMatrix = () => {
     const { width, height } = this.props;
@@ -350,7 +351,7 @@ class ZoomableImage extends React.PureComponent {
         translateY: translateY,
       },
     });
-  }
+  };
 
   handleZoomClick = e => {
     e.preventDefault();
@@ -392,18 +393,18 @@ class ZoomableImage extends React.PureComponent {
 
     this.container.style.cursor = 'grab';
     this.container.style.removeProperty('user-select');
-  }
+  };
 
   setContainerRef = c => {
     this.container = c;
-  }
+  };
 
   setImageRef = c => {
     this.image = c;
-  }
+  };
 
   render () {
-    const { alt, src, width, height, intl } = this.props;
+    const { alt, lang, src, width, height, intl } = this.props;
     const { scale, lockTranslate } = this.state;
     const overflow = scale === MIN_SCALE ? 'hidden' : 'scroll';
     const zoomButtonShouldHide = this.state.navigationHidden || this.props.zoomButtonHidden || this.state.zoomMatrix.rate <= MIN_SCALE ? 'media-modal__zoom-button--hidden' : '';
@@ -431,6 +432,7 @@ class ZoomableImage extends React.PureComponent {
             ref={this.setImageRef}
             alt={alt}
             title={alt}
+            lang={lang}
             src={src}
             width={width}
             height={height}
@@ -448,3 +450,5 @@ class ZoomableImage extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(ZoomableImage);
diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.jsx
index b05956606..0e73f4c09 100644
--- a/app/javascript/mastodon/features/ui/index.js
+++ b/app/javascript/mastodon/features/ui/index.jsx
@@ -13,7 +13,7 @@ import { debounce } from 'lodash';
 import { uploadCompose, resetCompose, changeComposeSpoilerness } from '../../actions/compose';
 import { expandHomeTimeline } from '../../actions/timelines';
 import { expandNotifications } from '../../actions/notifications';
-import { fetchServer } from '../../actions/server';
+import { fetchServer, fetchServerTranslationLanguages } from '../../actions/server';
 import { clearHeight } from '../../actions/height_cache';
 import { focusApp, unfocusApp, changeLayout } from 'mastodon/actions/app';
 import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'mastodon/actions/markers';
@@ -42,6 +42,7 @@ import {
   FollowRequests,
   FavouritedStatuses,
   BookmarkedStatuses,
+  FollowedTags,
   ListTimeline,
   Blocks,
   DomainBlocks,
@@ -54,7 +55,7 @@ import {
   About,
   PrivacyPolicy,
 } from './util/async-components';
-import initialState, { me, owner, singleUserMode, showTrends } from '../../initial_state';
+import initialState, { me, owner, singleUserMode, showTrends, trendsAsLanding } from '../../initial_state';
 import { closeOnboarding, INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
 import Header from './components/header';
 
@@ -147,7 +148,7 @@ class SwitchingColumnsArea extends React.PureComponent {
     if (c) {
       this.node = c;
     }
-  }
+  };
 
   render () {
     const { children, mobile } = this.props;
@@ -163,7 +164,7 @@ class SwitchingColumnsArea extends React.PureComponent {
       }
     } else if (singleUserMode && owner && initialState?.accounts[owner]) {
       redirect = <Redirect from='/' to={`/@${initialState.accounts[owner].username}`} exact />;
-    } else if (showTrends) {
+    } else if (showTrends && trendsAsLanding) {
       redirect = <Redirect from='/' to='/explore' exact />;
     } else {
       redirect = <Redirect from='/' to='/about' exact />;
@@ -216,6 +217,7 @@ class SwitchingColumnsArea extends React.PureComponent {
           <WrappedRoute path='/follow_requests' component={FollowRequests} content={children} />
           <WrappedRoute path='/blocks' component={Blocks} content={children} />
           <WrappedRoute path='/domain_blocks' component={DomainBlocks} content={children} />
+          <WrappedRoute path='/followed_tags' component={FollowedTags} content={children} />
           <WrappedRoute path='/mutes' component={Mutes} content={children} />
           <WrappedRoute path='/lists' component={Lists} content={children} />
 
@@ -227,9 +229,6 @@ class SwitchingColumnsArea extends React.PureComponent {
 
 }
 
-export default @connect(mapStateToProps)
-@injectIntl
-@withRouter
 class UI extends React.PureComponent {
 
   static contextTypes = {
@@ -268,16 +267,16 @@ class UI extends React.PureComponent {
       // but we set user-friendly message for other browsers, e.g. Edge.
       e.returnValue = intl.formatMessage(messages.beforeUnload);
     }
-  }
+  };
 
   handleWindowFocus = () => {
     this.props.dispatch(focusApp());
     this.props.dispatch(submitMarkers({ immediate: true }));
-  }
+  };
 
   handleWindowBlur = () => {
     this.props.dispatch(unfocusApp());
-  }
+  };
 
   handleDragEnter = (e) => {
     e.preventDefault();
@@ -293,7 +292,7 @@ class UI extends React.PureComponent {
     if (e.dataTransfer && Array.from(e.dataTransfer.types).includes('Files') && this.props.canUploadMore && this.context.identity.signedIn) {
       this.setState({ draggingOver: true });
     }
-  }
+  };
 
   handleDragOver = (e) => {
     if (this.dataTransferIsText(e.dataTransfer)) return false;
@@ -308,7 +307,7 @@ class UI extends React.PureComponent {
     }
 
     return false;
-  }
+  };
 
   handleDrop = (e) => {
     if (this.dataTransferIsText(e.dataTransfer)) return;
@@ -321,7 +320,7 @@ class UI extends React.PureComponent {
     if (e.dataTransfer && e.dataTransfer.files.length >= 1 && this.props.canUploadMore && this.context.identity.signedIn) {
       this.props.dispatch(uploadCompose(e.dataTransfer.files));
     }
-  }
+  };
 
   handleDragLeave = (e) => {
     e.preventDefault();
@@ -334,15 +333,15 @@ class UI extends React.PureComponent {
     }
 
     this.setState({ draggingOver: false });
-  }
+  };
 
   dataTransferIsText = (dataTransfer) => {
     return (dataTransfer && Array.from(dataTransfer.types).filter((type) => type === 'text/plain').length === 1);
-  }
+  };
 
   closeUploadModal = () => {
     this.setState({ draggingOver: false });
-  }
+  };
 
   handleServiceWorkerPostMessage = ({ data }) => {
     if (data.type === 'navigate') {
@@ -350,7 +349,7 @@ class UI extends React.PureComponent {
     } else {
       console.warn('Unknown message type:', data.type);
     }
-  }
+  };
 
   handleLayoutChange = debounce(() => {
     this.props.dispatch(clearHeight()); // The cached heights are no longer accurate, invalidate
@@ -367,7 +366,7 @@ class UI extends React.PureComponent {
     } else {
       this.handleLayoutChange();
     }
-  }
+  };
 
   componentDidMount () {
     const { signedIn } = this.context.identity;
@@ -397,6 +396,7 @@ class UI extends React.PureComponent {
       this.props.dispatch(fetchMarkers());
       this.props.dispatch(expandHomeTimeline());
       this.props.dispatch(expandNotifications());
+      this.props.dispatch(fetchServerTranslationLanguages());
 
       setTimeout(() => this.props.dispatch(fetchServer()), 3000);
     }
@@ -421,7 +421,7 @@ class UI extends React.PureComponent {
 
   setRef = c => {
     this.node = c;
-  }
+  };
 
   handleHotkeyNew = e => {
     e.preventDefault();
@@ -431,7 +431,7 @@ class UI extends React.PureComponent {
     if (element) {
       element.focus();
     }
-  }
+  };
 
   handleHotkeySearch = e => {
     e.preventDefault();
@@ -441,17 +441,17 @@ class UI extends React.PureComponent {
     if (element) {
       element.focus();
     }
-  }
+  };
 
   handleHotkeyForceNew = e => {
     this.handleHotkeyNew(e);
     this.props.dispatch(resetCompose());
-  }
+  };
 
   handleHotkeyToggleComposeSpoilers = e => {
     e.preventDefault();
     this.props.dispatch(changeComposeSpoilerness());
-  }
+  };
 
   handleHotkeyFocusColumn = e => {
     const index  = (e.key * 1) + 1; // First child is drawer, skip that
@@ -469,19 +469,19 @@ class UI extends React.PureComponent {
         status.focus();
       }
     }
-  }
+  };
 
   handleHotkeyBack = () => {
-    if (window.history && window.history.length === 1) {
-      this.context.router.history.push('/');
-    } else {
+    if (window.history && window.history.state) {
       this.context.router.history.goBack();
+    } else {
+      this.context.router.history.push('/');
     }
-  }
+  };
 
   setHotkeysRef = c => {
     this.hotkeys = c;
-  }
+  };
 
   handleHotkeyToggleHelp = () => {
     if (this.props.location.pathname === '/keyboard-shortcuts') {
@@ -489,55 +489,55 @@ class UI extends React.PureComponent {
     } else {
       this.context.router.history.push('/keyboard-shortcuts');
     }
-  }
+  };
 
   handleHotkeyGoToHome = () => {
     this.context.router.history.push('/home');
-  }
+  };
 
   handleHotkeyGoToNotifications = () => {
     this.context.router.history.push('/notifications');
-  }
+  };
 
   handleHotkeyGoToLocal = () => {
     this.context.router.history.push('/public/local');
-  }
+  };
 
   handleHotkeyGoToFederated = () => {
     this.context.router.history.push('/public');
-  }
+  };
 
   handleHotkeyGoToDirect = () => {
     this.context.router.history.push('/conversations');
-  }
+  };
 
   handleHotkeyGoToStart = () => {
     this.context.router.history.push('/getting-started');
-  }
+  };
 
   handleHotkeyGoToFavourites = () => {
     this.context.router.history.push('/favourites');
-  }
+  };
 
   handleHotkeyGoToPinned = () => {
     this.context.router.history.push('/pinned');
-  }
+  };
 
   handleHotkeyGoToProfile = () => {
     this.context.router.history.push(`/@${this.props.username}`);
-  }
+  };
 
   handleHotkeyGoToBlocked = () => {
     this.context.router.history.push('/blocks');
-  }
+  };
 
   handleHotkeyGoToMuted = () => {
     this.context.router.history.push('/mutes');
-  }
+  };
 
   handleHotkeyGoToRequests = () => {
     this.context.router.history.push('/follow_requests');
-  }
+  };
 
   render () {
     const { draggingOver } = this.state;
@@ -585,3 +585,5 @@ class UI extends React.PureComponent {
   }
 
 }
+
+export default connect(mapStateToProps)(injectIntl(withRouter(UI)));
diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js
index 6046578de..1cf07f645 100644
--- a/app/javascript/mastodon/features/ui/util/async-components.js
+++ b/app/javascript/mastodon/features/ui/util/async-components.js
@@ -90,6 +90,10 @@ export function FavouritedStatuses () {
   return import(/* webpackChunkName: "features/favourited_statuses" */'../../favourited_statuses');
 }
 
+export function FollowedTags () {
+  return import(/* webpackChunkName: "features/followed_tags" */'../../followed_tags');
+}
+
 export function BookmarkedStatuses () {
   return import(/* webpackChunkName: "features/bookmarked_statuses" */'../../bookmarked_statuses');
 }
diff --git a/app/javascript/mastodon/features/ui/util/react_router_helpers.js b/app/javascript/mastodon/features/ui/util/react_router_helpers.jsx
index 205dd6f10..21b352878 100644
--- a/app/javascript/mastodon/features/ui/util/react_router_helpers.js
+++ b/app/javascript/mastodon/features/ui/util/react_router_helpers.jsx
@@ -80,17 +80,17 @@ export class WrappedRoute extends React.Component {
         {Component => <Component params={match.params} multiColumn={multiColumn} {...componentParams}>{content}</Component>}
       </BundleContainer>
     );
-  }
+  };
 
   renderLoading = () => {
     const { multiColumn } = this.props;
 
     return <ColumnLoading multiColumn={multiColumn} />;
-  }
+  };
 
   renderError = (props) => {
     return <BundleColumnError {...props} errorType='network' />;
-  }
+  };
 
   render () {
     const { component: Component, content, ...rest } = this.props;
diff --git a/app/javascript/mastodon/features/ui/util/reduced_motion.js b/app/javascript/mastodon/features/ui/util/reduced_motion.jsx
index 95519042b..1123b80ed 100644
--- a/app/javascript/mastodon/features/ui/util/reduced_motion.js
+++ b/app/javascript/mastodon/features/ui/util/reduced_motion.jsx
@@ -17,7 +17,7 @@ class ReducedMotion extends React.Component {
     defaultStyle: PropTypes.object,
     style: PropTypes.object,
     children: PropTypes.func,
-  }
+  };
 
   render() {
 
diff --git a/app/javascript/mastodon/features/video/index.js b/app/javascript/mastodon/features/video/index.jsx
index 1c35ca9d1..e2637e0be 100644
--- a/app/javascript/mastodon/features/video/index.js
+++ b/app/javascript/mastodon/features/video/index.jsx
@@ -94,7 +94,6 @@ export const fileNameFromURL = str => {
   return pathname.slice(index + 1);
 };
 
-export default @injectIntl
 class Video extends React.PureComponent {
 
   static propTypes = {
@@ -102,6 +101,7 @@ class Video extends React.PureComponent {
     frameRate: PropTypes.string,
     src: PropTypes.string.isRequired,
     alt: PropTypes.string,
+    lang: PropTypes.string,
     width: PropTypes.number,
     height: PropTypes.number,
     sensitive: PropTypes.bool,
@@ -148,7 +148,7 @@ class Video extends React.PureComponent {
     if (this.player) {
       this._setDimensions();
     }
-  }
+  };
 
   _setDimensions () {
     const width = this.player.offsetWidth;
@@ -168,26 +168,26 @@ class Video extends React.PureComponent {
     if (this.video) {
       this.setState({ volume: this.video.volume, muted: this.video.muted });
     }
-  }
+  };
 
   setSeekRef = c => {
     this.seek = c;
-  }
+  };
 
   setVolumeRef = c => {
     this.volume = c;
-  }
+  };
 
   handleClickRoot = e => e.stopPropagation();
 
   handlePlay = () => {
     this.setState({ paused: false });
     this._updateTime();
-  }
+  };
 
   handlePause = () => {
     this.setState({ paused: true });
-  }
+  };
 
   _updateTime () {
     requestAnimationFrame(() => {
@@ -206,7 +206,7 @@ class Video extends React.PureComponent {
       currentTime: this.video.currentTime,
       duration:this.video.duration,
     });
-  }
+  };
 
   handleVolumeMouseDown = e => {
     document.addEventListener('mousemove', this.handleMouseVolSlide, true);
@@ -218,14 +218,14 @@ class Video extends React.PureComponent {
 
     e.preventDefault();
     e.stopPropagation();
-  }
+  };
 
   handleVolumeMouseUp = () => {
     document.removeEventListener('mousemove', this.handleMouseVolSlide, true);
     document.removeEventListener('mouseup', this.handleVolumeMouseUp, true);
     document.removeEventListener('touchmove', this.handleMouseVolSlide, true);
     document.removeEventListener('touchend', this.handleVolumeMouseUp, true);
-  }
+  };
 
   handleMouseVolSlide = throttle(e => {
     const { x } = getPointerPosition(this.volume, e);
@@ -249,7 +249,7 @@ class Video extends React.PureComponent {
 
     e.preventDefault();
     e.stopPropagation();
-  }
+  };
 
   handleMouseUp = () => {
     document.removeEventListener('mousemove', this.handleMouseMove, true);
@@ -259,7 +259,7 @@ class Video extends React.PureComponent {
 
     this.setState({ dragging: false });
     this.video.play();
-  }
+  };
 
   handleMouseMove = throttle(e => {
     const { x } = getPointerPosition(this.seek, e);
@@ -291,7 +291,7 @@ class Video extends React.PureComponent {
       e.stopPropagation();
       this.togglePlay();
     }
-  }
+  };
 
   handleKeyDown = e => {
     const frameTime = 1 / this.getFrameRate();
@@ -345,7 +345,7 @@ class Video extends React.PureComponent {
         exitFullscreen();
       }
     }
-  }
+  };
 
   togglePlay = () => {
     if (this.state.paused) {
@@ -353,7 +353,7 @@ class Video extends React.PureComponent {
     } else {
       this.setState({ paused: true }, () => this.video.pause());
     }
-  }
+  };
 
   toggleFullscreen = () => {
     if (isFullscreen()) {
@@ -361,7 +361,7 @@ class Video extends React.PureComponent {
     } else {
       requestFullscreen(this.player);
     }
-  }
+  };
 
   componentDidMount () {
     document.addEventListener('fullscreenchange', this.handleFullscreenChange, true);
@@ -434,19 +434,19 @@ class Video extends React.PureComponent {
 
       this.setState({ paused: true });
     }
-  }, 150, { trailing: true })
+  }, 150, { trailing: true });
 
   handleFullscreenChange = () => {
     this.setState({ fullscreen: isFullscreen() });
-  }
+  };
 
   handleMouseEnter = () => {
     this.setState({ hovered: true });
-  }
+  };
 
   handleMouseLeave = () => {
     this.setState({ hovered: false });
-  }
+  };
 
   toggleMute = () => {
     const muted = !this.video.muted;
@@ -454,7 +454,7 @@ class Video extends React.PureComponent {
     this.setState({ muted }, () => {
       this.video.muted = muted;
     });
-  }
+  };
 
   toggleReveal = () => {
     if (this.props.onToggleVisibility) {
@@ -462,7 +462,7 @@ class Video extends React.PureComponent {
     } else {
       this.setState({ revealed: !this.state.revealed });
     }
-  }
+  };
 
   handleLoadedData = () => {
     const { currentTime, volume, muted, autoPlay } = this.props;
@@ -482,7 +482,7 @@ class Video extends React.PureComponent {
     if (autoPlay) {
       this.video.play();
     }
-  }
+  };
 
   handleProgress = () => {
     const lastTimeRange = this.video.buffered.length - 1;
@@ -490,11 +490,11 @@ class Video extends React.PureComponent {
     if (lastTimeRange > -1) {
       this.setState({ buffer: Math.ceil(this.video.buffered.end(lastTimeRange) / this.video.duration * 100) });
     }
-  }
+  };
 
   handleVolumeChange = () => {
     this.setState({ volume: this.video.volume, muted: this.video.muted });
-  }
+  };
 
   handleOpenVideo = () => {
     this.video.pause();
@@ -505,12 +505,12 @@ class Video extends React.PureComponent {
       defaultVolume: this.state.volume,
       componentIndex: this.props.componentIndex,
     });
-  }
+  };
 
   handleCloseVideo = () => {
     this.video.pause();
     this.props.onCloseVideo();
-  }
+  };
 
   getFrameRate () {
     if (this.props.frameRate && isNaN(this.props.frameRate)) {
@@ -524,7 +524,7 @@ class Video extends React.PureComponent {
   }
 
   render () {
-    const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
+    const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, lang, detailed, sensitive, editable, blurhash, autoFocus } = this.props;
     const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
     const progress = Math.min((currentTime / duration) * 100, 100);
     const playerStyle = {};
@@ -582,9 +582,10 @@ class Video extends React.PureComponent {
           poster={preview}
           preload={preload}
           role='button'
-          tabIndex='0'
+          tabIndex={0}
           aria-label={alt}
           title={alt}
+          lang={lang}
           width={width}
           height={height}
           volume={volume}
@@ -610,7 +611,7 @@ class Video extends React.PureComponent {
 
             <span
               className={classNames('video-player__seek__handle', { active: dragging })}
-              tabIndex='0'
+              tabIndex={0}
               style={{ left: `${progress}%` }}
               onKeyDown={this.handleVideoKeyDown}
             />
@@ -626,7 +627,7 @@ class Video extends React.PureComponent {
 
                 <span
                   className={classNames('video-player__volume__handle')}
-                  tabIndex='0'
+                  tabIndex={0}
                   style={{ left: `${volume * 100}%` }}
                 />
               </div>
@@ -653,3 +654,5 @@ class Video extends React.PureComponent {
   }
 
 }
+
+export default injectIntl(Video);
diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js
index 8eec7f7c7..56b2f4eb2 100644
--- a/app/javascript/mastodon/initial_state.js
+++ b/app/javascript/mastodon/initial_state.js
@@ -55,7 +55,7 @@
  * @property {boolean=} delete_modal
  * @property {boolean=} disable_swiping
  * @property {string=} disabled_account_id
- * @property {boolean} display_media
+ * @property {string} display_media
  * @property {string} domain
  * @property {boolean=} expand_spoilers
  * @property {boolean} limited_federation_mode
@@ -75,11 +75,11 @@
  * @property {boolean} timeline_preview
  * @property {string} title
  * @property {boolean} trends
+ * @property {boolean} trends_as_landing_page
  * @property {boolean} unfollow_modal
  * @property {boolean} use_blurhash
  * @property {boolean=} use_pending_items
  * @property {string} version
- * @property {boolean} translation_enabled
  */
 
 /**
@@ -126,12 +126,14 @@ export const singleUserMode = getMeta('single_user_mode');
 export const source_url = getMeta('source_url');
 export const timelinePreview = getMeta('timeline_preview');
 export const title = getMeta('title');
+export const trendsAsLanding = getMeta('trends_as_landing_page');
 export const unfollowModal = getMeta('unfollow_modal');
 export const useBlurhash = getMeta('use_blurhash');
 export const usePendingItems = getMeta('use_pending_items');
 export const version = getMeta('version');
-export const translationEnabled = getMeta('translation_enabled');
 export const languages = initialState?.languages;
+// @ts-expect-error
+export const statusPageUrl = getMeta('status_page_url');
 
 // Glitch-soc-specific settings
 export const maxChars = (initialState && initialState.max_toot_chars) || 500;
diff --git a/app/javascript/mastodon/is_mobile.js b/app/javascript/mastodon/is_mobile.js
index 3c8ec1545..d0669b74b 100644
--- a/app/javascript/mastodon/is_mobile.js
+++ b/app/javascript/mastodon/is_mobile.js
@@ -1,6 +1,7 @@
 // @ts-check
 
 import { supportsPassiveEvents } from 'detect-passive-events';
+// @ts-expect-error
 import { forceSingleColumn } from 'mastodon/initial_state';
 
 const LAYOUT_BREAKPOINT = 630;
@@ -24,6 +25,7 @@ export const layoutFromWindow = () => {
   }
 };
 
+// @ts-expect-error
 const iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
 
 const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
@@ -33,7 +35,7 @@ let userTouching = false;
 const touchListener = () => {
   userTouching = true;
 
-  window.removeEventListener('touchstart', touchListener, listenerOptions);
+  window.removeEventListener('touchstart', touchListener);
 };
 
 window.addEventListener('touchstart', touchListener, listenerOptions);
diff --git a/app/javascript/mastodon/load_polyfills.js b/app/javascript/mastodon/load_polyfills.js
index f5a897f75..7909dc4ea 100644
--- a/app/javascript/mastodon/load_polyfills.js
+++ b/app/javascript/mastodon/load_polyfills.js
@@ -12,10 +12,8 @@ function importExtraPolyfills() {
 
 function loadPolyfills() {
   const needsBasePolyfills = !(
-    Array.prototype.includes &&
     HTMLCanvasElement.prototype.toBlob &&
     window.Intl &&
-    Number.isNaN &&
     Object.assign &&
     Object.values &&
     window.Symbol &&
diff --git a/app/javascript/mastodon/locales/af.json b/app/javascript/mastodon/locales/af.json
index abd664fc8..9c87eb44e 100644
--- a/app/javascript/mastodon/locales/af.json
+++ b/app/javascript/mastodon/locales/af.json
@@ -20,7 +20,7 @@
   "account.blocked": "Geblokkeer",
   "account.browse_more_on_origin_server": "Verken die oorspronklike profiel",
   "account.cancel_follow_request": "Herroep volgversoek",
-  "account.direct": "Stuur direkte boodskap aan @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Hou op om my van @{name} se plasings te laat weet",
   "account.domain_blocked": "Domein geblokkeer",
   "account.edit_profile": "Redigeer profiel",
@@ -102,7 +102,7 @@
   "column.blocks": "Geblokkeerde gebruikers",
   "column.bookmarks": "Boekmerke",
   "column.community": "Plaaslike tydlyn",
-  "column.direct": "Direkte boodskappe",
+  "column.direct": "Private mentions",
   "column.directory": "Blaai deur profiele",
   "column.domain_blocks": "Geblokkeerde domeine",
   "column.favourites": "Gunstelinge",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Blokkeer die hele domein",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Teken Uit",
   "confirmations.logout.message": "Is jy seker jy wil uitteken?",
   "confirmations.mute.confirm": "Mute",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "Jy het nog geen boekmerke gelaat nie. Boekmerke wat jy by plasings laat, sal jy hier sien.",
   "empty_column.community": "Die plaaslike tydlyn is leeg. Kry die bal aan die rol deur iets te skryf wat mense kan lees!",
-  "empty_column.direct": "Jy het nog nie direkte boodskappe nie. Wanneer jy een stuur of ontvang, sal dit hier verskyn.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "Jy het nog geen gunstelingplasings nie. As jy een as gunsteling merk, sal jy dit hier sien.",
   "empty_column.favourites": "Hierdie plasing het nog nie goedkeurings ontvang nie. As iemand dit as gunsteling merk, sien jy dit hier.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "Jy het nog geen volgversoeke nie. Wanneer jy een ontvang, sal dit hier vertoon.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Daar is nog niks vir hierdie hutsetiket nie.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "Oor",
   "footer.directory": "Profielgids",
   "footer.get_app": "Kry die app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Kortpadsleutels",
   "footer.privacy_policy": "Privaatheidsbeleid",
   "footer.source_code": "Wys bronkode",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Kom aan die gang",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Fokuskolom",
   "keyboard_shortcuts.compose": "Teksveld vir skryf",
   "keyboard_shortcuts.description": "Beskrywing",
-  "keyboard_shortcuts.direct": "om kolom vir direkte boodskappe oop te maak",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Gaan afwaarts in die lys",
   "keyboard_shortcuts.enter": "Sien plasing",
   "keyboard_shortcuts.favourite": "Gunsteling",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Boekmerke",
   "navigation_bar.community_timeline": "Plaaslike tydlyn",
   "navigation_bar.compose": "Skep nuwe plasing",
-  "navigation_bar.direct": "Direkte boodskappe",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Geblokkeerde domeine",
   "navigation_bar.edit_profile": "Redigeer profiel",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Gunstelinge",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lyste",
   "navigation_bar.logout": "Teken uit",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Soek",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Soek of plak URL",
-  "search_popout.search_format": "Formaat vir gevorderd soek",
-  "search_popout.tips.full_text": "Gewone teks laat sien plasings wat jy geskryf het, van gehou het, aangestuur het of in genoem is, asook ooreenstemmende gebruikersname, vertoonde name en hutsetikette.",
-  "search_popout.tips.hashtag": "hutsetiket",
-  "search_popout.tips.status": "plasing",
-  "search_popout.tips.text": "Gewone teks laat sien ooreenstemmende name, gebruikersname en hutsetikette",
-  "search_popout.tips.user": "gebruiker",
-  "search_results.accounts": "Mense",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Alles",
   "search_results.hashtags": "Hutsetiket",
   "search_results.nothing_found": "Hierdie soekwoorde lewer niks op nie",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open hierdie plasing as moderator",
@@ -551,7 +559,8 @@
   "status.copy": "Kopieer skakel na hierdie plasing",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/an.json b/app/javascript/mastodon/locales/an.json
index 0a86e32ef..9fea523c6 100644
--- a/app/javascript/mastodon/locales/an.json
+++ b/app/javascript/mastodon/locales/an.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocau",
   "account.browse_more_on_origin_server": "Veyer mas en o perfil orichinal",
   "account.cancel_follow_request": "Retirar solicitut de seguimiento",
-  "account.direct": "Mensache directo a @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Deixar de notificar-me quan @{name} publique bella cosa",
   "account.domain_blocked": "Dominio blocau",
   "account.edit_profile": "Editar perfil",
@@ -54,7 +54,7 @@
   "account.posts_with_replies": "Publicacions y respuestas",
   "account.report": "Denunciar a @{name}",
   "account.requested": "Esperando l'aprebación",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name} ha demandau seguir-te",
   "account.share": "Compartir lo perfil de @{name}",
   "account.show_reblogs": "Amostrar retutz de @{name}",
   "account.statuses_counter": "{count, plural, one {{counter} Publicación} other {{counter} Publicaciones}}",
@@ -102,7 +102,7 @@
   "column.blocks": "Usuarios blocaus",
   "column.bookmarks": "Marcadors",
   "column.community": "Linia de tiempo local",
-  "column.direct": "Mensaches directos",
+  "column.direct": "Private mentions",
   "column.directory": "Buscar perfils",
   "column.domain_blocks": "Dominios amagaus",
   "column.favourites": "Favoritos",
@@ -128,7 +128,7 @@
   "compose.language.search": "Buscar idiomas...",
   "compose_form.direct_message_warning_learn_more": "Aprender mas",
   "compose_form.encryption_warning": "Las publicacions en Mastodon no son zifradas de cabo a cabo. No comparta garra información sensible en Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "Este tut no s'amostrará en cada etiqueta, ya que no ye publico. Nomás los tuts publicos se pueden buscar per etiqueta.",
   "compose_form.lock_disclaimer": "La tuya cuenta no ye {locked}. Totz pueden seguir-te pa veyer las tuyas publicacions nomás pa seguidores.",
   "compose_form.lock_disclaimer.lock": "blocau",
   "compose_form.placeholder": "En qué yes pensando?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Tiens cambios sin alzar en a descripción u vista previa d'o fichero audiovisual, descartar-los de totz modos?",
   "confirmations.domain_block.confirm": "Amagar dominio entero",
   "confirmations.domain_block.message": "Yes seguro que quiers blocar lo dominio {domain} entero? En cheneral ye prou, y preferible, fer uns quantos bloqueyos y silenciaus concretos. Los tuyos seguidros d'ixe dominio serán eliminaus.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Zarrar sesión",
   "confirmations.logout.message": "Yes seguro de querer zarrar la sesión?",
   "confirmations.mute.confirm": "Silenciar",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Encara no has blocau a garra usuario.",
   "empty_column.bookmarked_statuses": "Encara no tiens garra publicación alzada como marcador. Quan alces una, s'amostrará aquí.",
   "empty_column.community": "La linia de tiempo local ye vueda. Escribe bella cosa pa empecipiar la fiesta!",
-  "empty_column.direct": "Encara no tiens garra mensache directo. Quan ninvies u recibas un, s'amostrará aquí.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Encara no i hai dominios amagaus.",
   "empty_column.explore_statuses": "Cosa ye en tendencia en este momento. Revisa mas tarde!",
   "empty_column.favourited_statuses": "Encara no tiens publicacions favoritas. Quan marques una como favorita, amaneixerá aquí.",
   "empty_column.favourites": "Dengún ha marcau esta publicación como favorita. Quan belún lo faiga, amaneixerá aquí.",
   "empty_column.follow_recommendations": "Pareixe que no s'ha puesto chenerar garra sucherencia pa tu. Puetz prebar a buscar a chent que talment conoixcas u explorar los hashtags que son en tendencia.",
   "empty_column.follow_requests": "No tiens garra petición de seguidor. Quan recibas una, s'amostrará aquí.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "No i hai cosa en este hashtag encara.",
   "empty_column.home": "La tuya linia temporal ye vueda! Sigue a mas personas pa replenar-la. {suggestions}",
   "empty_column.home.suggestions": "Veyer qualques sucherencias",
@@ -236,11 +239,11 @@
   "errors.unexpected_crash.copy_stacktrace": "Copiar lo seguimiento de pila en o portafuellas",
   "errors.unexpected_crash.report_issue": "Informar d'un problema/error",
   "explore.search_results": "Resultaus de busqueda",
-  "explore.suggested_follows": "For you",
+  "explore.suggested_follows": "Pa tu",
   "explore.title": "Explorar",
-  "explore.trending_links": "News",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "Hashtags",
+  "explore.trending_links": "Noticias",
+  "explore.trending_statuses": "Publicacions",
+  "explore.trending_tags": "Etiquetas",
   "filter_modal.added.context_mismatch_explanation": "Esta categoría de filtro no s'aplica a lo contexto en o qual ha accediu a esta publlicación. Si quiers que la publicación sía filtrada tamién en este contexto, habrás d'editar lo filtro.",
   "filter_modal.added.context_mismatch_title": "Lo contexto no coincide!",
   "filter_modal.added.expired_explanation": "Esta categoría de filtro ha caducau, amenesterá cambiar la calendata de caducidat pa que s'aplique.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autorizar",
   "follow_request.reject": "Refusar",
   "follow_requests.unlocked_explanation": "Tot y que la tuya cuenta no ye privada, lo personal de {domain} ha pensau que talment habrías de revisar manualment las solicitutz de seguimiento d'estas cuentas.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "Sobre",
   "footer.directory": "Directorio de perfils",
   "footer.get_app": "Obtener l'aplicación",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Alcorces de teclau",
   "footer.privacy_policy": "Politica de privacidat",
   "footer.source_code": "Veyer codigo fuent",
+  "footer.status": "Status",
   "generic.saved": "Alzau",
   "getting_started.heading": "Primers pasos",
   "hashtag.column_header.tag_mode.all": "y {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Enfocar en una d'as columnas",
   "keyboard_shortcuts.compose": "Enfocar l'aria de redacción de texto",
   "keyboard_shortcuts.description": "Descripción",
-  "keyboard_shortcuts.direct": "pa ubrir la columna de mensaches directos",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Mover enta baixo en a lista",
   "keyboard_shortcuts.enter": "Ubrir estau",
   "keyboard_shortcuts.favourite": "Anyadir en favoritos",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Marcadors",
   "navigation_bar.community_timeline": "Linia de tiempo local",
   "navigation_bar.compose": "Escribir nueva publicación",
-  "navigation_bar.direct": "Mensaches directos",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Descubrir",
   "navigation_bar.domain_blocks": "Dominios amagaus",
   "navigation_bar.edit_profile": "Editar perfil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoritos",
   "navigation_bar.filters": "Parolas silenciadas",
   "navigation_bar.follow_requests": "Solicitutz pa seguir-te",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Seguindo y seguidores",
   "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Zarrar sesión",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Infracción de regla",
   "report_notification.open": "Ubrir informe",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Buscar",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Buscar u apegar URL",
-  "search_popout.search_format": "Formato de busqueda abanzada",
-  "search_popout.tips.full_text": "Las busquedas de texto recuperan publicacions que has escrito, marcau como favoritas, retutau u en os quals has estau mencionau, asinas como usuarios, nombres y hashtags.",
-  "search_popout.tips.hashtag": "etiqueta",
-  "search_popout.tips.status": "status",
-  "search_popout.tips.text": "Lo texto simple torna correspondencias de nombre, usuario y hashtag",
-  "search_popout.tips.user": "usuario",
-  "search_results.accounts": "Chent",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Totz",
   "search_results.hashtags": "Etiquetas",
   "search_results.nothing_found": "No se podió trobar cosa pa estes termins de busqueda",
@@ -540,9 +548,9 @@
   "server_banner.server_stats": "Estatisticas d'o servidor:",
   "sign_in_banner.create_account": "Creyar cuenta",
   "sign_in_banner.sign_in": "Iniciar sesión",
-  "sign_in_banner.text": "Inicia sesión en este servidor pa seguir perfils u etiquetas, alzar, compartir y responder a mensaches. Tamién puetz interactuar dende unatra cuenta en un servidor diferent.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Ubrir interficie de moderación pa @{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_domain": "Ubrir interficie de moderación pa {domain}",
   "status.admin_status": "Ubrir este estau en a interficie de moderación",
   "status.block": "Blocar a @{name}",
   "status.bookmark": "Anyadir marcador",
@@ -551,7 +559,8 @@
   "status.copy": "Copiar vinclo a lo estau",
   "status.delete": "Borrar",
   "status.detailed_status": "Vista de conversación detallada",
-  "status.direct": "Mensache directo a @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Editar",
   "status.edited": "Editau {date}",
   "status.edited_x_times": "Editau {count, plural, one {{count} vez} other {{count} veces}}",
@@ -559,7 +568,7 @@
   "status.favourite": "Favorito",
   "status.filter": "Filtrar esta publicación",
   "status.filtered": "Filtrau",
-  "status.hide": "Hide post",
+  "status.hide": "Amagar la publicación",
   "status.history.created": "{name} creyó {date}",
   "status.history.edited": "{name} editó {date}",
   "status.load_more": "Cargar mas",
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 87804ab07..399efe143 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -20,7 +20,7 @@
   "account.blocked": "محظور",
   "account.browse_more_on_origin_server": "تصفح المزيد في الملف الشخصي الأصلي",
   "account.cancel_follow_request": "إلغاء طلب المتابعة",
-  "account.direct": "مراسلة @{name} بشكل مباشر",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "توقف عن إشعاري عندما ينشر @{name}",
   "account.domain_blocked": "اسم النِّطاق محظور",
   "account.edit_profile": "تعديل الملف الشخصي",
@@ -97,12 +97,12 @@
   "closed_registrations_modal.description": "لا يمكن إنشاء حساب على {domain} حاليا، ولكن على فكرة لست بحاجة إلى حساب على {domain} بذاته لاستخدام ماستدون.",
   "closed_registrations_modal.find_another_server": "ابحث على خادم آخر",
   "closed_registrations_modal.preamble": "ماستدون لامركزي، لذلك بغض النظر عن مكان إنشاء حسابك، سيكون بإمكانك المتابعة والتفاعل مع أي شخص على هذا الخادم. يمكنك حتى أن تستضيفه ذاتياً!",
-  "closed_registrations_modal.title": "تسجيل حساب في ماستدون",
+  "closed_registrations_modal.title": "إنشاء حساب على ماستدون",
   "column.about": "عن",
   "column.blocks": "المُستَخدِمون المَحظورون",
   "column.bookmarks": "الفواصل المرجعية",
   "column.community": "الخيط الزمني المحلي",
-  "column.direct": "الرسائل المباشِرة",
+  "column.direct": "Private mentions",
   "column.directory": "تَصَفُّحُ المَلفات الشخصية",
   "column.domain_blocks": "النطاقات المحظورة",
   "column.favourites": "المُفَضَّلَة",
@@ -128,7 +128,7 @@
   "compose.language.search": "البحث عن لغة…",
   "compose_form.direct_message_warning_learn_more": "تَعَلَّم المَزيد",
   "compose_form.encryption_warning": "إنّ المنشورات على ماستدون ليست مشفرة من النهاية إلى النهاية. لا تشارك أي معلومات حساسة عبر ماستدون.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "لن يُدرَج هذا المنشور تحت أي وسم بما أنَّه غير منشور للعامة. إلّا الرسائل المنشورة للعامة يُمكن البحث عنها بواسطة وسم.",
   "compose_form.lock_disclaimer": "حسابُك غير {locked}. يُمكن لأي شخص مُتابعتك لرؤية (منشورات المتابعين فقط).",
   "compose_form.lock_disclaimer.lock": "مُقفَل",
   "compose_form.placeholder": "فِيمَ تُفكِّر؟",
@@ -138,7 +138,7 @@
   "compose_form.poll.remove_option": "إزالة هذا الخيار",
   "compose_form.poll.switch_to_multiple": "تغيِير الاستطلاع للسماح باِخيارات مُتعدِّدة",
   "compose_form.poll.switch_to_single": "تغيِير الاستطلاع للسماح باِخيار واحد فقط",
-  "compose_form.publish": "انشر",
+  "compose_form.publish": "نشر",
   "compose_form.publish_form": "انشر",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.save_changes": "احفظ التعديلات",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "لديك تغييرات غير محفوظة لوصف الوسائط أو معاينتها، تجاهلها على أي حال؟",
   "confirmations.domain_block.confirm": "حظر اِسم النِّطاق بشكلٍ كامل",
   "confirmations.domain_block.message": "متأكد من أنك تود حظر اسم النطاق {domain} بالكامل ؟ في غالب الأحيان يُستَحسَن كتم أو حظر بعض الحسابات بدلا من حظر نطاق بالكامل.\nلن تتمكن مِن رؤية محتوى هذا النطاق لا على خيوطك العمومية و لا في إشعاراتك. سوف يتم كذلك إزالة كافة متابعيك المنتمين إلى هذا النطاق.",
+  "confirmations.edit.confirm": "تعديل",
+  "confirmations.edit.message": "التعديل في الحين سوف يُعيد كتابة الرسالة التي أنت بصدد تحريرها. متأكد من أنك تريد المواصلة؟",
   "confirmations.logout.confirm": "خروج",
   "confirmations.logout.message": "متأكد من أنك تريد الخروج؟",
   "confirmations.mute.confirm": "أكتم",
@@ -176,7 +178,7 @@
   "conversation.delete": "احذف المحادثة",
   "conversation.mark_as_read": "اعتبرها كمقروءة",
   "conversation.open": "اعرض المحادثة",
-  "conversation.with": "بـ {names}",
+  "conversation.with": "مع {names}",
   "copypaste.copied": "تم نسخه",
   "copypaste.copy": "انسخ",
   "directory.federated": "مِن الفديفرس المعروف",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "لم تقم بحظر أي مستخدِم بعد.",
   "empty_column.bookmarked_statuses": "ليس لديك أية منشورات في الفواصل المرجعية بعد. عندما ستقوم بإضافة البعض منها، ستظهر هنا.",
   "empty_column.community": "الخط العام المحلي فارغ. أكتب شيئا ما للعامة كبداية!",
-  "empty_column.direct": "لم تتلق أية رسالة خاصة مباشِرة بعد. سوف يتم عرض الرسائل المباشرة هنا إن قمت بإرسال واحدة أو تلقيت البعض منها.",
-  "empty_column.domain_blocks": "ليس هناك نطاقات مخفية بعد.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
+  "empty_column.domain_blocks": "ليس هناك نطاقات تم حجبها بعد.",
   "empty_column.explore_statuses": "ليس هناك ما هو متداوَل الآن. عد في وقت لاحق!",
   "empty_column.favourited_statuses": "ليس لديك أية منشورات مفضلة بعد. عندما ستقوم بالإعجاب بواحدة، ستظهر هنا.",
   "empty_column.favourites": "لم يقم أي أحد بالإعجاب بهذا المنشور بعد. عندما يقوم أحدهم بذلك سوف يظهر هنا.",
   "empty_column.follow_recommendations": "يبدو أنه لا يمكن إنشاء أي اقتراحات لك. يمكنك البحث عن أشخاص قد تعرفهم أو استكشاف الوسوم الرائجة.",
   "empty_column.follow_requests": "ليس عندك أي طلب للمتابعة بعد. سوف تظهر طلباتك هنا إن قمت بتلقي البعض منها.",
+  "empty_column.followed_tags": "لم تُتابع أي وسم بعدُ. ستظهر الوسوم هنا حينما تفعل ذلك.",
   "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
   "empty_column.home": "إنّ الخيط الزمني لصفحتك الرئيسية فارغ. قم بزيارة {public} أو استخدم حقل البحث لكي تكتشف مستخدمين آخرين.",
   "empty_column.home.suggestions": "شاهد بعض الاقتراحات",
@@ -231,7 +234,7 @@
   "empty_column.public": "لا يوجد أي شيء هنا! قم بنشر شيء ما للعامة، أو اتبع المستخدمين الآخرين المتواجدين على الخوادم الأخرى لملء خيط المحادثات",
   "error.unexpected_crash.explanation": "نظرا لوجود خطأ في التعليمات البرمجية أو مشكلة توافق مع المتصفّح، تعذر عرض هذه الصفحة بشكل صحيح.",
   "error.unexpected_crash.explanation_addons": "لا يمكن عرض هذه الصفحة بشكل صحيح. من المحتمل أن يكون هذا الخطأ بسبب إضافة متصفح أو أدوات ترجمة تلقائية.",
-  "error.unexpected_crash.next_steps": "حاول إعادة إنعاش الصفحة. إن لم تُحلّ المشكلة ، يمكنك دائمًا استخدام ماستدون عبر متصفّح آخر أو تطبيق أصلي.",
+  "error.unexpected_crash.next_steps": "حاول إعادة إنعاش الصفحة. إن لم تُحلّ المشكلة، يمكنك دائمًا استخدام ماستدون عبر متصفّح آخر أو تطبيق أصلي.",
   "error.unexpected_crash.next_steps_addons": "حاول تعطيلهم وإنعاش الصفحة. إن لم ينجح ذلك، يمكنك دائمًا استخدام ماستدون عبر متصفح آخر أو تطبيق أصلي.",
   "errors.unexpected_crash.copy_stacktrace": "انسخ تتبع الارتباطات إلى الحافظة",
   "errors.unexpected_crash.report_issue": "الإبلاغ عن خلل",
@@ -244,7 +247,7 @@
   "filter_modal.added.context_mismatch_explanation": "فئة عامل التصفية هذه لا تنطبق على السياق الذي وصلت فيه إلى هذه المشاركة. إذا كنت ترغب في تصفية المنشور في هذا السياق أيضا، فسيتعين عليك تعديل عامل التصفية.",
   "filter_modal.added.context_mismatch_title": "عدم تطابق السياق!",
   "filter_modal.added.expired_explanation": "انتهت صلاحية فئة عامل التصفية هذه، سوف تحتاج إلى تغيير تاريخ انتهاء الصلاحية لتطبيقها.",
-  "filter_modal.added.expired_title": "تصفية منتهية الصلاحية!",
+  "filter_modal.added.expired_title": "عامل تصفية انتهت صلاحيته!",
   "filter_modal.added.review_and_configure": "لمراجعة وزيادة تكوين فئة عوامل التصفية هذه، انتقل إلى {settings_link}.",
   "filter_modal.added.review_and_configure_title": "إعدادات التصفية",
   "filter_modal.added.settings_link": "صفحة الإعدادات",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "ترخيص",
   "follow_request.reject": "رفض",
   "follow_requests.unlocked_explanation": "على الرغم من أن حسابك غير مقفل، فإن موظفين الـ{domain} ظنوا أنك قد ترغب في مراجعة طلبات المتابعة من هذه الحسابات يدوياً.",
+  "followed_tags": "الوسوم المتابَعة",
   "footer.about": "حَول",
   "footer.directory": "دليل الصفحات التعريفية",
   "footer.get_app": "احصل على التطبيق",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "اختصارات لوحة المفاتيح",
   "footer.privacy_policy": "سياسة الخصوصية",
   "footer.source_code": "الاطلاع على الشفرة المصدرية",
+  "footer.status": "الحالة",
   "generic.saved": "تم الحفظ",
   "getting_started.heading": "استعدّ للبدء",
   "hashtag.column_header.tag_mode.all": "و {additional}",
@@ -288,10 +293,10 @@
   "home.column_settings.show_replies": "اعرض الردود",
   "home.hide_announcements": "إخفاء الإعلانات",
   "home.show_announcements": "إظهار الإعلانات",
-  "interaction_modal.description.favourite": "مع حساب في ماستدون، يمكنك تفضيل هذا المقال لإبلاغ الناشر بتقديرك وحفظه لاحقا.",
-  "interaction_modal.description.follow": "مع حساب في ماستدون، يمكنك متابعة {name} لتلقي مشاركاتهم في الصفحه الرئيسيه.",
-  "interaction_modal.description.reblog": "مع حساب في ماستدون، يمكنك تعزيز هذا المنشور لمشاركته مع متابعينك.",
-  "interaction_modal.description.reply": "مع حساب في ماستدون، يمكنك الرد على هذه المشاركة.",
+  "interaction_modal.description.favourite": "مع حساب في ماستدون، يمكنك إضافة هذا المنشور إلى مفضلتك لإبلاغ الناشر عن تقديرك وكذا للاحتفاظ به لوقت لاحق.",
+  "interaction_modal.description.follow": "مع حساب في ماستدون، يمكنك متابعة {name} وتلقي منشوراته على خيطك الرئيس.",
+  "interaction_modal.description.reblog": "مع حساب في ماستدون، يمكنك تعزيز هذا المنشور ومشاركته مع مُتابِعيك.",
+  "interaction_modal.description.reply": "مع حساب في ماستدون، يمكنك الرد على هذا المنشور.",
   "interaction_modal.on_another_server": "على خادم مختلف",
   "interaction_modal.on_this_server": "على هذا الخادم",
   "interaction_modal.other_server_instructions": "انسخ و الصق هذا الرابط في حقل البحث الخاص بك لتطبيق ماستدون المفضل لديك أو واجهة الويب لخادم ماستدون الخاص بك.",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "للتركيز على منشور على أحد الأعمدة",
   "keyboard_shortcuts.compose": "للتركيز على نافذة تحرير النصوص",
   "keyboard_shortcuts.description": "الوصف",
-  "keyboard_shortcuts.direct": "لفتح عمود الرسائل المباشرة",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "للانتقال إلى أسفل القائمة",
   "keyboard_shortcuts.enter": "لفتح المنشور",
   "keyboard_shortcuts.favourite": "للإضافة إلى المفضلة",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "الفواصل المرجعية",
   "navigation_bar.community_timeline": "الخيط المحلي",
   "navigation_bar.compose": "تحرير منشور جديد",
-  "navigation_bar.direct": "الرسائل المباشِرة",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "اكتشف",
   "navigation_bar.domain_blocks": "النطاقات المحظورة",
   "navigation_bar.edit_profile": "عدّل الملف التعريفي",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "المفضلة",
   "navigation_bar.filters": "الكلمات المكتومة",
   "navigation_bar.follow_requests": "طلبات المتابعة",
+  "navigation_bar.followed_tags": "الوسوم المتابَعة",
   "navigation_bar.follows_and_followers": "المتابِعين والمتابَعون",
   "navigation_bar.lists": "القوائم",
   "navigation_bar.logout": "خروج",
@@ -505,7 +511,7 @@
   "report.statuses.title": "هل توجد مشاركات تدعم صحة هذا البلاغ؟",
   "report.submit": "إرسال",
   "report.target": "ابلغ عن {target}",
-  "report.thanks.take_action": "يمكنك هنا التحكم في ما يعرض لك على ماستدون:",
+  "report.thanks.take_action": "فيما يلي خياراتك للتحكم بما يُعرَض عليك في ماستدون:",
   "report.thanks.take_action_actionable": "في أثناء مراجعتنا للبلاغ، يمكنك اتخاذ إجراء ضد @{name}:",
   "report.thanks.title": "هل ترغب في مشاهدة هذا؟",
   "report.thanks.title_actionable": "شُكرًا لَكَ على الإبلاغ، سَوفَ نَنظُرُ فِي هَذَا الأمر.",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "مزعج",
   "report_notification.categories.violation": "القاعدة المنتهَكة",
   "report_notification.open": "فتح التقرير",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "ابحث",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "ابحث أو أدخل رابطا تشعبيا URL",
-  "search_popout.search_format": "نمط البحث المتقدم",
-  "search_popout.tips.full_text": "النص البسيط يقوم بعرض المنشورات التي كتبتها أو قمت بإرسالها أو ترقيتها أو تمت الإشارة إليك فيها من طرف آخرين ، بالإضافة إلى مطابقة أسماء المستخدمين وأسماء العرض وعلامات التصنيف.",
-  "search_popout.tips.hashtag": "وسم",
-  "search_popout.tips.status": "حالة",
-  "search_popout.tips.text": "جملة قصيرة تُمكّنُك من عرض أسماء و حسابات و كلمات رمزية",
-  "search_popout.tips.user": "مستخدِم",
-  "search_results.accounts": "أشخاص",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "الكل",
   "search_results.hashtags": "الوُسوم",
   "search_results.nothing_found": "تعذر العثور على نتائج تتضمن هذه المصطلحات",
@@ -551,7 +559,8 @@
   "status.copy": "انسخ رابط الرسالة",
   "status.delete": "احذف",
   "status.detailed_status": "تفاصيل المحادثة",
-  "status.direct": "رسالة خاصة إلى @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "تعديل",
   "status.edited": "عُدّل في {date}",
   "status.edited_x_times": "عُدّل {count, plural, zero {} one {مرةً واحدة} two {مرّتان} few {{count} مرات} many {{count} مرة} other {{count} مرة}}",
diff --git a/app/javascript/mastodon/locales/ast.json b/app/javascript/mastodon/locales/ast.json
index 484a263cf..c0920bbec 100644
--- a/app/javascript/mastodon/locales/ast.json
+++ b/app/javascript/mastodon/locales/ast.json
@@ -4,13 +4,13 @@
   "about.disclaimer": "Mastodon ye software gratuito ya de códigu llibre, ya una marca rexistrada de Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "El motivu nun ta disponible",
   "about.domain_blocks.preamble": "Polo xeneral, Mastodon permítete ver el conteníu ya interactuar colos perfiles d'otros sirvidores nel fediversu. Estes son les esceiciones que se ficieron nesti sirvidor.",
-  "about.domain_blocks.silenced.explanation": "Polo xeneral, nun ves los perfiles ya el conteníu d'esti sirvidor sacante que los busques o decidas siguilos.",
+  "about.domain_blocks.silenced.explanation": "Polo xeneral, nun ves los perfiles ya'l conteníu d'esti sirvidor sacante que los busques o decidas siguilos.",
   "about.domain_blocks.silenced.title": "Llendóse",
   "about.domain_blocks.suspended.explanation": "Nun se procesa, atroxa nin intercambia nengún datu d'esti sirvidor, lo que fai que cualesquier interaición o comunicación colos sos perfiles seya imposible.",
   "about.domain_blocks.suspended.title": "Suspendióse",
   "about.not_available": "Esta información nun ta disponible nesti sirvidor.",
   "about.powered_by": "Una rede social descentralizada que tien la teunoloxía de {mastodon}",
-  "about.rules": "Regles del sirvidor",
+  "about.rules": "Normes del sirvidor",
   "account.account_note_header": "Nota",
   "account.add_or_remove_from_list": "Amestar o quitar de les llistes",
   "account.badges.bot": "Robó",
@@ -20,7 +20,7 @@
   "account.blocked": "Blocked",
   "account.browse_more_on_origin_server": "Restolar más nel perfil orixinal",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Unviar un mensaxe direutu a @{name}",
+  "account.direct": "Mentar a @{name} per privao",
   "account.disable_notifications": "Dexar d'avisame cuando @{name} espublice artículos",
   "account.domain_blocked": "Domain blocked",
   "account.edit_profile": "Editar el perfil",
@@ -34,11 +34,11 @@
   "account.followers.empty": "No one follows this user yet.",
   "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
   "account.following": "Following",
-  "account.following_counter": "{count, plural,one {Siguiendo a {counter}} other {Siguiendo a {counter}}}",
+  "account.following_counter": "{count, plural,one {Sigue a {counter}} other {Sigue a {counter}}}",
   "account.follows.empty": "Esti perfil entá nun sigue a naide.",
   "account.follows_you": "Síguete",
   "account.go_to_profile": "Go to profile",
-  "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.hide_reblogs": "Anubrir los artículos compartíos de @{name}",
   "account.joined_short": "Data de xunión",
   "account.languages": "Change subscribed languages",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
@@ -46,7 +46,7 @@
   "account.media": "Multimedia",
   "account.mention": "Mentar a @{name}",
   "account.moved_to": "{name} indicó qu'agora la so cuenta nueva ye:",
-  "account.mute": "Mute @{name}",
+  "account.mute": "Desactivar los avisos de @{name}",
   "account.mute_notifications": "Desactivar los avisos de @{name}",
   "account.muted": "Muted",
   "account.open_original_page": "Abrir la páxina orixinal",
@@ -54,18 +54,18 @@
   "account.posts_with_replies": "Artículos ya rempuestes",
   "account.report": "Report @{name}",
   "account.requested": "Awaiting approval. Click to cancel follow request",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name} solicitó siguite",
   "account.share": "Share @{name}'s profile",
   "account.show_reblogs": "Amosar los artículos compartíos de @{name}",
   "account.statuses_counter": "{count, plural, one {{counter} artículu} other {{counter} artículos}}",
   "account.unblock": "Unblock @{name}",
-  "account.unblock_domain": "Unblock domain {domain}",
+  "account.unblock_domain": "Desbloquiar el dominiu «{domain}»",
   "account.unblock_short": "Unblock",
   "account.unendorse": "Dexar de destacar nel perfil",
   "account.unfollow": "Dexar de siguir",
   "account.unmute": "Activar los avisos de @{name}",
   "account.unmute_notifications": "Activar los avisos de @{name}",
-  "account.unmute_short": "Unmute",
+  "account.unmute_short": "Activar los avisos",
   "account_note.placeholder": "Calca equí p'amestar una nota",
   "admin.dashboard.daily_retention": "User retention rate by day after sign-up",
   "admin.dashboard.monthly_retention": "User retention rate by month after sign-up",
@@ -100,16 +100,16 @@
   "closed_registrations_modal.title": "Rexistru en Mastodon",
   "column.about": "Tocante a",
   "column.blocks": "Perfiles bloquiaos",
-  "column.bookmarks": "Bookmarks",
+  "column.bookmarks": "Marcadores",
   "column.community": "Llinia de tiempu llocal",
-  "column.direct": "Mensaxes direutos",
+  "column.direct": "Menciones privaes",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Dominios bloquiaos",
-  "column.favourites": "Favourites",
+  "column.favourites": "Favoritos",
   "column.follow_requests": "Solicitúes de siguimientu",
-  "column.home": "Home",
-  "column.lists": "Lists",
-  "column.mutes": "Muted users",
+  "column.home": "Aniciu",
+  "column.lists": "Llistes",
+  "column.mutes": "Perfiles colos avisos desactivaos",
   "column.notifications": "Avisos",
   "column.pins": "Artículos fixaos",
   "column.public": "Llinia de tiempu federada",
@@ -138,10 +138,10 @@
   "compose_form.poll.remove_option": "Quitar esta opción",
   "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
   "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
-  "compose_form.publish": "Publish",
-  "compose_form.publish_form": "Publish",
+  "compose_form.publish": "Espublizar",
+  "compose_form.publish_form": "Espublizar",
   "compose_form.publish_loud": "¡{publish}!",
-  "compose_form.save_changes": "Save changes",
+  "compose_form.save_changes": "Guardar los cambeos",
   "compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}",
   "compose_form.sensitive.marked": "{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}",
   "compose_form.sensitive.unmarked": "{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Bloquiar tol dominiu",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "La edición va sobrescribir el mensaxe que tas escribiendo. ¿De xuru que quies siguir?",
   "confirmations.logout.confirm": "Zarrar la sesión",
   "confirmations.logout.message": "¿De xuru que quies zarrar la sesión?",
   "confirmations.mute.confirm": "Mute",
@@ -175,22 +177,22 @@
   "confirmations.unfollow.message": "¿De xuru que quies dexar de siguir a {name}?",
   "conversation.delete": "Delete conversation",
   "conversation.mark_as_read": "Mark as read",
-  "conversation.open": "View conversation",
+  "conversation.open": "Ver la conversación",
   "conversation.with": "Con {names}",
   "copypaste.copied": "Copióse",
   "copypaste.copy": "Copiar",
-  "directory.federated": "From known fediverse",
-  "directory.local": "From {domain} only",
-  "directory.new_arrivals": "New arrivals",
-  "directory.recently_active": "Recently active",
+  "directory.federated": "Del fediversu conocíu",
+  "directory.local": "De «{domain}» namás",
+  "directory.new_arrivals": "Cuentes nueves",
+  "directory.recently_active": "Con actividá recién",
   "disabled_account_banner.account_settings": "Account settings",
   "disabled_account_banner.text": "Your account {disabledAccount} is currently disabled.",
-  "dismissable_banner.community_timeline": "These are the most recent public posts from people whose accounts are hosted by {domain}.",
+  "dismissable_banner.community_timeline": "Esta seición contién los artículos públicos más actuales de los perfiles agospiaos nel dominiu {domain}.",
   "dismissable_banner.dismiss": "Escartar",
   "dismissable_banner.explore_links": "Esta seición contién les noticies que se tán comentando puramente agora, nesti ya otros sirvidores de la rede descentralizada.",
   "dismissable_banner.explore_statuses": "Esta seición contién los artículos d'esti ya otros sirvidores de la rede descentralizada que tán ganando popularidá nesti sirvidor.",
   "dismissable_banner.explore_tags": "Esta seición contién les etiquetes que tán ganando popularidá ente les persones d'esti ya otros sirvidores de la rede descentralizada.",
-  "dismissable_banner.public_timeline": "Esta seición contién los artículos públicos más recientes de persones nesti ya otros sirvidores de la rede descentralizada qu'esti sirvidor conoz.",
+  "dismissable_banner.public_timeline": "Esta seición contién los artículos públicos más actuales de persones nesti ya otros sirvidores de la rede descentralizada qu'esti sirvidor conoz.",
   "embed.instructions": "Empotra esti artículu nel to sitiu web pente la copia del códigu d'abaxo.",
   "embed.preview": "Va apaecer asina:",
   "emoji_button.activity": "Actividá",
@@ -212,27 +214,28 @@
   "empty_column.account_timeline": "¡Equí nun hai nengún artículu!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "Entá nun bloquiesti a nengún perfil.",
-  "empty_column.bookmarked_statuses": "Entá nun tienes nengún artículu en Marcadores. Cuando amiestes unu, apaez equí.",
-  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "Entá nun tienes nengún mensaxe direutu. Cuando unvies o recibas dalgún, apaecen equí.",
+  "empty_column.bookmarked_statuses": "Entá nun tienes nengún artículu en Marcadores. Cuando amiestes dalgún, apaez equí.",
+  "empty_column.community": "La llinia de tiempu llocal ta balera. ¡Espubliza daqué públicamente pa comenzar l'alderique!",
+  "empty_column.direct": "Entá nun tienes nenguna mención privada. Cuando unvies o recibas dalguna, apaez equí.",
   "empty_column.domain_blocks": "Entá nun hai nengún dominiu bloquiáu.",
   "empty_column.explore_statuses": "Agora nun hai nada en tendencia. ¡Volvi equí dempués!",
-  "empty_column.favourited_statuses": "Entá nun marquesti nengún artículu como favoritu. Cuando marques unu, apaez equí.",
-  "empty_column.favourites": "No one has favourited this post yet. When someone does, they will show up here.",
+  "empty_column.favourited_statuses": "Entá nun marquesti nengún artículu como favoritu. Cuando marques dalgún, apaez equí.",
+  "empty_column.favourites": "Naide marcó esti artículu como favoritu. Cuando dalgún perfil lo faiga, apaez equí.",
   "empty_column.follow_recommendations": "Paez que nun se puen xenerar suxerencies pa ti. Pues tentar d'usar la busca p'atopar perfiles que pues conocer o esplorar les etiquetes en tendencia.",
-  "empty_column.follow_requests": "Entá nun tienes nenguna solicitú de siguimientu. Cuando recibas una, apaez equí.",
+  "empty_column.follow_requests": "Entá nun tienes nenguna solicitú de siguimientu. Cuando recibas dalguna, apaez equí.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Entá nun hai nada con esta etiqueta.",
-  "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
-  "empty_column.home.suggestions": "See some suggestions",
-  "empty_column.list": "Entá nun hai nada nesta llista. Cuando los miembros d'esta llista espublicen artículos nuevos, apaecen equí.",
-  "empty_column.lists": "Entá nun tienes nenguna llista. Cuando crees una, apaez equí.",
-  "empty_column.mutes": "You haven't muted any users yet.",
-  "empty_column.notifications": "You don't have any notifications yet. When other people interact with you, you will see it here.",
-  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up",
+  "empty_column.home": "¡La to llinia de tiempu ta balera! Sigui a cuentes pa enllenala. {suggestions}",
+  "empty_column.home.suggestions": "Ver dalgunes suxerencies",
+  "empty_column.list": "Entá nun hai nada nesta llista. Cuando perfiles d'esta llista espublicen artículos nuevos, apaecen equí.",
+  "empty_column.lists": "Entá nun tienes nenguna llista. Cuando crees dalguna, apaez equí.",
+  "empty_column.mutes": "Entá nun tienes nengún perfil colos avisos desactivaos.",
+  "empty_column.notifications": "Entá nun tienes nengún avisu. Cuando otros perfiles interactúen contigo, apaez equí.",
+  "empty_column.public": "¡Equí nun hai nada! Escribi daqué públicamente o sigui a perfiles d'otros sirvidores pa enllenar esta seición",
   "error.unexpected_crash.explanation": "Pola mor d'un fallu nel códigu o un problema de compatibilidá del restolador, esta páxina nun se pudo amosar correutamente.",
   "error.unexpected_crash.explanation_addons": "Esta páxina nun se pudo amosar correutamente. Ye probable que dalgún complementu del restolador o dalguna ferramienta de traducción automática produxere esti error.",
-  "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
-  "error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
+  "error.unexpected_crash.next_steps": "Prueba a anovar la páxina. Si nun sirve, ye posible que tovía seyas a usar Mastodon pente otru restolador o una aplicación nativa.",
+  "error.unexpected_crash.next_steps_addons": "Prueba a desactivalos ya a anovar la páxina. Si nun sirve, ye posible que tovía seyas a usar Mastodon pente otru restolador o una aplicación nativa.",
   "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
   "errors.unexpected_crash.report_issue": "Report issue",
   "explore.search_results": "Resultaos de la busca",
@@ -258,20 +261,22 @@
   "filter_modal.select_filter.title": "Filter this post",
   "filter_modal.title.status": "Filter a post",
   "follow_recommendations.done": "Fecho",
-  "follow_recommendations.heading": "Follow people you'd like to see posts from! Here are some suggestions.",
+  "follow_recommendations.heading": "¡Sigui a perfiles que te prestaría ver nel feed personal! Equí tienes dalgunes suxerencies.",
   "follow_recommendations.lead": "Los artículos de los perfiles que sigas van apaecer n'orde cronolóxicu nel to feed d'aniciu. ¡Nun tengas mieu d'enquivocate, pues dexar de siguilos con facilidá en cualesquier momentu!",
   "follow_request.authorize": "Autorizar",
   "follow_request.reject": "Refugar",
   "follow_requests.unlocked_explanation": "Magar que la to cuenta nun seya privada, el personal del dominiu «{domain}» pensó qu'a lo meyor quies revisar manualmente les solicitúes de siguimientu d'estes cuentes.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "Tocante a",
   "footer.directory": "Direutoriu de perfiles",
   "footer.get_app": "Consiguir l'aplicación",
-  "footer.invite": "Invite people",
+  "footer.invite": "Convidar a persones",
   "footer.keyboard_shortcuts": "Atayos del tecláu",
   "footer.privacy_policy": "Política de privacidá",
   "footer.source_code": "Ver el códigu fonte",
+  "footer.status": "Estáu",
   "generic.saved": "Guardóse",
-  "getting_started.heading": "Getting started",
+  "getting_started.heading": "Comienzu",
   "hashtag.column_header.tag_mode.all": "y {additional}",
   "hashtag.column_header.tag_mode.any": "o {additional}",
   "hashtag.column_header.tag_mode.none": "ensin {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Enfocar una columna",
   "keyboard_shortcuts.compose": "Enfocar l'área de composición",
   "keyboard_shortcuts.description": "Descripción",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "p'abrir la columna de les menciones privaes",
   "keyboard_shortcuts.down": "Baxar na llista",
   "keyboard_shortcuts.enter": "Abrir un artículu",
   "keyboard_shortcuts.favourite": "Marcar un artículu como favoritu",
@@ -348,15 +353,15 @@
   "lists.account.remove": "Remove from list",
   "lists.delete": "Desaniciar la llista",
   "lists.edit": "Editar la llista",
-  "lists.edit.submit": "Change title",
+  "lists.edit.submit": "Camudar el títulu",
   "lists.new.create": "Amestar la llista",
-  "lists.new.title_placeholder": "New list title",
+  "lists.new.title_placeholder": "Títulu",
   "lists.replies_policy.followed": "Cualesquier perfil siguíu",
-  "lists.replies_policy.list": "Miembros de la llista",
+  "lists.replies_policy.list": "Perfiles de la llista",
   "lists.replies_policy.none": "Naide",
-  "lists.replies_policy.title": "Show replies to:",
-  "lists.search": "Search among people you follow",
-  "lists.subheading": "Your lists",
+  "lists.replies_policy.title": "Amosar les rempuestes a:",
+  "lists.search": "Buscar ente los perfiles que sigues",
+  "lists.subheading": "Les tos llistes",
   "load_pending": "{count, plural, one {# elementu nuevu} other {# elementos nuevos}}",
   "loading_indicator.label": "Cargando…",
   "media_gallery.toggle_visible": "{number, plural, one {Anubrir la imaxe} other {Anubrir les imáxenes}}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Marcadores",
   "navigation_bar.community_timeline": "Llinia de tiempu llocal",
   "navigation_bar.compose": "Compose new post",
-  "navigation_bar.direct": "Mensaxes direutos",
+  "navigation_bar.direct": "Menciones privaes",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Dominios bloquiaos",
   "navigation_bar.edit_profile": "Editar el perfil",
@@ -379,17 +384,18 @@
   "navigation_bar.favourites": "Favoritos",
   "navigation_bar.filters": "Pallabres desactivaes",
   "navigation_bar.follow_requests": "Solicitúes de siguimientu",
-  "navigation_bar.follows_and_followers": "Follows and followers",
+  "navigation_bar.followed_tags": "Followed hashtags",
+  "navigation_bar.follows_and_followers": "Perfiles que sigues ya te siguen",
   "navigation_bar.lists": "Llistes",
   "navigation_bar.logout": "Zarrar la sesión",
-  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.mutes": "Perfiles colos avisos desactivaos",
   "navigation_bar.personal": "Personal",
   "navigation_bar.pins": "Artículos fixaos",
   "navigation_bar.preferences": "Preferencies",
   "navigation_bar.public_timeline": "Llinia de tiempu federada",
   "navigation_bar.search": "Search",
   "navigation_bar.security": "Seguranza",
-  "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.",
+  "not_signed_in_indicator.not_signed_in": "Tienes d'aniciar la sesión p'acceder a esti recursu.",
   "notification.admin.report": "{name} informó de: {target}",
   "notification.admin.sign_up": "{name} rexistróse",
   "notification.favourite": "{name} marcó'l to artículu como favoritu",
@@ -416,7 +422,7 @@
   "notifications.column_settings.poll": "Resultaos de les encuestes:",
   "notifications.column_settings.push": "Push notifications",
   "notifications.column_settings.reblog": "Artículos compartíos:",
-  "notifications.column_settings.show": "Show in column",
+  "notifications.column_settings.show": "Amosar en columna",
   "notifications.column_settings.sound": "Reproducir un soníu",
   "notifications.column_settings.status": "Artículos nuevos:",
   "notifications.column_settings.unread_notifications.category": "Avisos ensin lleer",
@@ -453,21 +459,21 @@
   "privacy.direct.short": "Direct",
   "privacy.private.long": "Artículu visible namás pa los perfiles siguidores",
   "privacy.private.short": "Namás pa siguidores",
-  "privacy.public.long": "Visible for all",
+  "privacy.public.long": "Tol mundu pue ver l'artículu",
   "privacy.public.short": "Artículu públicu",
   "privacy.unlisted.long": "Artículu visible pa tol mundu mas escluyíu de les funciones de descubrimientu",
   "privacy.unlisted.short": "Unlisted",
   "privacy_policy.last_updated": "Data del últimu anovamientu: {date}",
   "privacy_policy.title": "Política de privacidá",
-  "refresh": "Refresh",
+  "refresh": "Anovar",
   "regeneration_indicator.label": "Cargando…",
   "regeneration_indicator.sublabel": "Your home feed is being prepared!",
   "relative_time.days": "{number} d",
-  "relative_time.full.days": "{number, plural, one {# day} other {# days}} ago",
-  "relative_time.full.hours": "{number, plural, one {# hour} other {# hours}} ago",
+  "relative_time.full.days": "hai {number, plural, one {# día} other {# díes}}",
+  "relative_time.full.hours": "hai {number, plural, one {# hora} other {# hores}}",
   "relative_time.full.just_now": "puramente agora",
-  "relative_time.full.minutes": "{number, plural, one {# minute} other {# minutes}} ago",
-  "relative_time.full.seconds": "{number, plural, one {# second} other {# seconds}} ago",
+  "relative_time.full.minutes": "hai {number, plural, one {# minutu} other {# minutos}}",
+  "relative_time.full.seconds": "hai {number, plural, one {# segundu} other {# segundos}}",
   "relative_time.hours": "{number} h",
   "relative_time.just_now": "agora",
   "relative_time.minutes": "{number} m",
@@ -478,7 +484,7 @@
   "report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.",
   "report.categories.other": "Other",
   "report.categories.spam": "Spam",
-  "report.categories.violation": "El conteníu incumple una o más regles del sirvidor",
+  "report.categories.violation": "El conteníu incumple una o más normes del sirvidor",
   "report.category.subtitle": "Escueyi la meyor opción",
   "report.category.title": "Dinos qué pasa con esti {type}",
   "report.category.title_account": "perfil",
@@ -487,44 +493,46 @@
   "report.comment.title": "¿Hai daqué más qu'habríemos saber?",
   "report.forward": "Reunviar a {target}",
   "report.forward_hint": "La cuenta ye d'otru sirvidor. ¿Quies unviar a esi sirvidor una copia anónima del informe?",
-  "report.mute": "Mute",
+  "report.mute": "Desactivar los avisos",
   "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.",
   "report.next": "Siguiente",
   "report.placeholder": "Comentarios adicionales",
-  "report.reasons.dislike": "I don't like it",
+  "report.reasons.dislike": "Nun me presta",
   "report.reasons.dislike_description": "Nun ye daqué que quiera ver",
   "report.reasons.other": "Ye daqué más",
   "report.reasons.other_description": "La incidencia nun s'axusta a les demás categoríes",
   "report.reasons.spam": "Ye spam",
-  "report.reasons.spam_description": "Malicious links, fake engagement, or repetitive replies",
-  "report.reasons.violation": "Incumple les regles del sirvidor",
-  "report.reasons.violation_description": "You are aware that it breaks specific rules",
+  "report.reasons.spam_description": "Contién enllaces maliciosos, conteníu fraudulentu o rempuestes repetitives",
+  "report.reasons.violation": "Incumple les normes del sirvidor",
+  "report.reasons.violation_description": "Yes consciente qu'incumple dalguna norma específica",
   "report.rules.subtitle": "Select all that apply",
-  "report.rules.title": "¿Qué regles s'incumplen?",
+  "report.rules.title": "¿Qué normes s'incumplen?",
   "report.statuses.subtitle": "Select all that apply",
-  "report.statuses.title": "Are there any posts that back up this report?",
+  "report.statuses.title": "¿Hai dalgún artículu qu'apoye esti informe?",
   "report.submit": "Unviar",
-  "report.target": "Report {target}",
+  "report.target": "Informe de: {target}",
   "report.thanks.take_action": "Equí tienes les opciones pa controlar qué ves en Mastodon:",
-  "report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:",
-  "report.thanks.title": "Don't want to see this?",
-  "report.thanks.title_actionable": "Thanks for reporting, we'll look into this.",
+  "report.thanks.take_action_actionable": "Mentanto revisamos esti informe, pues tomar midíes contra @{name}:",
+  "report.thanks.title": "¿Nun quies ver esti conteníu?",
+  "report.thanks.title_actionable": "Gracies pol informe, el casu xá ta n'investigación.",
   "report.unfollow": "Dexar de siguir a @{name}",
   "report.unfollow_explanation": "Sigues a esta cuenta. Pa dexar de ver los sos artículos nel to feed d'aniciu, dexa de siguila.",
-  "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached",
+  "report_notification.attached_statuses": "{count, plural, one {Axuntóse {count} artículu} other {Axuntáronse {count} artículos}}",
   "report_notification.categories.other": "Other",
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Abrir l'informe",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Buscar",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Busca o apiega una URL",
-  "search_popout.search_format": "Formatu de busca avanzada",
-  "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": "etiqueta",
-  "search_popout.tips.status": "artículu",
-  "search_popout.tips.text": "El testu simple devuelve nomes visibles, cuentes ya etiquetes que concasen",
-  "search_popout.tips.user": "perfil",
-  "search_results.accounts": "Perfiles",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Etiquetes",
   "search_results.nothing_found": "Nun se pudo atopar nada con esos términos de busca",
@@ -539,11 +547,11 @@
   "server_banner.learn_more": "Saber más",
   "server_banner.server_stats": "Estadístiques del sirvidor:",
   "sign_in_banner.create_account": "Crear una cuenta",
-  "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Anicia la sesión pa siguir a perfiles o etiquetes, marcar como favoritu, compartir o responder a artículos, o interactuar cola to cuenta nun sirvidor diferente.",
-  "status.admin_account": "Open moderation interface for @{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
-  "status.admin_status": "Open this status in the moderation interface",
+  "sign_in_banner.sign_in": "Aniciar la sesión",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
+  "status.admin_account": "Abrir la interfaz de moderación pa @{name}",
+  "status.admin_domain": "Abrir la interfaz de moderación pa «{domain}»",
+  "status.admin_status": "Abrir esti artículu na interfaz de moderación",
   "status.block": "Block @{name}",
   "status.bookmark": "Meter en Marcadores",
   "status.cancel_reblog_private": "Unboost",
@@ -551,9 +559,10 @@
   "status.copy": "Copiar l'enllaz al artículu",
   "status.delete": "Desaniciar",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Mentar a @{name} per privao",
+  "status.direct_indicator": "Mención privada",
   "status.edit": "Edit",
-  "status.edited": "Edited {date}",
+  "status.edited": "Editóse'l {date}",
   "status.edited_x_times": "Editóse {count, plural, one {{count} vegada} other {{count} vegaes}}",
   "status.embed": "Empotrar",
   "status.favourite": "Favourite",
@@ -563,7 +572,7 @@
   "status.history.created": "{name} creó {date}",
   "status.history.edited": "{name} editó {date}",
   "status.load_more": "Cargar más",
-  "status.media_hidden": "Media hidden",
+  "status.media_hidden": "Conteníu multimedia anubríu",
   "status.mention": "Mentar a @{name}",
   "status.more": "Más",
   "status.mute": "Desactivar los avisos de @{name}",
@@ -576,7 +585,7 @@
   "status.reblog_private": "Boost with original visibility",
   "status.reblogged_by": "{name} compartió",
   "status.reblogs.empty": "Naide nun compartió esti artículu entá. Cuando daquién lo faiga, apaez equí.",
-  "status.redraft": "Delete & re-draft",
+  "status.redraft": "Desaniciar ya reeditar",
   "status.remove_bookmark": "Remove bookmark",
   "status.replied_to": "En rempuesta a {name}",
   "status.reply": "Responder",
@@ -609,18 +618,18 @@
   "time_remaining.minutes": "{number, plural, one {Queda # minutu} other {Queden # minutos}}",
   "time_remaining.moments": "Moments remaining",
   "time_remaining.seconds": "{number, plural, one {Queda # segundu} other {Queden # segundos}}",
-  "timeline_hint.remote_resource_not_displayed": "{resource} from other servers are not displayed.",
+  "timeline_hint.remote_resource_not_displayed": "Nun s'amuesa'l recursu «{resource}» d'otros sirvidores.",
   "timeline_hint.resources.followers": "Siguidores",
   "timeline_hint.resources.follows": "Follows",
   "timeline_hint.resources.statuses": "Artículos antiguos",
-  "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {{days} days}}",
+  "trends.counter_by_accounts": "{count, plural, one {{counter} persona} other {{counter} persones}} {days, plural, one {nel últimu día} other {nos últimos {days} díes}}",
   "trends.trending_now": "En tendencia",
   "ui.beforeunload": "El borrador piérdese si coles de Mastodon.",
   "units.short.billion": "{count} MM",
   "units.short.million": "{count} M",
   "units.short.thousand": "{count} mil",
   "upload_area.title": "Drag & drop to upload",
-  "upload_button.label": "Add images, a video or an audio file",
+  "upload_button.label": "Amestar ficheros multimedia",
   "upload_error.limit": "File upload limit exceeded.",
   "upload_error.poll": "La xuba de ficheros nun ta permitida coles encuestes.",
   "upload_form.audio_description": "Describe for people who are hard of hearing",
@@ -643,7 +652,7 @@
   "upload_progress.label": "Xubiendo…",
   "upload_progress.processing": "Procesando…",
   "video.close": "Zarrar el videu",
-  "video.download": "Download file",
+  "video.download": "Baxar el ficheru",
   "video.exit_fullscreen": "Exit full screen",
   "video.expand": "Espander el videu",
   "video.fullscreen": "Pantalla completa",
diff --git a/app/javascript/mastodon/locales/be.json b/app/javascript/mastodon/locales/be.json
index ff99ec6e4..72224a71c 100644
--- a/app/javascript/mastodon/locales/be.json
+++ b/app/javascript/mastodon/locales/be.json
@@ -3,7 +3,7 @@
   "about.contact": "Кантакт:",
   "about.disclaimer": "Mastodon - свабоднае праграмнае забеспячэнне, з адкрытым зыходным кодам, і гандлёвай маркай Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Прычына недаступная",
-  "about.domain_blocks.preamble": "Mastodon звычайна дазваляе вам бачыць змесціва і камунікаваць з карыстальнікамі з іншых сервераў федэсвету. Гэта выключэнні, якія былі зробленыя на гэтым канкрэтным серверы.",
+  "about.domain_blocks.preamble": "Mastodon, у асноўным, дазваляе вам праглядаць кантэнт і ўзаемадзейнічаць з карыстальнікамі з іншых сервераў у федэсвету. Гэтыя выключэнні былі зроблены дакладна на гэтым серверы.",
   "about.domain_blocks.silenced.explanation": "Вы не будзеце бачыць профілі і змесціва гэтага серверу, калі не шукаеце іх мэтанакіравана ці не падпісаны на карыстальнікаў адтуль.",
   "about.domain_blocks.silenced.title": "Абмежаваны",
   "about.domain_blocks.suspended.explanation": "Ніякая інфармацыя з гэтага сервера не будзе апрацавана, захавана або абменена, узаемадзеянне або камунікацыя з карыстальнікамі гэтага сервера немагчымы.",
@@ -20,8 +20,8 @@
   "account.blocked": "Заблакіраваны",
   "account.browse_more_on_origin_server": "Глядзіце больш у арыгінальным профілі",
   "account.cancel_follow_request": "Скасаваць запыт на падпіску",
-  "account.direct": "Асабістае паведамленне @{name}",
-  "account.disable_notifications": "Не апавяшчаць мяне пра допісы @{name}",
+  "account.direct": "Асабістая згадка @{name}",
+  "account.disable_notifications": "Не паведамляць мне пра публікацыі @{name}",
   "account.domain_blocked": "Дамен заблакаваны",
   "account.edit_profile": "Рэдагаваць профіль",
   "account.enable_notifications": "Апавяшчаць мяне пра допісы @{name}",
@@ -102,7 +102,7 @@
   "column.blocks": "Заблакіраваныя карыстальнікі",
   "column.bookmarks": "Закладкі",
   "column.community": "Лакальная стужка",
-  "column.direct": "Асабістыя паведамленні",
+  "column.direct": "Асабістыя згадкі",
   "column.directory": "Праглядзець профілі",
   "column.domain_blocks": "Заблакіраваныя дамены",
   "column.favourites": "Упадабаныя",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "У вас ёсць незахаваныя змены ў апісанні або прэв'ю, усе роўна скасаваць іх?",
   "confirmations.domain_block.confirm": "Заблакіраваць дамен цалкам",
   "confirmations.domain_block.message": "Вы абсалютна дакладна ўпэўнены, што хочаце заблакіраваць {domain} зусім? У большасці выпадкаў, дастаткова некалькіх мэтавых блакіровак ці ігнараванняў. Вы перастанеце бачыць змесціва з гэтага дамену ва ўсіх стужках і апавяшчэннях. Вашы падпіскі з гэтага дамену будуць выдаленыя.",
+  "confirmations.edit.confirm": "Рэдагаваць",
+  "confirmations.edit.message": "Калі вы зменіце зараз, гэта ператрэ паведамленне, якое вы пішаце. Вы ўпэўнены, што хочаце працягнуць?",
   "confirmations.logout.confirm": "Выйсці",
   "confirmations.logout.message": "Вы ўпэўненыя, што хочаце выйсці?",
   "confirmations.mute.confirm": "Ігнараваць",
@@ -203,7 +205,7 @@
   "emoji_button.not_found": "Адпаведныя эмодзі не знойдзены",
   "emoji_button.objects": "Прадметы",
   "emoji_button.people": "Людзі",
-  "emoji_button.recent": "Карыстаныя найчасцей",
+  "emoji_button.recent": "Чата выкарыстаныя",
   "emoji_button.search": "Пошук...",
   "emoji_button.search_results": "Вынікі пошуку",
   "emoji_button.symbols": "Сімвалы",
@@ -213,14 +215,15 @@
   "empty_column.account_unavailable": "Профіль недаступны",
   "empty_column.blocks": "Вы яшчэ нікога не заблакіравалі.",
   "empty_column.bookmarked_statuses": "У вас яшчэ няма паведамленняў з закладкамі. Калі вы дадасце закладку, яна з'явіцца тут.",
-  "empty_column.community": "Мясцовая шкала часу пустая. Напішыце што-небудзь публічна, каб зрушыць з месца",
-  "empty_column.direct": "Пакуль у вас няма асабістых паведамленняў. Калі вы дашляце або атрымаеце штось, яно з'явіцца тут.",
+  "empty_column.community": "Мясцовая стужка пустая. Напішыце што-небудзь публічна, каб зрушыць з месца!",
+  "empty_column.direct": "Пакуль у вас няма асабістых згадак. Калі вы дашляце або атрымаеце штось, яно з'явіцца тут.",
   "empty_column.domain_blocks": "Заблакіраваных даменаў пакуль няма.",
   "empty_column.explore_statuses": "Зараз не ў трэндзе. Праверце пазней",
   "empty_column.favourited_statuses": "Вы яшчэ не ўпадабалі ніводны допіс. Калі гэта адбудзецца, вы ўбачыце яго тут.",
   "empty_column.favourites": "Ніхто яшчэ не ўпадабаў гэты допіс. Калі гэта адбудзецца, вы ўбачыце гэтых людзей тут.",
   "empty_column.follow_recommendations": "Здаецца, прапаноў для вас няма. Вы можаце паспрабаваць выкарыстаць пошук, каб знайсці людзей, якіх вы можаце ведаць, ці даследаваць папулярныя хэштэгі.",
   "empty_column.follow_requests": "У вас яшчэ няма запытаў на падпіскуі. Калі вы атрымаеце запыт, ён з'явяцца тут.",
+  "empty_column.followed_tags": "Вы пакуль не падпісаны ні на адзін хэштэг. Калі падпішацеся, яны з'явяцца тут.",
   "empty_column.hashtag": "Па гэтаму хэштэгу пакуль што нічога няма.",
   "empty_column.home": "Галоўная стужка пустая! Падпішыцеся на іншых людзей, каб запоўніць яе. {suggestions}",
   "empty_column.home.suggestions": "Глядзіце прапановы",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Аўтарызацыя",
   "follow_request.reject": "Адхіліць",
   "follow_requests.unlocked_explanation": "Ваш акаўнт не схаваны, аднак прадстаўнікі {domain} палічылі, што вы можаце захацець праглядзець запыты на падпіску з гэтых профіляў уручную.",
+  "followed_tags": "Падпіскі",
   "footer.about": "Пра нас",
   "footer.directory": "Дырэкторыя профіляў",
   "footer.get_app": "Спампаваць праграму",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Спалучэнні клавіш",
   "footer.privacy_policy": "Палітыка прыватнасці",
   "footer.source_code": "Прагледзець зыходны код",
+  "footer.status": "Статус",
   "generic.saved": "Захавана",
   "getting_started.heading": "Пачатак працы",
   "hashtag.column_header.tag_mode.all": "і {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Сфакусіравацца на калонцы",
   "keyboard_shortcuts.compose": "Сфакусіравацца на тэкставай вобласці",
   "keyboard_shortcuts.description": "Апісанне",
-  "keyboard_shortcuts.direct": "для адкрыцця калонкі прыватных паведамленняў",
+  "keyboard_shortcuts.direct": "адкрыць стоўп асабістых згадак",
   "keyboard_shortcuts.down": "Перамясціцца ўніз па спісе",
   "keyboard_shortcuts.enter": "Адкрыць допіс",
   "keyboard_shortcuts.favourite": "Упадабаць допіс",
@@ -351,10 +356,10 @@
   "lists.edit.submit": "Змяніць назву",
   "lists.new.create": "Дадаць спіс",
   "lists.new.title_placeholder": "Назва новага спіса",
-  "lists.replies_policy.followed": "Любы карыстальнік, за якім вы падпісаліся",
+  "lists.replies_policy.followed": "Любы карыстальнік, на якога вы падпісаліся",
   "lists.replies_policy.list": "Удзельнікі гэтага спісу",
   "lists.replies_policy.none": "Нікога",
-  "lists.replies_policy.title": "Паказаць адказы да:",
+  "lists.replies_policy.title": "Паказваць адказы:",
   "lists.search": "Шукайце сярод людзей, на якіх Вы падпісаны",
   "lists.subheading": "Вашыя спісы",
   "load_pending": "{count, plural, one {# новы элемент} few {# новыя элементы} many {# новых элементаў} other {# новых элементаў}}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Закладкі",
   "navigation_bar.community_timeline": "Лакальная стужка",
   "navigation_bar.compose": "Стварыць новы допіс",
-  "navigation_bar.direct": "Асабістыя паведамленні",
+  "navigation_bar.direct": "Асабістыя згадкі",
   "navigation_bar.discover": "Даведайцесь",
   "navigation_bar.domain_blocks": "Заблакіраваныя дамены",
   "navigation_bar.edit_profile": "Рэдагаваць профіль",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Упадабаныя",
   "navigation_bar.filters": "Ігнараваныя словы",
   "navigation_bar.follow_requests": "Запыты на падпіску",
+  "navigation_bar.followed_tags": "Падпіскі",
   "navigation_bar.follows_and_followers": "Падпіскі і падпісчыкі",
   "navigation_bar.lists": "Спісы",
   "navigation_bar.logout": "Выйсці",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Спам",
   "report_notification.categories.violation": "Парушэнне правілаў",
   "report_notification.open": "Адкрыць скаргу",
+  "search.no_recent_searches": "Гісторыя пошуку пустая",
   "search.placeholder": "Пошук",
+  "search.quick_action.account_search": "Супадзенне профіляў {x}",
+  "search.quick_action.go_to_account": "Перайсці да профілю {x}",
+  "search.quick_action.go_to_hashtag": "Перайсці да хэштэгу {x}",
+  "search.quick_action.open_url": "Адкрыць спасылку ў Mastodon",
+  "search.quick_action.status_search": "Супадзенне паведамленняў {x}",
   "search.search_or_paste": "Увядзіце спасылку або пошукавы запыт",
-  "search_popout.search_format": "Рэжым прасунутага пошуку",
-  "search_popout.tips.full_text": "Тэкставы пошук пакажа допісы, якія вы напісалі, упадабалі, пашырылі, альбо тыя, у якіх вас згадалі, а таксама адпаведныя імёны карыстальнікаў і тэгі.",
-  "search_popout.tips.hashtag": "хэштэг",
-  "search_popout.tips.status": "допіс",
-  "search_popout.tips.text": "Тэкставы пошук знаходзіць адпаведныя імёны карыстальнікаў, юзернеймы і хэштэгі",
-  "search_popout.tips.user": "карыстальнік",
-  "search_results.accounts": "Людзі",
+  "search_popout.quick_actions": "Хуткія дзеянні",
+  "search_popout.recent": "Нядаўнія запыты",
+  "search_results.accounts": "Профілі",
   "search_results.all": "Усё",
   "search_results.hashtags": "Хэштэгі",
   "search_results.nothing_found": "Па дадзенаму запыту нічога не знойдзена",
@@ -551,7 +559,8 @@
   "status.copy": "Скапіраваць спасылку на допіс",
   "status.delete": "Выдаліць",
   "status.detailed_status": "Дэтальны агляд размовы",
-  "status.direct": "Асабістае паведамленне @{name}",
+  "status.direct": "Асабістая згадка @{name}",
+  "status.direct_indicator": "Асабістая згадка",
   "status.edit": "Рэдагаваць",
   "status.edited": "Адрэдагавана {date}",
   "status.edited_x_times": "Рэдагавана {count, plural, one {{count} раз} few {{count} разы} many {{count} разоў} other {{count} разу}}",
diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json
index 284d162b1..284e6b22b 100644
--- a/app/javascript/mastodon/locales/bg.json
+++ b/app/javascript/mastodon/locales/bg.json
@@ -1,7 +1,7 @@
 {
   "about.blocks": "Модерирани сървъри",
   "about.contact": "За контакти:",
-  "about.disclaimer": "Mastodon е безплатен софтуер с отворен изходен код и търговска марка Mastodon gGmbH.",
+  "about.disclaimer": "Mastodon е безплатен софтуер с отворен изходен код и търговска марка на Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Няма налична причина",
   "about.domain_blocks.preamble": "Mastodon обикновено позволява да разглеждате съдържание и да взаимодействате с други потребители от всякакви сървъри във Федивърс. Има изключения, направени конкретно за този сървър.",
   "about.domain_blocks.silenced.explanation": "Обикновено няма да виждате профили и съдържание, освен ако изрично не го потърсите или се включете в него, следвайки го.",
@@ -18,9 +18,9 @@
   "account.block": "Блокиране на @{name}",
   "account.block_domain": "Блокиране на домейн {domain}",
   "account.blocked": "Блокирани",
-  "account.browse_more_on_origin_server": "Разглеждане на още в първообразния профил",
+  "account.browse_more_on_origin_server": "Разглеждане на още в оригиналния профил",
   "account.cancel_follow_request": "Оттегляне на заявката за последване",
-  "account.direct": "Директно съобщение до @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Сприране на известия при публикуване от @{name}",
   "account.domain_blocked": "Блокиран домейн",
   "account.edit_profile": "Редактиране на профила",
@@ -101,8 +101,8 @@
   "column.about": "Относно",
   "column.blocks": "Блокирани потребители",
   "column.bookmarks": "Отметки",
-  "column.community": "Локален инфопоток",
-  "column.direct": "Директни съобщения",
+  "column.community": "Локална часова ос",
+  "column.direct": "Private mentions",
   "column.directory": "Разглеждане на профили",
   "column.domain_blocks": "Блокирани домейни",
   "column.favourites": "Любими",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Не сте запазили промени на описанието или огледа на мултимедията, отхвърляте ли ги?",
   "confirmations.domain_block.confirm": "Блокиране на целия домейн",
   "confirmations.domain_block.message": "Наистина ли искате да блокирате целия {domain}? В повечето случаи няколко блокирания или заглушавания са достатъчно и за предпочитане. Няма да виждате съдържание от домейна из публични часови оси или известията си. Вашите последователи от този домейн ще се премахнат.",
+  "confirmations.edit.confirm": "Редактиране",
+  "confirmations.edit.message": "Редактирането сега ще замени съобщението, което в момента съставяте. Сигурни ли сте, че искате да продължите?",
   "confirmations.logout.confirm": "Излизане",
   "confirmations.logout.message": "Наистина ли искате да излезете?",
   "confirmations.mute.confirm": "Заглушаване",
@@ -213,14 +215,15 @@
   "empty_column.account_unavailable": "Профилът не е наличен",
   "empty_column.blocks": "Още не сте блокирали никакви потребители.",
   "empty_column.bookmarked_statuses": "Още не сте отметнали публикации. Отметвайки някоя, то тя ще се покаже тук.",
-  "empty_column.community": "Локалният инфопоток е празен. Публикувайте нещо, за да започнете!",
-  "empty_column.direct": "Все още нямате лични съобщения. Когато изпратите или получите някое, то ще се покаже тук.",
+  "empty_column.community": "Местната часова ос е празна. Напишете нещо обществено, за да завъртите нещата!",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Още няма блокирани домейни.",
   "empty_column.explore_statuses": "Няма нищо налагащо се в момента. Проверете пак по-късно!",
   "empty_column.favourited_statuses": "Още нямате любими публикации. Поставяйки някоя в любими, то тя ще се покаже тук.",
   "empty_column.favourites": "Още никой не е поставил публикацията в любими. Когато някой го направи, този човек ще се покаже тук.",
   "empty_column.follow_recommendations": "Изглежда, че няма предложения, които може да се породят за вас. Може да опитате да потърсите хора, които познавате или да разгледате налагащи се хаштагове.",
   "empty_column.follow_requests": "Още нямате заявки за последване. Получавайки такава, то тя ще се покаже тук.",
+  "empty_column.followed_tags": "Още не сте последвали никакви хаштагове. Последваните хаштагове ще се покажат тук.",
   "empty_column.hashtag": "Още няма нищо в този хаштаг.",
   "empty_column.home": "Вашата начална часова ос е празна! Последвайте повече хора, за да я запълните. {suggestions}",
   "empty_column.home.suggestions": "Преглед на някои предложения",
@@ -231,7 +234,7 @@
   "empty_column.public": "Тук няма нищо! Публикувайте нещо или последвайте потребители от други сървъри, за да го напълните",
   "error.unexpected_crash.explanation": "Поради грешка в нашия код или проблем със съвместимостта на браузъра, тази страница не може да се покаже правилно.",
   "error.unexpected_crash.explanation_addons": "Страницата не можа да се покаже правилно. Тази грешка вероятно е причинена от добавка на браузъра или от средства за автоматичен превод.",
-  "error.unexpected_crash.next_steps": "Опитайте да опресните страницата. Ако това не помогне, все още можете да използвате Mastodon чрез различен браузър или приложение.",
+  "error.unexpected_crash.next_steps": "Опитайте опресняване на страницата. Ако това не помогне, още може да използвате Mastodon през различен браузър или приложение.",
   "error.unexpected_crash.next_steps_addons": "Опитайте се да ги изключите и да опресните страницата. Ако това не помогне, то още може да използвате Mastodon чрез различен браузър или приложение.",
   "errors.unexpected_crash.copy_stacktrace": "Копиране на трасето на стека в буферната памет",
   "errors.unexpected_crash.report_issue": "Сигнал за проблем",
@@ -259,10 +262,11 @@
   "filter_modal.title.status": "Филтриране на публ.",
   "follow_recommendations.done": "Готово",
   "follow_recommendations.heading": "Следвайте хора, от които харесвате да виждате публикации! Ето някои предложения.",
-  "follow_recommendations.lead": "Съобщения от хора, които следвате, ще се показват в хронологичен ред във вашия основен инфопоток. Не се страхувайте, че ще сгрешите, по всяко време много лесно можете да спрете да ги следвате!",
+  "follow_recommendations.lead": "Публикациите от последваните, ще се показват в хронологичен ред в началния ви инфоканал. Не се страхувайте, че ще сгрешите, по всяко време много лесно може да спрете да ги следвате!",
   "follow_request.authorize": "Упълномощаване",
   "follow_request.reject": "Отхвърляне",
   "follow_requests.unlocked_explanation": "Въпреки че акаунтът ви не е заключен, служителите на {domain} помислиха, че може да искате да преглеждате ръчно заявките за последване на тези профили.",
+  "followed_tags": "Последвани хаштагове",
   "footer.about": "Относно",
   "footer.directory": "Директория на профилите",
   "footer.get_app": "Вземане на приложението",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Клавишни комбинации",
   "footer.privacy_policy": "Политика за поверителност",
   "footer.source_code": "Преглед на изходния код",
+  "footer.status": "Състояние",
   "generic.saved": "Запазено",
   "getting_started.heading": "Първи стъпки",
   "hashtag.column_header.tag_mode.all": "и {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Съсредоточение на колона",
   "keyboard_shortcuts.compose": "Фокус на текстовата зона за съставяне",
   "keyboard_shortcuts.description": "Опис",
-  "keyboard_shortcuts.direct": "за отваряне на колоната с лични съобщения",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Преместване надолу в списъка",
   "keyboard_shortcuts.enter": "Отваряне на публикация",
   "keyboard_shortcuts.favourite": "Към любими публикации",
@@ -350,7 +355,7 @@
   "lists.edit": "Промяна на списъка",
   "lists.edit.submit": "Промяна на заглавие",
   "lists.new.create": "Добавяне на списък",
-  "lists.new.title_placeholder": "Име на нов списък",
+  "lists.new.title_placeholder": "Ново заглавие на списъка",
   "lists.replies_policy.followed": "Някой последван потребител",
   "lists.replies_policy.list": "Членуващите в списъка",
   "lists.replies_policy.none": "Никого",
@@ -364,14 +369,14 @@
   "missing_indicator.sublabel": "Ресурсът не може да се намери",
   "moved_to_account_banner.text": "Вашият акаунт {disabledAccount} сега е изключен, защото се преместихте в {movedToAccount}.",
   "mute_modal.duration": "Времетраене",
-  "mute_modal.hide_notifications": "Скривате ли известията от този потребител?",
+  "mute_modal.hide_notifications": "Скривате ли известията от потребителя?",
   "mute_modal.indefinite": "Неопределено",
   "navigation_bar.about": "Относно",
   "navigation_bar.blocks": "Блокирани потребители",
   "navigation_bar.bookmarks": "Отметки",
   "navigation_bar.community_timeline": "Локален инфопоток",
   "navigation_bar.compose": "Съставяне на нова публикация",
-  "navigation_bar.direct": "Директни съобщения",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Откриване",
   "navigation_bar.domain_blocks": "Блокирани домейни",
   "navigation_bar.edit_profile": "Редактиране на профила",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Любими",
   "navigation_bar.filters": "Заглушени думи",
   "navigation_bar.follow_requests": "Заявки за последване",
+  "navigation_bar.followed_tags": "Последвани хаштагове",
   "navigation_bar.follows_and_followers": "Последвания и последователи",
   "navigation_bar.lists": "Списъци",
   "navigation_bar.logout": "Излизане",
@@ -389,7 +395,7 @@
   "navigation_bar.public_timeline": "Федеративна хронология",
   "navigation_bar.search": "Търсене",
   "navigation_bar.security": "Сигурност",
-  "not_signed_in_indicator.not_signed_in": "Трябва да влезете, за да имате достъп до този ресурс.",
+  "not_signed_in_indicator.not_signed_in": "Трябва ви вход за достъп до ресурса.",
   "notification.admin.report": "{name} докладва {target}",
   "notification.admin.sign_up": "{name} се регистрира",
   "notification.favourite": "{name} сложи в любими ваша публикация",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Спам",
   "report_notification.categories.violation": "Нарушение на правилото",
   "report_notification.open": "Отваряне на доклада",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Търсене",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Търсене или поставяне на URL адрес",
-  "search_popout.search_format": "Формат за разширено търсене",
-  "search_popout.tips.full_text": "Търсене с обикновен текст връща публикации, които сте написали, поставили в любими, споделили, или в които сте били споменати, както и съвпадащи потребителски имена, показвани имена и хаштагове.",
-  "search_popout.tips.hashtag": "хаштаг",
-  "search_popout.tips.status": "публикация",
-  "search_popout.tips.text": "Обикновеният текст връща съответстващи показвани имена, потребителски имена и хаштагове",
-  "search_popout.tips.user": "потребител",
-  "search_results.accounts": "Хора",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Всичко",
   "search_results.hashtags": "Хаштагове",
   "search_results.nothing_found": "Не може да се намери каквото и да било за тези термини при търсене",
@@ -540,10 +548,10 @@
   "server_banner.server_stats": "Статистика на сървъра:",
   "sign_in_banner.create_account": "Създаване на акаунт",
   "sign_in_banner.sign_in": "Вход",
-  "sign_in_banner.text": "Влезте, за да последвате профили или хаштагове, любимо, споделяне и отговаряне на публикации или взаимодействие от акаунта ви на друг сървър.",
+  "sign_in_banner.text": "Влезте, за да последвате профили или хаштагове, отбелязвате като любими, споделяте и отговаряте на публикации. Можете също така да взаимодействате от акаунта ви на друг сървър.",
   "status.admin_account": "Отваряне на интерфейс за модериране за @{name}",
   "status.admin_domain": "Отваряне на модериращия интерфейс за {domain}",
-  "status.admin_status": "Отваряне на публикацията в интерфейса за модериране",
+  "status.admin_status": "Отваряне на публикацията в модериращия интерфейс",
   "status.block": "Блокиране на @{name}",
   "status.bookmark": "Отмятане",
   "status.cancel_reblog_private": "Край на подсилването",
@@ -551,7 +559,8 @@
   "status.copy": "Копиране на връзката към публикация",
   "status.delete": "Изтриване",
   "status.detailed_status": "Подробен изглед на разговора",
-  "status.direct": "Директно съобщение до @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Редактиране",
   "status.edited": "Редактирано на {date}",
   "status.edited_x_times": "Редактирано {count, plural,one {{count} път} other {{count} пъти}}",
@@ -576,7 +585,7 @@
   "status.reblog_private": "Подсилване с оригиналната видимост",
   "status.reblogged_by": "{name} подсили",
   "status.reblogs.empty": "Още никого не е подсилвал публикацията. Подсилващият ще се покаже тук.",
-  "status.redraft": "Изтриване и преработване",
+  "status.redraft": "Изтриване и преначертаване",
   "status.remove_bookmark": "Премахване на отметката",
   "status.replied_to": "В отговор до {name}",
   "status.reply": "Отговор",
@@ -615,7 +624,7 @@
   "timeline_hint.resources.statuses": "По-стари публикации",
   "trends.counter_by_accounts": "{count, plural, one {{counter} човек} other {{counter} души}} {days, plural, one {за последния {days} ден} other {за последните {days} дни}}",
   "trends.trending_now": "Налагащи се сега",
-  "ui.beforeunload": "Черновата ви ще се загуби, ако излезете от Mastodon.",
+  "ui.beforeunload": "Черновата ви ще се загуби, излизайки от Mastodon.",
   "units.short.billion": "{count}млрд",
   "units.short.million": "{count}млн",
   "units.short.thousand": "{count}хил",
diff --git a/app/javascript/mastodon/locales/bn.json b/app/javascript/mastodon/locales/bn.json
index e4e240423..ccd856abc 100644
--- a/app/javascript/mastodon/locales/bn.json
+++ b/app/javascript/mastodon/locales/bn.json
@@ -20,7 +20,7 @@
   "account.blocked": "অবরুদ্ধ",
   "account.browse_more_on_origin_server": "মূল প্রোফাইলটিতে আরও ব্রাউজ করুন",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "@{name} কে সরাসরি বার্তা",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "ডোমেন গোপন করুন",
   "account.edit_profile": "প্রোফাইল পরিবর্তন করুন",
@@ -102,7 +102,7 @@
   "column.blocks": "যাদের ব্লক করা হয়েছে",
   "column.bookmarks": "বুকমার্ক",
   "column.community": "স্থানীয় সময়সারি",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "প্রোফাইল ব্রাউজ করুন",
   "column.domain_blocks": "লুকোনো ডোমেনগুলি",
   "column.favourites": "পছন্দের গুলো",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "এই ডোমেন থেকে সব লুকান",
   "confirmations.domain_block.message": "আপনি কি সত্যিই সত্যই নিশ্চিত যে আপনি পুরো {domain}'টি ব্লক করতে চান? বেশিরভাগ ক্ষেত্রে কয়েকটি লক্ষ্যযুক্ত ব্লক বা নীরবতা যথেষ্ট এবং পছন্দসই। আপনি কোনও পাবলিক টাইমলাইন বা আপনার বিজ্ঞপ্তিগুলিতে সেই ডোমেন থেকে সামগ্রী দেখতে পাবেন না। সেই ডোমেন থেকে আপনার অনুসরণকারীদের সরানো হবে।",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "প্রস্থান",
   "confirmations.logout.message": "আপনি লগ আউট করতে চান?",
   "confirmations.mute.confirm": "সরিয়ে ফেলুন",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "আপনি কোনো ব্যবহারকারীদের ব্লক করেন নি।",
   "empty_column.bookmarked_statuses": "আপনার কাছে এখনও কোনও বুকমার্কড টুট নেই। আপনি যখন একটি বুকমার্ক করেন, এটি এখানে প্রদর্শিত হবে।",
   "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.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "এখনও কোনও লুকানো ডোমেন নেই।",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "আপনার পছন্দের কোনো টুট এখনো নেই। আপনি কোনো লেখা পছন্দের হিসেবে চিহ্নিত করলে এখানে পাওয়া যাবে।",
   "empty_column.favourites": "কেও এখনো এটাকে পছন্দের টুট হিসেবে চিহ্নিত করেনি। যদি করে, তখন তাদের এখানে পাওয়া যাবে।",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "তোমার এখনো কোনো অনুসরণের আবেদন পাওনি। যদি কেউ পাঠায়, এখানে পাওয়া যাবে।",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "এই হেসটাগে এখনো কিছু নেই।",
   "empty_column.home": "আপনার বাড়ির সময়রেখা এখনো খালি!  {public} এ ঘুরে আসুন অথবা অনুসন্ধান বেবহার করে শুরু করতে পারেন এবং অন্য ব্যবহারকারীদের সাথে সাক্ষাৎ করতে পারেন।",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "অনুমতি দিন",
   "follow_request.reject": "প্রত্যাখ্যান করুন",
   "follow_requests.unlocked_explanation": "আপনার অ্যাকাউন্টটি লক না থাকলেও, {domain} কর্মীরা ভেবেছিলেন যে আপনি এই অ্যাকাউন্টগুলি থেকে ম্যানুয়ালি অনুসরণের অনুরোধগুলি পর্যালোচনা করতে চাইতে পারেন।",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "সংরক্ষণ হয়েছে",
   "getting_started.heading": "শুরু করা",
   "hashtag.column_header.tag_mode.all": "এবং {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "বুকমার্ক",
   "navigation_bar.community_timeline": "স্থানীয় সময়রেখা",
   "navigation_bar.compose": "নতুন টুট লিখুন",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "ঘুরে দেখুন",
   "navigation_bar.domain_blocks": "লুকানো ডোমেনগুলি",
   "navigation_bar.edit_profile": "নিজের পাতা সম্পাদনা করতে",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "পছন্দের",
   "navigation_bar.filters": "বন্ধ করা শব্দ",
   "navigation_bar.follow_requests": "অনুসরণের অনুরোধগুলি",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "অনুসরণ এবং অনুসরণকারী",
   "navigation_bar.lists": "তালিকাগুলো",
   "navigation_bar.logout": "বাইরে যান",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "অনুসন্ধান",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "বিস্তারিতভাবে খোঁজার পদ্ধতি",
-  "search_popout.tips.full_text": "সাধারণ লেখা দিয়ে খুঁজলে বের হবে সেরকম আপনার লেখা, পছন্দের লেখা, সমর্থন করা লেখা, আপনাকে উল্লেখকরা কোনো লেখা,  যা খুঁজছেন সেরকম কোনো ব্যবহারকারীর নাম বা কোনো হ্যাশট্যাগগুলো।",
-  "search_popout.tips.hashtag": "হ্যাশট্যাগ",
-  "search_popout.tips.status": "লেখা",
-  "search_popout.tips.text": "সাধারণ লেখা দিয়ে খুঁজলে বের হবে সেরকম ব্যবহারকারীর নাম বা কোনো হ্যাশট্যাগগুলো",
-  "search_popout.tips.user": "ব্যবহারকারী",
-  "search_results.accounts": "মানুষ",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "হ্যাশট্যাগগুলি",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "@{name} র জন্য পরিচালনার ইন্টারফেসে ঢুকুন",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "যায় লেখাটি পরিচালনার ইন্টারফেসে খুলুন",
@@ -551,7 +559,8 @@
   "status.copy": "লেখাটির লিংক কপি করতে",
   "status.delete": "মুছে ফেলতে",
   "status.detailed_status": "বিস্তারিত কথোপকথনের হিসেবে দেখতে",
-  "status.direct": "@{name} কে সরাসরি লেখা পাঠাতে",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/br.json b/app/javascript/mastodon/locales/br.json
index 51523ab22..c8ec088e1 100644
--- a/app/javascript/mastodon/locales/br.json
+++ b/app/javascript/mastodon/locales/br.json
@@ -20,7 +20,7 @@
   "account.blocked": "Stanket",
   "account.browse_more_on_origin_server": "Furchal pelloc'h war ar profil orin",
   "account.cancel_follow_request": "Nullañ ar reked heuliañ",
-  "account.direct": "Kas ur c'hannad eeun da @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Paouez d'am c'hemenn pa vez embannet traoù gant @{name}",
   "account.domain_blocked": "Domani stanket",
   "account.edit_profile": "Kemmañ ar profil",
@@ -102,7 +102,7 @@
   "column.blocks": "Implijer·ezed·ien berzet",
   "column.bookmarks": "Sinedoù",
   "column.community": "Red-amzer lec'hel",
-  "column.direct": "Kannadoù eeun",
+  "column.direct": "Private mentions",
   "column.directory": "Mont a-dreuz ar profiloù",
   "column.domain_blocks": "Domani berzet",
   "column.favourites": "Muiañ-karet",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Bez ez eus kemmoù n'int ket enrollet e deskrivadur ar media pe ar rakwel, nullañ anezho evelato?",
   "confirmations.domain_block.confirm": "Berzañ an domani a-bezh",
   "confirmations.domain_block.message": "Ha sur oc'h e fell deoc'h berzañ an {domain} a-bezh? Peurvuiañ eo trawalc'h berzañ pe mudañ un nebeud implijer·ezed·ien. Ne welot danvez ebet o tont eus an domani-mañ. Dilamet e vo ar c'houmanantoù war an domani-mañ.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Digevreañ",
   "confirmations.logout.message": "Ha sur oc'h e fell deoc'h digevreañ ?",
   "confirmations.mute.confirm": "Kuzhat",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "N'eus ket bet berzet implijer·ez ganeoc'h c'hoazh.",
   "empty_column.bookmarked_statuses": "N'ho peus toud ebet enrollet en ho sinedoù c'hoazh. Pa vo ouzhpennet unan e teuio war wel amañ.",
   "empty_column.community": "Goulo eo ar red-amzer lec'hel. Skrivit'ta un dra evit lakaat tan dezhi !",
-  "empty_column.direct": "N'ho peus kannad eeun ebet c'hoazh. Pa vo resevet pe kaset unan ganeoc'h e teuio war wel amañ.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "N'eus domani kuzh ebet c'hoazh.",
   "empty_column.explore_statuses": "N'eus tuadur ebet evit c'hoazh. Distroit diwezhatoc'h !",
   "empty_column.favourited_statuses": "N'ho peus toud muiañ-karet ebet c'hoazh. Pa vo ouzhpennet unan e teuio war wel amañ.",
   "empty_column.favourites": "Den ebet n'eus ouzhpennet an toud-mañ en e reoù muiañ-karet c'hoazh. Pa vo graet gant unan bennak e teuio war wel amañ.",
   "empty_column.follow_recommendations": "War a seblant ne c'hall ket bezañ savet erbedadenn ebet evidoc'h. Gallout a rit implijout un enklask evit kavout tud a anavezfec'h pe furchal ar gerioù-klik diouzh ar c'hiz.",
   "empty_column.follow_requests": "N'ho peus reked heuliañ ebet c'hoazh. Pa vo resevet unan e teuio war wel amañ.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "N'eus netra er ger-klik-mañ c'hoazh.",
   "empty_column.home": "Goullo eo ho red-amzer degemer! Kit da weladenniñ {public} pe implijit ar c'hlask evit kregiñ ganti ha kejañ gant implijer·ien·ezed all.",
   "empty_column.home.suggestions": "Gwellout damvenegoù",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Aotren",
   "follow_request.reject": "Nac'hañ",
   "follow_requests.unlocked_explanation": "Daoust ma n'eo ket ho kont prennet, skipailh {domain} a soñj e fellfe deoc'h gwiriekaat pedadennoù heuliañ deus ar c'hontoù-se diwar-zorn.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "Diwar-benn",
   "footer.directory": "Kavlec'h ar profiloù",
   "footer.get_app": "Pellgargañ an arload",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Berradennoù klavier",
   "footer.privacy_policy": "Reolennoù prevezded",
   "footer.source_code": "Gwelet kod mammenn",
+  "footer.status": "Status",
   "generic.saved": "Enrollet",
   "getting_started.heading": "Loc'hañ",
   "hashtag.column_header.tag_mode.all": "ha {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Fokus ar bann",
   "keyboard_shortcuts.compose": "Fokus an takad testenn",
   "keyboard_shortcuts.description": "Deskrivadur",
-  "keyboard_shortcuts.direct": "evit digeriñ bann ar c'hannadoù eeun",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Diskennañ er roll",
   "keyboard_shortcuts.enter": "Digeriñ an toud",
   "keyboard_shortcuts.favourite": "Ouzhpennañ an toud d'ar re vuiañ-karet",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Sinedoù",
   "navigation_bar.community_timeline": "Red-amzer lec'hel",
   "navigation_bar.compose": "Skrivañ un toud nevez",
-  "navigation_bar.direct": "Kannadoù eeun",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Dizoleiñ",
   "navigation_bar.domain_blocks": "Domanioù kuzhet",
   "navigation_bar.edit_profile": "Kemmañ ar profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Ar re vuiañ-karet",
   "navigation_bar.filters": "Gerioù kuzhet",
   "navigation_bar.follow_requests": "Pedadoù heuliañ",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Heuliadennoù ha heulier·ezed·ien",
   "navigation_bar.lists": "Listennoù",
   "navigation_bar.logout": "Digennaskañ",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Torradur da reolennoù ar servijer",
   "report_notification.open": "Digeriñ an disklêriadur",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Klask",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Klask pe pegañ un URL",
-  "search_popout.search_format": "Framm klask araokaet",
-  "search_popout.tips.full_text": "Testenn simpl a adkas toudoù skrivet ganeoc'h, merket ganeoc'h evel miuañ-karet, toudoù skignet, pe e-lec'h oc'h bet meneget, met ivez anvioù skrammañ, anvioù implijer ha gêrioù-klik hag a glot.",
-  "search_popout.tips.hashtag": "ger-klik",
-  "search_popout.tips.status": "toud",
-  "search_popout.tips.text": "Testenn simpl a adkas anvioù skrammañ, anvioù implijer ha gêrioù-klik hag a glot",
-  "search_popout.tips.user": "implijer·ez",
-  "search_results.accounts": "Tud",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Pep tra",
   "search_results.hashtags": "Gerioù-klik",
   "search_results.nothing_found": "Disoc'h ebet gant ar gerioù-se",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Stadegoù ar servijer :",
   "sign_in_banner.create_account": "Krouiñ ur gont",
   "sign_in_banner.sign_in": "Kevreañ",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Digeriñ etrefas evezherezh evit @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Digeriñ an toud e-barzh an etrefas evezherezh",
@@ -551,7 +559,8 @@
   "status.copy": "Eilañ liamm ar c'hannad",
   "status.delete": "Dilemel",
   "status.detailed_status": "Gwel kaozeadenn munudek",
-  "status.direct": "Kas ur c'hannad eeun da @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Kemmañ",
   "status.edited": "Aozet {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/bs.json b/app/javascript/mastodon/locales/bs.json
index ef30c76da..c2d34b715 100644
--- a/app/javascript/mastodon/locales/bs.json
+++ b/app/javascript/mastodon/locales/bs.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocked",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Domain blocked",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "Favourites",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "Mute",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
   "empty_column.favourites": "No one has favourited this post yet. When someone does, they will show up here.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new post",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lists",
   "navigation_bar.logout": "Logout",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json
index dbc1af4b1..dd30da215 100644
--- a/app/javascript/mastodon/locales/ca.json
+++ b/app/javascript/mastodon/locales/ca.json
@@ -18,23 +18,23 @@
   "account.block": "Bloca @{name}",
   "account.block_domain": "Bloca el domini {domain}",
   "account.blocked": "Blocat",
-  "account.browse_more_on_origin_server": "Navega més en el perfil original",
+  "account.browse_more_on_origin_server": "Explora'n més al perfil original",
   "account.cancel_follow_request": "Retira la sol·licitud de seguiment",
-  "account.direct": "Missatge directe a @{name}",
+  "account.direct": "Mencionant privadament @{name}",
   "account.disable_notifications": "Deixa de notificar-me els tuts de @{name}",
   "account.domain_blocked": "Domini blocat",
   "account.edit_profile": "Edita el perfil",
   "account.enable_notifications": "Notifica'm els tuts de @{name}",
   "account.endorse": "Recomana en el perfil",
-  "account.featured_tags.last_status_at": "Darrera publicació el {date}",
+  "account.featured_tags.last_status_at": "Darrer tut el {date}",
   "account.featured_tags.last_status_never": "No hi ha tuts",
   "account.featured_tags.title": "etiquetes destacades de {name}",
   "account.follow": "Segueix",
   "account.followers": "Seguidors",
   "account.followers.empty": "A aquest usuari encara no el segueix ningú.",
-  "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidors}}",
+  "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} Seguidors}}",
   "account.following": "Seguint",
-  "account.following_counter": "{count, plural, other {Seguint-ne {counter}}}",
+  "account.following_counter": "{count, plural, other {{counter} Seguint-ne}}",
   "account.follows.empty": "Aquest usuari encara no segueix ningú.",
   "account.follows_you": "Et segueix",
   "account.go_to_profile": "Vés al perfil",
@@ -102,7 +102,7 @@
   "column.blocks": "Usuaris blocats",
   "column.bookmarks": "Marcadors",
   "column.community": "Línia de temps local",
-  "column.direct": "Missatges directes",
+  "column.direct": "Mencions privades",
   "column.directory": "Navega pels perfils",
   "column.domain_blocks": "Dominis blocats",
   "column.favourites": "Favorits",
@@ -128,10 +128,10 @@
   "compose.language.search": "Cerca idiomes...",
   "compose_form.direct_message_warning_learn_more": "Més informació",
   "compose_form.encryption_warning": "Els tuts a Mastodon no estant xifrats punt a punt. No comparteixis informació sensible mitjançant Mastodon.",
-  "compose_form.hashtag_warning": "Aquest tut no es mostrarà en cap etiqueta, ja que no és públic. Només els tuts públics es poden cercar per etiqueta.",
+  "compose_form.hashtag_warning": "Aquest tut no apareixerà a les llistes d'etiquetes perquè no és públic. Només els tuts públics apareixen a les cerques per etiqueta.",
   "compose_form.lock_disclaimer": "El teu compte no està {locked}. Tothom pot seguir-te i veure els tuts de només per a seguidors.",
   "compose_form.lock_disclaimer.lock": "blocat",
-  "compose_form.placeholder": "Què et passa pel cap?",
+  "compose_form.placeholder": "En què penses?",
   "compose_form.poll.add_option": "Afegeix una opció",
   "compose_form.poll.duration": "Durada de l'enquesta",
   "compose_form.poll.option_placeholder": "Opció {number}",
@@ -139,7 +139,7 @@
   "compose_form.poll.switch_to_multiple": "Canvia l’enquesta per a permetre diverses opcions",
   "compose_form.poll.switch_to_single": "Canvia l’enquesta per a permetre una única opció",
   "compose_form.publish": "Tut",
-  "compose_form.publish_form": "Publica",
+  "compose_form.publish_form": "Tut",
   "compose_form.publish_loud": "Tut!",
   "compose_form.save_changes": "Desa els canvis",
   "compose_form.sensitive.hide": "{count, plural, one {Marca mèdia com a sensible} other {Marca mèdia com a sensible}}",
@@ -149,19 +149,21 @@
   "compose_form.spoiler.unmarked": "Afegeix avís de contingut",
   "compose_form.spoiler_placeholder": "Escriu l'avís aquí",
   "confirmation_modal.cancel": "Cancel·la",
-  "confirmations.block.block_and_report": "Bloca i informa",
+  "confirmations.block.block_and_report": "Bloca i denuncia",
   "confirmations.block.confirm": "Bloca",
   "confirmations.block.message": "Segur que vols blocar a {name}?",
   "confirmations.cancel_follow_request.confirm": "Retirar la sol·licitud",
   "confirmations.cancel_follow_request.message": "Segur que vols retirar la sol·licitud de seguiment de {name}?",
   "confirmations.delete.confirm": "Elimina",
-  "confirmations.delete.message": "Segur que vols eliminar la publicació?",
+  "confirmations.delete.message": "Segur que vols eliminar aquest tut?",
   "confirmations.delete_list.confirm": "Elimina",
   "confirmations.delete_list.message": "Segur que vols suprimir permanentment aquesta llista?",
   "confirmations.discard_edit_media.confirm": "Descarta",
   "confirmations.discard_edit_media.message": "Tens canvis no desats en la descripció del contingut o en la previsualització, els vols descartar?",
   "confirmations.domain_block.confirm": "Bloca el domini sencer",
   "confirmations.domain_block.message": "Segur que vols blocar {domain} del tot? En la majoria dels casos, només amb blocar o silenciar uns pocs comptes n'hi ha prou i és millor. No veuràs el contingut d’aquest domini en cap de les línies de temps ni en les notificacions. S'eliminaran els teus seguidors d’aquest domini.",
+  "confirmations.edit.confirm": "Edita",
+  "confirmations.edit.message": "Editant ara sobreescriuràs el missatge que estàs editant. Segur que vols continuar?",
   "confirmations.logout.confirm": "Tanca la sessió",
   "confirmations.logout.message": "Segur que vols tancar la sessió?",
   "confirmations.mute.confirm": "Silencia",
@@ -212,15 +214,16 @@
   "empty_column.account_timeline": "No hi ha tuts aquí!",
   "empty_column.account_unavailable": "Perfil no disponible",
   "empty_column.blocks": "Encara no has blocat cap usuari.",
-  "empty_column.bookmarked_statuses": "Encara no tens marcada cap publicació. Quan en marquis una apareixerà aquí.",
+  "empty_column.bookmarked_statuses": "Encara no has marcat cap tut. Quan en marquis un, apareixerà aquí.",
   "empty_column.community": "La línia de temps local és buida. Escriu alguna cosa públicament per posar-ho tot en marxa!",
-  "empty_column.direct": "Encara no tens missatges directes. Quan n'enviïs o en rebis un, sortirà aquí.",
+  "empty_column.direct": "Encara no tens mencions privades. Quan n'enviïs o en rebis una, et sortirà aquí.",
   "empty_column.domain_blocks": "Encara no hi ha dominis blocats.",
   "empty_column.explore_statuses": "No hi ha res en tendència ara mateix. Revisa-ho més tard!",
   "empty_column.favourited_statuses": "Encara no has afavorit cap tut. Quan ho facis, apareixerà aquí.",
   "empty_column.favourites": "Encara no ha marcat ningú aquesta publicació com a preferida. Quan ho faci algú apareixerà aquí.",
   "empty_column.follow_recommendations": "Sembla que no s'han pogut generar suggeriments per a tu. Pots provar d'usar la cerca per a trobar persones que vulguis conèixer o explorar les etiquetes en tendència.",
   "empty_column.follow_requests": "Encara no tens cap petició de seguiment. Quan en rebis una, apareixerà aquí.",
+  "empty_column.followed_tags": "Encara no segueixes cap etiqueta. Quan ho facis, es mostraran aquí.",
   "empty_column.hashtag": "Encara no hi ha res en aquesta etiqueta.",
   "empty_column.home": "La teva línia de temps és buida! Segueix més gent per a emplenar-la. {suggestions}",
   "empty_column.home.suggestions": "Mostra alguns suggeriments",
@@ -241,21 +244,21 @@
   "explore.trending_links": "Notícies",
   "explore.trending_statuses": "Tuts",
   "explore.trending_tags": "Etiquetes",
-  "filter_modal.added.context_mismatch_explanation": "Aquesta categoria de filtre no s'aplica al context en què has accedit a aquesta publicació. Si també vols que la publicació es filtri en aquest context, hauràs d'editar el filtre.",
+  "filter_modal.added.context_mismatch_explanation": "Aquesta categoria de filtre no s'aplica al context en què has accedit a aquest tut. Si també vols que el tut es filtri en aquest context, hauràs d'editar el filtre.",
   "filter_modal.added.context_mismatch_title": "El context no coincideix!",
   "filter_modal.added.expired_explanation": "La categoria d'aquest filtre ha caducat, necessitaràs canviar la seva data de caducitat per a aplicar-la.",
   "filter_modal.added.expired_title": "Filtre caducat!",
   "filter_modal.added.review_and_configure": "Per a revisar i configurar aquesta categoria de filtre, ves a {settings_link}.",
   "filter_modal.added.review_and_configure_title": "Configuració del filtre",
   "filter_modal.added.settings_link": "pàgina de configuració",
-  "filter_modal.added.short_explanation": "Aquesta publicació s'ha afegit a la següent categoria de filtre: {title}.",
+  "filter_modal.added.short_explanation": "Aquest tut s'ha afegit a la següent categoria de filtre: {title}.",
   "filter_modal.added.title": "Filtre afegit!",
   "filter_modal.select_filter.context_mismatch": "no aplica en aquest context",
   "filter_modal.select_filter.expired": "caducat",
   "filter_modal.select_filter.prompt_new": "Nova categoria: {name}",
   "filter_modal.select_filter.search": "Cerca o crea",
   "filter_modal.select_filter.subtitle": "Usa una categoria existent o crea'n una de nova",
-  "filter_modal.select_filter.title": "Filtra aquesta publicació",
+  "filter_modal.select_filter.title": "Filtra aquest tut",
   "filter_modal.title.status": "Filtra un tut",
   "follow_recommendations.done": "Fet",
   "follow_recommendations.heading": "Segueix a la gent de la que t'agradaria veure els seus tuts! Aquí hi ha algunes recomanacions.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autoritza",
   "follow_request.reject": "Rebutja",
   "follow_requests.unlocked_explanation": "Tot i que el teu compte no està blocat, el personal de {domain} ha pensat que és possible que vulguis revisar manualment les sol·licituds de seguiment d’aquests comptes.",
+  "followed_tags": "Etiquetes seguides",
   "footer.about": "Quant a",
   "footer.directory": "Directori de perfils",
   "footer.get_app": "Aconsegueix l'app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Dreceres de teclat",
   "footer.privacy_policy": "Política de privadesa",
   "footer.source_code": "Mostra el codi font",
+  "footer.status": "Estat",
   "generic.saved": "Desat",
   "getting_started.heading": "Primeres passes",
   "hashtag.column_header.tag_mode.all": "i {additional}",
@@ -288,18 +293,18 @@
   "home.column_settings.show_replies": "Mostra les respostes",
   "home.hide_announcements": "Amaga els anuncis",
   "home.show_announcements": "Mostra els anuncis",
-  "interaction_modal.description.favourite": "Amb un compte a Mastodon pots afavorir aquesta publicació, que l'autor sàpiga que t'ha agradat i desar-la per a més endavant.",
+  "interaction_modal.description.favourite": "Amb un compte a Mastodon pots afavorir aquest tut perquè l'autor sàpiga que t'ha agradat i desar-lo per a més endavant.",
   "interaction_modal.description.follow": "Amb un compte a Mastodon, pots seguir a {name} per a rebre els seus tuts en la teva línia de temps d'Inici.",
-  "interaction_modal.description.reblog": "Amb un compte a Mastodon, pots impulsar aquesta publicació per a compartir-la amb els teus seguidors.",
+  "interaction_modal.description.reblog": "Amb un compte a Mastodon, pots impulsar aquest tut per a compartir-lo amb els teus seguidors.",
   "interaction_modal.description.reply": "Amb un compte a Mastodon, pots respondre aquest tut.",
-  "interaction_modal.on_another_server": "A un altre servidor",
+  "interaction_modal.on_another_server": "En un servidor diferent",
   "interaction_modal.on_this_server": "En aquest servidor",
   "interaction_modal.other_server_instructions": "Copia i enganxa aquest URL en el camp de cerca de la teva aplicació Mastodon preferida o a la interfície web del teu servidor Mastodon.",
   "interaction_modal.preamble": "Com que Mastodon és descentralitzat, pots fer servir el teu compte existent en un altre servidor Mastodon o plataforma compatible si no tens compte en aquest.",
   "interaction_modal.title.favourite": "Marca el tut de {name}",
   "interaction_modal.title.follow": "Segueix {name}",
-  "interaction_modal.title.reblog": "Impulsa la publicació de {name}",
-  "interaction_modal.title.reply": "Respon a la publicació de {name}",
+  "interaction_modal.title.reblog": "Impulsa el tut de {name}",
+  "interaction_modal.title.reply": "Respon al tut de {name}",
   "intervals.full.days": "{number, plural, one {# dia} other {# dies}}",
   "intervals.full.hours": "{number, plural, one {# hora} other {# hores}}",
   "intervals.full.minutes": "{number, plural, one {# minut} other {# minuts}}",
@@ -309,10 +314,10 @@
   "keyboard_shortcuts.column": "Centra la columna",
   "keyboard_shortcuts.compose": "Centra l'àrea de composició de text",
   "keyboard_shortcuts.description": "Descripció",
-  "keyboard_shortcuts.direct": "Obre la columna de missatges directes",
+  "keyboard_shortcuts.direct": "per a obrir la columna de mencions privades",
   "keyboard_shortcuts.down": "Abaixa a la llista",
-  "keyboard_shortcuts.enter": "Obre la publicació",
-  "keyboard_shortcuts.favourite": "Afavoreix la publicació",
+  "keyboard_shortcuts.enter": "Obre el tut",
+  "keyboard_shortcuts.favourite": "Afavoreix el tut",
   "keyboard_shortcuts.favourites": "Obre la llista de preferits",
   "keyboard_shortcuts.federated": "Obre la línia de temps federada",
   "keyboard_shortcuts.heading": "Dreceres de teclat",
@@ -334,7 +339,7 @@
   "keyboard_shortcuts.start": "Obre la columna \"Primeres passes\"",
   "keyboard_shortcuts.toggle_hidden": "Mostra/amaga el text marcat com a sensible",
   "keyboard_shortcuts.toggle_sensitivity": "Mostra/amaga contingut",
-  "keyboard_shortcuts.toot": "Inicia una nova publicació",
+  "keyboard_shortcuts.toot": "Escriu un nou tut",
   "keyboard_shortcuts.unfocus": "Descentra l'àrea de composició de text/cerca",
   "keyboard_shortcuts.up": "Apuja a la llista",
   "lightbox.close": "Tanca",
@@ -343,7 +348,7 @@
   "lightbox.next": "Següent",
   "lightbox.previous": "Anterior",
   "limited_account_hint.action": "Mostra el perfil de totes maneres",
-  "limited_account_hint.title": "Aquest perfil ha estat amagat pels moderadors de {domain}.",
+  "limited_account_hint.title": "Aquest perfil l'han amagat els moderadors de {domain}.",
   "lists.account.add": "Afegeix a la llista",
   "lists.account.remove": "Elimina de la llista",
   "lists.delete": "Elimina la llista",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Marcadors",
   "navigation_bar.community_timeline": "Línia de temps local",
   "navigation_bar.compose": "Redacta un tut",
-  "navigation_bar.direct": "Missatges directes",
+  "navigation_bar.direct": "Mencions privades",
   "navigation_bar.discover": "Descobreix",
   "navigation_bar.domain_blocks": "Dominis blocats",
   "navigation_bar.edit_profile": "Edita el perfil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Preferits",
   "navigation_bar.filters": "Paraules silenciades",
   "navigation_bar.follow_requests": "Sol·licituds de seguiment",
+  "navigation_bar.followed_tags": "Etiquetes seguides",
   "navigation_bar.follows_and_followers": "Seguint i seguidors",
   "navigation_bar.lists": "Llistes",
   "navigation_bar.logout": "Tanca la sessió",
@@ -400,7 +406,7 @@
   "notification.poll": "Ha finalitzat una enquesta en què has votat",
   "notification.reblog": "{name} t'ha impulsat",
   "notification.status": "{name} acaba de publicar",
-  "notification.update": "{name} ha editat una publicació",
+  "notification.update": "{name} ha editat un tut",
   "notifications.clear": "Esborra les notificacions",
   "notifications.clear_confirmation": "Segur que vols esborrar permanentment totes les teves notificacions?",
   "notifications.column_settings.admin.report": "Nous informes:",
@@ -433,7 +439,7 @@
   "notifications.group": "{count} notificacions",
   "notifications.mark_as_read": "Marca cada notificació com a llegida",
   "notifications.permission_denied": "Les notificacions d’escriptori no estan disponibles perquè prèviament s’ha denegat el permís al navegador",
-  "notifications.permission_denied_alert": "No es poden activar les notificacions de l'escriptori perquè el permís del navegador ha estat denegat abans",
+  "notifications.permission_denied_alert": "No es poden activar les notificacions de l'escriptori perquè abans s'ha denegat el permís del navegador",
   "notifications.permission_required": "Les notificacions d'escriptori no estan disponibles perquè el permís requerit no ha estat concedit.",
   "notifications_permission_banner.enable": "Activa les notificacions d’escriptori",
   "notifications_permission_banner.how_to_control": "Per a rebre notificacions quan Mastodon no és obert cal activar les notificacions d’escriptori. Pots controlar amb precisió quins tipus d’interaccions generen notificacions d’escriptori després d’activar el botó {icon} de dalt.",
@@ -448,13 +454,13 @@
   "poll.votes": "{votes, plural, one {# vot} other {# vots}}",
   "poll_button.add_poll": "Afegeix una enquesta",
   "poll_button.remove_poll": "Elimina l'enquesta",
-  "privacy.change": "Canvia la privacitat de la publicació",
+  "privacy.change": "Canvia la privacitat del tut",
   "privacy.direct.long": "Visible només per als usuaris esmentats",
   "privacy.direct.short": "Només gent mencionada",
   "privacy.private.long": "Visible només per als seguidors",
   "privacy.private.short": "Només seguidors",
   "privacy.public.long": "Visible per a tothom",
-  "privacy.public.short": "Pública",
+  "privacy.public.short": "Públic",
   "privacy.unlisted.long": "Visible per a tothom però exclosa de les funcions de descobriment",
   "privacy.unlisted.short": "No llistada",
   "privacy_policy.last_updated": "Darrera actualització {date}",
@@ -462,16 +468,16 @@
   "refresh": "Actualitza",
   "regeneration_indicator.label": "Es carrega…",
   "regeneration_indicator.sublabel": "Es prepara la teva línia de temps d'Inici!",
-  "relative_time.days": "{number} d",
+  "relative_time.days": "{number}d",
   "relative_time.full.days": "fa {number, plural, one {# dia} other {# dies}}",
   "relative_time.full.hours": "fa {number, plural, one {# hora} other {# hores}}",
   "relative_time.full.just_now": "ara mateix",
   "relative_time.full.minutes": "fa {number, plural, one {# minut} other {# minuts}}",
   "relative_time.full.seconds": "fa {number, plural, one {# segon} other {# segons}}",
-  "relative_time.hours": "{number} h",
+  "relative_time.hours": "{number}h",
   "relative_time.just_now": "ara",
-  "relative_time.minutes": "{number} min",
-  "relative_time.seconds": "{number} s",
+  "relative_time.minutes": "{number}min",
+  "relative_time.seconds": "{number}s",
   "relative_time.today": "avui",
   "reply_indicator.cancel": "Cancel·la",
   "report.block": "Bloca",
@@ -504,7 +510,7 @@
   "report.statuses.subtitle": "Selecciona totes les aplicables",
   "report.statuses.title": "Hi ha cap tut que doni suport a aquest informe?",
   "report.submit": "Envia",
-  "report.target": "Es reporta {target}",
+  "report.target": "Es denuncia {target}",
   "report.thanks.take_action": "Aquestes són les teves opcions per a controlar el que veus a Mastodon:",
   "report.thanks.take_action_actionable": "Mentre ho revisem, pots prendre mesures contra @{name}:",
   "report.thanks.title": "No ho vols veure?",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Brossa",
   "report_notification.categories.violation": "Violació de norma",
   "report_notification.open": "Obre un informe",
+  "search.no_recent_searches": "No hi ha cerques recents",
   "search.placeholder": "Cerca",
+  "search.quick_action.account_search": "Perfils coincidents {x}",
+  "search.quick_action.go_to_account": "Anar al perfil {x}",
+  "search.quick_action.go_to_hashtag": "Anar a la etiqueta {x}",
+  "search.quick_action.open_url": "Obrir URL a Mastodon",
+  "search.quick_action.status_search": "Tuts coincidents {x}",
   "search.search_or_paste": "Cerca o escriu l'URL",
-  "search_popout.search_format": "Format de cerca avançada",
-  "search_popout.tips.full_text": "Text simple recupera tuts que has escrit, els marcats com a favorits, els impulsats o en els que has estat esmentat, així com usuaris, noms d'usuari i etiquetes.",
-  "search_popout.tips.hashtag": "etiqueta",
-  "search_popout.tips.status": "tut",
-  "search_popout.tips.text": "El text simple recupera coincidències amb els usuaris, els noms d'usuari i les etiquetes",
-  "search_popout.tips.user": "usuari",
-  "search_results.accounts": "Gent",
+  "search_popout.quick_actions": "Accions ràpides",
+  "search_popout.recent": "Cerques recents",
+  "search_results.accounts": "Perfils",
   "search_results.all": "Tots",
   "search_results.hashtags": "Etiquetes",
   "search_results.nothing_found": "No s'ha pogut trobar res per a aquests termes de cerca",
@@ -535,15 +543,15 @@
   "server_banner.about_active_users": "Gent que ha fet servir aquest servidor en els darrers 30 dies (Usuaris Actius Mensuals)",
   "server_banner.active_users": "usuaris actius",
   "server_banner.administered_by": "Administrat per:",
-  "server_banner.introduction": "{domain} és part de la xarxa social descentralitzada, potenciat per {mastodon}.",
+  "server_banner.introduction": "{domain} és part de la xarxa social descentralitzada impulsada per {mastodon}.",
   "server_banner.learn_more": "Més informació",
   "server_banner.server_stats": "Estadístiques del servidor:",
   "sign_in_banner.create_account": "Registra'm",
   "sign_in_banner.sign_in": "Inicia sessió",
-  "sign_in_banner.text": "Inicia la sessió per a seguir perfils o etiquetes, afavorir, compartir i respondre tuts o interactuar des del teu compte en un servidor diferent.",
+  "sign_in_banner.text": "Inicia la sessió per a seguir perfils o etiquetes, afavorir, compartir i respondre tuts. També pots interactuar des del teu compte a un servidor diferent.",
   "status.admin_account": "Obre la interfície de moderació per a @{name}",
   "status.admin_domain": "Obre la interfície de moderació per a @{domain}",
-  "status.admin_status": "Obrir aquest tut a la interfície de moderació",
+  "status.admin_status": "Obre aquest tut a la interfície de moderació",
   "status.block": "Bloca @{name}",
   "status.bookmark": "Marca",
   "status.cancel_reblog_private": "Desfés l'impuls",
@@ -551,13 +559,14 @@
   "status.copy": "Copia l'enllaç al tut",
   "status.delete": "Elimina",
   "status.detailed_status": "Vista detallada de la conversa",
-  "status.direct": "Missatge directe a @{name}",
+  "status.direct": "Mencionant privadament @{name}",
+  "status.direct_indicator": "Menció privada",
   "status.edit": "Edita",
   "status.edited": "Editat {date}",
   "status.edited_x_times": "Editat {count, plural, one {{count} vegada} other {{count} vegades}}",
   "status.embed": "Incrusta",
   "status.favourite": "Favorit",
-  "status.filter": "Filtra aquest tut",
+  "status.filter": "Filtra aquesta publicació",
   "status.filtered": "Filtrada",
   "status.hide": "Amaga el tut",
   "status.history.created": "creat per {name} {date}",
diff --git a/app/javascript/mastodon/locales/ckb.json b/app/javascript/mastodon/locales/ckb.json
index 62c24583d..ca0ca6f19 100644
--- a/app/javascript/mastodon/locales/ckb.json
+++ b/app/javascript/mastodon/locales/ckb.json
@@ -20,7 +20,7 @@
   "account.blocked": "بلۆککرا",
   "account.browse_more_on_origin_server": "گەڕانی فرەتر لە سەر پرۆفایلی سەرەکی",
   "account.cancel_follow_request": "داواکاری فۆڵۆو بکشێنەوە",
-  "account.direct": "پەیامی تایبەت بە @{name}",
+  "account.direct": "بە شێوەیەکی تایبەت باسی @{name} بکە",
   "account.disable_notifications": "ئاگانامە مەنێرە بۆم کاتێک @{name} پۆست دەکرێت",
   "account.domain_blocked": "دۆمەین قەپاتکرا",
   "account.edit_profile": "دەستکاری پرۆفایل",
@@ -29,14 +29,14 @@
   "account.featured_tags.last_status_at": "دوایین پۆست لە {date}",
   "account.featured_tags.last_status_never": "هیچ پۆستێک نییە",
   "account.featured_tags.title": "هاشتاگە تایبەتەکانی {name}",
-  "account.follow": "شوێنکەوتن",
+  "account.follow": "بەدواداچوون",
   "account.followers": "شوێنکەوتووان",
   "account.followers.empty": "کەسێک شوێن ئەم بەکارهێنەرە نەکەوتووە",
   "account.followers_counter": "{count, plural, one {{counter} شوێنکەوتوو} other {{counter} شوێنکەوتوو}}",
-  "account.following": "دواکەوتن",
+  "account.following": "بەدوادا",
   "account.following_counter": "{count, plural, one {{counter} شوێنکەوتوو} other {{counter} شوێنکەوتوو}}",
   "account.follows.empty": "ئەم بەکارهێنەرە تا ئێستا شوێن کەس نەکەوتووە.",
-  "account.follows_you": "شوێنکەوتووەکانت",
+  "account.follows_you": "شوێنت دەکەوێت",
   "account.go_to_profile": "بڕۆ بۆ پڕۆفایلی",
   "account.hide_reblogs": "داشاردنی بووستەکان لە @{name}",
   "account.joined_short": "بەشداری کردووە",
@@ -50,11 +50,11 @@
   "account.mute_notifications": "هۆشیارکەرەوەکان لاببە لە @{name}",
   "account.muted": "بێ دەنگ",
   "account.open_original_page": "لاپەڕەی ئەسڵی بکەرەوە",
-  "account.posts": "توتس",
+  "account.posts": "نووسراوەکان",
   "account.posts_with_replies": "توتس و وەڵامەکان",
   "account.report": "گوزارشت @{name}",
   "account.requested": "چاوەڕێی ڕەزامەندین. کرتە بکە بۆ هەڵوەشاندنەوەی داواکاری شوێنکەوتن",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name} داوای کردووە شوێنت بکەوێت",
   "account.share": "پرۆفایلی @{name} هاوبەش بکە",
   "account.show_reblogs": "پیشاندانی بەرزکردنەوەکان لە @{name}",
   "account.statuses_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
@@ -102,7 +102,7 @@
   "column.blocks": "بەکارهێنەرە بلۆککراوەکان",
   "column.bookmarks": "نیشانەکان",
   "column.community": "هێڵی کاتی ناوخۆیی",
-  "column.direct": "نامە ڕاستەوخۆ",
+  "column.direct": "ئاماژەی تایبەت",
   "column.directory": "گەڕان لە پرۆفایلەکان",
   "column.domain_blocks": "دۆمەینە داخراوەکان",
   "column.favourites": "دڵخوازترینەکان",
@@ -128,7 +128,7 @@
   "compose.language.search": "گەڕان بە زمانەکان...",
   "compose_form.direct_message_warning_learn_more": "زیاتر فێربه",
   "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "ئەم بڵاوکراوەیە لە ژێر هیچ هاشتاگێکدا دا نانرێت وەک ئەوەیە، کە گشتی نەبێت. تەنها بڵاوکراوە گشتیەکان دەتوانرێ بە هاشتاگ گەڕانی بۆ بکرێت.",
   "compose_form.lock_disclaimer": "هەژمێرەکەی لە حاڵەتی {locked}. هەر کەسێک دەتوانێت شوێنت بکەوێت بۆ پیشاندانی بابەتەکانی تەنها دوایخۆی.",
   "compose_form.lock_disclaimer.lock": "قفڵ دراوە",
   "compose_form.placeholder": "چی لە مێشکتدایە?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "گۆڕانکاریت لە وەسف یان پێشبینی میدیادا هەڵنەگیراوە، بەهەر حاڵ فڕێیان بدە؟",
   "confirmations.domain_block.confirm": "بلۆککردنی هەموو دۆمەینەکە",
   "confirmations.domain_block.message": "ئایا بەڕاستی، بەڕاستی تۆ دەتەوێت هەموو {domain} بلۆک بکەیت؟ لە زۆربەی حاڵەتەکاندا چەند بلۆکێکی ئامانجدار یان بێدەنگەکان پێویست و پەسەندن. تۆ ناوەڕۆک ێک نابینیت لە دۆمەینەکە لە هیچ هێڵی کاتی گشتی یان ئاگانامەکانت. شوێنکەوتوانی تۆ لەو دۆمەینەوە لادەبرێن.",
+  "confirmations.edit.confirm": "دەستکاری",
+  "confirmations.edit.message": "دەستکاری کردنی ئێستا: دەبێتە هۆی نووسینەوەی ئەو پەیامەی، کە ئێستا داتدەڕشت. ئایا دڵنیای، کە دەتەوێت بەردەوام بیت؟",
   "confirmations.logout.confirm": "چوونە دەرەوە",
   "confirmations.logout.message": "ئایا دڵنیایت لەوەی دەتەوێت بچیتە دەرەوە?",
   "confirmations.mute.confirm": "بێدەنگ",
@@ -221,6 +223,7 @@
   "empty_column.favourites": "کەس ئەم توتەی دڵخواز نەکردووە،کاتێک کەسێک وا بکات، لێرە دەرئەکەون.",
   "empty_column.follow_recommendations": "پێدەچێت هیچ پێشنیارێک بۆ تۆ دروست نەکرێت. دەتوانیت هەوڵبدەیت گەڕان بەکاربهێنیت بۆ گەڕان بەدوای ئەو کەسانەی کە ڕەنگە بیانناسیت یان بەدوای هاشتاگە ڕەوتەکاندا بگەڕێیت.",
   "empty_column.follow_requests": "تۆ هێشتا هیچ داواکارییەکی بەدواداچووت نیە. کاتێک یەکێکت بۆ هات، لێرە دەرئەکەویت.",
+  "empty_column.followed_tags": "تۆ هێشتا شوێن هیچ هاشتاگێک نەکەوتوویت. کاتێک کردت، ئەوان لێرە دەردەکەون.",
   "empty_column.hashtag": "هێشتا هیچ شتێک لەم هاشتاگەدا نییە.",
   "empty_column.home": "تایم لاینی ماڵەوەت بەتاڵە! سەردانی {public} بکە یان گەڕان بەکاربێنە بۆ دەستپێکردن و بینینی بەکارهێنەرانی تر.",
   "empty_column.home.suggestions": "چەند پێشنیارێک ببینە",
@@ -236,40 +239,42 @@
   "errors.unexpected_crash.copy_stacktrace": "کۆپیکردنی ستێکتراسی بۆ کلیپ بۆرد",
   "errors.unexpected_crash.report_issue": "کێشەی گوزارشت",
   "explore.search_results": "ئەنجامەکانی گەڕان",
-  "explore.suggested_follows": "For you",
+  "explore.suggested_follows": "بۆ تۆ",
   "explore.title": "گەڕان",
-  "explore.trending_links": "News",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "Hashtags",
+  "explore.trending_links": "هەواڵەکان",
+  "explore.trending_statuses": "بڵاوکراوەکان",
+  "explore.trending_tags": "هاشتاگەکان",
   "filter_modal.added.context_mismatch_explanation": "ئەم پۆلە فلتەرە ئەو چوارچێوەیە ناگرێتەوە کە تۆ تێیدا دەستت بەم پۆستە کردووە. ئەگەر بتەوێت پۆستەکە لەم چوارچێوەیەشدا فلتەر بکرێت، دەبێت دەستکاری فلتەرەکە بکەیت.",
   "filter_modal.added.context_mismatch_title": "ناتەبایی دەقی نووسراو!",
   "filter_modal.added.expired_explanation": "ئەم پۆلە فلتەرە بەسەرچووە، پێویستە بەرواری بەسەرچوونی بگۆڕیت بۆ ئەوەی جێبەجێی بکات.",
   "filter_modal.added.expired_title": "فلتەری بەسەرچووە!",
   "filter_modal.added.review_and_configure": "بۆ پێداچوونەوە و ڕێکخستنی زیاتری ئەم پۆلە فلتەرە، بچۆ بۆ {settings_link}.",
-  "filter_modal.added.review_and_configure_title": "Filter settings",
-  "filter_modal.added.settings_link": "settings page",
-  "filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.",
-  "filter_modal.added.title": "Filter added!",
-  "filter_modal.select_filter.context_mismatch": "does not apply to this context",
-  "filter_modal.select_filter.expired": "expired",
-  "filter_modal.select_filter.prompt_new": "New category: {name}",
-  "filter_modal.select_filter.search": "Search or create",
-  "filter_modal.select_filter.subtitle": "Use an existing category or create a new one",
-  "filter_modal.select_filter.title": "Filter this post",
-  "filter_modal.title.status": "Filter a post",
+  "filter_modal.added.review_and_configure_title": "ڕێکخستنەکانی پاڵاوتن",
+  "filter_modal.added.settings_link": "پەڕەی ڕێکخستن",
+  "filter_modal.added.short_explanation": "بڵاوکراوەکە زیادکرا بۆ ئەو پاڵاوتنانەی خوارەوە: {title}.",
+  "filter_modal.added.title": "پاڵێو زیادکرا!",
+  "filter_modal.select_filter.context_mismatch": "لەم چوارچێوەیەدا کارپێکراو نییە",
+  "filter_modal.select_filter.expired": "بەسەرچووە",
+  "filter_modal.select_filter.prompt_new": "پۆلێنی نوێ: {name}",
+  "filter_modal.select_filter.search": "گەڕان یان دروستکردن",
+  "filter_modal.select_filter.subtitle": "بەکارهێنانی پۆلێنی بەردەست یان دروستکردنی پۆلێنێکی نوێ",
+  "filter_modal.select_filter.title": "ئەم بڵاوکراوەیە بپاڵێوە",
+  "filter_modal.title.status": "بڵاوکراوەیەک بپاڵێوە",
   "follow_recommendations.done": "تەواو",
   "follow_recommendations.heading": "شوێن ئەو کەسانە بکەون کە دەتەوێت پۆستەکان ببینیت لە! لێرەدا چەند پێشنیارێک هەیە.",
   "follow_recommendations.lead": "بابەتەکانی ئەو کەسانەی کە بەدوایدا دەگەڕێیت بە فەرمانی کرۆنۆلۆجی لە خواردنەکانی ماڵەکەت دەردەکەون. مەترسە لە هەڵەکردن، دەتوانیت بە ئاسانی خەڵک هەڵبکەیت هەر کاتێک!",
   "follow_request.authorize": "ده‌سه‌ڵاتپێدراو",
   "follow_request.reject": "ڕەتکردنەوە",
   "follow_requests.unlocked_explanation": "هەرچەندە هەژمارەکەت داخراو نییە، ستافی {domain} وا بیریان کردەوە کە لەوانەیە بتانەوێت پێداچوونەوە بە داواکاریەکانی ئەم هەژمارەدا بکەن بە دەستی.",
-  "footer.about": "About",
-  "footer.directory": "Profiles directory",
-  "footer.get_app": "Get the app",
-  "footer.invite": "Invite people",
-  "footer.keyboard_shortcuts": "Keyboard shortcuts",
-  "footer.privacy_policy": "Privacy policy",
-  "footer.source_code": "View source code",
+  "followed_tags": "هاشتاگە شوێنکەوتووەکان",
+  "footer.about": "دەربارە",
+  "footer.directory": "ڕابەری پەڕەی ناساندن",
+  "footer.get_app": "بەرنامەکە بەدەست بێنە",
+  "footer.invite": "بانگهێشتکردنی خەڵک",
+  "footer.keyboard_shortcuts": "کورتەڕێکانی تەختەکلیک",
+  "footer.privacy_policy": "سیاسەتی تایبەتمەندێتی",
+  "footer.source_code": "پیشاندانی کۆدی سەرچاوە",
+  "footer.status": "دۆخ",
   "generic.saved": "پاشکەوتکرا",
   "getting_started.heading": "دەست پێکردن",
   "hashtag.column_header.tag_mode.all": "و {additional}",
@@ -281,25 +286,25 @@
   "hashtag.column_settings.tag_mode.any": "هەر کام لەمانە",
   "hashtag.column_settings.tag_mode.none": "هیچ کام لەمانە",
   "hashtag.column_settings.tag_toggle": "تاگی زیادە ی ئەم ستوونە لەخۆ بنووسە",
-  "hashtag.follow": "Follow hashtag",
-  "hashtag.unfollow": "Unfollow hashtag",
+  "hashtag.follow": "شوێنکەوتنی هاشتاگ",
+  "hashtag.unfollow": "شوێن نەکەوتنی هاشتاگ",
   "home.column_settings.basic": "بنەڕەتی",
   "home.column_settings.show_reblogs": "پیشاندانی بەهێزکردن",
   "home.column_settings.show_replies": "وەڵامدانەوەکان پیشان بدە",
   "home.hide_announcements": "شاردنەوەی راگەیەنراوەکان",
   "home.show_announcements": "پیشاندانی راگەیەنراوەکان",
-  "interaction_modal.description.favourite": "With an account on Mastodon, you can favourite this post to let the author know you appreciate it and save it for later.",
-  "interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.",
-  "interaction_modal.description.reblog": "With an account on Mastodon, you can boost this post to share it with your own followers.",
-  "interaction_modal.description.reply": "With an account on Mastodon, you can respond to this post.",
-  "interaction_modal.on_another_server": "On a different server",
-  "interaction_modal.on_this_server": "On this server",
-  "interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favourite Mastodon app or the web interface of your Mastodon server.",
-  "interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.",
-  "interaction_modal.title.favourite": "Favourite {name}'s post",
-  "interaction_modal.title.follow": "Follow {name}",
-  "interaction_modal.title.reblog": "Boost {name}'s post",
-  "interaction_modal.title.reply": "Reply to {name}'s post",
+  "interaction_modal.description.favourite": "بە هەژمارێک لەسەر ماستدۆن، دەتوانیت ئەم بڵاوکراوەیە زیادبکەیت بۆ دڵخوازەکانت. بۆ ئاگادارکردنەوەی بڵاوکەرەوەکە لە پێزانینەکەت و هێشتنەوەی بۆ دواتر.",
+  "interaction_modal.description.follow": "بە هەژمارێک لەسەر ماستدۆن، ئەتوانیت شوێن {name} بکەویت بۆ ئەوەی بڵاوکراوەکانی بگاتە پەڕەی سەرەکیت.",
+  "interaction_modal.description.reblog": "بە هەژمارێک لەسەر ماستدۆن، ئەتوانیت ئەم بڵاوکراوەیە بەرزبکەیتەوە تاوەکو بەژداری پێبکەیت لەگەل شوێنکەوتوانت.",
+  "interaction_modal.description.reply": "بە هەژمارێک لەسەر ماستدۆن، ئەتوانیت وەڵامی ئەم بڵاوکراوەیە بدەیتەوە.",
+  "interaction_modal.on_another_server": "لەسەر ڕاژەیەکی جیا",
+  "interaction_modal.on_this_server": "لەسەر ئەم ڕاژەیە",
+  "interaction_modal.other_server_instructions": "ئەم URLە کۆپی بکە و بیخە ناو بواری گەڕانی ئەپی دڵخوازت لە ماستۆدۆن یان ڕووکاری وێبی سێرڤەری ماستۆدۆنەکەت.",
+  "interaction_modal.preamble": "بەو پێیەی ماستۆدۆن لامەرکەزییە، دەتوانیت ئەکاونتی ئێستات بەکاربهێنیت کە لەلایەن سێرڤەرێکی تری ماستۆدۆن یان پلاتفۆرمی گونجاوەوە هۆست کراوە ئەگەر ئەکاونتێکت لەسەر ئەم ئەکاونتە نەبێت.",
+  "interaction_modal.title.favourite": "پۆستی {name}ی دڵخواز",
+  "interaction_modal.title.follow": "دوای {name} بکەوە",
+  "interaction_modal.title.reblog": "پۆستی {name} زیاد بکە",
+  "interaction_modal.title.reply": "وەڵامی پۆستەکەی {name} بدەرەوە",
   "intervals.full.days": "{number, plural, one {# ڕۆژ} other {# ڕۆژەک}}",
   "intervals.full.hours": "{number, plural, one {# کات} other {# کات}}",
   "intervals.full.minutes": "{number, plural, one {# خولەک} other {# خولەک}}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "بۆ ئەوەی تیشک بخاتە سەر توتێک لە یەکێک لە ستوونەکان",
   "keyboard_shortcuts.compose": "بۆ سەرنجدان بە نووسینی ناوچەی دەق",
   "keyboard_shortcuts.description": "وه‌سف",
-  "keyboard_shortcuts.direct": "بۆ کردنەوەی ستوونی نامە ڕاستەوخۆکان",
+  "keyboard_shortcuts.direct": "بۆ کردنەوەی ستوونی ئاماژەی تایبەت",
   "keyboard_shortcuts.down": "بۆ چوونە خوارەوە لە لیستەکەدا",
   "keyboard_shortcuts.enter": "بۆ کردنەوەی توت",
   "keyboard_shortcuts.favourite": "بۆ دڵخواز",
@@ -343,7 +348,7 @@
   "lightbox.next": "داهاتوو",
   "lightbox.previous": "پێشوو",
   "limited_account_hint.action": "بەهەر حاڵ پڕۆفایلی پیشان بدە",
-  "limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
+  "limited_account_hint.title": "ئەم پرۆفایلە لەلایەن بەڕێوەبەرانی {domain} شاراوەتەوە.",
   "lists.account.add": "زیادکردن بۆ لیست",
   "lists.account.remove": "لابردن لە لیست",
   "lists.delete": "سڕینەوەی لیست",
@@ -362,16 +367,16 @@
   "media_gallery.toggle_visible": "شاردنەوەی {number, plural, one {image} other {images}}",
   "missing_indicator.label": "نەدۆزرایەوە",
   "missing_indicator.sublabel": "ئەو سەرچاوەیە نادۆزرێتەوە",
-  "moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.",
+  "moved_to_account_banner.text": "ئەکاونتەکەت {disabledAccount} لە ئێستادا لەکارخراوە چونکە تۆ چوویتە {movedToAccount}.",
   "mute_modal.duration": "ماوە",
   "mute_modal.hide_notifications": "شاردنەوەی ئاگانامەکان لەم بەکارهێنەرە؟ ",
   "mute_modal.indefinite": "نادیار",
-  "navigation_bar.about": "About",
+  "navigation_bar.about": "دەربارە",
   "navigation_bar.blocks": "بەکارهێنەرە بلۆککراوەکان",
   "navigation_bar.bookmarks": "نیشانکراوەکان",
   "navigation_bar.community_timeline": "دەمنامەی ناوخۆیی",
   "navigation_bar.compose": "نووسینی توتی نوێ",
-  "navigation_bar.direct": "نامە ڕاستەوخۆ",
+  "navigation_bar.direct": "ئاماژەی تایبەت",
   "navigation_bar.discover": "دۆزینەوە",
   "navigation_bar.domain_blocks": "دۆمەینە بلۆک کراوەکان",
   "navigation_bar.edit_profile": "دەستکاری پرۆفایل بکە",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "دڵخوازەکان",
   "navigation_bar.filters": "وشە کپەکان",
   "navigation_bar.follow_requests": "بەدواداچوی داواکاریەکان بکە",
+  "navigation_bar.followed_tags": "هاشتاگی بەدوادا هات",
   "navigation_bar.follows_and_followers": "شوێنکەوتوو و شوێنکەوتوان",
   "navigation_bar.lists": "لیستەکان",
   "navigation_bar.logout": "دەرچوون",
@@ -387,10 +393,10 @@
   "navigation_bar.pins": "توتی چەسپاو",
   "navigation_bar.preferences": "پەسەندەکان",
   "navigation_bar.public_timeline": "نووسراوەکانی هەمووشوێنێک",
-  "navigation_bar.search": "Search",
+  "navigation_bar.search": "گەڕان",
   "navigation_bar.security": "ئاسایش",
-  "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.",
-  "notification.admin.report": "{name} reported {target}",
+  "not_signed_in_indicator.not_signed_in": "پێویستە بچیتە ژوورەوە بۆ دەستگەیشتن بەم سەرچاوەیە.",
+  "notification.admin.report": "{name} ڕاپۆرت کراوە {target}",
   "notification.admin.sign_up": "{name} تۆمارکرا",
   "notification.favourite": "{name} نووسراوەکەتی پەسەند کرد",
   "notification.follow": "{name} دوای تۆ کەوت",
@@ -403,7 +409,7 @@
   "notification.update": "{name} پۆستێکی دەستکاریکرد",
   "notifications.clear": "ئاگانامەکان بسڕیەوە",
   "notifications.clear_confirmation": "ئایا دڵنیایت لەوەی دەتەوێت بە هەمیشەیی هەموو ئاگانامەکانت بسڕیتەوە?",
-  "notifications.column_settings.admin.report": "New reports:",
+  "notifications.column_settings.admin.report": "ڕاپۆرتە نوێیەکان:",
   "notifications.column_settings.admin.sign_up": "چوونەژوورەوەی نوێ:",
   "notifications.column_settings.alert": "ئاگانامەکانی پیشانگەرر ڕومێزی",
   "notifications.column_settings.favourite": "دڵخوازترین:",
@@ -457,8 +463,8 @@
   "privacy.public.short": "گشتی",
   "privacy.unlisted.long": "بۆ هەمووان دیارە، بەڵام لە تایبەتمەندییەکانی دۆزینەوە دەرچووە",
   "privacy.unlisted.short": "لە لیست نەکراو",
-  "privacy_policy.last_updated": "Last updated {date}",
-  "privacy_policy.title": "Privacy Policy",
+  "privacy_policy.last_updated": "دوایین نوێکردنەوە {date}",
+  "privacy_policy.title": "سیاسەتی تایبەتێتی",
   "refresh": "نوێکردنەوە",
   "regeneration_indicator.label": "بارکردن…",
   "regeneration_indicator.sublabel": "ڕاگەیەنەری ماڵەوەت ئامادە دەکرێت!",
@@ -512,27 +518,29 @@
   "report.unfollow": "بەدوادانەچوو@{name}",
   "report.unfollow_explanation": "تۆ شوێنکەوتووی ئەم هەژماررەی دەکەیت. بۆ ئەوەی چیتر نووسراوەکانیان لە هۆم فیدی خۆت نەبینی، بەدوایان مەچۆ.",
   "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached",
-  "report_notification.categories.other": "Other",
-  "report_notification.categories.spam": "Spam",
-  "report_notification.categories.violation": "Rule violation",
-  "report_notification.open": "Open report",
+  "report_notification.categories.other": "هی تر",
+  "report_notification.categories.spam": "سپام",
+  "report_notification.categories.violation": "پێشێلکردنی یاسا",
+  "report_notification.open": "ڕاپۆرتەکان بکەوە",
+  "search.no_recent_searches": "گەڕانەکانی ئەم دواییە",
   "search.placeholder": "گەڕان",
-  "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "شێوەی گەڕانی پێشکەوتوو",
-  "search_popout.tips.full_text": "گەڕانێکی دەقی سادە دەتوانێت توتەکانی ئێوە کە، نووسیوتانە،پەسەنتان کردووە، دووبارەتانکردووە، یان ئەو توتانە کە باسی ئێوەی تێدا کراوە پەیدا دەکا. هەروەها ناوی بەکارهێنەران، ناوی پیشاندراو و هەشتەگەکانیش لە خۆ دەگرێت.",
-  "search_popout.tips.hashtag": "هەشتاگ",
-  "search_popout.tips.status": "توت",
-  "search_popout.tips.text": "دەقی سادە هەڵدەسێ بە گەڕاندنەوەی هاوتایی ناوی پیشاندان، ناوی بەکارهێنەر و هاشتاگەکان",
-  "search_popout.tips.user": "بەکارهێنەر",
-  "search_results.accounts": "خەڵک",
+  "search.quick_action.account_search": "پڕۆفایلی هاوتا لەگەڵ {x}",
+  "search.quick_action.go_to_account": "بڕۆ بۆ پڕۆفایلی{x}",
+  "search.quick_action.go_to_hashtag": "بڕۆ بۆ هاشتاگی {x}",
+  "search.quick_action.open_url": "بەستەرەکان لەناو ماستۆدۆن بکەوە",
+  "search.quick_action.status_search": "پڕۆفایلی هاوتا لەگەڵ {x}",
+  "search.search_or_paste": "گەڕان یان لێدانی URL",
+  "search_popout.quick_actions": "کرداری خێرا",
+  "search_popout.recent": "گەڕانەکانی ئەم دواییە",
+  "search_results.accounts": "پرۆفایلەکان",
   "search_results.all": "هەموو",
   "search_results.hashtags": "هەشتاگ",
-  "search_results.nothing_found": "Could not find anything for these search terms",
+  "search_results.nothing_found": "هیچ بۆ ئەم زاراوە گەڕانانە نەدۆزراوەتەوە",
   "search_results.statuses": "توتەکان",
   "search_results.statuses_fts_disabled": "گەڕانی توتەکان بە ناوەڕۆکیان لەسەر ئەم ڕاژەی ماستۆدۆن چالاک نەکراوە.",
-  "search_results.title": "Search for {q}",
+  "search_results.title": "گەڕان بەدوای {q}",
   "search_results.total": "{count, number} {count, plural, one {دەرئەنجام} other {دەرئەنجام}}",
-  "server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
+  "server_banner.about_active_users": "ئەو کەسانەی لە ماوەی ٣٠ ڕۆژی ڕابردوودا ئەم سێرڤەرە بەکاردەهێنن (بەکارهێنەرانی چالاک مانگانە)",
   "server_banner.active_users": "بەکارهێنەرانی چالاک",
   "server_banner.administered_by": "بەڕێوەبردن لەلایەن:",
   "server_banner.introduction": "{domain} بەشێکە لەو تۆڕە کۆمەڵایەتییە لامەرکەزییەی کە لەلایەن {mastodon}ەوە بەهێز دەکرێت.",
@@ -540,9 +548,9 @@
   "server_banner.server_stats": "دۆخی ڕاژەکار:",
   "sign_in_banner.create_account": "هەژمار دروستبکە",
   "sign_in_banner.sign_in": "بچۆ ژوورەوە",
-  "sign_in_banner.text": "چوونەژوورەوە بۆ فۆڵۆوکردنی پڕۆفایلی یان هاشتاگەکان، دڵخوازکردن، هاوبەشکردن و وەڵامدانەوەی پۆستەکان، یان کارلێککردن لە ئەکاونتەکەتەوە لەسەر سێرڤەرێکی جیاواز.",
+  "sign_in_banner.text": "چوونەژوورەوە بۆ فۆڵۆوکردنی پڕۆفایلی یان هاشتاگەکان، دڵخوازەکان، شەیرکردن و وەڵامدانەوەی پۆستەکان. هەروەها دەتوانیت لە ئەکاونتەکەتەوە لەسەر سێرڤەرێکی جیاواز کارلێک بکەیت.",
   "status.admin_account": "کردنەوەی میانڕەوی بەڕێوەبەر بۆ @{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_domain": "ڕووکاری مامناوەندی بکەرەوە بۆ {domain}",
   "status.admin_status": "ئەم توتە بکەوە لە ناو ڕووکاری بەڕیوەبەر",
   "status.block": "@{name} ئاستەنگ بکە",
   "status.bookmark": "نیشانه",
@@ -551,7 +559,8 @@
   "status.copy": "ڕوونووسی بەستەر بۆ توت",
   "status.delete": "سڕینەوە",
   "status.detailed_status": "ڕوانگەی گفتوگۆ بە وردەکاری",
-  "status.direct": "پەیامی ڕاستەوخۆ @{name}",
+  "status.direct": "بە شێوەیەکی تایبەت باسی @{name} بکە",
+  "status.direct_indicator": "ئاماژەی تایبەت",
   "status.edit": "دەستکاری",
   "status.edited": "بەشداری {date}",
   "status.edited_x_times": "دەستکاریکراوە {count, plural, one {{count} کات} other {{count} کات}}",
@@ -559,7 +568,7 @@
   "status.favourite": "دڵخواز",
   "status.filter": "ئەم پۆستە فلتەر بکە",
   "status.filtered": "پاڵاوتن",
-  "status.hide": "Hide post",
+  "status.hide": "شاردنەوەی پۆست",
   "status.history.created": "{name} دروستکراوە لە{date}",
   "status.history.edited": "{name} دروستکاریکراوە لە{date}",
   "status.load_more": "زیاتر بار بکە",
diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json
index 1820e511c..df675f151 100644
--- a/app/javascript/mastodon/locales/co.json
+++ b/app/javascript/mastodon/locales/co.json
@@ -20,7 +20,7 @@
   "account.blocked": "Bluccatu",
   "account.browse_more_on_origin_server": "Vede di più nant'à u prufile uriginale",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Missaghju direttu @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Ùn mi nutificate più quandu @{name} pubblica qualcosa",
   "account.domain_blocked": "Duminiu piattatu",
   "account.edit_profile": "Mudificà u prufile",
@@ -102,7 +102,7 @@
   "column.blocks": "Utilizatori bluccati",
   "column.bookmarks": "Segnalibri",
   "column.community": "Linea pubblica lucale",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Percorre i prufili",
   "column.domain_blocks": "Duminii piattati",
   "column.favourites": "Favuriti",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Piattà tuttu u duminiu",
   "confirmations.domain_block.message": "Site veramente sicuru·a che vulete piattà tuttu à {domain}? Saria forse abbastanza di bluccà ò piattà alcuni conti da quallà. Ùn viderete più nunda da quallà indè e linee pubbliche o e nutificazione. I vostri abbunati da stu duminiu saranu tolti.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Scunnettassi",
   "confirmations.logout.message": "Site sicuru·a che vulete scunnettà vi?",
   "confirmations.mute.confirm": "Piattà",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Per avà ùn avete bluccatu manc'un utilizatore.",
   "empty_column.bookmarked_statuses": "Ùn avete manc'un segnalibru. Quandu aghjunghjerate unu, sarà mustratu quì.",
   "empty_column.community": "Ùn c'hè nunda indè a linea lucale. Scrivete puru qualcosa!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Ùn c'hè manc'un duminiu bluccatu avà.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "Ùn avete manc'unu statutu favuritu. Quandu aghjunghjerate unu à i vostri favuriti, sarà mustratu quì.",
   "empty_column.favourites": "Nisunu hà aghjuntu stu statutu à i so favuriti. Quandu qualch'unu farà quessa, u so contu sarà mustratu quì.",
   "empty_column.follow_recommendations": "Si pare ch'ùn s'hè micca pussutu generà e ricumandazione per voi. Pudete sempre pruvà d'utilizà a ricerca per truvà ghjente chì cunnuscete, o splurà l'hashtag in tindenza.",
   "empty_column.follow_requests": "Ùn avete manc'una dumanda d'abbunamentu. Quandu averete una, sarà mustrata quì.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "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.suggestions": "Vede qualchì ricumandazione",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Auturizà",
   "follow_request.reject": "Righjittà",
   "follow_requests.unlocked_explanation": "U vostru contu ùn hè micca privatu, ma a squadra d'amministrazione di {domain} pensa chì e dumande d'abbunamentu di questi conti anu bisognu d'esse verificate manualmente.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Salvatu",
   "getting_started.heading": "Per principià",
   "hashtag.column_header.tag_mode.all": "è {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Segnalibri",
   "navigation_bar.community_timeline": "Linea pubblica lucale",
   "navigation_bar.compose": "Scrive un novu statutu",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Scopre",
   "navigation_bar.domain_blocks": "Duminii piattati",
   "navigation_bar.edit_profile": "Mudificà u prufile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favuriti",
   "navigation_bar.filters": "Parolle silenzate",
   "navigation_bar.follow_requests": "Dumande d'abbunamentu",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Abbunati è abbunamenti",
   "navigation_bar.lists": "Liste",
   "navigation_bar.logout": "Scunnettassi",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Circà",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "Ricerca avanzata",
-  "search_popout.tips.full_text": "I testi simplici rimandanu i statuti ch'avete scritti, aghjunti à i vostri favuriti, spartuti o induve quelli site mintuvatu·a, è ancu i cugnomi, nomi pubblichi è hashtag chì currispondenu.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "statutu",
-  "search_popout.tips.text": "Un testu simplice rimanda i nomi pubblichi, cugnomi è hashtag",
-  "search_popout.tips.user": "utilizatore",
-  "search_results.accounts": "Ghjente",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtag",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Apre l'interfaccia di muderazione per @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Apre stu statutu in l'interfaccia di muderazione",
@@ -551,7 +559,8 @@
   "status.copy": "Cupià ligame indè u statutu",
   "status.delete": "Toglie",
   "status.detailed_status": "Vista in ditagliu di a cunversazione",
-  "status.direct": "Mandà un missaghju @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/cs.json b/app/javascript/mastodon/locales/cs.json
index 0559cebf8..a8cc9a5b2 100644
--- a/app/javascript/mastodon/locales/cs.json
+++ b/app/javascript/mastodon/locales/cs.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blokovaný",
   "account.browse_more_on_origin_server": "Více na původním profilu",
   "account.cancel_follow_request": "Zrušit žádost o sledování",
-  "account.direct": "Poslat @{name} přímou zprávu",
+  "account.direct": "Soukromě zmínit @{name}",
   "account.disable_notifications": "Přestat mě upozorňovat, když @{name} zveřejní příspěvek",
   "account.domain_blocked": "Doména blokována",
   "account.edit_profile": "Upravit profil",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokovaní uživatelé",
   "column.bookmarks": "Záložky",
   "column.community": "Místní časová osa",
-  "column.direct": "Přímé zprávy",
+  "column.direct": "Soukromé zmínky",
   "column.directory": "Prozkoumat profily",
   "column.domain_blocks": "Blokované domény",
   "column.favourites": "Oblíbené",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Máte neuložené změny popisku médií nebo náhledu, chcete je přesto zahodit?",
   "confirmations.domain_block.confirm": "Blokovat celou doménu",
   "confirmations.domain_block.message": "Opravdu chcete blokovat celou doménu {domain}? Ve většině případů stačí blokovat nebo skrýt pár konkrétních uživatelů, což také doporučujeme. Z této domény neuvidíte obsah v žádné veřejné časové ose ani v oznámeních. Vaši sledující z této domény budou odstraněni.",
+  "confirmations.edit.confirm": "Upravit",
+  "confirmations.edit.message": "Editovat teď znamená přepsání zprávy, kterou právě tvoříte. Opravdu chcete pokračovat?",
   "confirmations.logout.confirm": "Odhlásit se",
   "confirmations.logout.message": "Opravdu se chcete odhlásit?",
   "confirmations.mute.confirm": "Skrýt",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Ještě jste nezablokovali žádného uživatele.",
   "empty_column.bookmarked_statuses": "Zatím v záložkách nemáte žádné příspěvky. Až si do nich nějaký přidáte, zobrazí se zde.",
   "empty_column.community": "Místní časová osa je prázdná. Napište něco veřejně a rozhýbejte to tu!",
-  "empty_column.direct": "Zatím nemáte žádné přímé zprávy. Až nějakou pošlete nebo dostanete, zobrazí se zde.",
+  "empty_column.direct": "Zatím nemáte žádné soukromé zmínky. Až nějakou pošlete nebo dostanete, zobrazí se zde.",
   "empty_column.domain_blocks": "Ještě nemáte žádné zablokované domény.",
   "empty_column.explore_statuses": "Momentálně není nic populární. Vraťte se později!",
   "empty_column.favourited_statuses": "Zatím nemáte žádné oblíbené příspěvky. Až si nějaký oblíbíte, zobrazí se zde.",
   "empty_column.favourites": "Tento příspěvek si zatím nikdo neoblíbil. Až to někdo udělá, zobrazí se zde.",
   "empty_column.follow_recommendations": "Zdá se, že pro vás nelze vygenerovat žádné návrhy. Můžete zkusit přes vyhledávání nalézt lidi, které znáte, nebo prozkoumat populární hashtagy.",
   "empty_column.follow_requests": "Zatím nemáte žádné žádosti o sledování. Až nějakou obdržíte, zobrazí se zde.",
+  "empty_column.followed_tags": "Zatím jste nesledovali žádné hashtagy. Až to uděláte, objeví se zde.",
   "empty_column.hashtag": "Pod tímto hashtagem zde zatím nic není.",
   "empty_column.home": "Vaše domovská časová osa je prázdná! Naplňte ji sledováním dalších lidí. {suggestions}",
   "empty_column.home.suggestions": "Prohlédněte si návrhy",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autorizovat",
   "follow_request.reject": "Zamítnout",
   "follow_requests.unlocked_explanation": "Přestože váš účet není zamčený, administrátor {domain} usoudil, že byste mohli chtít tyto žádosti o sledování zkontrolovat ručně.",
+  "followed_tags": "Sledované hashtagy",
   "footer.about": "O aplikaci",
   "footer.directory": "Adresář profilů",
   "footer.get_app": "Získat aplikaci",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Klávesové zkratky",
   "footer.privacy_policy": "Zásady ochrany osobních údajů",
   "footer.source_code": "Zobrazit zdrojový kód",
+  "footer.status": "Stav",
   "generic.saved": "Uloženo",
   "getting_started.heading": "Začínáme",
   "hashtag.column_header.tag_mode.all": "a {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Focus na sloupec",
   "keyboard_shortcuts.compose": "Zaměřit se na textové pole nového příspěvku",
   "keyboard_shortcuts.description": "Popis",
-  "keyboard_shortcuts.direct": "k otevření sloupce přímých zpráv",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Posunout v seznamu dolů",
   "keyboard_shortcuts.enter": "Otevřít příspěvek",
   "keyboard_shortcuts.favourite": "Oblíbit si příspěvek",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Záložky",
   "navigation_bar.community_timeline": "Místní časová osa",
   "navigation_bar.compose": "Vytvořit nový příspěvek",
-  "navigation_bar.direct": "Přímé zprávy",
+  "navigation_bar.direct": "Soukromé zmínky",
   "navigation_bar.discover": "Objevit",
   "navigation_bar.domain_blocks": "Blokované domény",
   "navigation_bar.edit_profile": "Upravit profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Oblíbené",
   "navigation_bar.filters": "Skrytá slova",
   "navigation_bar.follow_requests": "Žádosti o sledování",
+  "navigation_bar.followed_tags": "Sledované hashtagy",
   "navigation_bar.follows_and_followers": "Sledovaní a sledující",
   "navigation_bar.lists": "Seznamy",
   "navigation_bar.logout": "Odhlásit se",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Porušení pravidla",
   "report_notification.open": "Otevřít hlášení",
+  "search.no_recent_searches": "Žádná nedávná vyhledávání",
   "search.placeholder": "Hledat",
+  "search.quick_action.account_search": "Profily odpovídající {x}",
+  "search.quick_action.go_to_account": "Přejít na profil {x}",
+  "search.quick_action.go_to_hashtag": "Přejít na hashtag {x}",
+  "search.quick_action.open_url": "Otevřít URL v Mastodonu",
+  "search.quick_action.status_search": "Příspěvky odpovídající {x}",
   "search.search_or_paste": "Hledat nebo vložit URL",
-  "search_popout.search_format": "Pokročilé hledání",
-  "search_popout.tips.full_text": "Jednoduchý text vrací příspěvky, které jste napsali, oblíbili si, boostnuli, nebo vás v nich někdo zmínil, a také odpovídající přezdívky, zobrazovaná jména a hashtagy.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "příspěvek",
-  "search_popout.tips.text": "Jednoduchý text vrací odpovídající zobrazovaná jména, přezdívky a hashtagy",
-  "search_popout.tips.user": "uživatel",
-  "search_results.accounts": "Lidé",
+  "search_popout.quick_actions": "Rychlé akce",
+  "search_popout.recent": "Nedávná vyhledávání",
+  "search_results.accounts": "Profily",
   "search_results.all": "Vše",
   "search_results.hashtags": "Hashtagy",
   "search_results.nothing_found": "Pro tyto hledané výrazy nebylo nic nenalezeno",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Statistiky serveru:",
   "sign_in_banner.create_account": "Vytvořit účet",
   "sign_in_banner.sign_in": "Přihlásit se",
-  "sign_in_banner.text": "Přihlaste se pro sledování profilů nebo hashtagů, oblíbení, sdílení a odpovědí na příspěvky nebo interakci z vašeho účtu na jiném serveru.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Otevřít moderátorské rozhraní pro @{name}",
   "status.admin_domain": "Otevřít moderátorské rozhraní pro {domain}",
   "status.admin_status": "Otevřít tento příspěvek v moderátorském rozhraní",
@@ -551,7 +559,8 @@
   "status.copy": "Zkopírovat odkaz na příspěvek",
   "status.delete": "Smazat",
   "status.detailed_status": "Podrobné zobrazení konverzace",
-  "status.direct": "Poslat @{name} přímou zprávu",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Soukromá zmínka",
   "status.edit": "Upravit",
   "status.edited": "Upraveno {date}",
   "status.edited_x_times": "Upraveno {count, plural, one {{count}krát} few {{count}krát} many {{count}krát} other {{count}krát}}",
diff --git a/app/javascript/mastodon/locales/csb.json b/app/javascript/mastodon/locales/csb.json
new file mode 100644
index 000000000..2150c1d7a
--- /dev/null
+++ b/app/javascript/mastodon/locales/csb.json
@@ -0,0 +1,664 @@
+{
+  "about.blocks": "Moderated servers",
+  "about.contact": "Contact:",
+  "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
+  "about.domain_blocks.no_reason_available": "Reason not available",
+  "about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",
+  "about.domain_blocks.silenced.explanation": "You will generally not see profiles and content from this server, unless you explicitly look it up or opt into it by following.",
+  "about.domain_blocks.silenced.title": "Limited",
+  "about.domain_blocks.suspended.explanation": "No data from this server will be processed, stored or exchanged, making any interaction or communication with users from this server impossible.",
+  "about.domain_blocks.suspended.title": "Suspended",
+  "about.not_available": "This information has not been made available on this server.",
+  "about.powered_by": "Decentralized social media powered by {mastodon}",
+  "about.rules": "Server rules",
+  "account.account_note_header": "Note",
+  "account.add_or_remove_from_list": "Add or Remove from lists",
+  "account.badges.bot": "Bot",
+  "account.badges.group": "Group",
+  "account.block": "Block @{name}",
+  "account.block_domain": "Block domain {domain}",
+  "account.blocked": "Blocked",
+  "account.browse_more_on_origin_server": "Browse more on the original profile",
+  "account.cancel_follow_request": "Withdraw follow request",
+  "account.direct": "Privately mention @{name}",
+  "account.disable_notifications": "Stop notifying me when @{name} posts",
+  "account.domain_blocked": "Domain blocked",
+  "account.edit_profile": "Edit profile",
+  "account.enable_notifications": "Notify me when @{name} posts",
+  "account.endorse": "Feature on profile",
+  "account.featured_tags.last_status_at": "Last post on {date}",
+  "account.featured_tags.last_status_never": "No posts",
+  "account.featured_tags.title": "{name}'s featured hashtags",
+  "account.follow": "Follow",
+  "account.followers": "Followers",
+  "account.followers.empty": "No one follows this user yet.",
+  "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
+  "account.following": "Following",
+  "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
+  "account.follows.empty": "This user doesn't follow anyone yet.",
+  "account.follows_you": "Follows you",
+  "account.go_to_profile": "Go to profile",
+  "account.hide_reblogs": "Hide boosts from @{name}",
+  "account.joined_short": "Joined",
+  "account.languages": "Change subscribed languages",
+  "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
+  "account.media": "Media",
+  "account.mention": "Mention @{name}",
+  "account.moved_to": "{name} has indicated that their new account is now:",
+  "account.mute": "Mute @{name}",
+  "account.mute_notifications": "Mute notifications from @{name}",
+  "account.muted": "Muted",
+  "account.open_original_page": "Open original page",
+  "account.posts": "Posts",
+  "account.posts_with_replies": "Posts and replies",
+  "account.report": "Report @{name}",
+  "account.requested": "Awaiting approval. Click to cancel follow request",
+  "account.requested_follow": "{name} has requested to follow you",
+  "account.share": "Share @{name}'s profile",
+  "account.show_reblogs": "Show boosts from @{name}",
+  "account.statuses_counter": "{count, plural, one {{counter} Post} other {{counter} Posts}}",
+  "account.unblock": "Unblock @{name}",
+  "account.unblock_domain": "Unblock domain {domain}",
+  "account.unblock_short": "Unblock",
+  "account.unendorse": "Don't feature on profile",
+  "account.unfollow": "Unfollow",
+  "account.unmute": "Unmute @{name}",
+  "account.unmute_notifications": "Unmute notifications from @{name}",
+  "account.unmute_short": "Unmute",
+  "account_note.placeholder": "Click to add a note",
+  "admin.dashboard.daily_retention": "User retention rate by day after sign-up",
+  "admin.dashboard.monthly_retention": "User retention rate by month after sign-up",
+  "admin.dashboard.retention.average": "Average",
+  "admin.dashboard.retention.cohort": "Sign-up month",
+  "admin.dashboard.retention.cohort_size": "New users",
+  "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
+  "alert.rate_limited.title": "Rate limited",
+  "alert.unexpected.message": "An unexpected error occurred.",
+  "alert.unexpected.title": "Oops!",
+  "announcement.announcement": "Announcement",
+  "attachments_list.unprocessed": "(unprocessed)",
+  "audio.hide": "Hide audio",
+  "autosuggest_hashtag.per_week": "{count} per week",
+  "boost_modal.combo": "You can press {combo} to skip this next time",
+  "bundle_column_error.copy_stacktrace": "Copy error report",
+  "bundle_column_error.error.body": "The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.",
+  "bundle_column_error.error.title": "Oh, no!",
+  "bundle_column_error.network.body": "There was an error when trying to load this page. This could be due to a temporary problem with your internet connection or this server.",
+  "bundle_column_error.network.title": "Network error",
+  "bundle_column_error.retry": "Try again",
+  "bundle_column_error.return": "Go back home",
+  "bundle_column_error.routing.body": "The requested page could not be found. Are you sure the URL in the address bar is correct?",
+  "bundle_column_error.routing.title": "404",
+  "bundle_modal_error.close": "Close",
+  "bundle_modal_error.message": "Something went wrong while loading this component.",
+  "bundle_modal_error.retry": "Try again",
+  "closed_registrations.other_server_instructions": "Since Mastodon is decentralized, you can create an account on another server and still interact with this one.",
+  "closed_registrations_modal.description": "Creating an account on {domain} is currently not possible, but please keep in mind that you do not need an account specifically on {domain} to use Mastodon.",
+  "closed_registrations_modal.find_another_server": "Find another server",
+  "closed_registrations_modal.preamble": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
+  "closed_registrations_modal.title": "Signing up on Mastodon",
+  "column.about": "About",
+  "column.blocks": "Blocked users",
+  "column.bookmarks": "Bookmarks",
+  "column.community": "Local timeline",
+  "column.direct": "Private mentions",
+  "column.directory": "Browse profiles",
+  "column.domain_blocks": "Blocked domains",
+  "column.favourites": "Favourites",
+  "column.follow_requests": "Follow requests",
+  "column.home": "Home",
+  "column.lists": "Lists",
+  "column.mutes": "Muted users",
+  "column.notifications": "Notifications",
+  "column.pins": "Pinned post",
+  "column.public": "Federated timeline",
+  "column_back_button.label": "Back",
+  "column_header.hide_settings": "Hide settings",
+  "column_header.moveLeft_settings": "Move column to the left",
+  "column_header.moveRight_settings": "Move column to the right",
+  "column_header.pin": "Pin",
+  "column_header.show_settings": "Show settings",
+  "column_header.unpin": "Unpin",
+  "column_subheading.settings": "Settings",
+  "community.column_settings.local_only": "Local only",
+  "community.column_settings.media_only": "Media only",
+  "community.column_settings.remote_only": "Remote only",
+  "compose.language.change": "Change language",
+  "compose.language.search": "Search languages...",
+  "compose_form.direct_message_warning_learn_more": "Learn more",
+  "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
+  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.",
+  "compose_form.lock_disclaimer.lock": "locked",
+  "compose_form.placeholder": "What is on your mind?",
+  "compose_form.poll.add_option": "Add a choice",
+  "compose_form.poll.duration": "Poll duration",
+  "compose_form.poll.option_placeholder": "Choice {number}",
+  "compose_form.poll.remove_option": "Remove this choice",
+  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
+  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
+  "compose_form.publish": "Publish",
+  "compose_form.publish_form": "Publish",
+  "compose_form.publish_loud": "{publish}!",
+  "compose_form.save_changes": "Save changes",
+  "compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}",
+  "compose_form.sensitive.marked": "{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}",
+  "compose_form.sensitive.unmarked": "{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}",
+  "compose_form.spoiler.marked": "Text is hidden behind warning",
+  "compose_form.spoiler.unmarked": "Text is not hidden",
+  "compose_form.spoiler_placeholder": "Write your warning here",
+  "confirmation_modal.cancel": "Cancel",
+  "confirmations.block.block_and_report": "Block & Report",
+  "confirmations.block.confirm": "Block",
+  "confirmations.block.message": "Are you sure you want to block {name}?",
+  "confirmations.cancel_follow_request.confirm": "Withdraw request",
+  "confirmations.cancel_follow_request.message": "Are you sure you want to withdraw your request to follow {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.discard_edit_media.confirm": "Discard",
+  "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
+  "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
+  "confirmations.logout.confirm": "Log out",
+  "confirmations.logout.message": "Are you sure you want to log out?",
+  "confirmations.mute.confirm": "Mute",
+  "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
+  "confirmations.mute.message": "Are you sure you want to mute {name}?",
+  "confirmations.redraft.confirm": "Delete & redraft",
+  "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
+  "confirmations.reply.confirm": "Reply",
+  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
+  "confirmations.unfollow.confirm": "Unfollow",
+  "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
+  "conversation.delete": "Delete conversation",
+  "conversation.mark_as_read": "Mark as read",
+  "conversation.open": "View conversation",
+  "conversation.with": "With {names}",
+  "copypaste.copied": "Copied",
+  "copypaste.copy": "Copy",
+  "directory.federated": "From known fediverse",
+  "directory.local": "From {domain} only",
+  "directory.new_arrivals": "New arrivals",
+  "directory.recently_active": "Recently active",
+  "disabled_account_banner.account_settings": "Account settings",
+  "disabled_account_banner.text": "Your account {disabledAccount} is currently disabled.",
+  "dismissable_banner.community_timeline": "These are the most recent public posts from people whose accounts are hosted by {domain}.",
+  "dismissable_banner.dismiss": "Dismiss",
+  "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
+  "dismissable_banner.explore_statuses": "These posts from this and other servers in the decentralized network are gaining traction on this server right now.",
+  "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
+  "dismissable_banner.public_timeline": "These are the most recent public posts from people on this and other servers of the decentralized network that this server knows about.",
+  "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.clear": "Clear",
+  "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 matching emojis found",
+  "emoji_button.objects": "Objects",
+  "emoji_button.people": "People",
+  "emoji_button.recent": "Frequently used",
+  "emoji_button.search": "Search...",
+  "emoji_button.search_results": "Search results",
+  "emoji_button.symbols": "Symbols",
+  "emoji_button.travel": "Travel & Places",
+  "empty_column.account_suspended": "Account suspended",
+  "empty_column.account_timeline": "No posts found",
+  "empty_column.account_unavailable": "Profile unavailable",
+  "empty_column.blocks": "You haven't blocked any users yet.",
+  "empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
+  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
+  "empty_column.domain_blocks": "There are no blocked domains yet.",
+  "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
+  "empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
+  "empty_column.favourites": "No one has favourited this post yet. When someone does, they will show up here.",
+  "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
+  "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
+  "empty_column.hashtag": "There is nothing in this hashtag yet.",
+  "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
+  "empty_column.home.suggestions": "See some suggestions",
+  "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. When other people interact with you, you will see it here.",
+  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up",
+  "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.",
+  "error.unexpected_crash.explanation_addons": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.",
+  "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
+  "error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
+  "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
+  "errors.unexpected_crash.report_issue": "Report issue",
+  "explore.search_results": "Search results",
+  "explore.suggested_follows": "For you",
+  "explore.title": "Explore",
+  "explore.trending_links": "News",
+  "explore.trending_statuses": "Posts",
+  "explore.trending_tags": "Hashtags",
+  "filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.",
+  "filter_modal.added.context_mismatch_title": "Context mismatch!",
+  "filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.",
+  "filter_modal.added.expired_title": "Expired filter!",
+  "filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.",
+  "filter_modal.added.review_and_configure_title": "Filter settings",
+  "filter_modal.added.settings_link": "settings page",
+  "filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.",
+  "filter_modal.added.title": "Filter added!",
+  "filter_modal.select_filter.context_mismatch": "does not apply to this context",
+  "filter_modal.select_filter.expired": "expired",
+  "filter_modal.select_filter.prompt_new": "New category: {name}",
+  "filter_modal.select_filter.search": "Search or create",
+  "filter_modal.select_filter.subtitle": "Use an existing category or create a new one",
+  "filter_modal.select_filter.title": "Filter this post",
+  "filter_modal.title.status": "Filter a post",
+  "follow_recommendations.done": "Done",
+  "follow_recommendations.heading": "Follow people you'd like to see posts from! Here are some suggestions.",
+  "follow_recommendations.lead": "Posts from people you follow will show up in chronological order on your home feed. Don't be afraid to make mistakes, you can unfollow people just as easily any time!",
+  "follow_request.authorize": "Authorize",
+  "follow_request.reject": "Reject",
+  "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
+  "footer.about": "About",
+  "footer.directory": "Profiles directory",
+  "footer.get_app": "Get the app",
+  "footer.invite": "Invite people",
+  "footer.keyboard_shortcuts": "Keyboard shortcuts",
+  "footer.privacy_policy": "Privacy policy",
+  "footer.source_code": "View source code",
+  "footer.status": "Status",
+  "generic.saved": "Saved",
+  "getting_started.heading": "Getting started",
+  "hashtag.column_header.tag_mode.all": "and {additional}",
+  "hashtag.column_header.tag_mode.any": "or {additional}",
+  "hashtag.column_header.tag_mode.none": "without {additional}",
+  "hashtag.column_settings.select.no_options_message": "No suggestions found",
+  "hashtag.column_settings.select.placeholder": "Enter hashtags…",
+  "hashtag.column_settings.tag_mode.all": "All of these",
+  "hashtag.column_settings.tag_mode.any": "Any of these",
+  "hashtag.column_settings.tag_mode.none": "None of these",
+  "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
+  "hashtag.follow": "Follow hashtag",
+  "hashtag.unfollow": "Unfollow hashtag",
+  "home.column_settings.basic": "Basic",
+  "home.column_settings.show_reblogs": "Show boosts",
+  "home.column_settings.show_replies": "Show replies",
+  "home.hide_announcements": "Hide announcements",
+  "home.show_announcements": "Show announcements",
+  "interaction_modal.description.favourite": "With an account on Mastodon, you can favourite this post to let the author know you appreciate it and save it for later.",
+  "interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.",
+  "interaction_modal.description.reblog": "With an account on Mastodon, you can boost this post to share it with your own followers.",
+  "interaction_modal.description.reply": "With an account on Mastodon, you can respond to this post.",
+  "interaction_modal.on_another_server": "On a different server",
+  "interaction_modal.on_this_server": "On this server",
+  "interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favourite Mastodon app or the web interface of your Mastodon server.",
+  "interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.",
+  "interaction_modal.title.favourite": "Favourite {name}'s post",
+  "interaction_modal.title.follow": "Follow {name}",
+  "interaction_modal.title.reblog": "Boost {name}'s post",
+  "interaction_modal.title.reply": "Reply to {name}'s post",
+  "intervals.full.days": "{number, plural, one {# day} other {# days}}",
+  "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
+  "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
+  "keyboard_shortcuts.back": "to navigate back",
+  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.boost": "to boost",
+  "keyboard_shortcuts.column": "to focus a status in one of the columns",
+  "keyboard_shortcuts.compose": "to focus the compose textarea",
+  "keyboard_shortcuts.description": "Description",
+  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.down": "to move down in the list",
+  "keyboard_shortcuts.enter": "to open status",
+  "keyboard_shortcuts.favourite": "to favourite",
+  "keyboard_shortcuts.favourites": "to open favourites list",
+  "keyboard_shortcuts.federated": "to open federated timeline",
+  "keyboard_shortcuts.heading": "Keyboard Shortcuts",
+  "keyboard_shortcuts.home": "to open home timeline",
+  "keyboard_shortcuts.hotkey": "Hotkey",
+  "keyboard_shortcuts.legend": "to display this legend",
+  "keyboard_shortcuts.local": "to open local timeline",
+  "keyboard_shortcuts.mention": "to mention author",
+  "keyboard_shortcuts.muted": "to open muted users list",
+  "keyboard_shortcuts.my_profile": "to open your profile",
+  "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "to open media",
+  "keyboard_shortcuts.pinned": "to open pinned posts list",
+  "keyboard_shortcuts.profile": "to open author's profile",
+  "keyboard_shortcuts.reply": "to reply",
+  "keyboard_shortcuts.requests": "to open follow requests list",
+  "keyboard_shortcuts.search": "to focus search",
+  "keyboard_shortcuts.spoilers": "to show/hide CW field",
+  "keyboard_shortcuts.start": "to open \"get started\" column",
+  "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
+  "keyboard_shortcuts.toggle_sensitivity": "to show/hide media",
+  "keyboard_shortcuts.toot": "to start a brand new post",
+  "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
+  "keyboard_shortcuts.up": "to move up in the list",
+  "lightbox.close": "Close",
+  "lightbox.compress": "Compress image view box",
+  "lightbox.expand": "Expand image view box",
+  "lightbox.next": "Next",
+  "lightbox.previous": "Previous",
+  "limited_account_hint.action": "Show profile anyway",
+  "limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
+  "lists.account.add": "Add to list",
+  "lists.account.remove": "Remove from list",
+  "lists.delete": "Delete list",
+  "lists.edit": "Edit list",
+  "lists.edit.submit": "Change title",
+  "lists.new.create": "Add list",
+  "lists.new.title_placeholder": "New list title",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Members of the list",
+  "lists.replies_policy.none": "No one",
+  "lists.replies_policy.title": "Show replies to:",
+  "lists.search": "Search among people you follow",
+  "lists.subheading": "Your lists",
+  "load_pending": "{count, plural, one {# new item} other {# new items}}",
+  "loading_indicator.label": "Loading...",
+  "media_gallery.toggle_visible": "{number, plural, one {Hide image} other {Hide images}}",
+  "missing_indicator.label": "Not found",
+  "missing_indicator.sublabel": "This resource could not be found",
+  "moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.",
+  "mute_modal.duration": "Duration",
+  "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "mute_modal.indefinite": "Indefinite",
+  "navigation_bar.about": "About",
+  "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.bookmarks": "Bookmarks",
+  "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.compose": "Compose new post",
+  "navigation_bar.direct": "Private mentions",
+  "navigation_bar.discover": "Discover",
+  "navigation_bar.domain_blocks": "Hidden domains",
+  "navigation_bar.edit_profile": "Edit profile",
+  "navigation_bar.explore": "Explore",
+  "navigation_bar.favourites": "Favourites",
+  "navigation_bar.filters": "Muted words",
+  "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
+  "navigation_bar.follows_and_followers": "Follows and followers",
+  "navigation_bar.lists": "Lists",
+  "navigation_bar.logout": "Logout",
+  "navigation_bar.mutes": "Muted users",
+  "navigation_bar.personal": "Personal",
+  "navigation_bar.pins": "Pinned posts",
+  "navigation_bar.preferences": "Preferences",
+  "navigation_bar.public_timeline": "Federated timeline",
+  "navigation_bar.search": "Search",
+  "navigation_bar.security": "Security",
+  "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.",
+  "notification.admin.report": "{name} reported {target}",
+  "notification.admin.sign_up": "{name} signed up",
+  "notification.favourite": "{name} favourited your status",
+  "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
+  "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "Your poll has ended",
+  "notification.poll": "A poll you have voted in has ended",
+  "notification.reblog": "{name} boosted your status",
+  "notification.status": "{name} just posted",
+  "notification.update": "{name} edited a post",
+  "notifications.clear": "Clear notifications",
+  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+  "notifications.column_settings.admin.report": "New reports:",
+  "notifications.column_settings.admin.sign_up": "New sign-ups:",
+  "notifications.column_settings.alert": "Desktop notifications",
+  "notifications.column_settings.favourite": "Favourites:",
+  "notifications.column_settings.filter_bar.advanced": "Display all categories",
+  "notifications.column_settings.filter_bar.category": "Quick filter bar",
+  "notifications.column_settings.filter_bar.show_bar": "Show filter bar",
+  "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
+  "notifications.column_settings.mention": "Mentions:",
+  "notifications.column_settings.poll": "Poll results:",
+  "notifications.column_settings.push": "Push notifications",
+  "notifications.column_settings.reblog": "Boosts:",
+  "notifications.column_settings.show": "Show in column",
+  "notifications.column_settings.sound": "Play sound",
+  "notifications.column_settings.status": "New posts:",
+  "notifications.column_settings.unread_notifications.category": "Unread notifications",
+  "notifications.column_settings.unread_notifications.highlight": "Highlight unread notifications",
+  "notifications.column_settings.update": "Edits:",
+  "notifications.filter.all": "All",
+  "notifications.filter.boosts": "Boosts",
+  "notifications.filter.favourites": "Favourites",
+  "notifications.filter.follows": "Follows",
+  "notifications.filter.mentions": "Mentions",
+  "notifications.filter.polls": "Poll results",
+  "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
+  "notifications.group": "{count} notifications",
+  "notifications.mark_as_read": "Mark every notification as read",
+  "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
+  "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
+  "notifications_permission_banner.enable": "Enable desktop notifications",
+  "notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.",
+  "notifications_permission_banner.title": "Never miss a thing",
+  "picture_in_picture.restore": "Put it back",
+  "poll.closed": "Closed",
+  "poll.refresh": "Refresh",
+  "poll.total_people": "{count, plural, one {# person} other {# people}}",
+  "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
+  "poll.vote": "Vote",
+  "poll.voted": "You voted for this answer",
+  "poll.votes": "{votes, plural, one {# vote} other {# votes}}",
+  "poll_button.add_poll": "Add a poll",
+  "poll_button.remove_poll": "Remove poll",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Visible for mentioned users only",
+  "privacy.direct.short": "Direct",
+  "privacy.private.long": "Visible for followers only",
+  "privacy.private.short": "Followers-only",
+  "privacy.public.long": "Visible for all",
+  "privacy.public.short": "Public",
+  "privacy.unlisted.long": "Visible for all, but opted-out of discovery features",
+  "privacy.unlisted.short": "Unlisted",
+  "privacy_policy.last_updated": "Last updated {date}",
+  "privacy_policy.title": "Privacy Policy",
+  "refresh": "Refresh",
+  "regeneration_indicator.label": "Loading…",
+  "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+  "relative_time.days": "{number}d",
+  "relative_time.full.days": "{number, plural, one {# day} other {# days}} ago",
+  "relative_time.full.hours": "{number, plural, one {# hour} other {# hours}} ago",
+  "relative_time.full.just_now": "just now",
+  "relative_time.full.minutes": "{number, plural, one {# minute} other {# minutes}} ago",
+  "relative_time.full.seconds": "{number, plural, one {# second} other {# seconds}} ago",
+  "relative_time.hours": "{number}h",
+  "relative_time.just_now": "now",
+  "relative_time.minutes": "{number}m",
+  "relative_time.seconds": "{number}s",
+  "relative_time.today": "today",
+  "reply_indicator.cancel": "Cancel",
+  "report.block": "Block",
+  "report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.",
+  "report.categories.other": "Other",
+  "report.categories.spam": "Spam",
+  "report.categories.violation": "Content violates one or more server rules",
+  "report.category.subtitle": "Choose the best match",
+  "report.category.title": "Tell us what's going on with this {type}",
+  "report.category.title_account": "profile",
+  "report.category.title_status": "post",
+  "report.close": "Done",
+  "report.comment.title": "Is there anything else you think we should know?",
+  "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.mute": "Mute",
+  "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.",
+  "report.next": "Next",
+  "report.placeholder": "Type or paste additional comments",
+  "report.reasons.dislike": "I don't like it",
+  "report.reasons.dislike_description": "It is not something you want to see",
+  "report.reasons.other": "It's something else",
+  "report.reasons.other_description": "The issue does not fit into other categories",
+  "report.reasons.spam": "It's spam",
+  "report.reasons.spam_description": "Malicious links, fake engagement, or repetitive replies",
+  "report.reasons.violation": "It violates server rules",
+  "report.reasons.violation_description": "You are aware that it breaks specific rules",
+  "report.rules.subtitle": "Select all that apply",
+  "report.rules.title": "Which rules are being violated?",
+  "report.statuses.subtitle": "Select all that apply",
+  "report.statuses.title": "Are there any posts that back up this report?",
+  "report.submit": "Submit report",
+  "report.target": "Report {target}",
+  "report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:",
+  "report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:",
+  "report.thanks.title": "Don't want to see this?",
+  "report.thanks.title_actionable": "Thanks for reporting, we'll look into this.",
+  "report.unfollow": "Unfollow @{name}",
+  "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.",
+  "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached",
+  "report_notification.categories.other": "Other",
+  "report_notification.categories.spam": "Spam",
+  "report_notification.categories.violation": "Rule violation",
+  "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
+  "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
+  "search.search_or_paste": "Search or paste URL",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
+  "search_results.all": "All",
+  "search_results.hashtags": "Hashtags",
+  "search_results.nothing_found": "Could not find anything for these search terms",
+  "search_results.statuses": "Posts",
+  "search_results.statuses_fts_disabled": "Searching posts by their content is not enabled on this Mastodon server.",
+  "search_results.title": "Search for {q}",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
+  "server_banner.active_users": "active users",
+  "server_banner.administered_by": "Administered by:",
+  "server_banner.introduction": "{domain} is part of the decentralized social network powered by {mastodon}.",
+  "server_banner.learn_more": "Learn more",
+  "server_banner.server_stats": "Server stats:",
+  "sign_in_banner.create_account": "Create account",
+  "sign_in_banner.sign_in": "Sign in",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
+  "status.admin_account": "Open moderation interface for @{name}",
+  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_status": "Open this status in the moderation interface",
+  "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
+  "status.cancel_reblog_private": "Unboost",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "status.copy": "Copy link to status",
+  "status.delete": "Delete",
+  "status.detailed_status": "Detailed conversation view",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
+  "status.edit": "Edit",
+  "status.edited": "Edited {date}",
+  "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
+  "status.embed": "Embed",
+  "status.favourite": "Favourite",
+  "status.filter": "Filter this post",
+  "status.filtered": "Filtered",
+  "status.hide": "Hide post",
+  "status.history.created": "{name} created {date}",
+  "status.history.edited": "{name} edited {date}",
+  "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 post",
+  "status.read_more": "Read more",
+  "status.reblog": "Boost",
+  "status.reblog_private": "Boost with original visibility",
+  "status.reblogged_by": "{name} boosted",
+  "status.reblogs.empty": "No one has boosted this post yet. When someone does, they will show up here.",
+  "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
+  "status.replied_to": "Replied to {name}",
+  "status.reply": "Reply",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Report @{name}",
+  "status.sensitive_warning": "Sensitive content",
+  "status.share": "Share",
+  "status.show_filter_reason": "Show anyway",
+  "status.show_less": "Show less",
+  "status.show_less_all": "Show less for all",
+  "status.show_more": "Show more",
+  "status.show_more_all": "Show more for all",
+  "status.show_original": "Show original",
+  "status.translate": "Translate",
+  "status.translated_from_with": "Translated from {lang} using {provider}",
+  "status.uncached_media_warning": "Not available",
+  "status.unmute_conversation": "Unmute conversation",
+  "status.unpin": "Unpin from profile",
+  "subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
+  "subscribed_languages.save": "Save changes",
+  "subscribed_languages.target": "Change subscribed languages for {target}",
+  "suggestions.dismiss": "Dismiss suggestion",
+  "suggestions.header": "You might be interested in…",
+  "tabs_bar.federated_timeline": "Federated",
+  "tabs_bar.home": "Home",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Notifications",
+  "time_remaining.days": "{number, plural, one {# day} other {# days}} left",
+  "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left",
+  "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
+  "time_remaining.moments": "Moments remaining",
+  "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
+  "timeline_hint.remote_resource_not_displayed": "{resource} from other servers are not displayed.",
+  "timeline_hint.resources.followers": "Followers",
+  "timeline_hint.resources.follows": "Follows",
+  "timeline_hint.resources.statuses": "Older posts",
+  "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {{days} days}}",
+  "trends.trending_now": "Trending now",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "units.short.billion": "{count}B",
+  "units.short.million": "{count}M",
+  "units.short.thousand": "{count}K",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Add images, a video or an audio file",
+  "upload_error.limit": "File upload limit exceeded.",
+  "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people who are hard of hearing",
+  "upload_form.description": "Describe for people who are blind or have low vision",
+  "upload_form.description_missing": "No description added",
+  "upload_form.edit": "Edit",
+  "upload_form.thumbnail": "Change thumbnail",
+  "upload_form.undo": "Delete",
+  "upload_form.video_description": "Describe for people who are deaf, hard of hearing, blind or have low vision",
+  "upload_modal.analyzing_picture": "Analyzing picture…",
+  "upload_modal.apply": "Apply",
+  "upload_modal.applying": "Applying…",
+  "upload_modal.choose_image": "Choose image",
+  "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
+  "upload_modal.detect_text": "Detect text from picture",
+  "upload_modal.edit_media": "Edit media",
+  "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.",
+  "upload_modal.preparing_ocr": "Preparing OCR…",
+  "upload_modal.preview_label": "Preview ({ratio})",
+  "upload_progress.label": "Uploading…",
+  "upload_progress.processing": "Processing…",
+  "video.close": "Close video",
+  "video.download": "Download file",
+  "video.exit_fullscreen": "Exit full screen",
+  "video.expand": "Expand video",
+  "video.fullscreen": "Full screen",
+  "video.hide": "Hide video",
+  "video.mute": "Mute sound",
+  "video.pause": "Pause",
+  "video.play": "Play",
+  "video.unmute": "Unmute sound"
+}
diff --git a/app/javascript/mastodon/locales/cy.json b/app/javascript/mastodon/locales/cy.json
index c854501fa..5a076ca9d 100644
--- a/app/javascript/mastodon/locales/cy.json
+++ b/app/javascript/mastodon/locales/cy.json
@@ -1,26 +1,26 @@
 {
   "about.blocks": "Gweinyddion sy'n cael eu cymedroli",
   "about.contact": "Cysylltwch â:",
-  "about.disclaimer": "Mae Mastodon yn feddalwedd rhydd, cod agored ac o dan hawlfraint Mastodon gGmbH.",
+  "about.disclaimer": "Mae Mastodon yn feddalwedd cod agored rhydd ac o dan hawlfraint Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Nid yw'r rheswm ar gael",
-  "about.domain_blocks.preamble": "Yn gyffredinol, mae Mastodon yn caniatáu i chi weld cynnwys gan unrhyw weinyddwr arall yn y ffederasiwn a rhyngweithio â hi. Dyma'r eithriadau a wnaed ar y gweinydd penodol hwn.",
-  "about.domain_blocks.silenced.explanation": "Yn gyffredinol, fyddwch chi ddim yn gweld proffiliau a chynnwys o'r gweinydd hwn, oni bai eich bod yn chwilio'n benodol amdano neu yn ymuno drwy ei ddilyn.",
+  "about.domain_blocks.preamble": "Fel rheol, mae Mastodon yn caniatáu i chi weld cynnwys gan unrhyw weinyddwr arall yn y ffederasiwn a rhyngweithio â hi. Dyma'r eithriadau a wnaed ar y gweinydd penodol hwn.",
+  "about.domain_blocks.silenced.explanation": "Fel rheol, fyddwch chi ddim yn gweld proffiliau a chynnwys o'r gweinydd hwn, oni bai eich bod yn chwilio'n benodol amdano neu yn ymuno drwy ei ddilyn.",
   "about.domain_blocks.silenced.title": "Cyfyngedig",
-  "about.domain_blocks.suspended.explanation": "Ni fydd data o'r gweinydd hwn yn cael ei brosesu, ei storio na'i gyfnewid, gan wneud unrhyw ryngweithio neu gyfathrebu gyda defnyddwyr o'r gweinydd hwn yn amhosibl.",
+  "about.domain_blocks.suspended.explanation": "Ni fydd data o'r gweinydd hwn yn cael ei brosesu, ei gadw na'i gyfnewid, gan wneud unrhyw ryngweithio neu gyfathrebu gyda defnyddwyr o'r gweinydd hwn yn amhosibl.",
   "about.domain_blocks.suspended.title": "Ataliwyd",
   "about.not_available": "Nid yw'r wybodaeth hon ar gael ar y gweinydd hwn.",
-  "about.powered_by": "Cyfrwng cymdeithasol datganoledig yn cael ei yrru gan {mastodon}",
+  "about.powered_by": "Cyfrwng cymdeithasol datganoledig sy'n cael ei yrru gan {mastodon}",
   "about.rules": "Rheolau'r gweinydd",
   "account.account_note_header": "Nodyn",
-  "account.add_or_remove_from_list": "Ychwanegu neu Dileu o'r rhestrau",
+  "account.add_or_remove_from_list": "Ychwanegu neu Ddileu o'r rhestrau",
   "account.badges.bot": "Bot",
   "account.badges.group": "Grŵp",
   "account.block": "Blocio @{name}",
   "account.block_domain": "Blocio parth {domain}",
   "account.blocked": "Blociwyd",
   "account.browse_more_on_origin_server": "Pori mwy ar y proffil gwreiddiol",
-  "account.cancel_follow_request": "Tynnu nôl cais i ddilyn",
-  "account.direct": "Neges breifat @{name}",
+  "account.cancel_follow_request": "Tynnu cais i ddilyn",
+  "account.direct": "Crybwyll yn breifat @{name}",
   "account.disable_notifications": "Stopiwch fy hysbysu pan fydd @{name} yn postio",
   "account.domain_blocked": "Parth wedi ei flocio",
   "account.edit_profile": "Golygu proffil",
@@ -46,9 +46,9 @@
   "account.media": "Cyfryngau",
   "account.mention": "Crybwyll @{name}",
   "account.moved_to": "Mae {name} wedi nodi fod eu cyfrif newydd yn:",
-  "account.mute": "Tewi @{name}",
-  "account.mute_notifications": "Tewi hysbysiadau o @{name}",
-  "account.muted": "Wedi tewi",
+  "account.mute": "Anwybyddu @{name}",
+  "account.mute_notifications": "Diffodd hysbysiadau o @{name}",
+  "account.muted": "Wedi anwybyddu",
   "account.open_original_page": "Agor y dudalen wreiddiol",
   "account.posts": "Postiadau",
   "account.posts_with_replies": "Postiadau ac atebion",
@@ -100,14 +100,14 @@
   "closed_registrations_modal.title": "Ymgofrestru ar Mastodon",
   "column.about": "Ynghylch",
   "column.blocks": "Defnyddwyr a flociwyd",
-  "column.bookmarks": "Nodau tudalen",
+  "column.bookmarks": "Llyfrnodau",
   "column.community": "Ffrwd lleol",
-  "column.direct": "Negeseuon preifat",
+  "column.direct": "Crybwylliadau preifat",
   "column.directory": "Pori proffiliau",
   "column.domain_blocks": "Parthau wedi'u blocio",
   "column.favourites": "Ffefrynnau",
   "column.follow_requests": "Ceisiadau dilyn",
-  "column.home": "Cartref",
+  "column.home": "Hafan",
   "column.lists": "Rhestrau",
   "column.mutes": "Defnyddwyr wedi'u tewi",
   "column.notifications": "Hysbysiadau",
@@ -145,8 +145,8 @@
   "compose_form.sensitive.hide": "Marcio cyfryngau fel eu bod yn sensitif",
   "compose_form.sensitive.marked": "Cyfryngau wedi'u marcio'n sensitif",
   "compose_form.sensitive.unmarked": "Nid yw'r cyfryngau wedi'u marcio'n sensitif",
-  "compose_form.spoiler.marked": "Testun wedi ei guddio gan rybudd",
-  "compose_form.spoiler.unmarked": "Nid yw'r testun wedi ei guddio",
+  "compose_form.spoiler.marked": "Dileu rhybudd cynnwys",
+  "compose_form.spoiler.unmarked": "Ychwanegu rhybudd cynnwys",
   "compose_form.spoiler_placeholder": "Ysgrifenwch eich rhybudd yma",
   "confirmation_modal.cancel": "Diddymu",
   "confirmations.block.block_and_report": "Rhwystro ac Adrodd",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Mae gennych newidiadau heb eu cadw i'r disgrifiad cyfryngau neu'r rhagolwg, eu taflu beth bynnag?",
   "confirmations.domain_block.confirm": "Blocio parth cyfan",
   "confirmations.domain_block.message": "Ydych chi wir, wir eisiau blocio'r holl {domain}? Fel arfer, mae blocio neu dewi pobl penodol yn broses mwy effeithiol. Fyddwch chi ddim yn gweld cynnwys o'r parth hwnnw mewn ffrydiau cyhoeddus neu yn eich hysbysiadau. Bydd eich dilynwyr o'r parth hwnnw yn cael eu ddileu.",
+  "confirmations.edit.confirm": "Golygu",
+  "confirmations.edit.message": "Bydd golygu nawr yn trosysgrifennu'r neges rydych yn ei ysgrifennu ar hyn o bryd. Ydych chi'n siŵr eich bod eisiau gwneud hyn?",
   "confirmations.logout.confirm": "Allgofnodi",
   "confirmations.logout.message": "Ydych chi'n siŵr eich bod am allgofnodi?",
   "confirmations.mute.confirm": "Tewi",
@@ -179,7 +181,7 @@
   "conversation.with": "Gyda {names}",
   "copypaste.copied": "Wedi ei gopïo",
   "copypaste.copy": "Copïo",
-  "directory.federated": "O'r fydysawd cyfan",
+  "directory.federated": "O'r ffedysawd cyfan",
   "directory.local": "O {domain} yn unig",
   "directory.new_arrivals": "Defnyddwyr newydd",
   "directory.recently_active": "Ar-lein yn ddiweddar",
@@ -187,20 +189,20 @@
   "disabled_account_banner.text": "Mae eich cyfrif {disabledAccount} wedi ei analluogi ar hyn o bryd.",
   "dismissable_banner.community_timeline": "Dyma'r postiadau cyhoeddus diweddaraf gan bobl gyda chyfrifon ar {domain}.",
   "dismissable_banner.dismiss": "Diddymu",
-  "dismissable_banner.explore_links": "Mae'r straeon newyddion hyn yn cael eu trafod gan bobl ar y gweinydd hwn a rhai eraill ar y rhwydwaith datganoledig hwn, ar hyn o bryd.",
-  "dismissable_banner.explore_statuses": "Mae'r postiadau hyn o'r gweinydd hwn a gweinyddion eraill yn y rhwydwaith datganoledig yn denu sylw ar y gweinydd hwn ar hyn o bryd.",
+  "dismissable_banner.explore_links": "Dyma'r straeon newyddion sy'n cael eu trafod ar hyn o bryd gan bobl ar y gweinydd hwn a rhai eraill ar y rhwydwaith datganoledig yma.",
+  "dismissable_banner.explore_statuses": "Dyma'r postiadau o'r gweinydd hwn a gweinyddion eraill ar y rhwydwaith datganoledig sy'n denu sylw ar y gweinydd hwn ar hyn o bryd.",
   "dismissable_banner.explore_tags": "Mae'r hashnodau hyn yn denu sylw ymhlith pobl ar y gweinydd hwn a gweinyddwyr eraill y rhwydwaith datganoledig ar hyn o bryd.",
   "dismissable_banner.public_timeline": "Dyma'r postiadau cyhoeddus diweddaraf gan bobl ar y gweinydd hwn a gweinyddwyr eraill y rhwydwaith datganoledig y mae'r gweinydd hwn yn gwybod amdano.",
   "embed.instructions": "Gosodwch y post hwn ar eich gwefan drwy gopïo'r côd isod.",
   "embed.preview": "Dyma sut olwg fydd arno:",
   "emoji_button.activity": "Gweithgarwch",
   "emoji_button.clear": "Clirio",
-  "emoji_button.custom": "Unigryw",
+  "emoji_button.custom": "Cyfaddas",
   "emoji_button.flags": "Baneri",
   "emoji_button.food": "Bwyd & Diod",
-  "emoji_button.label": "Mewnosodwch emoji",
+  "emoji_button.label": "Mewnosod emoji",
   "emoji_button.nature": "Natur",
-  "emoji_button.not_found": "Dim emojau'n cydweddu",
+  "emoji_button.not_found": "Dim emojiau'n cydweddu i'w cael",
   "emoji_button.objects": "Gwrthrychau",
   "emoji_button.people": "Pobl",
   "emoji_button.recent": "Defnydd cyffredin",
@@ -210,17 +212,18 @@
   "emoji_button.travel": "Teithio a Llefydd",
   "empty_column.account_suspended": "Cyfrif wedi'i atal",
   "empty_column.account_timeline": "Dim postiadau yma!",
-  "empty_column.account_unavailable": "Proffil ddim ar gael",
+  "empty_column.account_unavailable": "Nid yw'r proffil ar gael",
   "empty_column.blocks": "Nid ydych wedi blocio unrhyw ddefnyddwyr eto.",
-  "empty_column.bookmarked_statuses": "Nid oes gennych unrhyw bostiad wedi'u cadw fel nodau tudalen eto. Pan fyddwch yn gosod nod tudalen i un, mi fydd yn ymddangos yma.",
-  "empty_column.community": "Mae'r ffrwd lleol yn wag. Beth am ysgrifennu rhywbeth yn gyhoeddus?",
-  "empty_column.direct": "Does gennych unrhyw negeseuon preifat eto. Pan byddwch yn anfon neu derbyn un, bydd yn ymddangos yma.",
+  "empty_column.bookmarked_statuses": "Nid oes gennych unrhyw bostiad wedi'u cadw fel llyfrnodau eto. Pan fyddwch yn gosod nod tudalen i un, mi fydd yn ymddangos yma.",
+  "empty_column.community": "Mae'r ffrwd lleol yn wag. Beth am ysgrifennu rhywbeth cyhoeddus?",
+  "empty_column.direct": "Nid oes gennych unrhyw grybwylliadau preifat eto. Pan fyddwch chi'n anfon neu'n derbyn un, bydd yn ymddangos yma.",
   "empty_column.domain_blocks": "Nid oes yna unrhyw barthau cuddiedig eto.",
-  "empty_column.explore_statuses": "Does dim trendio ar hyn o bryd. Dewch nôl nes ymlaen!",
-  "empty_column.favourited_statuses": "Nid oes gennych unrhyw hoff bostiadau eto. Pan y byddwch yn hoffi un, mi fydd yn ymddangos yma.",
+  "empty_column.explore_statuses": "Does dim yn trendio ar hyn o bryd. Dewch nôl nes ymlaen!",
+  "empty_column.favourited_statuses": "Nid oes gennych unrhyw hoff bostiadau eto. Pan byddwch yn hoffi un, bydd yn ymddangos yma.",
   "empty_column.favourites": "Does neb wedi hoffi'r post hwn eto. Pan bydd rhywun yn ei hoffi, byddent yn ymddangos yma.",
-  "empty_column.follow_recommendations": "Does dim awgrymiadau yma i chi. Gallwch geisio chwilio am bobl yr ydych yn eu hadnabod neu archwilio hashnodau sy'n trendio.",
+  "empty_column.follow_recommendations": "Does dim awgrymiadau yma i chi. Gallwch geisio chwilio am bobl rydych yn eu hadnabod neu edrych drwy hashnodau sy'n trendio.",
   "empty_column.follow_requests": "Nid oes gennych unrhyw geisiadau dilyn eto. Pan fyddwch yn derbyn un, byddan nhw'n ymddangos yma.",
+  "empty_column.followed_tags": "Nid ydych wedi dilyn unrhyw hashnodau eto. Pan fyddwch chi'n gwneud hynny, byddan nhw'n ymddangos yma.",
   "empty_column.hashtag": "Nid oes dim ar yr hashnod hwn eto.",
   "empty_column.home": "Mae eich ffrwd gartref yn wag! Ymwelwch â {public} neu defnyddiwch y chwilotwr i ddechrau arni ac i gwrdd â defnyddwyr eraill.",
   "empty_column.home.suggestions": "Dyma rai awgrymiadau",
@@ -237,7 +240,7 @@
   "errors.unexpected_crash.report_issue": "Rhoi gwybod am broblem",
   "explore.search_results": "Canlyniadau chwilio",
   "explore.suggested_follows": "I chi",
-  "explore.title": "Archwilio",
+  "explore.title": "Darganfod",
   "explore.trending_links": "Newyddion",
   "explore.trending_statuses": "Postiadau",
   "explore.trending_tags": "Hashnodau",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Awdurdodi",
   "follow_request.reject": "Gwrthod",
   "follow_requests.unlocked_explanation": "Er nid yw eich cyfrif wedi'i gloi, roedd y staff {domain} yn meddwl efallai hoffech adolygu ceisiadau dilyn o'r cyfrifau rhain wrth law.",
+  "followed_tags": "Hashnodau rydych yn eu dilyn",
   "footer.about": "Ynghylch",
   "footer.directory": "Cyfeiriadur proffiliau",
   "footer.get_app": "Lawrlwytho'r ap",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Bysellau brys",
   "footer.privacy_policy": "Polisi preifatrwydd",
   "footer.source_code": "Gweld y cod ffynhonnell",
+  "footer.status": "Statws",
   "generic.saved": "Wedi'i Gadw",
   "getting_started.heading": "Dechrau",
   "hashtag.column_header.tag_mode.all": "a {additional}",
@@ -300,7 +305,7 @@
   "interaction_modal.title.follow": "Dilyn {name}",
   "interaction_modal.title.reblog": "Hybu postiad {name}",
   "interaction_modal.title.reply": "Ymateb i bostiad {name}",
-  "intervals.full.days": "{number, plural, one {# dydd} two {# ddydd} other {# o ddyddiau}}",
+  "intervals.full.days": "{number, plural, one {# diwrnod} two {# ddiwrnod} other {# diwrnod}}",
   "intervals.full.hours": "{number, plural, one {# awr} other {# o oriau}}",
   "intervals.full.minutes": "{number, plural, one {# funud} other {# o funudau}}",
   "keyboard_shortcuts.back": "Llywio nôl",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Ffocysu colofn",
   "keyboard_shortcuts.compose": "Ffocysu ar ardal cyfansoddi testun",
   "keyboard_shortcuts.description": "Disgrifiad",
-  "keyboard_shortcuts.direct": "i agor colofn negeseuon preifat",
+  "keyboard_shortcuts.direct": "i agor colofn crybwylliadau preifat",
   "keyboard_shortcuts.down": "Symud lawr yn y rhestr",
   "keyboard_shortcuts.enter": "Agor post",
   "keyboard_shortcuts.favourite": "Hoffi postiad",
@@ -368,17 +373,18 @@
   "mute_modal.indefinite": "Parhaus",
   "navigation_bar.about": "Ynghylch",
   "navigation_bar.blocks": "Defnyddwyr wedi eu blocio",
-  "navigation_bar.bookmarks": "Nodau tudalen",
+  "navigation_bar.bookmarks": "Llyfrnodau",
   "navigation_bar.community_timeline": "Ffrwd leol",
   "navigation_bar.compose": "Cyfansoddi post newydd",
-  "navigation_bar.direct": "Negeseuon preifat",
+  "navigation_bar.direct": "Crybwylliadau preifat",
   "navigation_bar.discover": "Darganfod",
   "navigation_bar.domain_blocks": "Parthau wedi'u blocio",
   "navigation_bar.edit_profile": "Golygu proffil",
-  "navigation_bar.explore": "Archwilio",
+  "navigation_bar.explore": "Darganfod",
   "navigation_bar.favourites": "Ffefrynnau",
   "navigation_bar.filters": "Geiriau wedi'u tewi",
   "navigation_bar.follow_requests": "Ceisiadau dilyn",
+  "navigation_bar.followed_tags": "Hashnodau'n cael eu dilyn",
   "navigation_bar.follows_and_followers": "Yn dilyn a dilynwyr",
   "navigation_bar.lists": "Rhestrau",
   "navigation_bar.logout": "Allgofnodi",
@@ -487,8 +493,8 @@
   "report.comment.title": "Oes unrhyw beth arall y dylem ei wybod yn eich barn chi?",
   "report.forward": "Ymlaen i {target}",
   "report.forward_hint": "Mae'r cyfrif o weinydd arall. Anfon copi anhysbys o'r adroddiad yno hefyd?",
-  "report.mute": "Tewi",
-  "report.mute_explanation": "Ni fyddwch yn gweld eu postiadau. Gallant eich dilyn o hyd a gweld eich postiadau ac ni fyddant yn gwybod eu bod nhw wedi'u mudo.",
+  "report.mute": "Anwybyddu",
+  "report.mute_explanation": "Ni fyddwch yn gweld eu postiadau. Gallant eich dilyn o hyd a gweld eich postiadau ac ni fyddant yn gwybod eu bod nhw wedi'u hanwybyddu.",
   "report.next": "Nesaf",
   "report.placeholder": "Sylwadau ychwanegol",
   "report.reasons.dislike": "Dydw i ddim yn ei hoffi",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Sbam",
   "report_notification.categories.violation": "Torri rheol",
   "report_notification.open": "Agor adroddiad",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Chwilio",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Chwilio neu gludo URL",
-  "search_popout.search_format": "Fformat chwilio uwch",
-  "search_popout.tips.full_text": "Mae testun syml yn dychwelyd postiadau yr ydych wedi ysgrifennu, hoffi, wedi'u hybio, neu wedi'ch crybwyll ynddynt, ynghyd a chyfateb a enwau defnyddwyr, enwau arddangos ac hashnodau.",
-  "search_popout.tips.hashtag": "hashnod",
-  "search_popout.tips.status": "post",
-  "search_popout.tips.text": "Mae testun syml yn dychwelyd enwau dangos, enwau defnyddwyr a hashnodau sy'n cyfateb",
-  "search_popout.tips.user": "defnyddiwr",
-  "search_results.accounts": "Pobl",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Popeth",
   "search_results.hashtags": "Hashnodau",
   "search_results.nothing_found": "Methu dod o hyd i unrhyw beth ar gyfer y termau chwilio hyn",
@@ -534,24 +542,25 @@
   "search_results.total": "{count, number} {count, plural, zero {canlyniad} one {canlyniad} two {ganlyniad} other {canlyniad}}",
   "server_banner.about_active_users": "Pobl sy'n defnyddio'r gweinydd hwn yn ystod y 30 diwrnod diwethaf (Defnyddwyr Gweithredol Misol)",
   "server_banner.active_users": "defnyddwyr gweithredol",
-  "server_banner.administered_by": "Yn cael ei weinyddu gan:",
+  "server_banner.administered_by": "Gweinyddir gan:",
   "server_banner.introduction": "Mae {domain} yn rhan o'r rhwydwaith cymdeithasol datganoledig a bwerir gan {mastodon}.",
   "server_banner.learn_more": "Dysgu mwy",
-  "server_banner.server_stats": "Ystagedau'r gweinydd:",
+  "server_banner.server_stats": "Ystadegau'r gweinydd:",
   "sign_in_banner.create_account": "Creu cyfrif",
   "sign_in_banner.sign_in": "Mewngofnodi",
-  "sign_in_banner.text": "Mewngofnodwch i ddilyn proffiliau neu hashnodau, ffefrynnau, rhannu ac ymateb i bostiadau, neu ryngweithio o'ch cyfrif ar weinydd gwahanol.",
+  "sign_in_banner.text": "Mewngofnodwch i ddilyn proffiliau neu hashnodau, ffefrynnau, rhannu ac ateb postiadau. Gallwch hefyd ryngweithio o'ch cyfrif ar weinydd gwahanol.",
   "status.admin_account": "Agor rhyngwyneb cymedroli ar gyfer @{name}",
   "status.admin_domain": "Agor rhyngwyneb cymedroli {domain}",
-  "status.admin_status": "Agor y post hwn yn y rhyngwyneb goruwchwylio",
+  "status.admin_status": "Agor y postiad hwn yn y rhyngwyneb cymedroli",
   "status.block": "Blocio @{name}",
-  "status.bookmark": "Nod Tudalen",
+  "status.bookmark": "Llyfrnodi",
   "status.cancel_reblog_private": "Dadhybu",
   "status.cannot_reblog": "Nid oes modd hybu'r postiad hwn",
   "status.copy": "Copïo dolen i'r post",
   "status.delete": "Dileu",
   "status.detailed_status": "Golwg manwl o'r sgwrs",
-  "status.direct": "Neges breifat @{name}",
+  "status.direct": "Crybwyll yn breifat @{name}",
+  "status.direct_indicator": "Crybwyll preifat",
   "status.edit": "Golygu",
   "status.edited": "Golygwyd {date}",
   "status.edited_x_times": "Golygwyd {count, plural, one {waith} two {waith} other {{count} gwaith}}",
@@ -566,18 +575,18 @@
   "status.media_hidden": "Cyfryngau wedi'u cuddio",
   "status.mention": "Crybwyll @{name}",
   "status.more": "Rhagor",
-  "status.mute": "Tewi @{name}",
-  "status.mute_conversation": "Tewi sgwrs",
+  "status.mute": "Anwybyddu @{name}",
+  "status.mute_conversation": "Anwybyddu sgwrs",
   "status.open": "Ehangu'r post hwn",
   "status.pin": "Pinio ar y proffil",
-  "status.pinned": "Post wedi'i binio",
+  "status.pinned": "Postiad wedi'i binio",
   "status.read_more": "Darllen rhagor",
   "status.reblog": "Hybu",
   "status.reblog_private": "Hybu i'r gynulleidfa wreiddiol",
   "status.reblogged_by": "Hybodd {name}",
   "status.reblogs.empty": "Does neb wedi hybio'r post yma eto. Pan y bydd rhywun yn gwneud, byddent yn ymddangos yma.",
   "status.redraft": "Dileu ac ailddrafftio",
-  "status.remove_bookmark": "Tynnu Nod Tudalen",
+  "status.remove_bookmark": "Dileu llyfrnod",
   "status.replied_to": "Wedi ateb {name}",
   "status.reply": "Ateb",
   "status.replyAll": "Ateb i edefyn",
@@ -600,7 +609,7 @@
   "subscribed_languages.target": "Newid ieithoedd tanysgrifio {target}",
   "suggestions.dismiss": "Diystyru'r awgrym",
   "suggestions.header": "Efallai y bydd gennych ddiddordeb mewn…",
-  "tabs_bar.federated_timeline": "Ffedereiddiwyd",
+  "tabs_bar.federated_timeline": "Ffederasiwn",
   "tabs_bar.home": "Cartref",
   "tabs_bar.local_timeline": "Lleol",
   "tabs_bar.notifications": "Hysbysiadau",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 743822366..ca9caf79d 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blokeret",
   "account.browse_more_on_origin_server": "Se mere på den oprindelige profil",
   "account.cancel_follow_request": "Annullér anmodning om at følge",
-  "account.direct": "Direkte besked til @{name}",
+  "account.direct": "Privat omtale @{name}",
   "account.disable_notifications": "Advisér mig ikke længere, når @{name} poster",
   "account.domain_blocked": "Domæne blokeret",
   "account.edit_profile": "Redigér profil",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokerede brugere",
   "column.bookmarks": "Bogmærker",
   "column.community": "Lokal tidslinje",
-  "column.direct": "Direkte beskeder",
+  "column.direct": "Private omtaler",
   "column.directory": "Tjek profiler",
   "column.domain_blocks": "Blokerede domæner",
   "column.favourites": "Favoritter",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Der er ugemte ændringer i mediebeskrivelsen eller forhåndsvisningen, kassér dem alligevel?",
   "confirmations.domain_block.confirm": "Blokér hele domænet",
   "confirmations.domain_block.message": "Er du fuldstændig sikker på, at du vil blokere hele {domain}-domænet? Oftest vil nogle få målrettede blokeringer eller skjulninger være tilstrækkelige og at foretrække. Du vil ikke se indhold fra dette domæne i nogle offentlige tidslinjer eller i dine notifikationer, og dine følgere herfra fjernes ligeledes.",
+  "confirmations.edit.confirm": "Redigér",
+  "confirmations.edit.message": "Redigeres nu, overskrive den besked, der forfattes pt. Fortsæt alligevel?",
   "confirmations.logout.confirm": "Log ud",
   "confirmations.logout.message": "Er du sikker på, at du vil logge ud?",
   "confirmations.mute.confirm": "Skjul (mute)",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Ingen brugere blokeret endnu.",
   "empty_column.bookmarked_statuses": "Du har ingen bogmærkede indlæg endnu. Når du bogmærker ét, vil det dukke op hér.",
   "empty_column.community": "Den lokale tidslinje er tom. Skriv noget offentligt for at sætte tingene i gang!",
-  "empty_column.direct": "Der er endnu ingen direkte beskeder. Når en sendes eller modtages, dukker den op hér.",
+  "empty_column.direct": "Der er endnu ingen private omtaler. Når en sendes eller modtages, dukker den op hér.",
   "empty_column.domain_blocks": "Ingen blokerede domæner endnu.",
   "empty_column.explore_statuses": "Ingen nye tendenser lige nu. Tjek igen senere!",
   "empty_column.favourited_statuses": "Du har endnu ingen favoritindlæg. Når du favoritmarkerer ét, vil det dukke op hér.",
   "empty_column.favourites": "Ingen har endnu gjort dette indlæg til favorit. Når nogen gør dét, vil det dukke op hér.",
   "empty_column.follow_recommendations": "Det ser ud til, at der ikke kunne genereres forslag til dig. Du kan prøve med Søg for at lede efter personer, du måske kender, eller udforske hashtags.",
   "empty_column.follow_requests": "Du har endnu ingen følgeanmodninger. Når du modtager én, vil den dukke op hér.",
+  "empty_column.followed_tags": "Ingen hashtags følges endnu. Når det sker, vil de fremgå hér.",
   "empty_column.hashtag": "Der er intet med dette hashtag endnu.",
   "empty_column.home": "Din hjemmetidslinje er tom! Følg nogle personer, for at udfylde den. {suggestions}",
   "empty_column.home.suggestions": "Se nogle forslag",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Godkend",
   "follow_request.reject": "Afvis",
   "follow_requests.unlocked_explanation": "Selvom din konto ikke er låst, antog {domain}-personalet, at du måske vil gennemgå dine anmodninger manuelt.",
+  "followed_tags": "Hashtag, som følges",
   "footer.about": "Om",
   "footer.directory": "Profiloversigt",
   "footer.get_app": "Hent appen",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Tastaturgenveje",
   "footer.privacy_policy": "Fortrolighedspolitik",
   "footer.source_code": "Vis kildekode",
+  "footer.status": "Status",
   "generic.saved": "Gemt",
   "getting_started.heading": "Startmenu",
   "hashtag.column_header.tag_mode.all": "og {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Fokusér kolonne",
   "keyboard_shortcuts.compose": "Fokusér skriveområdet",
   "keyboard_shortcuts.description": "Beskrivelse",
-  "keyboard_shortcuts.direct": "for at åbne direkte beskeder-kolonnen",
+  "keyboard_shortcuts.direct": "for at åbne kolonnen private omtaler",
   "keyboard_shortcuts.down": "Flyt nedad på listen",
   "keyboard_shortcuts.enter": "Åbn indlæg",
   "keyboard_shortcuts.favourite": "Favoritmarkér indlæg",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bogmærker",
   "navigation_bar.community_timeline": "Lokal tidslinje",
   "navigation_bar.compose": "Skriv nyt indlæg",
-  "navigation_bar.direct": "Direkte beskeder",
+  "navigation_bar.direct": "Private omtaler",
   "navigation_bar.discover": "Opdag",
   "navigation_bar.domain_blocks": "Blokerede domæner",
   "navigation_bar.edit_profile": "Redigér profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoritter",
   "navigation_bar.filters": "Skjulte ord (mutede)",
   "navigation_bar.follow_requests": "Følgeanmodninger",
+  "navigation_bar.followed_tags": "Hashtag, som følges",
   "navigation_bar.follows_and_followers": "Følges og følgere",
   "navigation_bar.lists": "Lister",
   "navigation_bar.logout": "Log af",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Regelovertrædelse",
   "report_notification.open": "Åbn anmeldelse",
+  "search.no_recent_searches": "Ingen seneste søgninger",
   "search.placeholder": "Søg",
+  "search.quick_action.account_search": "Profiler matchende {x}",
+  "search.quick_action.go_to_account": "Gå til profilen {x}",
+  "search.quick_action.go_to_hashtag": "Gå til hashtagget {x}",
+  "search.quick_action.open_url": "Åbn URL i Mastodon",
+  "search.quick_action.status_search": "Indlæg matchende {x}",
   "search.search_or_paste": "Søg efter eller angiv URL",
-  "search_popout.search_format": "Avanceret søgeformat",
-  "search_popout.tips.full_text": "Simpel tekst returnerer indlæg, du har skrevet, gjort til favorit, boostet eller er nævnt i, såvel som matchende bruger- og profilnavne samt hashtags.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "indlæg",
-  "search_popout.tips.text": "Simpel tekst returnerer matchende visnings- og brugernavne samt hashtags",
-  "search_popout.tips.user": "bruger",
-  "search_results.accounts": "Personer",
+  "search_popout.quick_actions": "Hurtige handlinger",
+  "search_popout.recent": "Seneste søgninger",
+  "search_results.accounts": "Profiler",
   "search_results.all": "Alle",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Ingen resultater for disse søgeord",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Serverstatstik:",
   "sign_in_banner.create_account": "Opret konto",
   "sign_in_banner.sign_in": "Log ind",
-  "sign_in_banner.text": "Log ind for at følge profiler eller hashtags, markere som favorit, dele og svare på indlæg eller interagere fra din konto på en anden server.",
+  "sign_in_banner.text": "Log ind for at følge profiler eller hashtags, markere som favorit, dele og besvare indlæg eller interagere fra din konto på en anden server.",
   "status.admin_account": "Åbn modereringsbrugerflade for @{name}",
   "status.admin_domain": "Åbn modereringsbrugerflade for {domain}",
   "status.admin_status": "Åbn dette indlæg i modereringsbrugerfladen",
@@ -551,7 +559,8 @@
   "status.copy": "Kopiér link til indlæg",
   "status.delete": "Slet",
   "status.detailed_status": "Detaljeret samtalevisning",
-  "status.direct": "Direkte besked til @{name}",
+  "status.direct": "Privat omtale @{name}",
+  "status.direct_indicator": "Privat omtale",
   "status.edit": "Redigér",
   "status.edited": "Redigeret {date}",
   "status.edited_x_times": "Redigeret {count, plural, one {{count} gang} other {{count} gange}}",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index c43ff56f7..d56cdcd85 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -16,11 +16,11 @@
   "account.badges.bot": "Bot",
   "account.badges.group": "Gruppe",
   "account.block": "@{name} blockieren",
-  "account.block_domain": "Alles von {domain} verstecken",
+  "account.block_domain": "{domain} sperren",
   "account.blocked": "Blockiert",
   "account.browse_more_on_origin_server": "Mehr auf dem Originalprofil durchsuchen",
   "account.cancel_follow_request": "Folgeanfrage zurückziehen",
-  "account.direct": "Direktnachricht an @{name}",
+  "account.direct": "Direktnachricht an @{name} senden",
   "account.disable_notifications": "Höre auf mich zu benachrichtigen wenn @{name} etwas postet",
   "account.domain_blocked": "Domain versteckt",
   "account.edit_profile": "Profil bearbeiten",
@@ -39,7 +39,7 @@
   "account.follows_you": "Folgt dir",
   "account.go_to_profile": "Profil aufrufen",
   "account.hide_reblogs": "Geteilte Beiträge von @{name} verbergen",
-  "account.joined_short": "Beigetreten",
+  "account.joined_short": "Registriert",
   "account.languages": "Genutzte Sprachen überarbeiten",
   "account.link_verified_on": "Das Profil mit dieser E-Mail-Adresse wurde bereits am {date} bestätigt",
   "account.locked_info": "Die Privatsphäre dieses Kontos wurde auf „geschützt“ gesetzt. Die Person bestimmt manuell, wer ihrem Profil folgen darf.",
@@ -53,7 +53,7 @@
   "account.posts": "Beiträge",
   "account.posts_with_replies": "Beiträge und Antworten",
   "account.report": "@{name} melden",
-  "account.requested": "Warte auf Genehmigung. Klicke hier, um die Anfrage zum Folgen abzubrechen",
+  "account.requested": "Die Genehmigung steht noch aus. Klicke hier, um die Folgeanfrage abzubrechen",
   "account.requested_follow": "{name} hat angefragt, dir folgen zu dürfen",
   "account.share": "Profil von @{name} teilen",
   "account.show_reblogs": "Geteilte Beiträge von @{name} wieder anzeigen",
@@ -78,7 +78,7 @@
   "alert.unexpected.title": "Ups!",
   "announcement.announcement": "Ankündigung",
   "attachments_list.unprocessed": "(ausstehend)",
-  "audio.hide": "Audio verbergen",
+  "audio.hide": "Audio ausblenden",
   "autosuggest_hashtag.per_week": "{count} pro Woche",
   "boost_modal.combo": "Mit {combo} wird dieses Fenster beim nächsten Mal nicht mehr angezeigt",
   "bundle_column_error.copy_stacktrace": "Fehlerbericht kopieren",
@@ -106,7 +106,7 @@
   "column.directory": "Profile durchsuchen",
   "column.domain_blocks": "Gesperrte Domains",
   "column.favourites": "Favoriten",
-  "column.follow_requests": "Follower-Anfragen",
+  "column.follow_requests": "Folgeanfragen",
   "column.home": "Startseite",
   "column.lists": "Listen",
   "column.mutes": "Stummgeschaltete Profile",
@@ -114,7 +114,7 @@
   "column.pins": "Angeheftete Beiträge",
   "column.public": "Föderierte Timeline",
   "column_back_button.label": "Zurück",
-  "column_header.hide_settings": "Einstellungen verbergen",
+  "column_header.hide_settings": "Einstellungen ausblenden",
   "column_header.moveLeft_settings": "Diese Spalte nach links verschieben",
   "column_header.moveRight_settings": "Diese Spalte nach rechts verschieben",
   "column_header.pin": "Anheften",
@@ -131,7 +131,7 @@
   "compose_form.hashtag_warning": "Dieser Beitrag wird unter keinem Hashtag sichtbar sein, weil er nicht öffentlich ist. Nur öffentliche Beiträge können nach Hashtags durchsucht werden.",
   "compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Andere können dir folgen und deine Beiträge sehen, die nur für Follower bestimmt sind.",
   "compose_form.lock_disclaimer.lock": "geschützt",
-  "compose_form.placeholder": "Was gibt's Neues?",
+  "compose_form.placeholder": "Was gibt es Neues?",
   "compose_form.poll.add_option": "Auswahlfeld hinzufügen",
   "compose_form.poll.duration": "Umfragedauer",
   "compose_form.poll.option_placeholder": "{number}. Auswahl",
@@ -162,8 +162,10 @@
   "confirmations.discard_edit_media.message": "Du hast Änderungen an der Medienbeschreibung oder -vorschau vorgenommen, die noch nicht gespeichert sind. Trotzdem verwerfen?",
   "confirmations.domain_block.confirm": "Domain sperren",
   "confirmations.domain_block.message": "Bist du dir wirklich sicher, dass du die ganze Domain {domain} sperren willst? In den meisten Fällen reichen ein paar gezielte Sperren oder Stummschaltungen aus. Du wirst den Inhalt von dieser Domain nicht in irgendwelchen öffentlichen Timelines oder den Benachrichtigungen finden. Auch deine Follower von dieser Domain werden entfernt.",
+  "confirmations.edit.confirm": "Bearbeiten",
+  "confirmations.edit.message": "Das Bearbeiten überschreibt die Nachricht, die du gerade verfasst. Bist du dir sicher, dass du fortfahren möchtest?",
   "confirmations.logout.confirm": "Abmelden",
-  "confirmations.logout.message": "Bist du sicher, dass du dich abmelden möchtest?",
+  "confirmations.logout.message": "Bist du dir sicher, dass du dich abmelden möchtest?",
   "confirmations.mute.confirm": "Stummschalten",
   "confirmations.mute.explanation": "Dies wird Beiträge von dieser Person und Beiträge, die diese Person erwähnen, ausblenden, aber es wird der Person trotzdem erlauben, deine Beiträge zu sehen und dir zu folgen.",
   "confirmations.mute.message": "Bist du dir sicher, dass du {name} stummschalten möchtest?",
@@ -220,7 +222,8 @@
   "empty_column.favourited_statuses": "Du hast noch keine Beiträge favorisiert. Sobald 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_recommendations": "Es sieht so aus, als könnten keine Vorschläge für dich generiert werden. Du kannst versuchen, nach Leuten zu suchen, die du vielleicht kennst, oder du kannst angesagte Hashtags erkunden.",
-  "empty_column.follow_requests": "Es liegen derzeit keine Follower-Anfragen vor. Sobald du eine erhältst, wird sie hier angezeigt.",
+  "empty_column.follow_requests": "Es liegen derzeit keine Folgeanfragen vor. Sobald du eine erhältst, wird sie hier angezeigt.",
+  "empty_column.followed_tags": "Du folgst noch keinen Hashtags. Wenn du dies tust, werden sie hier erscheinen.",
   "empty_column.hashtag": "Unter diesem Hashtag gibt es noch nichts.",
   "empty_column.home": "Die Timeline deiner Startseite ist leer! Folge mehr Leuten, um sie zu füllen. {suggestions}",
   "empty_column.home.suggestions": "Ein paar Vorschläge ansehen",
@@ -246,7 +249,7 @@
   "filter_modal.added.expired_explanation": "Diese Filterkategorie ist abgelaufen. Du musst das Ablaufdatum für diese Kategorie ändern.",
   "filter_modal.added.expired_title": "Abgelaufener Filter!",
   "filter_modal.added.review_and_configure": "Um diesen Filter zu überprüfen oder noch weiter zu konfigurieren, rufe die {settings_link} auf.",
-  "filter_modal.added.review_and_configure_title": "Filtereinstellungen",
+  "filter_modal.added.review_and_configure_title": "Filter-Einstellungen",
   "filter_modal.added.settings_link": "Einstellungen",
   "filter_modal.added.short_explanation": "Dieser Post wurde folgender Filterkategorie hinzugefügt: {title}.",
   "filter_modal.added.title": "Filter hinzugefügt!",
@@ -259,10 +262,11 @@
   "filter_modal.title.status": "Beitrag per Filter ausblenden",
   "follow_recommendations.done": "Fertig",
   "follow_recommendations.heading": "Folge Leuten, deren Beiträge du sehen möchtest! Hier sind einige Vorschläge.",
-  "follow_recommendations.lead": "Beiträge von Personen, denen du folgst, werden in chronologischer Reihenfolge auf deiner Startseite angezeigt. Hab keine Angst, Fehler zu machen, du kannst den Leuten jederzeit wieder entfolgen!",
+  "follow_recommendations.lead": "Beiträge von Profilen, denen du folgst, werden in chronologischer Reihenfolge auf deiner Startseite angezeigt. Sei unbesorgt, mal Fehler zu begehen. Du kannst diesen Konten ganz einfach und jederzeit wieder entfolgen.",
   "follow_request.authorize": "Genehmigen",
   "follow_request.reject": "Ablehnen",
   "follow_requests.unlocked_explanation": "Auch wenn dein Konto öffentlich bzw. nicht geschützt ist, haben die Moderator*innen von {domain} gedacht, dass du diesen Follower lieber manuell bestätigen solltest.",
+  "followed_tags": "Gefolgte Hashtags",
   "footer.about": "Über",
   "footer.directory": "Profilverzeichnis",
   "footer.get_app": "App herunterladen",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Tastenkombinationen",
   "footer.privacy_policy": "Datenschutzerklärung",
   "footer.source_code": "Quellcode anzeigen",
+  "footer.status": "Status",
   "generic.saved": "Gespeichert",
   "getting_started.heading": "Auf geht's!",
   "hashtag.column_header.tag_mode.all": "und {additional}",
@@ -277,16 +282,16 @@
   "hashtag.column_header.tag_mode.none": "ohne {additional}",
   "hashtag.column_settings.select.no_options_message": "Keine Vorschläge gefunden",
   "hashtag.column_settings.select.placeholder": "Hashtags eingeben …",
-  "hashtag.column_settings.tag_mode.all": "All diese",
+  "hashtag.column_settings.tag_mode.all": "Alle",
   "hashtag.column_settings.tag_mode.any": "Eines von diesen",
   "hashtag.column_settings.tag_mode.none": "Keines von diesen",
-  "hashtag.column_settings.tag_toggle": "Zusätzliche Hashtags für diese Spalte einfügen",
+  "hashtag.column_settings.tag_toggle": "Zusätzliche Hashtags dieser Spalte hinzufügen",
   "hashtag.follow": "Hashtag folgen",
   "hashtag.unfollow": "Hashtag entfolgen",
   "home.column_settings.basic": "Einfach",
   "home.column_settings.show_reblogs": "Geteilte Beiträge anzeigen",
   "home.column_settings.show_replies": "Antworten anzeigen",
-  "home.hide_announcements": "Ankündigungen verbergen",
+  "home.hide_announcements": "Ankündigungen ausblenden",
   "home.show_announcements": "Ankündigungen anzeigen",
   "interaction_modal.description.favourite": "Mit einem Mastodon-Konto kannst du diesen Beitrag favorisieren, um deine Wertschätzung auszudrücken, und ihn für einen späteren Zeitpunkt speichern.",
   "interaction_modal.description.follow": "Mit einem Mastodon-Konto kannst du {name} folgen, um die Beiträge auf deiner Startseite zu sehen.",
@@ -306,10 +311,10 @@
   "keyboard_shortcuts.back": "zurücknavigieren",
   "keyboard_shortcuts.blocked": "Liste gesperrter Profile öffnen",
   "keyboard_shortcuts.boost": "Beitrag teilen",
-  "keyboard_shortcuts.column": "Spalte fokussieren",
+  "keyboard_shortcuts.column": "Auf die aktuelle Spalte fokussieren",
   "keyboard_shortcuts.compose": "Eingabefeld fokussieren",
   "keyboard_shortcuts.description": "Beschreibung",
-  "keyboard_shortcuts.direct": "Direktnachrichten öffnen",
+  "keyboard_shortcuts.direct": "um die Direktnachrichtenspalte zu öffnen",
   "keyboard_shortcuts.down": "sich in der Liste nach unten bewegen",
   "keyboard_shortcuts.enter": "Beitrag öffnen",
   "keyboard_shortcuts.favourite": "favorisieren",
@@ -327,19 +332,19 @@
   "keyboard_shortcuts.open_media": "Medien-Datei öffnen",
   "keyboard_shortcuts.pinned": "Liste angehefteter Beiträge öffnen",
   "keyboard_shortcuts.profile": "Profil öffnen",
-  "keyboard_shortcuts.reply": "auf Beitrag antworten",
-  "keyboard_shortcuts.requests": "Liste der Follower-Anfragen öffnen",
+  "keyboard_shortcuts.reply": "Auf Beitrag antworten",
+  "keyboard_shortcuts.requests": "Liste der Folgeanfragen öffnen",
   "keyboard_shortcuts.search": "Suchleiste fokussieren",
-  "keyboard_shortcuts.spoilers": "Inhaltswarnung anzeigen/verbergen",
+  "keyboard_shortcuts.spoilers": "Inhaltswarnung anzeigen/ausblenden",
   "keyboard_shortcuts.start": "„Erste Schritte“-Spalte öffnen",
-  "keyboard_shortcuts.toggle_hidden": "Beitragstext hinter der Inhaltswarnung anzeigen/verbergen",
+  "keyboard_shortcuts.toggle_hidden": "Beitragstext hinter der Inhaltswarnung anzeigen/ausblenden",
   "keyboard_shortcuts.toggle_sensitivity": "Medien anzeigen/verbergen",
   "keyboard_shortcuts.toot": "neuen Beitrag erstellen",
   "keyboard_shortcuts.unfocus": "Eingabefeld/Suche nicht mehr fokussieren",
   "keyboard_shortcuts.up": "sich in der Liste hinaufbewegen",
   "lightbox.close": "Schließen",
-  "lightbox.compress": "Bildansicht komprimieren",
-  "lightbox.expand": "Bildansicht erweitern",
+  "lightbox.compress": "Bildansicht verkleinern",
+  "lightbox.expand": "Bildansicht vergrößern",
   "lightbox.next": "Vor",
   "lightbox.previous": "Zurück",
   "limited_account_hint.action": "Profil trotzdem anzeigen",
@@ -359,12 +364,12 @@
   "lists.subheading": "Deine Listen",
   "load_pending": "{count, plural, one {# neuer Beitrag} other {# neue Beiträge}}",
   "loading_indicator.label": "Wird geladen …",
-  "media_gallery.toggle_visible": "{number, plural, one {Bild verbergen} other {Bilder verbergen}}",
+  "media_gallery.toggle_visible": "{number, plural, one {Medium ausblenden} other {Medien ausblenden}}",
   "missing_indicator.label": "Nicht gefunden",
   "missing_indicator.sublabel": "Der Inhalt konnte nicht gefunden werden",
   "moved_to_account_banner.text": "Dein Konto {disabledAccount} ist derzeit deaktiviert, weil du zu {movedToAccount} umgezogen bist.",
   "mute_modal.duration": "Dauer",
-  "mute_modal.hide_notifications": "Benachrichtigungen dieses Profils verbergen?",
+  "mute_modal.hide_notifications": "Benachrichtigungen dieses Profils ausblenden?",
   "mute_modal.indefinite": "Unbegrenzt",
   "navigation_bar.about": "Über",
   "navigation_bar.blocks": "Gesperrte Profile",
@@ -378,8 +383,9 @@
   "navigation_bar.explore": "Entdecken",
   "navigation_bar.favourites": "Favoriten",
   "navigation_bar.filters": "Stummgeschaltete Wörter",
-  "navigation_bar.follow_requests": "Follower-Anfragen",
-  "navigation_bar.follows_and_followers": "Folge ich und Follower",
+  "navigation_bar.follow_requests": "Folgeanfragen",
+  "navigation_bar.followed_tags": "Gefolgte Hashtags",
+  "navigation_bar.follows_and_followers": "Follower und Folge ich",
   "navigation_bar.lists": "Listen",
   "navigation_bar.logout": "Abmelden",
   "navigation_bar.mutes": "Stummgeschaltete Profile",
@@ -390,7 +396,7 @@
   "navigation_bar.search": "Suche",
   "navigation_bar.security": "Sicherheit",
   "not_signed_in_indicator.not_signed_in": "Du musst dich anmelden, um auf diesen Inhalt zugreifen zu können.",
-  "notification.admin.report": "{target} wurde von {name} gemeldet",
+  "notification.admin.report": "{name} meldete {target}",
   "notification.admin.sign_up": "{name} registrierte sich",
   "notification.favourite": "{name} hat deinen Beitrag favorisiert",
   "notification.follow": "{name} folgt dir jetzt",
@@ -399,7 +405,7 @@
   "notification.own_poll": "Deine Umfrage ist beendet",
   "notification.poll": "Eine Umfrage, an der du teilgenommen hast, ist beendet",
   "notification.reblog": "{name} teilte deinen Beitrag",
-  "notification.status": "{name} hat etwas mitgeteilt",
+  "notification.status": "{name} veröffentlichte gerade",
   "notification.update": "{name} bearbeitete einen Beitrag",
   "notifications.clear": "Mitteilungen löschen",
   "notifications.clear_confirmation": "Bist du dir sicher, dass du diese Mitteilungen für immer löschen möchtest?",
@@ -411,7 +417,7 @@
   "notifications.column_settings.filter_bar.category": "Filterleiste:",
   "notifications.column_settings.filter_bar.show_bar": "Filterleiste anzeigen",
   "notifications.column_settings.follow": "Neue Follower:",
-  "notifications.column_settings.follow_request": "Neue Follower-Anfragen:",
+  "notifications.column_settings.follow_request": "Neue Folgeanfragen:",
   "notifications.column_settings.mention": "Erwähnungen:",
   "notifications.column_settings.poll": "Umfrageergebnisse:",
   "notifications.column_settings.push": "Push-Benachrichtigungen",
@@ -437,7 +443,7 @@
   "notifications.permission_required": "Desktop-Benachrichtigungen sind nicht verfügbar, da die erforderliche Berechtigung nicht erteilt wurde.",
   "notifications_permission_banner.enable": "Aktiviere Desktop-Benachrichtigungen",
   "notifications_permission_banner.how_to_control": "Um Benachrichtigungen zu erhalten, wenn Mastodon nicht geöffnet ist, aktiviere die Desktop-Benachrichtigungen. Du kannst genau bestimmen, welche Arten von Interaktionen Desktop-Benachrichtigungen über die {icon} -Taste erzeugen, sobald diese aktiviert sind.",
-  "notifications_permission_banner.title": "Verpasse nie etwas",
+  "notifications_permission_banner.title": "Nichts verpassen",
   "picture_in_picture.restore": "Zurücksetzen",
   "poll.closed": "Beendet",
   "poll.refresh": "Aktualisieren",
@@ -448,12 +454,12 @@
   "poll.votes": "{votes, plural, one {# Stimme} other {# Stimmen}}",
   "poll_button.add_poll": "Umfrage erstellen",
   "poll_button.remove_poll": "Umfrage entfernen",
-  "privacy.change": "Sichtbarkeit des Beitrags anpassen",
+  "privacy.change": "Sichtbarkeit anpassen",
   "privacy.direct.long": "Nur für die genannten Profile sichtbar",
   "privacy.direct.short": "Nur erwähnte Profile",
   "privacy.private.long": "Nur für deine Follower sichtbar",
   "privacy.private.short": "Nur Follower",
-  "privacy.public.long": "Für alle sichtbar",
+  "privacy.public.long": "Für alle sichtbar, auch für nicht-registrierte bzw. nicht-angemeldete Nutzer*innen",
   "privacy.public.short": "Öffentlich",
   "privacy.unlisted.long": "Sichtbar für alle, aber nicht über Suchfunktion",
   "privacy.unlisted.short": "Nicht gelistet",
@@ -462,16 +468,16 @@
   "refresh": "Aktualisieren",
   "regeneration_indicator.label": "Wird geladen …",
   "regeneration_indicator.sublabel": "Deine Startseite wird gerade vorbereitet!",
-  "relative_time.days": "{number}T",
+  "relative_time.days": "{number} T.",
   "relative_time.full.days": "vor {number, plural, one {# Tag} other {# Tagen}}",
   "relative_time.full.hours": "vor {number, plural, one {# Stunde} other {# Stunden}}",
   "relative_time.full.just_now": "soeben",
   "relative_time.full.minutes": "vor {number, plural, one {# Minute} other {# Minuten}}",
   "relative_time.full.seconds": "vor {number, plural, one {1 Sekunde} other {# Sekunden}}",
-  "relative_time.hours": "{number} Std",
+  "relative_time.hours": "{number} Std.",
   "relative_time.just_now": "jetzt",
-  "relative_time.minutes": "{number} min",
-  "relative_time.seconds": "{number} sek",
+  "relative_time.minutes": "{number} Min.",
+  "relative_time.seconds": "{number} Sek.",
   "relative_time.today": "heute",
   "reply_indicator.cancel": "Abbrechen",
   "report.block": "Sperren",
@@ -485,7 +491,7 @@
   "report.category.title_status": "Beitrag",
   "report.close": "Fertig",
   "report.comment.title": "Gibt es etwas anderes, was wir wissen sollten?",
-  "report.forward": "An {target} weiterleiten",
+  "report.forward": "Meldung zusätzlich an {target} weiterleiten",
   "report.forward_hint": "Dieses Konto gehört zu einem anderen Server. Soll eine anonymisierte Kopie der Meldung auch dorthin geschickt werden?",
   "report.mute": "Stummschalten",
   "report.mute_explanation": "Du wirst die Beiträge vom Konto nicht mehr sehen. Das Konto kann dir immer noch folgen, und die Person hinter dem Konto wird deine Beiträge sehen können und nicht wissen, dass du sie stummgeschaltet hast.",
@@ -497,18 +503,18 @@
   "report.reasons.other_description": "Der Vorfall passt zu keiner dieser Kategorien",
   "report.reasons.spam": "Das ist Spam",
   "report.reasons.spam_description": "Bösartige Links, gefälschtes Engagement oder wiederholte Antworten",
-  "report.reasons.violation": "Es verstößt gegen Serverregeln",
-  "report.reasons.violation_description": "Du bist dir bewusst, dass es gegen bestimmte Regeln verstößt",
+  "report.reasons.violation": "Er verstößt gegen Serverregeln",
+  "report.reasons.violation_description": "Du bist dir sicher, dass eine bestimmte Regel gebrochen wurde",
   "report.rules.subtitle": "Wähle alle zutreffenden Inhalte aus",
   "report.rules.title": "Welche Regeln werden verletzt?",
   "report.statuses.subtitle": "Wähle alle zutreffenden Inhalte aus",
-  "report.statuses.title": "Gibt es Beiträge, die diesen Bericht unterstützen?",
+  "report.statuses.title": "Gibt es Beiträge, die diesen Bericht untermauern?",
   "report.submit": "Abschicken",
   "report.target": "{target} melden",
   "report.thanks.take_action": "Das sind deine Möglichkeiten zu bestimmen, was du auf Mastodon sehen möchtest:",
   "report.thanks.take_action_actionable": "Während wir den Vorfall überprüfen, kannst du gegen @{name} weitere Maßnahmen ergreifen:",
   "report.thanks.title": "Möchtest du das nicht mehr sehen?",
-  "report.thanks.title_actionable": "Vielen Dank für die Meldung, wir werden uns das ansehen.",
+  "report.thanks.title_actionable": "Vielen Dank für die Meldung! Wir werden uns das ansehen.",
   "report.unfollow": "@{name} entfolgen",
   "report.unfollow_explanation": "Du folgst diesem Konto. Um die Beiträge nicht mehr auf deiner Startseite zu sehen, entfolge dem Konto.",
   "report_notification.attached_statuses": "{count, plural, one {{count} angehangener Beitrag} other {{count} angehängte Beiträge}}",
@@ -516,18 +522,20 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Regelverstoß",
   "report_notification.open": "Meldung öffnen",
+  "search.no_recent_searches": "Keine kürzlichen Suchanfragen",
   "search.placeholder": "Suche",
+  "search.quick_action.account_search": "Profile passend zu {x}",
+  "search.quick_action.go_to_account": "Profil {x} aufrufen",
+  "search.quick_action.go_to_hashtag": "Hashtag {x} aufrufen",
+  "search.quick_action.open_url": "URL in Mastodon öffnen",
+  "search.quick_action.status_search": "Beiträge passend zu {x}",
   "search.search_or_paste": "Suchen oder URL einfügen",
-  "search_popout.search_format": "Erweiterte Suche",
-  "search_popout.tips.full_text": "Einfache Texteingabe gibt Beiträge, die du geschrieben, favorisiert und geteilt hast, zurück; außerdem auch Beiträge, in denen du erwähnt wurdest, aber auch passende Nutzernamen, Anzeigenamen oder Hashtags.",
-  "search_popout.tips.hashtag": "Hashtag",
-  "search_popout.tips.status": "Beitrag",
-  "search_popout.tips.text": "Einfache Texteingabe gibt Anzeigenamen, Profilnamen und Hashtags zurück",
-  "search_popout.tips.user": "Profil",
+  "search_popout.quick_actions": "Schnellaktionen",
+  "search_popout.recent": "Kürzliche Suchanfragen",
   "search_results.accounts": "Profile",
   "search_results.all": "Alles",
   "search_results.hashtags": "Hashtags",
-  "search_results.nothing_found": "Nichts für diese Suchbegriffe gefunden",
+  "search_results.nothing_found": "Nichts zu diesen Suchbegriffen gefunden",
   "search_results.statuses": "Beiträge",
   "search_results.statuses_fts_disabled": "Die Suche nach Beitragsinhalten ist auf diesem Mastodon-Server deaktiviert.",
   "search_results.title": "Suchergebnisse für {q}",
@@ -540,10 +548,10 @@
   "server_banner.server_stats": "Serverstatistiken:",
   "sign_in_banner.create_account": "Konto erstellen",
   "sign_in_banner.sign_in": "Anmelden",
-  "sign_in_banner.text": "Melde dich an, um Profilen oder Hashtags zu folgen, Beiträge zu favorisieren, zu teilen und auf sie zu antworten oder um von deinem Konto aus auf einem anderen Server zu interagieren.",
-  "status.admin_account": "Moderationsoberfläche für @{name} öffnen",
-  "status.admin_domain": "Moderationsoberfläche für {domain} öffnen",
-  "status.admin_status": "Diesen Beitrag in der Moderationsoberfläche öffnen",
+  "sign_in_banner.text": "Melde dich an, um Profilen oder Hashtags zu folgen, Beiträge zu favorisieren, zu teilen und auf sie zu antworten. Du kannst auch von deinem Konto aus auf einem anderen Server interagieren.",
+  "status.admin_account": "@{name} moderieren",
+  "status.admin_domain": "{domain} moderieren",
+  "status.admin_status": "Beitrag moderieren",
   "status.block": "@{name} blockieren",
   "status.bookmark": "Beitrag als Lesezeichen setzen",
   "status.cancel_reblog_private": "Teilen des Beitrags rückgängig machen",
@@ -552,6 +560,7 @@
   "status.delete": "Beitrag löschen",
   "status.detailed_status": "Detaillierte Ansicht der Unterhaltung",
   "status.direct": "Direktnachricht an @{name}",
+  "status.direct_indicator": "Direktnachricht",
   "status.edit": "Beitrag bearbeiten",
   "status.edited": "Bearbeitet {date}",
   "status.edited_x_times": "{count, plural, one {{count} mal} other {{count} mal}} bearbeitet",
@@ -563,7 +572,7 @@
   "status.history.created": "{name} erstellte {date}",
   "status.history.edited": "{name} bearbeitete {date}",
   "status.load_more": "Weitere laden",
-  "status.media_hidden": "Medien versteckt",
+  "status.media_hidden": "Inhalt verborgen",
   "status.mention": "@{name} im Beitrag erwähnen",
   "status.more": "Mehr",
   "status.mute": "@{name} stummschalten",
@@ -592,7 +601,7 @@
   "status.show_original": "Ursprünglichen Beitrag anzeigen",
   "status.translate": "Übersetzen",
   "status.translated_from_with": "Aus {lang} mittels {provider} übersetzt",
-  "status.uncached_media_warning": "Nicht verfügbar",
+  "status.uncached_media_warning": "Medien-Datei auf diesem Server noch nicht verfügbar",
   "status.unmute_conversation": "Stummschaltung der Unterhaltung aufheben",
   "status.unpin": "Vom Profil lösen",
   "subscribed_languages.lead": "Nach der Änderung werden nur noch Beiträge in den ausgewählten Sprachen in den Timelines deiner Startseite und deiner Listen angezeigt. Wähle keine Sprache aus, um alle Beiträge zu sehen.",
@@ -616,14 +625,14 @@
   "trends.counter_by_accounts": "{count, plural, one {{counter} Profil} other {{counter} Profile}} {days, plural, one {seit gestern} other {in {days} Tagen}}",
   "trends.trending_now": "Aktuelle Trends",
   "ui.beforeunload": "Dein Entwurf geht verloren, wenn du Mastodon verlässt.",
-  "units.short.billion": "{count} Mrd",
-  "units.short.million": "{count} Mio",
-  "units.short.thousand": "{count} Tsd",
+  "units.short.billion": "{count} Mrd.",
+  "units.short.million": "{count} Mio.",
+  "units.short.thousand": "{count} Tsd.",
   "upload_area.title": "Zum Hochladen hereinziehen",
   "upload_button.label": "Bilder, Videos oder Audios hinzufügen",
   "upload_error.limit": "Dateiupload-Limit überschritten.",
   "upload_error.poll": "Medien-Anhänge sind zusammen mit Umfragen nicht erlaubt.",
-  "upload_form.audio_description": "Für Gehörlose und hörbehinderte Menschen beschreiben",
+  "upload_form.audio_description": "Beschreibe für Menschen mit Hörbehinderung",
   "upload_form.description": "Beschreibe für Menschen mit Sehbehinderung",
   "upload_form.description_missing": "Keine Beschreibung hinzugefügt",
   "upload_form.edit": "Beschreiben",
@@ -643,11 +652,11 @@
   "upload_progress.label": "Wird hochgeladen …",
   "upload_progress.processing": "Wird verarbeitet…",
   "video.close": "Video schließen",
-  "video.download": "Datei herunterladen",
+  "video.download": "Video-Datei herunterladen",
   "video.exit_fullscreen": "Vollbild verlassen",
   "video.expand": "Video vergrößern",
   "video.fullscreen": "Vollbild",
-  "video.hide": "Video verbergen",
+  "video.hide": "Video ausblenden",
   "video.mute": "Stummschalten",
   "video.pause": "Pausieren",
   "video.play": "Abspielen",
diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json
index 6f3c5e1a0..fb6181e1a 100644
--- a/app/javascript/mastodon/locales/defaultMessages.json
+++ b/app/javascript/mastodon/locales/defaultMessages.json
@@ -560,7 +560,7 @@
         "id": "status.edit"
       },
       {
-        "defaultMessage": "Direct message @{name}",
+        "defaultMessage": "Privately mention @{name}",
         "id": "status.direct"
       },
       {
@@ -762,6 +762,10 @@
         "id": "status.reblogged_by"
       },
       {
+        "defaultMessage": "Private mention",
+        "id": "status.direct_indicator"
+      },
+      {
         "defaultMessage": "Replied to {name}",
         "id": "status.replied_to"
       }
@@ -834,6 +838,14 @@
         "id": "confirmations.reply.message"
       },
       {
+        "defaultMessage": "Edit",
+        "id": "confirmations.edit.confirm"
+      },
+      {
+        "defaultMessage": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
+        "id": "confirmations.edit.message"
+      },
+      {
         "defaultMessage": "Hide entire domain",
         "id": "confirmations.domain_block.confirm"
       },
@@ -1101,7 +1113,7 @@
         "id": "account.mention"
       },
       {
-        "defaultMessage": "Direct message @{name}",
+        "defaultMessage": "Privately mention @{name}",
         "id": "account.direct"
       },
       {
@@ -1173,6 +1185,10 @@
         "id": "navigation_bar.lists"
       },
       {
+        "defaultMessage": "Followed hashtags",
+        "id": "navigation_bar.followed_tags"
+      },
+      {
         "defaultMessage": "Blocked users",
         "id": "navigation_bar.blocks"
       },
@@ -1392,6 +1408,10 @@
         "id": "navigation_bar.lists"
       },
       {
+        "defaultMessage": "Followed hashtags",
+        "id": "navigation_bar.followed_tags"
+      },
+      {
         "defaultMessage": "Blocked users",
         "id": "navigation_bar.blocks"
       },
@@ -1645,7 +1665,7 @@
         "id": "suggestions.header"
       },
       {
-        "defaultMessage": "People",
+        "defaultMessage": "Profiles",
         "id": "search_results.accounts"
       },
       {
@@ -1678,28 +1698,36 @@
         "id": "search.search_or_paste"
       },
       {
-        "defaultMessage": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
-        "id": "search_popout.tips.full_text"
+        "defaultMessage": "Open URL in Mastodon",
+        "id": "search.quick_action.open_url"
       },
       {
-        "defaultMessage": "Simple text returns matching display names, usernames and hashtags",
-        "id": "search_popout.tips.text"
+        "defaultMessage": "Go to hashtag {x}",
+        "id": "search.quick_action.go_to_hashtag"
       },
       {
-        "defaultMessage": "Advanced search format",
-        "id": "search_popout.search_format"
+        "defaultMessage": "Go to profile {x}",
+        "id": "search.quick_action.go_to_account"
       },
       {
-        "defaultMessage": "hashtag",
-        "id": "search_popout.tips.hashtag"
+        "defaultMessage": "Posts matching {x}",
+        "id": "search.quick_action.status_search"
       },
       {
-        "defaultMessage": "user",
-        "id": "search_popout.tips.user"
+        "defaultMessage": "Profiles matching {x}",
+        "id": "search.quick_action.account_search"
       },
       {
-        "defaultMessage": "status",
-        "id": "search_popout.tips.status"
+        "defaultMessage": "Recent searches",
+        "id": "search_popout.recent"
+      },
+      {
+        "defaultMessage": "No recent searches",
+        "id": "search.no_recent_searches"
+      },
+      {
+        "defaultMessage": "Quick actions",
+        "id": "search_popout.quick_actions"
       }
     ],
     "path": "app/javascript/mastodon/features/compose/components/search.json"
@@ -1909,7 +1937,7 @@
   {
     "descriptors": [
       {
-        "defaultMessage": "Direct messages",
+        "defaultMessage": "Private mentions",
         "id": "column.direct"
       },
       {
@@ -1921,7 +1949,7 @@
         "id": "compose_form.direct_message_warning_learn_more"
       },
       {
-        "defaultMessage": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+        "defaultMessage": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
         "id": "empty_column.direct"
       }
     ],
@@ -2087,7 +2115,7 @@
         "id": "search_results.all"
       },
       {
-        "defaultMessage": "People",
+        "defaultMessage": "Profiles",
         "id": "search_results.accounts"
       },
       {
@@ -2307,6 +2335,19 @@
   {
     "descriptors": [
       {
+        "defaultMessage": "Followed hashtags",
+        "id": "followed_tags"
+      },
+      {
+        "defaultMessage": "You have not followed any hashtags yet. When you do, they will show up here.",
+        "id": "empty_column.followed_tags"
+      }
+    ],
+    "path": "app/javascript/mastodon/features/followed_tags/index.json"
+  },
+  {
+    "descriptors": [
+      {
         "defaultMessage": "Followers",
         "id": "timeline_hint.resources.followers"
       },
@@ -2403,7 +2444,7 @@
         "id": "navigation_bar.explore"
       },
       {
-        "defaultMessage": "Direct messages",
+        "defaultMessage": "Private mentions",
         "id": "navigation_bar.direct"
       },
       {
@@ -3521,7 +3562,7 @@
         "id": "status.edit"
       },
       {
-        "defaultMessage": "Direct message @{name}",
+        "defaultMessage": "Privately mention @{name}",
         "id": "status.direct"
       },
       {
@@ -3661,6 +3702,10 @@
       {
         "defaultMessage": "Direct",
         "id": "privacy.direct.short"
+      },
+      {
+        "defaultMessage": "Private mention",
+        "id": "status.direct_indicator"
       }
     ],
     "path": "app/javascript/mastodon/features/status/components/detailed_status.json"
@@ -4055,6 +4100,10 @@
         "id": "footer.about"
       },
       {
+        "defaultMessage": "Status",
+        "id": "footer.status"
+      },
+      {
         "defaultMessage": "Invite people",
         "id": "footer.invite"
       },
@@ -4166,7 +4215,7 @@
         "id": "tabs_bar.federated_timeline"
       },
       {
-        "defaultMessage": "Direct messages",
+        "defaultMessage": "Private mentions",
         "id": "navigation_bar.direct"
       },
       {
@@ -4220,7 +4269,7 @@
         "id": "sign_in_banner.create_account"
       },
       {
-        "defaultMessage": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+        "defaultMessage": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
         "id": "sign_in_banner.text"
       },
       {
diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json
index ad88129e9..cefc86853 100644
--- a/app/javascript/mastodon/locales/el.json
+++ b/app/javascript/mastodon/locales/el.json
@@ -1,14 +1,14 @@
 {
-  "about.blocks": "Κανένας πρόσφατος διακομιστής",
+  "about.blocks": "Συντονισμένοι διακομιστές",
   "about.contact": "Επικοινωνία:",
   "about.disclaimer": "Το Mastodon είναι ελεύθερο λογισμικό ανοιχτού κώδικα και εμπορικό σήμα της Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Αιτιολογία μη διαθέσιμη",
-  "about.domain_blocks.preamble": "Σε γενικές γραμμές το Mastodon σού επιτρέπει να βλέπεις περιεχόμενο και να αλληλεπιδράς με χρήστες από οποιονδήποτε άλλο server σε ένα διασυνδεδεμένο σύμπαν διακομιστών (fediverse). Ακολουθούν οι εξαιρέσεις που ισχύουν για τον συγκεκριμένο server.",
-  "about.domain_blocks.silenced.explanation": "Γενικά δεν μπορείς να δεις τα προφίλ και το περιεχόμενο αυτού του server, εκτός αν κάνεις μια στοχευμένη αναζήτηση ή επιλέξεις να τον ακολουθήσεις.",
-  "about.domain_blocks.silenced.title": "Η μετάφραση είναι ανοιχτή μόνο σε περιορισμένη ομάδα μεταφραστών, αν θέλετε να συνεισφέρετε, επικοινωνήστε με τους συντηρητές των έργων.",
-  "about.domain_blocks.suspended.explanation": "Κανένα δεδομένο αυτού του server δεν θα τυγχάνει επεξεργασίας, ούτε θα αποθηκεύεται, ούτε θα ανταλλάσεται, οπότε η οποιαδήποτε αλληλεπίδραση ή επικοινωνία με χρήστες αυτού του server είναι αδύνατη.",
+  "about.domain_blocks.preamble": "Σε γενικές γραμμές το Mastodon σού επιτρέπει να βλέπεις περιεχόμενο και να αλληλεπιδράς με χρήστες από οποιονδήποτε άλλο διακομιστή σε ένα διασυνδεδεμένο σύμπαν διακομιστών (fediverse). Ακολουθούν οι εξαιρέσεις που ισχύουν για τον συγκεκριμένο διακομιστή.",
+  "about.domain_blocks.silenced.explanation": "Συνήθως σε θα βλέπεις προφίλ και περιεχόμενο απ'αυτόν τον διακομιστή, εκτός αν κάνεις συγκεκριμένη αναζήτηση ή επιλέξεις να τον ακολουθήσεις.",
+  "about.domain_blocks.silenced.title": "Περιορισμένος",
+  "about.domain_blocks.suspended.explanation": "Τα δεδομένα αυτού του διακομιστή, δε θα επεξεργάζονται, δε θα αποθηκεύονται και δε θα ανταλλάσσονται, καθιστώντας οποιαδήποτε αλληλεπίδραση ή επικοινωνία με χρήστες από αυτόν το διακομιστή αδύνατη.",
   "about.domain_blocks.suspended.title": "Σε αναστολή",
-  "about.not_available": "Αυτές οι πληροφορίες δεν έχουν καταστεί διαθέσιμες σε αυτόν τον server.",
+  "about.not_available": "Αυτές οι πληροφορίες δεν έχουν είναι διαθέσιμες σε αυτόν τον διακομιστή.",
   "about.powered_by": "Αποκεντρωμένα μέσα κοινωνικής δικτύωσης που βασίζονται στο {mastodon}",
   "about.rules": "Κανόνες διακομιστή",
   "account.account_note_header": "Σημείωση",
@@ -16,37 +16,37 @@
   "account.badges.bot": "Μποτ",
   "account.badges.group": "Ομάδα",
   "account.block": "Αποκλεισμός @{name}",
-  "account.block_domain": "Αποκλεισμός {domain}",
+  "account.block_domain": "Αποκλεισμός τομέα {domain}",
   "account.blocked": "Αποκλεισμένος/η",
   "account.browse_more_on_origin_server": "Δες περισσότερα στο αρχικό προφίλ",
   "account.cancel_follow_request": "Απόσυρση αιτήματος παρακολούθησης",
-  "account.direct": "Άμεσο μήνυμα προς @{name}",
+  "account.direct": "Ιδιωτική αναφορά @{name}",
   "account.disable_notifications": "Διακοπή ειδοποιήσεων για τις δημοσιεύσεις του/της @{name}",
   "account.domain_blocked": "Ο τομέας αποκλείστηκε",
   "account.edit_profile": "Επεξεργασία προφίλ",
-  "account.enable_notifications": "Έναρξη ειδοποιήσεων για τις δημοσιεύσεις του/της @{name}",
+  "account.enable_notifications": "Ειδοποιήστε με όταν δημοσιεύει ο @{name}",
   "account.endorse": "Προβολή στο προφίλ",
-  "account.featured_tags.last_status_at": "Τελευταία δημοσίευση στις {date}",
-  "account.featured_tags.last_status_never": "Καμία Ανάρτηση",
-  "account.featured_tags.title": "προβεβλημένα hashtags του/της {name}",
+  "account.featured_tags.last_status_at": "Τελευταία ανάρτηση στις {date}",
+  "account.featured_tags.last_status_never": "Καμία ανάρτηση",
+  "account.featured_tags.title": "προβεβλημένες ετικέτες του/της {name}",
   "account.follow": "Ακολούθησε",
   "account.followers": "Ακόλουθοι",
   "account.followers.empty": "Κανείς δεν ακολουθεί αυτό τον χρήστη ακόμα.",
   "account.followers_counter": "{count, plural, one {{counter} Ακόλουθος} other {{counter} Ακόλουθοι}}",
-  "account.following": "Αυτό το πρόγραμμα χρέωσης καλύπτει τα ακόλουθα έργα:",
-  "account.following_counter": "{count, plural, other {{counter} Ακολουθεί}}",
+  "account.following": "Ακολουθείτε",
+  "account.following_counter": "{count, plural, one {{counter} Ακολουθεί} other {{counter} Ακολουθούν}}",
   "account.follows.empty": "Αυτός ο χρήστης δεν ακολουθεί κανέναν ακόμα.",
   "account.follows_you": "Σε ακολουθεί",
   "account.go_to_profile": "Μετάβαση στο προφίλ",
-  "account.hide_reblogs": "Απόκρυψη προωθήσεων από @{name}",
-  "account.joined_short": "Εγγραφή στο <x id=\"INTERPOLATION\" equiv-text=\"{{ account.createdAt | date }}\"/> ",
-  "account.languages": "Είστε συνδρομητής",
-  "account.link_verified_on": "Η ιδιοκτησία αυτού του συνδέσμου ελέχθηκε την {date}",
-  "account.locked_info": "Η κατάσταση απορρήτου αυτού του λογαριασμού είναι κλειδωμένη. Ο ιδιοκτήτης επιβεβαιώνει χειροκίνητα ποιος μπορεί να τον ακολουθήσει.",
+  "account.hide_reblogs": "Απόκρυψη ενισχύσεων από @{name}",
+  "account.joined_short": "Έγινε μέλος",
+  "account.languages": "Αλλαγή εγγεγραμμένων γλωσσών",
+  "account.link_verified_on": "Η ιδιοκτησία αυτού του συνδέσμου ελέχθηκε στις {date}",
+  "account.locked_info": "Η κατάσταση απορρήτου αυτού του λογαριασμού έχει ρυθμιστεί σε κλειδωμένη. Ο ιδιοκτήτης αξιολογεί χειροκίνητα ποιος μπορεί να τον ακολουθήσει.",
   "account.media": "Πολυμέσα",
   "account.mention": "Ανάφερε @{name}",
   "account.moved_to": "Ο/Η {name} έχει υποδείξει ότι ο νέος λογαριασμός του/της είναι τώρα:",
-  "account.mute": "Σώπασε @{name}",
+  "account.mute": "Σώπασε τον @{name}",
   "account.mute_notifications": "Σώπασε τις ειδοποιήσεις από @{name}",
   "account.muted": "Αποσιωπημένος/η",
   "account.open_original_page": "Ανοικτό",
@@ -56,23 +56,23 @@
   "account.requested": "Εκκρεμεί έγκριση. Κάνε κλικ για να ακυρώσεις το αίτημα παρακολούθησης",
   "account.requested_follow": "Ο/Η {name} αιτήθηκε να σε ακολουθήσει",
   "account.share": "Μοίρασμα του προφίλ @{name}",
-  "account.show_reblogs": "Εμφάνιση προωθήσεων από @{name}",
-  "account.statuses_counter": "{count, plural, one {{counter} Τουτ} other {{counter} Τουτ}}",
-  "account.unblock": "Ξεμπλόκαρε @{name}",
-  "account.unblock_domain": "Αποκάλυψε το {domain}",
-  "account.unblock_short": "Ξεμπλοκάρισμα",
-  "account.unendorse": "Άνευ προβολής στο προφίλ",
-  "account.unfollow": "Διακοπή παρακολούθησης",
-  "account.unmute": "Διακοπή αποσιώπησης @{name}",
-  "account.unmute_notifications": "Διακοπή αποσιώπησης ειδοποιήσεων του/της @{name}",
+  "account.show_reblogs": "Εμφάνιση ενισχύσεων από @{name}",
+  "account.statuses_counter": "{count, plural, one {{counter} Ανάρτηση} other {{counter} Αναρτήσεις}}",
+  "account.unblock": "Άρση αποκλεισμού @{name}",
+  "account.unblock_domain": "Άρση αποκλεισμού του τομέα {domain}",
+  "account.unblock_short": "Άρση αποκλεισμού",
+  "account.unendorse": "Να μην παρέχεται στο προφίλ",
+  "account.unfollow": "Άρση ακολούθησης",
+  "account.unmute": "Διακοπή σίγασης @{name}",
+  "account.unmute_notifications": "Άρση σίγασης ειδοποιήσεων του/της @{name}",
   "account.unmute_short": "Κατάργηση σίγασης",
-  "account_note.placeholder": "Κλικ για να βάλεις σημείωση",
+  "account_note.placeholder": "Κάνε κλικ για να προσθέσεις σημείωση",
   "admin.dashboard.daily_retention": "Ποσοστό χρηστών που παραμένουν μετά την εγγραφή, ανά ημέρα",
   "admin.dashboard.monthly_retention": "Ποσοστό χρηστών που παραμένουν μετά την εγγραφή, ανά μήνα",
-  "admin.dashboard.retention.average": "%(display_name)s άφησε %(ratings_total)s βαθμολογία, <br />η μέση βαθμολογία είναι %(rating_average)s",
+  "admin.dashboard.retention.average": "Μέσος όρος",
   "admin.dashboard.retention.cohort": "Μήνας εγγραφής",
   "admin.dashboard.retention.cohort_size": "Νέοι χρήστες",
-  "alert.rate_limited.message": "Παρακαλούμε δοκίμασε ξανά αφού περάσει η {retry_time, time, medium}.",
+  "alert.rate_limited.message": "Παρακαλούμε δοκίμασε ξανά μετά τις {retry_time, time, medium}",
   "alert.rate_limited.title": "Περιορισμός συχνότητας",
   "alert.unexpected.message": "Προέκυψε απροσδόκητο σφάλμα.",
   "alert.unexpected.title": "Εεπ!",
@@ -102,7 +102,7 @@
   "column.blocks": "Αποκλεισμένοι χρήστες",
   "column.bookmarks": "Σελιδοδείκτες",
   "column.community": "Τοπική ροή",
-  "column.direct": "Προσωπικά μηνύματα",
+  "column.direct": "Ιδιωτικές αναφορές",
   "column.directory": "Δες προφίλ",
   "column.domain_blocks": "Κρυμμένοι τομείς",
   "column.favourites": "Αγαπημένα",
@@ -128,7 +128,7 @@
   "compose.language.search": "Αναζήτηση γλωσσών...",
   "compose_form.direct_message_warning_learn_more": "Μάθετε περισσότερα",
   "compose_form.encryption_warning": "Οι δημοσιεύσεις στο Mastodon δεν είναι κρυπτογραφημένες από άκρο σε άκρο. Μην μοιράζεστε ευαίσθητες πληροφορίες μέσω του Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "Αυτή η δημοσίευση δεν θα εμφανίζεται κάτω από οποιαδήποτε ετικέτα καθώς δεν είναι δημόσια. Μόνο οι δημόσιες δημοσιεύσεις μπορούν να αναζητηθούν με ετικέτα.",
   "compose_form.lock_disclaimer": "Ο λογαριασμός σου δεν είναι {locked}. Οποιοσδήποτε μπορεί να σε ακολουθήσει για να δει τις δημοσιεύσεις σας προς τους ακολούθους σας.",
   "compose_form.lock_disclaimer.lock": "κλειδωμένο",
   "compose_form.placeholder": "Τι σκέφτεσαι;",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Έχετε μη αποθηκευμένες αλλαγές στην περιγραφή πολυμέσων ή στην προεπισκόπηση, απορρίψτε τις ούτως ή άλλως;",
   "confirmations.domain_block.confirm": "Απόκρυψη ολόκληρου του τομέα",
   "confirmations.domain_block.message": "Σίγουρα θες να μπλοκάρεις ολόκληρο το {domain}; Συνήθως μερικά εστιασμένα μπλοκ ή αποσιωπήσεις επαρκούν και προτιμούνται. Δεν θα βλέπεις περιεχόμενο από αυτό τον κόμβο σε καμία δημόσια ροή, ούτε στις ειδοποιήσεις σου. Όσους ακόλουθους έχεις αυτό αυτό τον κόμβο θα αφαιρεθούν.",
+  "confirmations.edit.confirm": "Επεξεργασία",
+  "confirmations.edit.message": "Αν το επεξεργαστείτε τώρα θα αντικατασταθεί το μήνυμα που συνθέτετε. Είστε σίγουροι ότι θέλετε να συνεχίσετε;",
   "confirmations.logout.confirm": "Αποσύνδεση",
   "confirmations.logout.message": "Σίγουρα θέλεις να αποσυνδεθείς;",
   "confirmations.mute.confirm": "Αποσιώπηση",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Δεν έχεις αποκλείσει κανέναν χρήστη ακόμα.",
   "empty_column.bookmarked_statuses": "Δεν έχεις κανένα αποθηκευμένο τουτ ακόμα. Μόλις αποθηκεύσεις κάποιο, θα εμφανιστεί εδώ.",
   "empty_column.community": "Η τοπική ροή είναι κενή. Γράψε κάτι δημόσιο παραμύθι ν' αρχινίσει!",
-  "empty_column.direct": "Δεν έχεις προσωπικά μηνύματα ακόμα. Όταν στείλεις ή λάβεις κανένα, θα εμφανιστεί εδώ.",
+  "empty_column.direct": "Δεν έχεις καμία προσωπική αναφορά ακόμα. Όταν στείλεις ή λάβεις μία, θα εμφανιστεί εδώ.",
   "empty_column.domain_blocks": "Δεν υπάρχουν αποκλεισμένοι τομείς ακόμα.",
   "empty_column.explore_statuses": "Τίποτα δεν τρεντάρει αυτή τη στιγμή. Ελέγξτε αργότερα!",
   "empty_column.favourited_statuses": "Δεν έχεις κανένα αγαπημένο τουτ ακόμα. Μόλις αγαπήσεις κάποιο, θα εμφανιστεί εδώ.",
   "empty_column.favourites": "Κανείς δεν έχει αγαπήσει αυτό το τουτ ακόμα. Μόλις το κάνει κάποια, θα εμφανιστούν εδώ.",
   "empty_column.follow_recommendations": "Φαίνεται ότι δεν υπάρχει καμία πρόταση για σένα. Μπορείς να κάνεις μια αναζήτηση για άτομα που μπορεί να γνωρίζεις ή για hashtags που τρεντάρουν.",
   "empty_column.follow_requests": "Δεν έχεις κανένα αίτημα παρακολούθησης ακόμα. Μόλις λάβεις κάποιο, θα εμφανιστεί εδώ.",
+  "empty_column.followed_tags": "Δεν έχετε παρακολουθήσει ακόμα καμία ετικέτα. Όταν το κάνετε, θα εμφανιστούν εδώ.",
   "empty_column.hashtag": "Δεν υπάρχει ακόμα κάτι για αυτή την ετικέτα.",
   "empty_column.home": "Η τοπική σου ροή είναι κενή! Πήγαινε στο {public} ή κάνε αναζήτηση για να ξεκινήσεις και να γνωρίσεις άλλους χρήστες.",
   "empty_column.home.suggestions": "Ορίστε μερικές προτάσεις",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Ενέκρινε",
   "follow_request.reject": "Απέρριψε",
   "follow_requests.unlocked_explanation": "Παρόλο που ο λογαριασμός σου δεν είναι κλειδωμένος, οι διαχειριστές του {domain} θεώρησαν πως ίσως να θέλεις να ελέγξεις χειροκίνητα αυτά τα αιτήματα ακολούθησης.",
+  "followed_tags": "Ετικέτες που ακολουθούνται",
   "footer.about": "Σχετικά με",
   "footer.directory": "Κατάλογος προφίλ",
   "footer.get_app": "Αποκτήστε την Εφαρμογή",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Συντομεύσεις πληκτρολογίου",
   "footer.privacy_policy": "Πολιτική απορρήτου",
   "footer.source_code": "Προβολή πηγαίου κώδικα",
+  "footer.status": "Κατάσταση",
   "generic.saved": "Αποθηκεύτηκε",
   "getting_started.heading": "Αφετηρία",
   "hashtag.column_header.tag_mode.all": "και {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "εμφάνιση της κατάστασης σε μια από τις στήλες",
   "keyboard_shortcuts.compose": "εστίαση στην περιοχή συγγραφής",
   "keyboard_shortcuts.description": "Περιγραφή",
-  "keyboard_shortcuts.direct": "άνοιγμα στήλης προσωπικών μηνυμάτων",
+  "keyboard_shortcuts.direct": "για το άνοιγμα της στήλης ιδιωτικών αναφορών",
   "keyboard_shortcuts.down": "κίνηση προς τα κάτω στη λίστα",
   "keyboard_shortcuts.enter": "εμφάνιση κατάστασης",
   "keyboard_shortcuts.favourite": "σημείωση ως αγαπημένο",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Σελιδοδείκτες",
   "navigation_bar.community_timeline": "Τοπική ροή",
   "navigation_bar.compose": "Γράψε νέο τουτ",
-  "navigation_bar.direct": "Προσωπικά μηνύματα",
+  "navigation_bar.direct": "Ιδιωτικές αναφορές",
   "navigation_bar.discover": "Ανακάλυψη",
   "navigation_bar.domain_blocks": "Κρυμμένοι τομείς",
   "navigation_bar.edit_profile": "Επεξεργασία προφίλ",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Αγαπημένα",
   "navigation_bar.filters": "Αποσιωπημένες λέξεις",
   "navigation_bar.follow_requests": "Αιτήματα ακολούθησης",
+  "navigation_bar.followed_tags": "Ετικέτες που ακολουθούνται",
   "navigation_bar.follows_and_followers": "Ακολουθείς και σε ακολουθούν",
   "navigation_bar.lists": "Λίστες",
   "navigation_bar.logout": "Αποσύνδεση",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Ανεπιθύμητα",
   "report_notification.categories.violation": "Παραβίαση κανόνα",
   "report_notification.open": "Ανοικτό",
+  "search.no_recent_searches": "Καμμία πρόσφατη αναζήτηση",
   "search.placeholder": "Αναζήτηση",
+  "search.quick_action.account_search": "Προφίλ που ταιριάζουν με {x}",
+  "search.quick_action.go_to_account": "Μετάβαση στο προφίλ {x}",
+  "search.quick_action.go_to_hashtag": "Μετάβαση στην ετικέτα {x}",
+  "search.quick_action.open_url": "Άνοιγμα διεύθυνσης URL στο Mastodon",
+  "search.quick_action.status_search": "Αναρτήσεις που ταιριάζουν με {x}",
   "search.search_or_paste": "Αναζήτηση ή εισαγωγή URL",
-  "search_popout.search_format": "Προχωρημένη αναζήτηση",
-  "search_popout.tips.full_text": "Απλό κείμενο που επιστρέφει καταστάσεις που έχεις γράψει, έχεις σημειώσει ως αγαπημένες, έχεις προωθήσει ή έχεις αναφερθεί σε αυτές, καθώς και όσα ονόματα χρηστών και ετικέτες ταιριάζουν.",
-  "search_popout.tips.hashtag": "ετικέτα",
-  "search_popout.tips.status": "κατάσταση",
-  "search_popout.tips.text": "Απλό κείμενο που επιστρέφει ονόματα και ετικέτες που ταιριάζουν",
-  "search_popout.tips.user": "χρήστης",
-  "search_results.accounts": "Άνθρωποι",
+  "search_popout.quick_actions": "Γρήγορες ενέργειες",
+  "search_popout.recent": "Πρόσφατες αναζητήσεις",
+  "search_results.accounts": "Προφίλ",
   "search_results.all": "Όλα",
   "search_results.hashtags": "Ετικέτες",
   "search_results.nothing_found": "Δεν βρέθηκε τίποτα με αυτούς τους όρους αναζήτησης",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Στατιστικά διακομιστή:",
   "sign_in_banner.create_account": "Δημιουργία λογαριασμού",
   "sign_in_banner.sign_in": "Σύνδεση",
-  "sign_in_banner.text": "Συνδεθείτε για να ακολουθήσετε προφίλ ή ταμπέλες, αγαπημένα, να μοιραστείτε και να απαντήσετε σε δημοσιεύσεις ή να αλληλεπιδράσετε από το λογαριασμό σας σε διαφορετικό διακομιστή.",
+  "sign_in_banner.text": "Συνδεθείτε για να ακολουθήσετε προφίλ ή ετικέτες, αγαπήστε, μοιραστείτε και απαντήστε σε δημοσιεύσεις. Μπορείτε επίσης να αλληλεπιδράσετε από τον λογαριασμό σας σε διαφορετικό διακομιστή.",
   "status.admin_account": "Άνοιγμα λειτουργίας διαμεσολάβησης για τον/την @{name}",
   "status.admin_domain": "Άνοιγμα λειτουργίας διαμεσολάβησης για {domain}",
   "status.admin_status": "Άνοιγμα αυτής της δημοσίευσης στη λειτουργία διαμεσολάβησης",
@@ -551,7 +559,8 @@
   "status.copy": "Αντιγραφή συνδέσμου της δημοσίευσης",
   "status.delete": "Διαγραφή",
   "status.detailed_status": "Προβολή λεπτομερειών συζήτησης",
-  "status.direct": "Προσωπικό μήνυμα προς @{name}",
+  "status.direct": "Ιδιωτική αναφορά @{name}",
+  "status.direct_indicator": "Ιδιωτική αναφορά",
   "status.edit": "Επεξεργασία",
   "status.edited": "Επεξεργάστηκε στις {date}",
   "status.edited_x_times": "Επεξεργάστηκε {count, plural, one {{count} φορά} other {{count} φορές}}",
@@ -595,7 +604,7 @@
   "status.uncached_media_warning": "Μη διαθέσιμα",
   "status.unmute_conversation": "Διέκοψε την αποσιώπηση της συζήτησης",
   "status.unpin": "Ξεκαρφίτσωσε από το προφίλ",
-  "subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
+  "subscribed_languages.lead": "Μόνο δημοσιεύσεις σε επιλεγμένες γλώσσες θα εμφανίζονται στην αρχική σας και θα εμφανίζονται χρονοδιαγράμματα μετά την αλλαγή. Επιλέξτε καμία για να λαμβάνετε δημοσιεύσεις σε όλες τις γλώσσες.",
   "subscribed_languages.save": "Αποθήκευση αλλαγών",
   "subscribed_languages.target": "Αλλαγή εγγεγραμμένων γλωσσών για {target}",
   "suggestions.dismiss": "Απόρριψη πρότασης",
diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json
index 242569c4d..5d381bc7d 100644
--- a/app/javascript/mastodon/locales/en-GB.json
+++ b/app/javascript/mastodon/locales/en-GB.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocked",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Domain blocked",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "Favourites",
@@ -128,7 +128,7 @@
   "compose.language.search": "Search languages...",
   "compose_form.direct_message_warning_learn_more": "Learn more",
   "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any sensitive information over Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is not public. Only public posts 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's on your mind?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Block 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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "Mute",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
   "empty_column.favourites": "No one has favourited this post yet. When someone does, they will show up here.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorise",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -309,7 +314,7 @@
   "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.direct": "to open private mentions column",
   "keyboard_shortcuts.down": "to move down in the list",
   "keyboard_shortcuts.enter": "to open status",
   "keyboard_shortcuts.favourite": "to favourite",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new post",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lists",
   "navigation_bar.logout": "Logout",
@@ -447,7 +453,7 @@
   "poll.voted": "You voted for this answer",
   "poll.votes": "{votes, plural, one {# vote} other {# votes}}",
   "poll_button.add_poll": "Add a poll",
-  "poll_button.remove_poll": "Remove poll",
+  "poll_button.remove_poll": "Add a poll",
   "privacy.change": "Adjust status privacy",
   "privacy.direct.long": "Visible for mentioned users only",
   "privacy.direct.short": "Direct",
@@ -486,7 +492,7 @@
   "report.close": "Done",
   "report.comment.title": "Is there anything else you think we should know?",
   "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.forward_hint": "The account is from another server. Send an anonymised copy of the report there as well?",
   "report.mute": "Mute",
   "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.",
   "report.next": "Next",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -535,12 +543,12 @@
   "server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
   "server_banner.active_users": "active users",
   "server_banner.administered_by": "Administered by:",
-  "server_banner.introduction": "{domain} is part of the decentralized social network powered by {mastodon}.",
+  "server_banner.introduction": "{domain} is part of the decentralised social network powered by {mastodon}.",
   "server_banner.learn_more": "Learn more",
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
@@ -630,7 +639,7 @@
   "upload_form.thumbnail": "Change thumbnail",
   "upload_form.undo": "Delete",
   "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
-  "upload_modal.analyzing_picture": "Analyzing picture…",
+  "upload_modal.analyzing_picture": "Analysing picture…",
   "upload_modal.apply": "Apply",
   "upload_modal.applying": "Applying…",
   "upload_modal.choose_image": "Choose image",
diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json
index 8175c2bdf..601b61f2c 100644
--- a/app/javascript/mastodon/locales/en.json
+++ b/app/javascript/mastodon/locales/en.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocked",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Domain blocked",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "Favourites",
@@ -120,10 +120,6 @@
   "column_header.pin": "Pin",
   "column_header.show_settings": "Show settings",
   "column_header.unpin": "Unpin",
-  "column.heading": "Misc",
-  "column.subheading": "Miscellaneous options",
-  "column_subheading.lists": "Lists",
-  "column_subheading.navigation": "Navigation",
   "column_subheading.settings": "Settings",
   "community.column_settings.local_only": "Local only",
   "community.column_settings.media_only": "Media Only",
@@ -166,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Block 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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "Mute",
@@ -218,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
   "empty_column.favourites": "No one has favourited this post yet. When someone does, they will show up here.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -267,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -274,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -313,7 +314,7 @@
   "keyboard_shortcuts.column": "Focus column",
   "keyboard_shortcuts.compose": "Focus compose textarea",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.direct": "to open private mentions column",
   "keyboard_shortcuts.down": "Move down in the list",
   "keyboard_shortcuts.enter": "Open post",
   "keyboard_shortcuts.favourite": "Favourite post",
@@ -375,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new post",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Blocked domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -383,9 +384,9 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lists",
-  "navigation_bar.misc": "Misc",
   "navigation_bar.logout": "Logout",
   "navigation_bar.mutes": "Muted users",
   "navigation_bar.personal": "Personal",
@@ -521,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "Advanced search format",
-  "search_popout.tips.full_text": "Simple text returns posts 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": "post",
-  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
-  "search_popout.tips.user": "user",
-  "search_results.accounts": "People",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -545,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this post in the moderation interface",
@@ -556,7 +559,8 @@
   "status.copy": "Copy link to post",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json
index de2f7e670..be5ec1dc7 100644
--- a/app/javascript/mastodon/locales/eo.json
+++ b/app/javascript/mastodon/locales/eo.json
@@ -1,9 +1,9 @@
 {
   "about.blocks": "Administritaj serviloj",
   "about.contact": "Kontakto:",
-  "about.disclaimer": "Mastodon estas libera, malfermitkoda programaro kaj varmarko de la firmao Mastodon gGmbH.",
+  "about.disclaimer": "Mastodon estas libera, malfermitkoda programo kaj varmarko de la firmao Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Kialo ne disponebla",
-  "about.domain_blocks.preamble": "Mastodono ebligas vidi la enhavojn de uzantoj el aliaj serviloj en la Fediverso, kaj komuniki kun ili. Jen la limigoj deciditaj de tiu ĉi servilo mem.",
+  "about.domain_blocks.preamble": "Mastodon ĝenerale rajtigas vidi la enhavojn de uzantoj el aliaj serviloj en la fediverso, kaj komuniki kun ili. Jen la limigoj deciditaj de tiu ĉi servilo mem.",
   "about.domain_blocks.silenced.explanation": "Vi ne ĝenerale vidos profilojn kaj enhavojn de ĉi tiu servilo, krom se vi eksplice trovas aŭ estas permesita de via sekvato.",
   "about.domain_blocks.silenced.title": "Limigita",
   "about.domain_blocks.suspended.explanation": "Neniuj datumoj el tiu servilo estos prilaboritaj, konservitaj, aŭ interŝanĝitaj, do neeblas interagi aŭ komuniki kun uzantoj de tiu servilo.",
@@ -20,7 +20,7 @@
   "account.blocked": "Blokita",
   "account.browse_more_on_origin_server": "Foliumi pli ĉe la originala profilo",
   "account.cancel_follow_request": "Nuligi peton por sekvado",
-  "account.direct": "Rekte mesaĝi @{name}",
+  "account.direct": "Private mencii @{name}",
   "account.disable_notifications": "Ne plu sciigi min, kiam @{name} mesaĝas",
   "account.domain_blocked": "Domajno blokita",
   "account.edit_profile": "Redakti la profilon",
@@ -43,7 +43,7 @@
   "account.languages": "Ŝanĝi la abonitajn lingvojn",
   "account.link_verified_on": "Propreco de tiu ligilo estis konfirmita je {date}",
   "account.locked_info": "Tiu konto estas privatigita. La posedanto mane akceptas tiun, kiu povas sekvi rin.",
-  "account.media": "Aŭdovidaĵoj",
+  "account.media": "Plurmedioj",
   "account.mention": "Mencii @{name}",
   "account.moved_to": "{name} indikis, ke ria nova konto estas nun:",
   "account.mute": "Silentigi @{name}",
@@ -54,7 +54,7 @@
   "account.posts_with_replies": "Mesaĝoj kaj respondoj",
   "account.report": "Raporti @{name}",
   "account.requested": "Atendo de aprobo. Klaku por nuligi la peton por sekvado",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name} petis sekvi vin",
   "account.share": "Diskonigi la profilon de @{name}",
   "account.show_reblogs": "Montri diskonigojn de @{name}",
   "account.statuses_counter": "{count, plural, one {{counter} Afiŝo} other {{counter} Afiŝoj}}",
@@ -76,7 +76,7 @@
   "alert.rate_limited.title": "Mesaĝkvante limigita",
   "alert.unexpected.message": "Neatendita eraro okazis.",
   "alert.unexpected.title": "Aj!",
-  "announcement.announcement": "Anonco",
+  "announcement.announcement": "Anoncoj",
   "attachments_list.unprocessed": "(neprilaborita)",
   "audio.hide": "Kaŝi aŭdion",
   "autosuggest_hashtag.per_week": "po {count} por semajno",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokitaj uzantoj",
   "column.bookmarks": "Legosignoj",
   "column.community": "Loka templinio",
-  "column.direct": "Rektaj mesaĝoj",
+  "column.direct": "Privataj mencioj",
   "column.directory": "Foliumi la profilojn",
   "column.domain_blocks": "Blokitaj domajnoj",
   "column.favourites": "Stelumoj",
@@ -122,13 +122,13 @@
   "column_header.unpin": "Malfiksi",
   "column_subheading.settings": "Agordoj",
   "community.column_settings.local_only": "Nur loka",
-  "community.column_settings.media_only": "Nur aŭdovidaĵoj",
+  "community.column_settings.media_only": "Nur plurmedioj",
   "community.column_settings.remote_only": "Nur fora",
   "compose.language.change": "Ŝanĝi lingvon",
   "compose.language.search": "Serĉi lingvojn...",
   "compose_form.direct_message_warning_learn_more": "Lerni pli",
   "compose_form.encryption_warning": "La afiŝoj en Mastodon ne estas tutvoje ĉifritaj. Ne kunhavigu tiklajn informojn ĉe Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "Ĉi tiu afiŝo ne estos listigita en neniu kradvorto ĉar ĝi ne estas publika. Nur publikaj afiŝoj povas esti serĉitaj per kradvortoj.",
   "compose_form.lock_disclaimer": "Via konto ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn afiŝojn nur al la sekvantoj.",
   "compose_form.lock_disclaimer.lock": "ŝlosita",
   "compose_form.placeholder": "Kion vi pensas?",
@@ -138,13 +138,13 @@
   "compose_form.poll.remove_option": "Forigi ĉi tiu elekteblon",
   "compose_form.poll.switch_to_multiple": "Ŝanĝi la balotenketon por permesi multajn elektojn",
   "compose_form.poll.switch_to_single": "Ŝanĝi la balotenketon por permesi unu solan elekton",
-  "compose_form.publish": "Hup",
-  "compose_form.publish_form": "Hup",
+  "compose_form.publish": "Afiŝi",
+  "compose_form.publish_form": "Afiŝi",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.save_changes": "Konservi ŝanĝojn",
-  "compose_form.sensitive.hide": "{count, plural, one {Marki la aŭdovidaĵon kiel tikla} other {Marki la aŭdovidaĵojn kiel tikla}}",
-  "compose_form.sensitive.marked": "{count, plural, one {La aŭdovidaĵo estas markita kiel tikla} other {La aŭdovidaĵoj estas markitaj kiel tikla}}",
-  "compose_form.sensitive.unmarked": "{count, plural, one {La aŭdovidaĵo ne estas markita kiel tikla} other {La aŭdovidaĵoj ne estas markitaj kiel tikla}}",
+  "compose_form.sensitive.hide": "{count, plural, one {Marki la plurmedio kiel tikla} other {Marki la plurmedioj kiel tiklaj}}",
+  "compose_form.sensitive.marked": "{count, plural, one {La plurmedio estas markita kiel tikla} other {La plurmedioj estas markitaj kiel tiklaj}}",
+  "compose_form.sensitive.unmarked": "{count, plural, one {La plurmedio ne estas markita kiel tikla} other {La plurmedioj ne estas markitaj kiel tiklaj}}",
   "compose_form.spoiler.marked": "Forigi la averton de enhavo",
   "compose_form.spoiler.unmarked": "Aldoni averton de enhavo",
   "compose_form.spoiler_placeholder": "Skribu vian averton ĉi tie",
@@ -159,9 +159,11 @@
   "confirmations.delete_list.confirm": "Forigi",
   "confirmations.delete_list.message": "Ĉu vi certas, ke vi volas porĉiame forigi ĉi tiun liston?",
   "confirmations.discard_edit_media.confirm": "Forĵeti",
-  "confirmations.discard_edit_media.message": "Vi havas nekonservitajn ŝanĝojn de la priskribo aŭ la antaŭmontro de la aŭdovidaĵo, ĉu forĵetu ilin malgraŭe?",
+  "confirmations.discard_edit_media.message": "Vi havas nekonservitajn ŝanĝojn de la priskribo aŭ la antaŭmontro de la plurmedio, ĉu vi forĵetu ilin malgraŭe?",
   "confirmations.domain_block.confirm": "Bloki la tutan domajnon",
   "confirmations.domain_block.message": "Ĉu vi vere, vere certas, ke vi volas tute bloki {domain}? Plej ofte, trafa blokado kaj silentigado sufiĉas kaj preferindas. Vi ne vidos enhavon de tiu domajno en publika templinio aŭ en viaj sciigoj. Viaj sekvantoj de tiu domajno estos forigitaj.",
+  "confirmations.edit.confirm": "Redakti",
+  "confirmations.edit.message": "Redakti nun anstataŭigos la skribatan afiŝon. Ĉu vi certas, ke vi volas daŭrigi?",
   "confirmations.logout.confirm": "Adiaŭi",
   "confirmations.logout.message": "Ĉu vi certas ke vi volas adiaŭi?",
   "confirmations.mute.confirm": "Silentigi",
@@ -170,7 +172,7 @@
   "confirmations.redraft.confirm": "Forigi kaj reskribi",
   "confirmations.redraft.message": "Ĉu vi certas ke vi volas forigi tiun afiŝon kaj reskribi ĝin? Ĉiuj diskonigoj kaj stelumoj estos perditaj, kaj respondoj al la originala mesaĝo estos senparentaj.",
   "confirmations.reply.confirm": "Respondi",
-  "confirmations.reply.message": "Respondi nun anstataŭigos la mesaĝon, kiun vi nun skribas. Ĉu vi certas, ke vi volas daŭrigi?",
+  "confirmations.reply.message": "Respondi nun anstataŭigos la skribatan afiŝon. Ĉu vi certas, ke vi volas daŭrigi?",
   "confirmations.unfollow.confirm": "Ne plu sekvi",
   "confirmations.unfollow.message": "Ĉu vi certas, ke vi volas ĉesi sekvi {name}?",
   "conversation.delete": "Forigi konversacion",
@@ -188,10 +190,10 @@
   "dismissable_banner.community_timeline": "Jen la plej novaj publikaj afiŝoj de uzantoj, kies kontojn gastigas {domain}.",
   "dismissable_banner.dismiss": "Eksigi",
   "dismissable_banner.explore_links": "Tiuj novaĵoj estas aktuale priparolataj de uzantoj en tiu ĉi kaj aliaj serviloj, sur la malcentrigita reto.",
-  "dismissable_banner.explore_statuses": "Ĉi tiuj mesaĝoj de ĉi tiu kaj aliaj serviloj en la malcentra reto pli populariĝas en ĉi tiu servilo nun.",
+  "dismissable_banner.explore_statuses": "Ĉi tiuj afiŝoj de ĉi tiu kaj aliaj serviloj en la malcentra reto pli populariĝas en ĉi tiu servilo nun.",
   "dismissable_banner.explore_tags": "Ĉi tiuj kradvostoj populariĝas en ĉi tiu kaj aliaj serviloj en la malcentraliza reto nun.",
-  "dismissable_banner.public_timeline": "Ĉi tiuj estas plej lastaj publika mesaĝoj de personoj ĉe ĉi tiu kaj aliaj serviloj de la malcentra reto kiun ĉi tiu servilo scias.",
-  "embed.instructions": "Enkorpigu ĉi tiun mesaĝon en vian retejon per kopio de la suba kodo.",
+  "dismissable_banner.public_timeline": "Ĉi tiuj estas plej lastaj publikaj afiŝoj de personoj ĉe ĉi tiu kaj aliaj serviloj de la malcentra reto kiun tiu ĉi servilo scias.",
+  "embed.instructions": "Enkorpigu ĉi tiun afiŝon en vian retejon per kopio de la suba kodo.",
   "embed.preview": "Ĝi aperos tiel:",
   "emoji_button.activity": "Agadoj",
   "emoji_button.clear": "Forviŝi",
@@ -209,22 +211,23 @@
   "emoji_button.symbols": "Simboloj",
   "emoji_button.travel": "Vojaĝoj kaj lokoj",
   "empty_column.account_suspended": "Konto suspendita",
-  "empty_column.account_timeline": "Neniu mesaĝo ĉi tie!",
+  "empty_column.account_timeline": "Neniu afiŝo ĉi tie!",
   "empty_column.account_unavailable": "Profilo ne disponebla",
   "empty_column.blocks": "Vi ankoraŭ ne blokis uzanton.",
-  "empty_column.bookmarked_statuses": "Vi ankoraŭ ne aldonis mesaĝon al viaj legosignoj. Kiam vi aldonos iun, tiu aperos ĉi tie.",
+  "empty_column.bookmarked_statuses": "Vi ankoraŭ ne aldonis afiŝon al viaj legosignoj. Kiam vi aldonos iun, tiu aperos ĉi tie.",
   "empty_column.community": "La loka templinio estas malplena. Skribu ion por plenigi ĝin!",
-  "empty_column.direct": "Vi ankoraŭ ne havas rektan mesaĝon. Kiam vi sendos aŭ ricevos iun, ĝi aperos ĉi tie.",
+  "empty_column.direct": "Vi ankoraŭ ne havas privatan mencion. Kiam vi sendos aŭ ricevos iun, tiu aperos ĉi tie.",
   "empty_column.domain_blocks": "Ankoraŭ neniu domajno estas blokita.",
   "empty_column.explore_statuses": "Nenio tendencas nun. Rekontrolu poste!",
   "empty_column.favourited_statuses": "Vi ankoraŭ ne stelumis afiŝon. Kiam vi stelumos iun, ĝi aperos ĉi tie.",
-  "empty_column.favourites": "Ankoraŭ neniu stelumis tiun mesaĝon. Kiam iu faros tion, tiu aperos ĉi tie.",
+  "empty_column.favourites": "Ankoraŭ neniu stelumis ĉi tiun afiŝon. Kiam iu faros tion, tiu aperos ĉi tie.",
   "empty_column.follow_recommendations": "Ŝajnas, ke neniuj sugestoj povis esti generitaj por vi. Vi povas provi uzi serĉon por serĉi homojn, kiujn vi eble konas, aŭ esplori tendencajn kradvortojn.",
   "empty_column.follow_requests": "Vi ne ankoraŭ havas iun peton de sekvado. Kiam vi ricevos unu, ĝi aperos ĉi tie.",
+  "empty_column.followed_tags": "Vi ankoraŭ ne sekvas iujn kradvortojn. Kiam vi faras, ili aperos ĉi tie.",
   "empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.",
   "empty_column.home": "Via hejma tempolinio estas malplena! Vizitu {public} aŭ uzu la serĉilon por renkonti aliajn uzantojn.",
   "empty_column.home.suggestions": "Vidu iujn sugestojn",
-  "empty_column.list": "Ankoraŭ estas nenio en ĉi tiu listo. Kiam membroj de ĉi tiu listo afiŝos novajn mesaĝojn, ili aperos ĉi tie.",
+  "empty_column.list": "Ankoraŭ estas nenio en ĉi tiu listo. Kiam membroj de ĉi tiu listo afiŝos novajn afiŝojn, ili aperos ĉi tie.",
   "empty_column.lists": "Vi ankoraŭ ne havas liston. Kiam vi kreos iun, ĝi aperos ĉi tie.",
   "empty_column.mutes": "Vi ne ankoraŭ silentigis iun uzanton.",
   "empty_column.notifications": "Vi ankoraŭ ne havas sciigojn. Interagu kun aliaj por komenci konversacion.",
@@ -236,12 +239,12 @@
   "errors.unexpected_crash.copy_stacktrace": "Kopii stakspuron en tondujo",
   "errors.unexpected_crash.report_issue": "Raporti problemon",
   "explore.search_results": "Serĉaj rezultoj",
-  "explore.suggested_follows": "For you",
+  "explore.suggested_follows": "Por vi",
   "explore.title": "Esplori",
-  "explore.trending_links": "News",
+  "explore.trending_links": "Novaĵoj",
   "explore.trending_statuses": "Afiŝoj",
-  "explore.trending_tags": "Hashtags",
-  "filter_modal.added.context_mismatch_explanation": "Ĉi tiu filtrilkategorio ne kongruas la kuntekston de ĉi tiu mesaĝo. Vi devas redakti la filtrilon.",
+  "explore.trending_tags": "Kradvortoj",
+  "filter_modal.added.context_mismatch_explanation": "Ĉi tiu filtrilkategorio ne kongruas kun la kunteksto en kiu vi akcesis ĉi tiun afiŝon. Se vi volas ke la afiŝo estas ankaŭ filtrita en ĉi tiu kunteksto, vi devus redakti la filtrilon.",
   "filter_modal.added.context_mismatch_title": "Ne kongruas la kunteksto!",
   "filter_modal.added.expired_explanation": "Ĉi tiu filtrilkategorio eksvalidiĝis, vu bezonos ŝanĝi la eksvaliddaton por ĝi.",
   "filter_modal.added.expired_title": "Eksvalida filtrilo!",
@@ -262,7 +265,8 @@
   "follow_recommendations.lead": "La mesaĝoj de personoj kiujn vi sekvas, aperos laŭ kronologia ordo en via hejma templinio. Ne timu erari, vi povas ĉesi sekvi facile iam ajn!",
   "follow_request.authorize": "Rajtigi",
   "follow_request.reject": "Rifuzi",
-  "follow_requests.unlocked_explanation": "Kvankam via konto ne estas ŝlosita, la dungitaro de {domain} opiniis, ke vi eble volus revizii petojn de sekvadon el ĉi tiuj kontoj permane.",
+  "follow_requests.unlocked_explanation": "Kvankam via konto ne estas ŝlosita, la dungitaro de {domain} opinias, ke vi eble volas revizii petojn pri sekvado de ĉi tiuj kontoj permane.",
+  "followed_tags": "Sekvataj kradvortoj",
   "footer.about": "Pri",
   "footer.directory": "Profilujo",
   "footer.get_app": "Akiru la Programon",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Fulmoklavoj",
   "footer.privacy_policy": "Politiko de privateco",
   "footer.source_code": "Montri fontkodon",
+  "footer.status": "Stato",
   "generic.saved": "Konservita",
   "getting_started.heading": "Por komenci",
   "hashtag.column_header.tag_mode.all": "kaj {additional}",
@@ -288,14 +293,14 @@
   "home.column_settings.show_replies": "Montri respondojn",
   "home.hide_announcements": "Kaŝi la anoncojn",
   "home.show_announcements": "Montri anoncojn",
-  "interaction_modal.description.favourite": "Kun konto de Mastodon, vi povos stelumi ĉi tiun mesaĝon por konservi ĝin kaj por sciigi al la afiŝinto, ke vi estimas ĝin.",
+  "interaction_modal.description.favourite": "Kun konto ĉe Mastodon, vi povos stelumi ĉi tiun afiŝon por konservi ĝin kaj por sciigi al la afiŝinto, ke vi estimas ĝin.",
   "interaction_modal.description.follow": "Kun konto ĉe Mastodon, vi povos sekvi {name} por vidi ties mesaĝojn en via hejmo.",
-  "interaction_modal.description.reblog": "Kun konto de Mastodon, vi povas diskonigi ĉi tiun mesaĝon, por ke viaj propraj sekvantoj vidu ĝin.",
+  "interaction_modal.description.reblog": "Kun konto ĉe Mastodon, vi povas diskonigi ĉi tiun afiŝon, por ke viaj propraj sekvantoj vidu ĝin.",
   "interaction_modal.description.reply": "Kun konto ĉe Mastodon, vi povos respondi al ĉi tiu mesaĝo.",
   "interaction_modal.on_another_server": "En alia servilo",
   "interaction_modal.on_this_server": "En ĉi tiu servilo",
   "interaction_modal.other_server_instructions": "Preni ĉi tiun retadreson (URL) kaj meti ĝin en la serĉbreton de via preferata apo aŭ retfoliumilo por Mastodon.",
-  "interaction_modal.preamble": "Ĉar Mastodon estas malcentraliza, vi povas uzi jam ekzistantan konton, gastigatan de alia servilo Mastodon aŭ konforma platformo, se vi ne havas konton ĉe tiu ĉi.",
+  "interaction_modal.preamble": "Ĉar Mastodon estas malcentrigita, vi povas uzi jam ekzistantan konton gastigatan de alia Mastodona servilo aŭ kongrua substrato, se vi ne havas konton ĉe tiu ĉi.",
   "interaction_modal.title.favourite": "Stelumi la afiŝon de {name}",
   "interaction_modal.title.follow": "Sekvi {name}",
   "interaction_modal.title.reblog": "Akceli la afiŝon de {name}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "fokusi mesaĝon en unu el la kolumnoj",
   "keyboard_shortcuts.compose": "enfokusigi la tekstujon",
   "keyboard_shortcuts.description": "Priskribo",
-  "keyboard_shortcuts.direct": "malfermi la kolumnon de rektaj mesaĝoj",
+  "keyboard_shortcuts.direct": "por malfermi la kolumnon pri privataj mencioj",
   "keyboard_shortcuts.down": "iri suben en la listo",
   "keyboard_shortcuts.enter": "malfermi mesaĝon",
   "keyboard_shortcuts.favourite": "Stelumi",
@@ -324,7 +329,7 @@
   "keyboard_shortcuts.muted": "malfermi la liston de silentigitaj uzantoj",
   "keyboard_shortcuts.my_profile": "malfermi vian profilon",
   "keyboard_shortcuts.notifications": "malfermi la kolumnon de sciigoj",
-  "keyboard_shortcuts.open_media": "Malfermi la aŭdovidaĵon",
+  "keyboard_shortcuts.open_media": "Malfermi plurmedion",
   "keyboard_shortcuts.pinned": "malfermi la liston de alpinglitaj mesaĝoj",
   "keyboard_shortcuts.profile": "malfermi la profilon de la aŭtoro",
   "keyboard_shortcuts.reply": "respondi",
@@ -333,7 +338,7 @@
   "keyboard_shortcuts.spoilers": "Montri/kaŝi la kampon de averto de enhavo (\"CW\")",
   "keyboard_shortcuts.start": "malfermi la kolumnon «por komenci»",
   "keyboard_shortcuts.toggle_hidden": "Montri/kaŝi tekston malantaŭ la averto de enhavo (\"CW\")",
-  "keyboard_shortcuts.toggle_sensitivity": "Montri/kaŝi la aŭdovidaĵojn",
+  "keyboard_shortcuts.toggle_sensitivity": "Montri/kaŝi plurmedion",
   "keyboard_shortcuts.toot": "Krei novan mesaĝon",
   "keyboard_shortcuts.unfocus": "malenfokusigi la tekstujon aŭ la serĉilon",
   "keyboard_shortcuts.up": "iri supren en la listo",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Legosignoj",
   "navigation_bar.community_timeline": "Loka templinio",
   "navigation_bar.compose": "Skribi novan mesaĝon",
-  "navigation_bar.direct": "Rektaj mesaĝoj",
+  "navigation_bar.direct": "Privataj mencioj",
   "navigation_bar.discover": "Esplori",
   "navigation_bar.domain_blocks": "Blokitaj domajnoj",
   "navigation_bar.edit_profile": "Redakti profilon",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Stelumoj",
   "navigation_bar.filters": "Silentigitaj vortoj",
   "navigation_bar.follow_requests": "Petoj de sekvado",
+  "navigation_bar.followed_tags": "Sekvataj kradvortoj",
   "navigation_bar.follows_and_followers": "Sekvatoj kaj sekvantoj",
   "navigation_bar.lists": "Listoj",
   "navigation_bar.logout": "Adiaŭi",
@@ -398,8 +404,8 @@
   "notification.mention": "{name} menciis vin",
   "notification.own_poll": "Via enketo finiĝis",
   "notification.poll": "Partoprenita balotenketo finiĝis",
-  "notification.reblog": "{name} diskonigis vian mesaĝon",
-  "notification.status": "{name} ĵus afiŝita",
+  "notification.reblog": "{name} diskonigis vian afiŝon",
+  "notification.status": "{name} ĵus afiŝis",
   "notification.update": "{name} redaktis afiŝon",
   "notifications.clear": "Forviŝi sciigojn",
   "notifications.clear_confirmation": "Ĉu vi certas, ke vi volas porĉiame forviŝi ĉiujn viajn sciigojn?",
@@ -457,7 +463,7 @@
   "privacy.public.short": "Publika",
   "privacy.unlisted.long": "Videbla por ĉiuj, sed ekskluzive el la funkcio de esploro",
   "privacy.unlisted.short": "Nelistigita",
-  "privacy_policy.last_updated": "Laste ĝisdatigita sur {date}",
+  "privacy_policy.last_updated": "Laste ĝisdatigita en {date}",
   "privacy_policy.title": "Politiko de privateco",
   "refresh": "Refreŝigu",
   "regeneration_indicator.label": "Ŝargado…",
@@ -484,7 +490,7 @@
   "report.category.title_account": "profilo",
   "report.category.title_status": "afiŝo",
   "report.close": "Farita",
-  "report.comment.title": "Ĉu estas io alia kion vi pensas ke ni devas scii?",
+  "report.comment.title": "Ĉu estas ajn ion alian kiun vi pensas ke ni devus scii?",
   "report.forward": "Plusendi al {target}",
   "report.forward_hint": "La konto estas de alia servilo. Ĉu vi volas sendi anoniman kopion de la raporto ankaŭ al tie?",
   "report.mute": "Silentigi",
@@ -516,19 +522,21 @@
   "report_notification.categories.spam": "Trudmesaĝo",
   "report_notification.categories.violation": "Malobservo de la regulo",
   "report_notification.open": "Malfermi la raporton",
+  "search.no_recent_searches": "Neniuj lastaj serĉoj",
   "search.placeholder": "Serĉi",
+  "search.quick_action.account_search": "Profiloj kiuj kongruas kun {x}",
+  "search.quick_action.go_to_account": "Iri al profilo {x}",
+  "search.quick_action.go_to_hashtag": "Iri al kradvorto {x}",
+  "search.quick_action.open_url": "Malfermi URL en Mastodono",
+  "search.quick_action.status_search": "Afiŝoj kiuj kongruas kun {x}",
   "search.search_or_paste": "Serĉu aŭ algluu URL-on",
-  "search_popout.search_format": "Detala serĉo",
-  "search_popout.tips.full_text": "Simplaj tekstoj montras la mesaĝojn, kiujn vi skribis, stelumis, diskonigis, aŭ en kiuj vi estis menciita, sed ankaŭ kongruajn uzantnomojn, montratajn nomojn, kaj kradvortojn.",
-  "search_popout.tips.hashtag": "kradvorto",
-  "search_popout.tips.status": "afiŝoj",
-  "search_popout.tips.text": "Simpla teksto montras la kongruajn afiŝitajn nomojn, uzantnomojn kaj kradvortojn",
-  "search_popout.tips.user": "uzanto",
-  "search_results.accounts": "Homoj",
+  "search_popout.quick_actions": "Rapidaj agoj",
+  "search_popout.recent": "Lastaj serĉoj",
+  "search_results.accounts": "Profiloj",
   "search_results.all": "Ĉiuj",
   "search_results.hashtags": "Kradvortoj",
   "search_results.nothing_found": "Povis trovi nenion por ĉi tiuj serĉaj terminoj",
-  "search_results.statuses": "Mesaĝoj",
+  "search_results.statuses": "Afiŝoj",
   "search_results.statuses_fts_disabled": "Serĉi afiŝojn laŭ enhavo ne estas ebligita en ĉi tiu Mastodon-servilo.",
   "search_results.title": "Serĉ-rezultoj por {q}",
   "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezultoj}}",
@@ -540,18 +548,19 @@
   "server_banner.server_stats": "Statistikoj de la servilo:",
   "sign_in_banner.create_account": "Krei konton",
   "sign_in_banner.sign_in": "Saluti",
-  "sign_in_banner.text": "Ensalutu por sekvi profilojn aŭ kradvortojn, stelumi, diskonigi afiŝojn kaj respondi al ili, aŭ interagi per via konto de alia servilo.",
+  "sign_in_banner.text": "Salutu por sekvi profilojn aŭ kradvortojn, stelumi, kundividi kaj respondi afiŝojn. Vi ankaŭ povas interagi per via konto ĉe alia servilo.",
   "status.admin_account": "Malfermi fasadon de moderigado por @{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_domain": "Malfermu moderigan interfacon por {domain}",
   "status.admin_status": "Malfermi ĉi tiun mesaĝon en la kontrola interfaco",
   "status.block": "Bloki @{name}",
   "status.bookmark": "Aldoni al la legosignoj",
   "status.cancel_reblog_private": "Ne plu diskonigi",
-  "status.cannot_reblog": "Ĉi tiu mesaĝo ne diskonigeblas",
+  "status.cannot_reblog": "Ĉi tiun afiŝon ne eblas diskonigi",
   "status.copy": "Kopii la ligilon al la mesaĝo",
   "status.delete": "Forigi",
   "status.detailed_status": "Detala konversacia vido",
-  "status.direct": "Rekte mesaĝi @{name}",
+  "status.direct": "Private mencii @{name}",
+  "status.direct_indicator": "Privata mencio",
   "status.edit": "Redakti",
   "status.edited": "Redaktita {date}",
   "status.edited_x_times": "Redactita {count, plural, one {{count} fojon} other {{count} fojojn}}",
@@ -563,7 +572,7 @@
   "status.history.created": "{name} kreis {date}",
   "status.history.edited": "{name} redaktis {date}",
   "status.load_more": "Ŝargi pli",
-  "status.media_hidden": "Aŭdovidaĵo kaŝita",
+  "status.media_hidden": "Plurmedio kaŝita",
   "status.mention": "Mencii @{name}",
   "status.more": "Pli",
   "status.mute": "Silentigi @{name}",
@@ -575,7 +584,7 @@
   "status.reblog": "Diskonigi",
   "status.reblog_private": "Diskonigi kun la sama videbleco",
   "status.reblogged_by": "{name} diskonigis",
-  "status.reblogs.empty": "Ankoraŭ neniu diskonigis tiun mesaĝon. Kiam iu faras tion, ri aperos ĉi tie.",
+  "status.reblogs.empty": "Ankoraŭ neniu diskonigis tiun afiŝon. Kiam iu faras tion, ri aperos ĉi tie.",
   "status.redraft": "Forigi kaj reskribi",
   "status.remove_bookmark": "Forigi legosignon",
   "status.replied_to": "Respondis al {name}",
@@ -636,7 +645,7 @@
   "upload_modal.choose_image": "Elekti bildon",
   "upload_modal.description_placeholder": "Laŭ Ludoviko Zamenhof bongustas freŝa ĉeĥa manĝaĵo kun spicoj",
   "upload_modal.detect_text": "Detekti tekston de la bildo",
-  "upload_modal.edit_media": "Redakti la aŭdovidaĵon",
+  "upload_modal.edit_media": "Redakti la plurmedion",
   "upload_modal.hint": "Klaku aŭ trenu la cirklon en la antaŭvidilo por elekti la fokuspunkton kiu ĉiam videblos en ĉiuj etigitaj bildoj.",
   "upload_modal.preparing_ocr": "Preparante OSR…",
   "upload_modal.preview_label": "Antaŭvido ({ratio})",
diff --git a/app/javascript/mastodon/locales/es-AR.json b/app/javascript/mastodon/locales/es-AR.json
index cd6e91e67..a577cec79 100644
--- a/app/javascript/mastodon/locales/es-AR.json
+++ b/app/javascript/mastodon/locales/es-AR.json
@@ -20,7 +20,7 @@
   "account.blocked": "Bloqueado",
   "account.browse_more_on_origin_server": "Explorar más en el perfil original",
   "account.cancel_follow_request": "Retirar la solicitud de seguimiento",
-  "account.direct": "Mensaje directo a @{name}",
+  "account.direct": "Mención privada a @{name}",
   "account.disable_notifications": "Dejar de notificarme cuando @{name} envíe mensajes",
   "account.domain_blocked": "Dominio bloqueado",
   "account.edit_profile": "Editar perfil",
@@ -32,9 +32,9 @@
   "account.follow": "Seguir",
   "account.followers": "Seguidores",
   "account.followers.empty": "Todavía nadie sigue a este usuario.",
-  "account.followers_counter": "{count, plural, one {{counter} Seguidor} other {{counter} Seguidores}}",
+  "account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}",
   "account.following": "Siguiendo",
-  "account.following_counter": "{count, plural, other {{counter} Siguiendo}}",
+  "account.following_counter": "{count, plural, other {Siguiendo a {counter}}}",
   "account.follows.empty": "Todavía este usuario no sigue a nadie.",
   "account.follows_you": "Te sigue",
   "account.go_to_profile": "Ir al perfil",
@@ -57,7 +57,7 @@
   "account.requested_follow": "{name} solicitó seguirte",
   "account.share": "Compartir el perfil de @{name}",
   "account.show_reblogs": "Mostrar adhesiones de @{name}",
-  "account.statuses_counter": "{count, plural, one {{counter} Mensaje} other {{counter} Mensajes}}",
+  "account.statuses_counter": "{count, plural, one {{counter} mensaje} other {{counter} mensajes}}",
   "account.unblock": "Desbloquear a @{name}",
   "account.unblock_domain": "Desbloquear dominio {domain}",
   "account.unblock_short": "Desbloquear",
@@ -94,7 +94,7 @@
   "bundle_modal_error.message": "Algo salió mal al cargar este componente.",
   "bundle_modal_error.retry": "Intentá de nuevo",
   "closed_registrations.other_server_instructions": "Ya que Mastodon es descentralizado, podés crearte una cuenta en otro servidor y todavía interactuar con éste.",
-  "closed_registrations_modal.description": "Actualmente no es posible la creación de una cuenta en {domain}. pero tené en cuenta que no necesitás una cuenta específica en {domain} para usar Mastodon.",
+  "closed_registrations_modal.description": "Actualmente no es posible crearte una cuenta en {domain}. pero recordá que no necesitás tener una cuenta puntualmente dentro de {domain} para poder usar Mastodon.",
   "closed_registrations_modal.find_another_server": "Buscar otro servidor",
   "closed_registrations_modal.preamble": "Mastodon es descentralizado, por lo que no importa dónde creés tu cuenta, podrás seguir e interactuar con cualquier persona en este servidor. ¡Incluso podés montar tu propio servidor!",
   "closed_registrations_modal.title": "Registrarse en Mastodon",
@@ -102,7 +102,7 @@
   "column.blocks": "Usuarios bloqueados",
   "column.bookmarks": "Marcadores",
   "column.community": "Línea temporal local",
-  "column.direct": "Mensajes directos",
+  "column.direct": "Menciones privadas",
   "column.directory": "Explorar perfiles",
   "column.domain_blocks": "Dominios bloqueados",
   "column.favourites": "Favoritos",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Tenés cambios sin guardar en la descripción de medios o en la vista previa, ¿querés descartarlos de todos modos?",
   "confirmations.domain_block.confirm": "Bloquear dominio entero",
   "confirmations.domain_block.message": "¿Estás completamente seguro que querés bloquear el {domain} entero? En la mayoría de los casos, unos cuantos bloqueos y silenciados puntuales son suficientes y preferibles. No vas a ver contenido de ese dominio en ninguna de tus líneas temporales o en tus notificaciones. Tus seguidores de ese dominio serán quitados.",
+  "confirmations.edit.confirm": "Editar",
+  "confirmations.edit.message": "Editar ahora sobreescribirá el mensaje que estás redactando actualmente. ¿Estás seguro que querés seguir?",
   "confirmations.logout.confirm": "Cerrar sesión",
   "confirmations.logout.message": "¿Estás seguro que querés cerrar la sesión?",
   "confirmations.mute.confirm": "Silenciar",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Todavía no bloqueaste a ningún usuario.",
   "empty_column.bookmarked_statuses": "Todavía no tenés mensajes guardados en \"Marcadores\". Cuando guardés uno en \"Marcadores\", se mostrará acá.",
   "empty_column.community": "La línea temporal local está vacía. ¡Escribí algo en modo público para que se empiece a correr la bola!",
-  "empty_column.direct": "Todavía no tenés ningún mensaje directo. Cuando enviés o recibás uno, se mostrará acá.",
+  "empty_column.direct": "Todavía no tenés ninguna mención privada. Cuando enviés o recibás una, se mostrará acá.",
   "empty_column.domain_blocks": "Todavía no hay dominios bloqueados.",
   "empty_column.explore_statuses": "No hay nada en tendencia ahora mismo. ¡Volvé a revisar más tarde!",
   "empty_column.favourited_statuses": "Todavía no tenés mensajes favoritos. Cuando marqués uno como favorito, se mostrará acá.",
   "empty_column.favourites": "Todavía nadie marcó este mensaje como favorito. Cuando alguien lo haga, se mostrará acá.",
   "empty_column.follow_recommendations": "Parece que no se pudieron generar sugerencias para vos. Podés intentar buscar gente que conozcas o explorar las tendencias de las etiquetas.",
   "empty_column.follow_requests": "Todavía no tenés ninguna solicitud de seguimiento. Cuando recibás una, se mostrará acá.",
+  "empty_column.followed_tags": "Todavía no tenés ninguna etiqueta en seguimiento. Cuando tengás una, se mostrará acá.",
   "empty_column.hashtag": "Todavía no hay nada con esta etiqueta.",
   "empty_column.home": "¡Tu línea temporal principal está vacía! Seguí a más cuentas para llenarla. {suggestions}",
   "empty_column.home.suggestions": "Mirá algunas sugerencias.",
@@ -238,7 +241,7 @@
   "explore.search_results": "Resultados de búsqueda",
   "explore.suggested_follows": "Para vos",
   "explore.title": "Explorá",
-  "explore.trending_links": "Novedades",
+  "explore.trending_links": "Noticias",
   "explore.trending_statuses": "Mensajes",
   "explore.trending_tags": "Etiquetas",
   "filter_modal.added.context_mismatch_explanation": "Esta categoría de filtro no se aplica al contexto en el que accediste a este mensaje. Si querés que el mensaje sea filtrado también en este contexto, vas a tener que editar el filtro.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autorizar",
   "follow_request.reject": "Rechazar",
   "follow_requests.unlocked_explanation": "A pesar de que tu cuenta no es privada, el equipo de {domain} pensó que podrías querer revisar manualmente las solicitudes de seguimiento de estas cuentas.",
+  "followed_tags": "Etiquetas seguidas",
   "footer.about": "Información",
   "footer.directory": "Directorio de perfiles",
   "footer.get_app": "Conseguí la aplicación",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Atajos de teclado",
   "footer.privacy_policy": "Política de privacidad",
   "footer.source_code": "Ver código fuente",
+  "footer.status": "Estado",
   "generic.saved": "Guardado",
   "getting_started.heading": "Inicio de Mastodon",
   "hashtag.column_header.tag_mode.all": "y {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Enfocar columna",
   "keyboard_shortcuts.compose": "Enfocar el área de texto de redacción",
   "keyboard_shortcuts.description": "Descripción",
-  "keyboard_shortcuts.direct": "para abrir columna de mensajes directos",
+  "keyboard_shortcuts.direct": "para abrir la columna de menciones privadas",
   "keyboard_shortcuts.down": "Bajar en la lista",
   "keyboard_shortcuts.enter": "Abrir mensaje",
   "keyboard_shortcuts.favourite": "Marcar mensaje como favorito",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Marcadores",
   "navigation_bar.community_timeline": "Línea temporal local",
   "navigation_bar.compose": "Redactar un nuevo mensaje",
-  "navigation_bar.direct": "Mensajes directos",
+  "navigation_bar.direct": "Menciones privadas",
   "navigation_bar.discover": "Descubrir",
   "navigation_bar.domain_blocks": "Dominios bloqueados",
   "navigation_bar.edit_profile": "Editar perfil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoritos",
   "navigation_bar.filters": "Palabras silenciadas",
   "navigation_bar.follow_requests": "Solicitudes de seguimiento",
+  "navigation_bar.followed_tags": "Etiquetas seguidas",
   "navigation_bar.follows_and_followers": "Cuentas seguidas y seguidores",
   "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Cerrar sesión",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Violación de regla",
   "report_notification.open": "Abrir denuncia",
+  "search.no_recent_searches": "Sin búsquedas recientes",
   "search.placeholder": "Buscar",
+  "search.quick_action.account_search": "Perfiles que coinciden con {x}",
+  "search.quick_action.go_to_account": "Ir al perfil de {x}",
+  "search.quick_action.go_to_hashtag": "Ir a la etiqueta {x}",
+  "search.quick_action.open_url": "Abrir enlace en Mastodon",
+  "search.quick_action.status_search": "Mensajes que coinciden con {x}",
   "search.search_or_paste": "Buscar o pegar dirección web",
-  "search_popout.search_format": "Formato de búsqueda avanzada",
-  "search_popout.tips.full_text": "Las búsquedas de texto simple devuelven los mensajes que escribiste, los marcados como favoritos, los adheridos o en los que te mencionaron, así como nombres de usuarios, nombres mostrados y etiquetas.",
-  "search_popout.tips.hashtag": "etiqueta",
-  "search_popout.tips.status": "mensaje",
-  "search_popout.tips.text": "Las búsquedas de texto simple devuelven nombres de usuarios, nombres mostrados y etiquetas que coincidan",
-  "search_popout.tips.user": "usuario",
-  "search_results.accounts": "Gente",
+  "search_popout.quick_actions": "Acciones rápidas",
+  "search_popout.recent": "Búsquedas recientes",
+  "search_results.accounts": "Perfiles",
   "search_results.all": "Todos",
   "search_results.hashtags": "Etiquetas",
   "search_results.nothing_found": "No se pudo encontrar nada para estos términos de búsqueda",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Estadísticas del servidor:",
   "sign_in_banner.create_account": "Crear cuenta",
   "sign_in_banner.sign_in": "Iniciar sesión",
-  "sign_in_banner.text": "Iniciá sesión para seguir cuentas o etiquetas, marcar mensajes como favoritos, compartirlos y responderlos o interactuar desde tu cuenta en un servidor diferente.",
+  "sign_in_banner.text": "Iniciá sesión para seguir cuentas o etiquetas, marcar mensajes como favoritos, compartirlos y responderlos. También podés interactuar desde tu cuenta en un servidor diferente.",
   "status.admin_account": "Abrir interface de moderación para @{name}",
   "status.admin_domain": "Abrir interface de moderación para {domain}",
   "status.admin_status": "Abrir este mensaje en la interface de moderación",
@@ -551,7 +559,8 @@
   "status.copy": "Copiar enlace al mensaje",
   "status.delete": "Eliminar",
   "status.detailed_status": "Vista de conversación detallada",
-  "status.direct": "Mensaje directo para @{name}",
+  "status.direct": "Mención privada a @{name}",
+  "status.direct_indicator": "Mención privada",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/es-MX.json b/app/javascript/mastodon/locales/es-MX.json
index 00c815554..1338b8b46 100644
--- a/app/javascript/mastodon/locales/es-MX.json
+++ b/app/javascript/mastodon/locales/es-MX.json
@@ -2,7 +2,7 @@
   "about.blocks": "Servidores moderados",
   "about.contact": "Contacto:",
   "about.disclaimer": "Mastodon es software libre de código abierto, y una marca comercial de Mastodon gGmbH.",
-  "about.domain_blocks.no_reason_available": "Razón no disponible",
+  "about.domain_blocks.no_reason_available": "Motivo no disponible",
   "about.domain_blocks.preamble": "Mastodon generalmente te permite ver contenido e interactuar con usuarios de cualquier otro servidor del fediverso. Estas son las excepciones que se han hecho en este servidor en particular.",
   "about.domain_blocks.silenced.explanation": "Normalmente no verás perfiles y contenido de este servidor, a menos que lo busques explicitamente o vayas a el siguiendo alguna cuenta.",
   "about.domain_blocks.silenced.title": "Limitado",
@@ -20,7 +20,7 @@
   "account.blocked": "Bloqueado",
   "account.browse_more_on_origin_server": "Ver más en el perfil original",
   "account.cancel_follow_request": "Retirar solicitud de seguimiento",
-  "account.direct": "Mensaje a @{name}",
+  "account.direct": "Mención privada @{name}",
   "account.disable_notifications": "Dejar de notificarme cuando @{name} publique algo",
   "account.domain_blocked": "Dominio oculto",
   "account.edit_profile": "Editar perfil",
@@ -102,7 +102,7 @@
   "column.blocks": "Usuarios bloqueados",
   "column.bookmarks": "Marcadores",
   "column.community": "Línea de tiempo local",
-  "column.direct": "Mensajes",
+  "column.direct": "Menciones privadas",
   "column.directory": "Buscar perfiles",
   "column.domain_blocks": "Dominios ocultados",
   "column.favourites": "Favoritos",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Tienes cambios sin guardar en la descripción o vista previa del archivo, ¿deseas descartarlos de cualquier manera?",
   "confirmations.domain_block.confirm": "Ocultar dominio entero",
   "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.edit.confirm": "Editar",
+  "confirmations.edit.message": "Editar sobrescribirá el mensaje que estás escribiendo. ¿Estás seguro de que deseas continuar?",
   "confirmations.logout.confirm": "Cerrar sesión",
   "confirmations.logout.message": "¿Estás seguro de querer cerrar la sesión?",
   "confirmations.mute.confirm": "Silenciar",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Aún no has bloqueado a ningún usuario.",
   "empty_column.bookmarked_statuses": "Aún no tienes ningún toot guardado como marcador. Cuando guardes uno, se mostrará aquí.",
   "empty_column.community": "La línea de tiempo local está vacía. ¡Escribe algo para empezar la fiesta!",
-  "empty_column.direct": "Aún no tienes ningún mensaje. Cuando envíes o recibas uno, se mostrará aquí.",
+  "empty_column.direct": "Aún no tienes menciones privadas. Cuando envíes o recibas una, aparecerán aquí.",
   "empty_column.domain_blocks": "Todavía no hay dominios ocultos.",
   "empty_column.explore_statuses": "Nada es tendencia en este momento. ¡Revisa más tarde!",
   "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_recommendations": "Parece que no se ha podido generar ninguna sugerencia para ti. Puedes probar a buscar a gente que quizá conozcas o explorar los hashtags que están en tendencia.",
   "empty_column.follow_requests": "No tienes ninguna petición de seguidor. Cuando recibas una, se mostrará aquí.",
+  "empty_column.followed_tags": "No estás siguiendo ningún hashtag todavía. Cuando lo hagas, aparecerá 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.suggestions": "Ver algunas sugerencias",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autorizar",
   "follow_request.reject": "Rechazar",
   "follow_requests.unlocked_explanation": "A pesar de que tu cuenta no es privada, el personal de {domain} ha pensado que quizás deberías revisar manualmente las solicitudes de seguimiento de estas cuentas.",
+  "followed_tags": "Hashtags seguidos",
   "footer.about": "Acerca de",
   "footer.directory": "Directorio de perfiles",
   "footer.get_app": "Obtener la aplicación",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Atajos de teclado",
   "footer.privacy_policy": "Política de privacidad",
   "footer.source_code": "Ver código fuente",
+  "footer.status": "Estado",
   "generic.saved": "Guardado",
   "getting_started.heading": "Primeros pasos",
   "hashtag.column_header.tag_mode.all": "y {additional}",
@@ -309,7 +314,7 @@
   "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": "Descripción",
-  "keyboard_shortcuts.direct": "abrir la columna de mensajes",
+  "keyboard_shortcuts.direct": "para abrir la columna de menciones privadas",
   "keyboard_shortcuts.down": "mover hacia abajo en la lista",
   "keyboard_shortcuts.enter": "abrir estado",
   "keyboard_shortcuts.favourite": "añadir a favoritos",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Marcadores",
   "navigation_bar.community_timeline": "Historia local",
   "navigation_bar.compose": "Escribir un nuevo toot",
-  "navigation_bar.direct": "Mensajes",
+  "navigation_bar.direct": "Menciones privadas",
   "navigation_bar.discover": "Descubrir",
   "navigation_bar.domain_blocks": "Dominios ocultos",
   "navigation_bar.edit_profile": "Editar perfil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoritos",
   "navigation_bar.filters": "Palabras silenciadas",
   "navigation_bar.follow_requests": "Solicitudes para seguirte",
+  "navigation_bar.followed_tags": "Hashtags seguidos",
   "navigation_bar.follows_and_followers": "Siguiendo y seguidores",
   "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Cerrar sesión",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Infracción de regla",
   "report_notification.open": "Abrir denuncia",
+  "search.no_recent_searches": "Sin búsquedas recientes",
   "search.placeholder": "Buscar",
+  "search.quick_action.account_search": "Perfiles que coinciden con {x}",
+  "search.quick_action.go_to_account": "Ir al perfil {x}",
+  "search.quick_action.go_to_hashtag": "Ir a la etiqueta {x}",
+  "search.quick_action.open_url": "Abrir enlace en Mastodon",
+  "search.quick_action.status_search": "Publicaciones que coinciden con {x}",
   "search.search_or_paste": "Buscar o pegar URL",
-  "search_popout.search_format": "Formato de búsqueda avanzada",
-  "search_popout.tips.full_text": "Búsquedas de texto recuperan posts que has escrito, marcado como favoritos, retooteado o en los que has sido mencionado, así como usuarios, nombres y hashtags.",
-  "search_popout.tips.hashtag": "etiqueta",
-  "search_popout.tips.status": "publicación",
-  "search_popout.tips.text": "El texto simple devuelve nombres, usuarios y etiquetas coincidentes",
-  "search_popout.tips.user": "usuario",
-  "search_results.accounts": "Gente",
+  "search_popout.quick_actions": "Acciones rápidas",
+  "search_popout.recent": "Búsquedas recientes",
+  "search_results.accounts": "Perfiles",
   "search_results.all": "Todos",
   "search_results.hashtags": "Etiquetas",
   "search_results.nothing_found": "No se pudo encontrar nada para estos términos de búsqueda",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Estadísticas del servidor:",
   "sign_in_banner.create_account": "Crear cuenta",
   "sign_in_banner.sign_in": "Iniciar sesión",
-  "sign_in_banner.text": "Inicia sesión para seguir perfiles o etiquetas, marcar favorito, compartir y responder a publicaciones, o interactua desde tu cuenta en un servidor diferente.",
+  "sign_in_banner.text": "Inicia sesión para seguir perfiles o hashtags, marcar favorito, compartir y responder a publicaciones. También puedes interactuar desde tu cuenta en un servidor diferente.",
   "status.admin_account": "Abrir interfaz de moderación para @{name}",
   "status.admin_domain": "Abrir interfaz de moderación para {domain}",
   "status.admin_status": "Abrir este estado en la interfaz de moderación",
@@ -551,7 +559,8 @@
   "status.copy": "Copiar enlace al estado",
   "status.delete": "Borrar",
   "status.detailed_status": "Vista de conversación detallada",
-  "status.direct": "Enviar mensaje a @{name}",
+  "status.direct": "Mención privada @{name}",
+  "status.direct_indicator": "Mención privada",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} time} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json
index 0c1b3c690..25a568e24 100644
--- a/app/javascript/mastodon/locales/es.json
+++ b/app/javascript/mastodon/locales/es.json
@@ -20,7 +20,7 @@
   "account.blocked": "Bloqueado",
   "account.browse_more_on_origin_server": "Ver más en el perfil original",
   "account.cancel_follow_request": "Retirar solicitud de seguimiento",
-  "account.direct": "Mensaje directo a @{name}",
+  "account.direct": "Mención privada @{name}",
   "account.disable_notifications": "Dejar de notificarme cuando @{name} publique algo",
   "account.domain_blocked": "Dominio bloqueado",
   "account.edit_profile": "Editar perfil",
@@ -51,7 +51,7 @@
   "account.muted": "Silenciado",
   "account.open_original_page": "Abrir página original",
   "account.posts": "Publicaciones",
-  "account.posts_with_replies": "Publicaciones y respuestas",
+  "account.posts_with_replies": "Pub. y respuestas",
   "account.report": "Reportar a @{name}",
   "account.requested": "Esperando aprobación. Clica para cancelar la solicitud de seguimiento",
   "account.requested_follow": "{name} ha solicitado seguirte",
@@ -59,13 +59,13 @@
   "account.show_reblogs": "Mostrar impulsos de @{name}",
   "account.statuses_counter": "{count, plural, one {{counter} Publicación} other {{counter} Publicaciones}}",
   "account.unblock": "Desbloquear a @{name}",
-  "account.unblock_domain": "Mostrar {domain}",
+  "account.unblock_domain": "Desbloquear dominio {domain}",
   "account.unblock_short": "Desbloquear",
   "account.unendorse": "No destacar 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.unmute_short": "Desilenciar",
+  "account.unmute_short": "Dejar de silenciar",
   "account_note.placeholder": "Clic para añadir nota",
   "admin.dashboard.daily_retention": "Tasa de retención de usuarios por día después del registro",
   "admin.dashboard.monthly_retention": "Tasa de retención de usuarios por mes después del registro",
@@ -76,7 +76,7 @@
   "alert.rate_limited.title": "Tráfico limitado",
   "alert.unexpected.message": "Hubo un error inesperado.",
   "alert.unexpected.title": "¡Ups!",
-  "announcement.announcement": "Comunicación",
+  "announcement.announcement": "Anuncio",
   "attachments_list.unprocessed": "(sin procesar)",
   "audio.hide": "Ocultar audio",
   "autosuggest_hashtag.per_week": "{count} por semana",
@@ -102,7 +102,7 @@
   "column.blocks": "Usuarios bloqueados",
   "column.bookmarks": "Marcadores",
   "column.community": "Cronología local",
-  "column.direct": "Mensajes directos",
+  "column.direct": "Menciones privadas",
   "column.directory": "Buscar perfiles",
   "column.domain_blocks": "Dominios bloqueados",
   "column.favourites": "Favoritos",
@@ -128,7 +128,7 @@
   "compose.language.search": "Buscar idiomas...",
   "compose_form.direct_message_warning_learn_more": "Aprender más",
   "compose_form.encryption_warning": "Las publicaciones en Mastodon no están cifradas de extremo a extremo. No comparta ninguna información sensible en Mastodon.",
-  "compose_form.hashtag_warning": "Esta publicación no se mostrará bajo ningún hashtag no está pública. Solo las publicaciones públicas se pueden buscar por hashtag.",
+  "compose_form.hashtag_warning": "Esta publicación no se mostrará bajo ninguna etiqueta, ya que no es pública. Solo las publicaciones públicas pueden ser buscadas por etiqueta.",
   "compose_form.lock_disclaimer": "Tu cuenta no está {locked}. Todos pueden seguirte para ver tus publicaciones solo para seguidores.",
   "compose_form.lock_disclaimer.lock": "bloqueado",
   "compose_form.placeholder": "¿En qué estás pensando?",
@@ -140,12 +140,12 @@
   "compose_form.poll.switch_to_single": "Modificar encuesta para permitir una única opción",
   "compose_form.publish": "Publicar",
   "compose_form.publish_form": "Publicar",
-  "compose_form.publish_loud": "{publish}!",
+  "compose_form.publish_loud": "¡{publish}!",
   "compose_form.save_changes": "Guardar cambios",
   "compose_form.sensitive.hide": "{count, plural, one {Marcar material como sensible} other {Marcar material como sensible}}",
   "compose_form.sensitive.marked": "{count, plural, one {Material marcado como sensible} other {Material marcado como sensible}}",
   "compose_form.sensitive.unmarked": "{count, plural, one {Material no marcado como sensible} other {Material no marcado como sensible}}",
-  "compose_form.spoiler.marked": "Texto oculto tras la advertencia",
+  "compose_form.spoiler.marked": "Quitar advertencia de contenido",
   "compose_form.spoiler.unmarked": "Añadir advertencia de contenido",
   "compose_form.spoiler_placeholder": "Escribe aquí tu advertencia",
   "confirmation_modal.cancel": "Cancelar",
@@ -160,8 +160,10 @@
   "confirmations.delete_list.message": "¿Seguro que quieres borrar esta lista permanentemente?",
   "confirmations.discard_edit_media.confirm": "Descartar",
   "confirmations.discard_edit_media.message": "Tienes cambios sin guardar en la descripción o vista previa del archivo audiovisual, ¿descartarlos de todos modos?",
-  "confirmations.domain_block.confirm": "Ocultar dominio entero",
+  "confirmations.domain_block.confirm": "Bloquear dominio entero",
   "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.edit.confirm": "Editar",
+  "confirmations.edit.message": "Editar ahora reemplazará el mensaje que está escribiendo. ¿Está seguro que quiere proceder?",
   "confirmations.logout.confirm": "Cerrar sesión",
   "confirmations.logout.message": "¿Estás seguro de querer cerrar la sesión?",
   "confirmations.mute.confirm": "Silenciar",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Aún no has bloqueado a ningún usuario.",
   "empty_column.bookmarked_statuses": "Aún no tienes ninguna publicación guardada como marcador. Cuando guardes una, se mostrará aquí.",
   "empty_column.community": "La línea de tiempo local está vacía. ¡Escribe algo para empezar la fiesta!",
-  "empty_column.direct": "Aún no tienes ningún mensaje directo. Cuando envíes o recibas uno, se mostrará aquí.",
+  "empty_column.direct": "Aún no tienes menciones privadas. Cuando envíes o recibas una, aparecerán aquí.",
   "empty_column.domain_blocks": "Todavía no hay dominios ocultos.",
   "empty_column.explore_statuses": "Nada está en tendencia en este momento. ¡Revisa más tarde!",
   "empty_column.favourited_statuses": "Aún no tienes publicaciones favoritas. Cuando marques una como favorita, aparecerá aquí.",
   "empty_column.favourites": "Nadie ha marcado esta publicación como favorita. Cuando alguien lo haga, aparecerá aquí.",
   "empty_column.follow_recommendations": "Parece que no se ha podido generar ninguna sugerencia para ti. Puedes probar a buscar a gente que quizá conozcas o explorar los hashtags que están en tendencia.",
   "empty_column.follow_requests": "No tienes ninguna petición de seguidor. Cuando recibas una, se mostrará aquí.",
+  "empty_column.followed_tags": "No has seguido ninguna etiqueta todavía. Cuando lo hagas, se mostrarán aquí.",
   "empty_column.hashtag": "No hay nada en este hashtag aún.",
   "empty_column.home": "¡Tu línea temporal está vacía! Sigue a más personas para rellenarla. {suggestions}",
   "empty_column.home.suggestions": "Ver algunas sugerencias",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autorizar",
   "follow_request.reject": "Rechazar",
   "follow_requests.unlocked_explanation": "A pesar de que tu cuenta no es privada, el personal de {domain} ha pensado que quizás deberías revisar manualmente las solicitudes de seguimiento de estas cuentas.",
+  "followed_tags": "Etiquetas seguidas",
   "footer.about": "Acerca de",
   "footer.directory": "Directorio de perfiles",
   "footer.get_app": "Obtener la aplicación",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Atajos de teclado",
   "footer.privacy_policy": "Política de privacidad",
   "footer.source_code": "Ver código fuente",
+  "footer.status": "Estado",
   "generic.saved": "Guardado",
   "getting_started.heading": "Primeros pasos",
   "hashtag.column_header.tag_mode.all": "y {additional}",
@@ -309,7 +314,7 @@
   "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": "Descripción",
-  "keyboard_shortcuts.direct": "para abrir la columna de mensajes directos",
+  "keyboard_shortcuts.direct": "para abrir la columna de menciones privadas",
   "keyboard_shortcuts.down": "mover hacia abajo en la lista",
   "keyboard_shortcuts.enter": "abrir estado",
   "keyboard_shortcuts.favourite": "añadir a favoritos",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Marcadores",
   "navigation_bar.community_timeline": "Cronología local",
   "navigation_bar.compose": "Escribir nueva publicación",
-  "navigation_bar.direct": "Mensajes directos",
+  "navigation_bar.direct": "Menciones privadas",
   "navigation_bar.discover": "Descubrir",
   "navigation_bar.domain_blocks": "Dominios ocultos",
   "navigation_bar.edit_profile": "Editar perfil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoritos",
   "navigation_bar.filters": "Palabras silenciadas",
   "navigation_bar.follow_requests": "Solicitudes para seguirte",
+  "navigation_bar.followed_tags": "Etiquetas seguidas",
   "navigation_bar.follows_and_followers": "Siguiendo y seguidores",
   "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Cerrar sesión",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Infracción de regla",
   "report_notification.open": "Abrir informe",
+  "search.no_recent_searches": "Sin búsquedas recientes",
   "search.placeholder": "Buscar",
+  "search.quick_action.account_search": "Perfiles que coinciden con {x}",
+  "search.quick_action.go_to_account": "Ir al perfil {x}",
+  "search.quick_action.go_to_hashtag": "Ir a la etiqueta {x}",
+  "search.quick_action.open_url": "Abrir enlace en Mastodon",
+  "search.quick_action.status_search": "Publicaciones que coinciden con {x}",
   "search.search_or_paste": "Buscar o pegar URL",
-  "search_popout.search_format": "Formato de búsqueda avanzada",
-  "search_popout.tips.full_text": "Las búsquedas de texto recuperan publicaciones que has escrito, marcado como favoritas, retooteado o en los que has sido mencionado, así como usuarios, nombres y hashtags.",
-  "search_popout.tips.hashtag": "etiqueta",
-  "search_popout.tips.status": "status",
-  "search_popout.tips.text": "El texto simple devuelve correspondencias de nombre, usuario y hashtag",
-  "search_popout.tips.user": "usuario",
-  "search_results.accounts": "Gente",
+  "search_popout.quick_actions": "Acciones rápidas",
+  "search_popout.recent": "Búsquedas recientes",
+  "search_results.accounts": "Perfiles",
   "search_results.all": "Todos",
   "search_results.hashtags": "Etiquetas",
   "search_results.nothing_found": "No se pudo encontrar nada para estos términos de búsqueda",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Estadísticas del servidor:",
   "sign_in_banner.create_account": "Crear cuenta",
   "sign_in_banner.sign_in": "Iniciar sesión",
-  "sign_in_banner.text": "Inicia sesión en este servidor para seguir perfiles o etiquetas, guardar, compartir y responder a mensajes. También puedes interactuar desde otra cuenta en un servidor diferente.",
+  "sign_in_banner.text": "Inicia sesión para seguir perfiles o etiquetas, marcar como favorito, compartir y responder a publicaciones. También puedes interactuar desde tu cuenta en un servidor diferente.",
   "status.admin_account": "Abrir interfaz de moderación para @{name}",
   "status.admin_domain": "Abrir interfaz de moderación para {domain}",
   "status.admin_status": "Abrir este estado en la interfaz de moderación",
@@ -551,7 +559,8 @@
   "status.copy": "Copiar enlace al estado",
   "status.delete": "Borrar",
   "status.detailed_status": "Vista de conversación detallada",
-  "status.direct": "Mensaje directo a @{name}",
+  "status.direct": "Mención privada @{name}",
+  "status.direct_indicator": "Mención privada",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
diff --git a/app/javascript/mastodon/locales/et.json b/app/javascript/mastodon/locales/et.json
index 5b36f41e4..a61a577d6 100644
--- a/app/javascript/mastodon/locales/et.json
+++ b/app/javascript/mastodon/locales/et.json
@@ -2,7 +2,7 @@
   "about.blocks": "Modereeritavad serverid",
   "about.contact": "Kontakt:",
   "about.disclaimer": "Mastodon on tasuta ja vaba tarkvara ning Mastodon gGmbH kaubamärk.",
-  "about.domain_blocks.no_reason_available": "Mittesaadavuse põhjus",
+  "about.domain_blocks.no_reason_available": "Põhjus teadmata",
   "about.domain_blocks.preamble": "Mastodon lubab tavaliselt vaadata sisu ning suhelda kasutajatega ükskõik millisest teisest fediversumi serverist. Need on erandid, mis on paika pandud sellel kindlal serveril.",
   "about.domain_blocks.silenced.explanation": "Sa ei näe üldiselt profiile ja sisu sellelt serverilt, kui sa just tahtlikult seda ei otsi või jälgimise moel nõusolekut ei anna.",
   "about.domain_blocks.silenced.title": "Piiratud",
@@ -20,7 +20,7 @@
   "account.blocked": "Blokeeritud",
   "account.browse_more_on_origin_server": "Vaata rohkem algsel profiilil",
   "account.cancel_follow_request": "Võta jälgimistaotlus tagasi",
-  "account.direct": "Otsesõnum kasutajale @{name}",
+  "account.direct": "Maini privaatselt @{name}",
   "account.disable_notifications": "Peata teavitused @{name} postitustest",
   "account.domain_blocked": "Domeen peidetud",
   "account.edit_profile": "Muuda profiili",
@@ -66,7 +66,7 @@
   "account.unmute": "Ära vaigista @{name}",
   "account.unmute_notifications": "Ära vaigista teateid kasutajalt @{name}",
   "account.unmute_short": "Lõpeta vaigistamine",
-  "account_note.placeholder": "Klõpsa märkme lisamiseks",
+  "account_note.placeholder": "Klõpsa märke lisamiseks",
   "admin.dashboard.daily_retention": "Kasutajate päevane allesjäämine peale registreerumist",
   "admin.dashboard.monthly_retention": "Kasutajate kuine allesjäämine peale registreerumist",
   "admin.dashboard.retention.average": "Keskmine",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokeeritud kasutajad",
   "column.bookmarks": "Järjehoidjad",
   "column.community": "Kohalik ajajoon",
-  "column.direct": "Otsesõnumid",
+  "column.direct": "Privaatsed mainimised",
   "column.directory": "Sirvi profiile",
   "column.domain_blocks": "Peidetud domeenid",
   "column.favourites": "Lemmikud",
@@ -145,7 +145,7 @@
   "compose_form.sensitive.hide": "{count, plural, one {Märgi meedia tundlikuks} other {Märgi meediad tundlikuks}}",
   "compose_form.sensitive.marked": "{count, plural, one {Meedia on märgitud tundlikuks} other {Meediad on märgitud tundlikuks}}",
   "compose_form.sensitive.unmarked": "{count, plural, one {Meedia ei ole tundlikuks märgitud} other {Meediad ei ole märgitud tundlikuks}}",
-  "compose_form.spoiler.marked": "Tekst on peidetud hoiatuse taha",
+  "compose_form.spoiler.marked": "Tekst on hoiatuse taha peidetud",
   "compose_form.spoiler.unmarked": "Märgi sisu tundlikuks",
   "compose_form.spoiler_placeholder": "Kirjuta hoiatus siia",
   "confirmation_modal.cancel": "Katkesta",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Sul on salvestamata muudatusi meediakirjelduses või eelvaates, kas hülgad need?",
   "confirmations.domain_block.confirm": "Peida terve domeen",
   "confirmations.domain_block.message": "Oled ikka päris-päris kindel, et soovid blokeerida terve {domain}? Enamikel juhtudel piisab mõnest sihitud blokist või vaigistusest, mis on eelistatavam. Sa ei näe selle domeeni sisu ühelgi avalikul ajajoonel või enda teadetes. Su jälgijad sellest domeenist eemaldatakse.",
+  "confirmations.edit.confirm": "Muuda",
+  "confirmations.edit.message": "Muutes praegu kirjutatakse hetkel loodav sõnum üle. Kas oled kindel, et soovid jätkata?",
   "confirmations.logout.confirm": "Välju",
   "confirmations.logout.message": "Kas oled kindel, et soovid välja logida?",
   "confirmations.mute.confirm": "Vaigista",
@@ -214,14 +216,15 @@
   "empty_column.blocks": "Blokeeritud kasutajaid pole.",
   "empty_column.bookmarked_statuses": "Järjehoidjatesse pole veel lisatud postitusi. Kui lisad mõne, näed neid siin.",
   "empty_column.community": "Kohalik ajajoon on tühi. Kirjuta midagi avalikult, et pall veerema ajada!",
-  "empty_column.direct": "Ei ole veel otsesõnumeid. Kui saadad või võtad mõne vastu, ilmuvad nad siia.",
+  "empty_column.direct": "Sul pole veel ühtegi privaatset mainimist. Kui saadad või saad mõne, ilmuvad need siin.",
   "empty_column.domain_blocks": "Siin ei ole veel peidetud domeene.",
   "empty_column.explore_statuses": "Praegu pole ühtegi trendi. Tule hiljem tagasi!",
   "empty_column.favourited_statuses": "Pole veel lemmikpostitusi. Kui märgid mõne, näed neid siin.",
   "empty_column.favourites": "Keegi pole veel seda postitust lemmikuks märkinud. Kui seegi seda teeb, näed seda siin.",
   "empty_column.follow_recommendations": "Tundub, et sinu jaoks ei ole võimalik soovitusi luua. Proovi kasutada otsingut, et leida tuttavaid inimesi, või sirvi populaarseid silte.",
   "empty_column.follow_requests": "Pole hetkel ühtegi jälgimistaotlust. Kui saad mõne, näed neid siin.",
-  "empty_column.hashtag": "Seda sildi all ei ole ühtegi postitust.",
+  "empty_column.followed_tags": "Sa ei jälgi veel ühtegi märksõna. Kui jälgid, ilmuvad need siia.",
+  "empty_column.hashtag": "Selle sildi all ei ole ühtegi postitust.",
   "empty_column.home": "Su koduajajoon on tühi. Jälgi rohkemaid inimesi, et seda täita {suggestions}",
   "empty_column.home.suggestions": "Vaata mõndasid soovitusi",
   "empty_column.list": "Siin loetelus pole veel midagi. Kui loetelu liikmed teevad uusi postitusi, näed neid siin.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autoriseeri",
   "follow_request.reject": "Hülga",
   "follow_requests.unlocked_explanation": "Kuigi su konto pole lukustatud, soovitab {domain} personal siiski nende kontode jälgimistaotlused käsitsi üle vaadata.",
+  "followed_tags": "Jälgitavad märksõnad",
   "footer.about": "Teave",
   "footer.directory": "Profiilikataloog",
   "footer.get_app": "Tõmba äpp",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Kiirklahvid",
   "footer.privacy_policy": "Isikuandmete kaitse",
   "footer.source_code": "Lähtekood",
+  "footer.status": "Olek",
   "generic.saved": "Salvestatud",
   "getting_started.heading": "Alustamine",
   "hashtag.column_header.tag_mode.all": "ja {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Fookus veerule",
   "keyboard_shortcuts.compose": "Fookus teksti koostamise alale",
   "keyboard_shortcuts.description": "Kirjeldus",
-  "keyboard_shortcuts.direct": "ava otsesõnumite veerg",
+  "keyboard_shortcuts.direct": "privaatsete mainimiste veeru avamiseks",
   "keyboard_shortcuts.down": "Liigu loetelus alla",
   "keyboard_shortcuts.enter": "Ava postitus",
   "keyboard_shortcuts.favourite": "Märgi lemmikuks",
@@ -330,9 +335,9 @@
   "keyboard_shortcuts.reply": "Vasta postitusele",
   "keyboard_shortcuts.requests": "Ava jälgimistaotluste loetelu",
   "keyboard_shortcuts.search": "Fookus otsingule",
-  "keyboard_shortcuts.spoilers": "Näita/peida CW väli",
+  "keyboard_shortcuts.spoilers": "Näita/peida hoiatuse väli",
   "keyboard_shortcuts.start": "Ava veerg \"Alusta\"",
-  "keyboard_shortcuts.toggle_hidden": "Näida/peida teksti CW taga",
+  "keyboard_shortcuts.toggle_hidden": "Näita/peida teksti hoiatuse taga",
   "keyboard_shortcuts.toggle_sensitivity": "Näita/peida meediat",
   "keyboard_shortcuts.toot": "Alusta uut postitust",
   "keyboard_shortcuts.unfocus": "Fookus tekstialalt/otsingult ära",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Järjehoidjad",
   "navigation_bar.community_timeline": "Kohalik ajajoon",
   "navigation_bar.compose": "Koosta uus postitus",
-  "navigation_bar.direct": "Otsesõnumid",
+  "navigation_bar.direct": "Privaatsed mainimised",
   "navigation_bar.discover": "Avasta",
   "navigation_bar.domain_blocks": "Peidetud domeenid",
   "navigation_bar.edit_profile": "Muuda profiili",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Lemmikud",
   "navigation_bar.filters": "Vaigistatud sõnad",
   "navigation_bar.follow_requests": "Jälgimistaotlused",
+  "navigation_bar.followed_tags": "Jälgitavad märksõnad",
   "navigation_bar.follows_and_followers": "Jälgitavad ja jälgijad",
   "navigation_bar.lists": "Nimekirjad",
   "navigation_bar.logout": "Logi välja",
@@ -394,8 +400,8 @@
   "notification.admin.sign_up": "{name} registreerus",
   "notification.favourite": "{name} märkis su postituse lemmikuks",
   "notification.follow": "{name} alustas su jälgimist",
-  "notification.follow_request": "{name} soovib teid jälgida",
-  "notification.mention": "{name} mainis teid",
+  "notification.follow_request": "{name} soovib sind jälgida",
+  "notification.mention": "{name} mainis sind",
   "notification.own_poll": "Su küsitlus on lõppenud",
   "notification.poll": "Küsitlus, milles osalesid, on lõppenud",
   "notification.reblog": "{name} jagas edasi postitust",
@@ -455,7 +461,7 @@
   "privacy.private.short": "Jälgijad ainult",
   "privacy.public.long": "Kõigile nähtav",
   "privacy.public.short": "Avalik",
-  "privacy.unlisted.long": "Kõgile nähtav, aga ei ilmu avastamise vaadetes",
+  "privacy.unlisted.long": "Kõigile nähtav, aga ei ilmu avastamise vaadetes",
   "privacy.unlisted.short": "Määramata",
   "privacy_policy.last_updated": "Viimati uuendatud {date}",
   "privacy_policy.title": "Isikuandmete kaitse",
@@ -485,7 +491,7 @@
   "report.category.title_status": "postitusega",
   "report.close": "Valmis",
   "report.comment.title": "Kas arvad, et on veel midagi, mida me peaks teadma?",
-  "report.forward": "Edasta kasutajale {target}",
+  "report.forward": "Edasta ka {target} domeeni",
   "report.forward_hint": "See kasutaja on teisest serverist. Kas saadan anonümiseeritud koopia sellest teatest sinna ka?",
   "report.mute": "Vaigista",
   "report.mute_explanation": "Sa ei näe tema postitusi. Ta võib ikka sind jälgida ja su postitusi näha. Ta ei saa teada, et ta on vaigistatud.",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Rämpspost",
   "report_notification.categories.violation": "Reeglite rikkumine",
   "report_notification.open": "Ava teavitus",
+  "search.no_recent_searches": "Pole viimatisi otsinguid",
   "search.placeholder": "Otsi",
+  "search.quick_action.account_search": "Sobivaid profiile {x}",
+  "search.quick_action.go_to_account": "Mine profiili {x}",
+  "search.quick_action.go_to_hashtag": "Ava silt {x}",
+  "search.quick_action.open_url": "Ava URL Mastodonis",
+  "search.quick_action.status_search": "Sobivad postitused {x}",
   "search.search_or_paste": "Otsi või kleebi URL",
-  "search_popout.search_format": "Täiustatud otsiformaat",
-  "search_popout.tips.full_text": "Lihttekst annab vastuseks postitused, mille oled kirjutanud, lisanud lemmikuks, jaganud või kus on sind mainitud, ning lisaks kokkusobivad kasutajanimed, profiili kuvanimed ja sildid.",
-  "search_popout.tips.hashtag": "silt",
-  "search_popout.tips.status": "postitus",
-  "search_popout.tips.text": "Lihtne tekst toob esile kattuvad kuvanimed, kasutajanimed ning sildid",
-  "search_popout.tips.user": "kasutaja",
-  "search_results.accounts": "Inimesed",
+  "search_popout.quick_actions": "Kiirtegevused",
+  "search_popout.recent": "Viimatised otsingud",
+  "search_results.accounts": "Profiilid",
   "search_results.all": "Kõik",
   "search_results.hashtags": "Sildid",
   "search_results.nothing_found": "Otsisõnadele vastavat sisu ei leitud",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Serveri statistika:",
   "sign_in_banner.create_account": "Loo konto",
   "sign_in_banner.sign_in": "Logi sisse",
-  "sign_in_banner.text": "Logi sisse, et jälgida profiile või silte, märkida lemmikuks, jagada ja vastata postitustele või kasutada suhtlemiseks kontot teises serveris.",
+  "sign_in_banner.text": "Logi sisse, et jälgida profiile või silte, märkida lemmikuks, jagada ja vastata postitustele. Võid suhelda ka mõne teise serveri konto kaudu.",
   "status.admin_account": "Ava @{name} moderaatorivaates",
   "status.admin_domain": "Ava {domain} modeereerimisliides",
   "status.admin_status": "Ava postitus moderaatorivaates",
@@ -551,7 +559,8 @@
   "status.copy": "Kopeeri postituse link",
   "status.delete": "Kustuta",
   "status.detailed_status": "Detailne vestluskuva",
-  "status.direct": "Saada otsesõnum @{name}'ile",
+  "status.direct": "Maini privaatselt @{name}",
+  "status.direct_indicator": "Privaatne mainimine",
   "status.edit": "Muuda",
   "status.edited": "{date} muudetud",
   "status.edited_x_times": "Muudetud {count, plural, one{{count} kord} other {{count} korda}}",
@@ -599,7 +608,7 @@
   "subscribed_languages.save": "Salvesta muudatused",
   "subscribed_languages.target": "Muuda tellitud keeli {target} jaoks",
   "suggestions.dismiss": "Eira soovitust",
-  "suggestions.header": "Teid võib huvitada…",
+  "suggestions.header": "Sind võib huvitada…",
   "tabs_bar.federated_timeline": "Föderatiivne",
   "tabs_bar.home": "Kodu",
   "tabs_bar.local_timeline": "Kohalik",
diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json
index 9f771a811..597f4fb50 100644
--- a/app/javascript/mastodon/locales/eu.json
+++ b/app/javascript/mastodon/locales/eu.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blokeatuta",
   "account.browse_more_on_origin_server": "Arakatu gehiago jatorrizko profilean",
   "account.cancel_follow_request": "Baztertu jarraitzeko eskaera",
-  "account.direct": "Mezu zuzena @{name}(r)i",
+  "account.direct": "Pribatuki aipatu @{name}",
   "account.disable_notifications": "Utzi jakinarazteari @{name} erabiltzailearen bidalketetan",
   "account.domain_blocked": "Ezkutatutako domeinua",
   "account.edit_profile": "Aldatu profila",
@@ -54,7 +54,7 @@
   "account.posts_with_replies": "Bidalketak eta erantzunak",
   "account.report": "Salatu @{name}",
   "account.requested": "Onarpenaren zain. Klikatu jarraitzeko eskaera ezeztatzeko",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name}-(e)k zu jarraitzeko eskaera egin du",
   "account.share": "@{name}(e)ren profila elkarbanatu",
   "account.show_reblogs": "Erakutsi @{name}(r)en bultzadak",
   "account.statuses_counter": "{count, plural, one {Bidalketa {counter}} other {{counter} bidalketa}}",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokeatutako erabiltzaileak",
   "column.bookmarks": "Laster-markak",
   "column.community": "Denbora-lerro lokala",
-  "column.direct": "Mezu zuzenak",
+  "column.direct": "Aipamen pribatuak",
   "column.directory": "Arakatu profilak",
   "column.domain_blocks": "Ezkutatutako domeinuak",
   "column.favourites": "Gogokoak",
@@ -128,7 +128,7 @@
   "compose.language.search": "Bilatu hizkuntzak...",
   "compose_form.direct_message_warning_learn_more": "Ikasi gehiago",
   "compose_form.encryption_warning": "Mastodoneko bidalketak ez daude muturretik muturrera enkriptatuta. Ez partekatu informazio sentikorrik Mastodonen.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "Tut hau ez da inolako traolatan zerrendatuko, ez baita publikoa. Tut publikoak soilik traolen bitartez bila daitezke.",
   "compose_form.lock_disclaimer": "Zure kontua ez dago {locked}. Edonork jarraitu zaitzake zure jarraitzaileentzako soilik diren bidalketak ikusteko.",
   "compose_form.lock_disclaimer.lock": "giltzapetuta",
   "compose_form.placeholder": "Zer duzu buruan?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Multimediaren deskribapen edo aurrebistan gorde gabeko aldaketak daude, baztertu nahi dituzu?",
   "confirmations.domain_block.confirm": "Ezkutatu domeinu osoa",
   "confirmations.domain_block.message": "Ziur, erabat ziur, {domain} domeinu osoa blokeatu nahi duzula? Gehienetan gutxi batzuk blokeatu edo mututzearekin nahikoa da. Ez duzu domeinu horretako edukirik ikusiko denbora lerroetan edo jakinarazpenetan. Domeinu horretako zure jarraitzaileak kenduko dira ere.",
+  "confirmations.edit.confirm": "Editatu",
+  "confirmations.edit.message": "Orain editatzen baduzu, idazten zauden mezua gainidatziko da. Ziur al zaude jarraitu nahi duzula?",
   "confirmations.logout.confirm": "Amaitu saioa",
   "confirmations.logout.message": "Ziur saioa amaitu nahi duzula?",
   "confirmations.mute.confirm": "Mututu",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Ez duzu erabiltzailerik blokeatu oraindik.",
   "empty_column.bookmarked_statuses": "Oraindik ez dituzu bidalketa laster-markatutarik. Bat laster-markatzerakoan, hemen agertuko da.",
   "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.direct": "Ez duzu aipamen pribaturik oraindik. Baten bat bidali edo jasotzen duzunean, hemen agertuko da.",
   "empty_column.domain_blocks": "Ez dago ezkutatutako domeinurik oraindik.",
   "empty_column.explore_statuses": "Ez dago joerarik une honetan. Begiratu beranduago!",
   "empty_column.favourited_statuses": "Ez duzu gogokorik oraindik. Gogokoren bat duzunean hemen agertuko da.",
   "empty_column.favourites": "Ez du inork gogokoetara gehitu bidalketa hau oraindik. Inork egiten duenean, hemen agertuko dira.",
   "empty_column.follow_recommendations": "Dirudienez ezin izan da zuretzako proposamenik sortu. Bilaketa erabili dezakezu ezagutzen duzun jendea aurkitzeko edo traolen joerak arakatu.",
   "empty_column.follow_requests": "Ez duzu jarraitzeko eskaririk oraindik. Baten bat jasotzen duzunean, hemen agertuko da.",
+  "empty_column.followed_tags": "Oraindik ez duzu traolik jarraitzen. Egiterakoan, hemen agertuko dira.",
   "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.suggestions": "Ikusi proposamen batzuk",
@@ -239,8 +242,8 @@
   "explore.suggested_follows": "Zuretzako",
   "explore.title": "Arakatu",
   "explore.trending_links": "Berriak",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "Hashtags",
+  "explore.trending_statuses": "Tutak",
+  "explore.trending_tags": "Traolak",
   "filter_modal.added.context_mismatch_explanation": "Iragazki-kategoria hau ez zaio aplikatzen bidalketa honetara sartzeko erabili duzun testuinguruari. Bidalketa testuinguru horretan ere iragaztea nahi baduzu, iragazkia editatu beharko duzu.",
   "filter_modal.added.context_mismatch_title": "Testuingurua ez dator bat!",
   "filter_modal.added.expired_explanation": "Iragazki kategoria hau iraungi da, eragina izan dezan bere iraungitze-data aldatu beharko duzu.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Baimendu",
   "follow_request.reject": "Ukatu",
   "follow_requests.unlocked_explanation": "Zure kontua blokeatuta ez badago ere, {domain} domeinuko arduradunek uste dute kontu hauetako jarraipen eskariak agian eskuz begiratu nahiko dituzula.",
+  "followed_tags": "Jarraitutako traolak",
   "footer.about": "Honi buruz",
   "footer.directory": "Profil-direktorioa",
   "footer.get_app": "Eskuratu aplikazioa",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Lasterbideak",
   "footer.privacy_policy": "Pribatutasun politika",
   "footer.source_code": "Ikusi iturburu kodea",
+  "footer.status": "Egoera",
   "generic.saved": "Gordea",
   "getting_started.heading": "Menua",
   "hashtag.column_header.tag_mode.all": "eta {osagarria}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "mezu bat zutabe batean fokatzea",
   "keyboard_shortcuts.compose": "testua konposatzeko arean fokatzea",
   "keyboard_shortcuts.description": "Deskripzioa",
-  "keyboard_shortcuts.direct": "mezu zuzenen zutabea irekitzeko",
+  "keyboard_shortcuts.direct": "aipamen pribatuen zutabea irekitzeko",
   "keyboard_shortcuts.down": "zerrendan behera mugitzea",
   "keyboard_shortcuts.enter": "Ireki bidalketa",
   "keyboard_shortcuts.favourite": "Egin gogoko bidalketa",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Laster-markak",
   "navigation_bar.community_timeline": "Denbora-lerro lokala",
   "navigation_bar.compose": "Idatzi bidalketa berria",
-  "navigation_bar.direct": "Mezu zuzenak",
+  "navigation_bar.direct": "Aipamen pribatuak",
   "navigation_bar.discover": "Aurkitu",
   "navigation_bar.domain_blocks": "Ezkutatutako domeinuak",
   "navigation_bar.edit_profile": "Aldatu profila",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Gogokoak",
   "navigation_bar.filters": "Mutututako hitzak",
   "navigation_bar.follow_requests": "Jarraitzeko eskariak",
+  "navigation_bar.followed_tags": "Jarraitutako traolak",
   "navigation_bar.follows_and_followers": "Jarraitutakoak eta jarraitzaileak",
   "navigation_bar.lists": "Zerrendak",
   "navigation_bar.logout": "Amaitu saioa",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Arau haustea",
   "report_notification.open": "Ireki salaketa",
+  "search.no_recent_searches": "Duela gutxiko bilaketarik ez",
   "search.placeholder": "Bilatu",
+  "search.quick_action.account_search": "{x}-(r)ekin bat datozen profilak",
+  "search.quick_action.go_to_account": "Joan {x} profilera",
+  "search.quick_action.go_to_hashtag": "Joan {x} traolara",
+  "search.quick_action.open_url": "Ireki URLa Mastodonen",
+  "search.quick_action.status_search": "{x}-(r)ekin bat datozen argitalpenak",
   "search.search_or_paste": "Bilatu edo itsatsi URLa",
-  "search_popout.search_format": "Bilaketa aurreratuaren formatua",
-  "search_popout.tips.full_text": "Testu hutsarekin zuk idatzitako bidalketak, gogokoak, bultzadak edo aipamenak aurkitu ditzakezu, bat datozen erabiltzaile-izenak, pantaila-izenak, eta traolak.",
-  "search_popout.tips.hashtag": "traola",
-  "search_popout.tips.status": "bidalketa",
-  "search_popout.tips.text": "Testu hutsak pantaila-izenak, erabiltzaile-izenak eta traolak bilatzen ditu",
-  "search_popout.tips.user": "erabiltzailea",
-  "search_results.accounts": "Jendea",
+  "search_popout.quick_actions": "Ekintza azkarrak",
+  "search_popout.recent": "Duela gutxiko bilaketak",
+  "search_results.accounts": "Profilak",
   "search_results.all": "Guztiak",
   "search_results.hashtags": "Traolak",
   "search_results.nothing_found": "Ez da emaitzarik aurkitu bilaketa-termino horientzat",
@@ -540,9 +548,9 @@
   "server_banner.server_stats": "Zerbitzariaren estatistikak:",
   "sign_in_banner.create_account": "Sortu kontua",
   "sign_in_banner.sign_in": "Hasi saioa",
-  "sign_in_banner.text": "Hasi saioa beste zerbitzari bateko zure kontuarekin profilak edo traolak jarraitu, bidalketei erantzun, gogoko egin edo partekatzeko.",
+  "sign_in_banner.text": "Hasi saioa profilak edo traolak jarraitzeko, tutak gogokoetara gehitzeko, partekatzeko edo erantzuteko. Zure kontutik ere komunika zaitezke beste zerbitzari ezberdin batean.",
   "status.admin_account": "Ireki @{name} erabiltzailearen moderazio interfazea",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_domain": "{domain}-(r)en moderazio-interfazea ireki",
   "status.admin_status": "Ireki bidalketa hau moderazio interfazean",
   "status.block": "Blokeatu @{name}",
   "status.bookmark": "Laster-marka",
@@ -551,7 +559,8 @@
   "status.copy": "Kopiatu bidalketaren esteka",
   "status.delete": "Ezabatu",
   "status.detailed_status": "Elkarrizketaren ikuspegi xehetsua",
-  "status.direct": "Mezu zuzena @{name}(r)i",
+  "status.direct": "Pribatuki aipatu @{name}",
+  "status.direct_indicator": "Aipamen pribatua",
   "status.edit": "Editatu",
   "status.edited": "Editatua {date}",
   "status.edited_x_times": "{count, plural, one {behin} other {{count} aldiz}} editatua",
@@ -559,7 +568,7 @@
   "status.favourite": "Gogokoa",
   "status.filter": "Iragazi bidalketa hau",
   "status.filtered": "Iragazita",
-  "status.hide": "Hide post",
+  "status.hide": "Tuta ezkutatu",
   "status.history.created": "{name} erabiltzaileak sortua {date}",
   "status.history.edited": "{name} erabiltzaileak editatua {date}",
   "status.load_more": "Kargatu gehiago",
diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json
index a58d9824a..37e213c01 100644
--- a/app/javascript/mastodon/locales/fa.json
+++ b/app/javascript/mastodon/locales/fa.json
@@ -1,7 +1,7 @@
 {
   "about.blocks": "کارسازهای نظارت شده",
   "about.contact": "تماس:",
-  "about.disclaimer": "ماستودون نرم‌افزار آزاد، متن باز و یک شرکت غیر انتفاعی با مسئولیت محدود طبق قوانین آلمان است.",
+  "about.disclaimer": "ماستودون نرم‌افزار آزاد و یک شرکت غیر انتفاعی آلمانی با مسئولیت محدود است.",
   "about.domain_blocks.no_reason_available": "دلیلی موجود نیست",
   "about.domain_blocks.preamble": "ماستودون عموماً می‌گذارد محتوا را از از هر کارساز دیگری در دنیای شبکه‌های اجتماعی غیرمتمرکز دیده و با آنان برهم‌کنش داشته باشید. این‌ها استثناهایی هستند که روی این کارساز خاص وضع شده‌اند.",
   "about.domain_blocks.silenced.explanation": "عموماً نمایه‌ها و محتوا از این کارساز را نمی‌بینید، مگر این که به طور خاص دنبالشان گشته یا با پی گیری، داوطلب دیدنشان شوید.",
@@ -20,7 +20,7 @@
   "account.blocked": "مسدود",
   "account.browse_more_on_origin_server": "مرور بیش‌تر روی نمایهٔ اصلی",
   "account.cancel_follow_request": "رد کردن درخواست پی‌گیری",
-  "account.direct": "پیام مستقیم به ‎@{name}",
+  "account.direct": "خصوصی از @{name} نام ببرید",
   "account.disable_notifications": "آگاه کردن من هنگام فرسته‌های ‎@{name} را متوقّف کن",
   "account.domain_blocked": "دامنه مسدود شد",
   "account.edit_profile": "ویرایش نمایه",
@@ -29,14 +29,14 @@
   "account.featured_tags.last_status_at": "آخرین فرسته در {date}",
   "account.featured_tags.last_status_never": "بدون فرسته",
   "account.featured_tags.title": "برچسب‌های برگزیدهٔ {name}",
-  "account.follow": "پی‌گیری",
+  "account.follow": "پی‌گرفتن",
   "account.followers": "پی‌گیرندگان",
-  "account.followers.empty": "هنوز کسی این کاربر را پی‌گیری نمی‌کند.",
+  "account.followers.empty": "هنوز کسی پی‌گیر این کاربر نیست.",
   "account.followers_counter": "{count, plural, one {{counter} پی‌گیرنده} other {{counter} پی‌گیرنده}}",
   "account.following": "پی می‌گیرید",
   "account.following_counter": "{count, plural, one {{counter} پی‌گرفته} other {{counter} پی‌گرفته}}",
   "account.follows.empty": "این کاربر هنوز پی‌گیر کسی نیست.",
-  "account.follows_you": "پی می‌گیردتان",
+  "account.follows_you": "پی‌گیرتان است",
   "account.go_to_profile": "رفتن به نمایه",
   "account.hide_reblogs": "نهفتن تقویت‌های ‎@{name}",
   "account.joined_short": "پیوسته",
@@ -54,7 +54,7 @@
   "account.posts_with_replies": "فرسته‌ها و پاسخ‌ها",
   "account.report": "گزارش ‎@{name}",
   "account.requested": "منتظر پذیرش است. برای لغو درخواست پی‌گیری کلیک کنید",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name} درخواست پی‌گیریتان را داد",
   "account.share": "هم‌رسانی نمایهٔ ‎@{name}",
   "account.show_reblogs": "نمایش تقویت‌های ‎@{name}",
   "account.statuses_counter": "{count, plural, one {{counter} فرسته} other {{counter} فرسته}}",
@@ -62,7 +62,7 @@
   "account.unblock_domain": "رفع مسدودیت دامنهٔ {domain}",
   "account.unblock_short": "رفع مسدودیت",
   "account.unendorse": "معرّفی نکردن در نمایه",
-  "account.unfollow": "ناپی‌گیری",
+  "account.unfollow": "پی‌نگرفتن",
   "account.unmute": "ناخموشی ‎@{name}",
   "account.unmute_notifications": "ناخموشی آگاهی‌های ‎@{name}",
   "account.unmute_short": "ناخموشی",
@@ -102,7 +102,7 @@
   "column.blocks": "کاربران مسدود شده",
   "column.bookmarks": "نشانک‌ها",
   "column.community": "خط زمانی محلّی",
-  "column.direct": "پیام‌های مستقیم",
+  "column.direct": "خصوصی نام ببرید",
   "column.directory": "مرور نمایه‌ها",
   "column.domain_blocks": "دامنه‌های مسدود شده",
   "column.favourites": "پسندیده‌ها",
@@ -128,7 +128,7 @@
   "compose.language.search": "جست‌وجوی زبان‌ها…",
   "compose_form.direct_message_warning_learn_more": "بیشتر بدانید",
   "compose_form.encryption_warning": "فرسته‌های ماستودون رمزگذاری سرتاسری نشده‌اند. هیچ اطّلاعات حساسی را روی ماستودون هم‌رسانی نکنید.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "از آن‌جا که این فرسته عمومی نیست زیر هیچ برچسبی سیاهه نخواهد شد. تنها فرسته‌های عمومی می‌توانند با برچسب جست‌وجو شوند.",
   "compose_form.lock_disclaimer": "حسابتان {locked} نیست. هر کسی می‌تواند پی‌گیرتان شده و فرسته‌های ویژهٔ پی‌گیرانتان را ببیند.",
   "compose_form.lock_disclaimer.lock": "قفل‌شده",
   "compose_form.placeholder": "تازه چه خبر؟",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "تغییرات ذخیره نشده‌ای در توضیحات یا پیش‌نمایش رسانه دارید. همگی نادیده گرفته شوند؟",
   "confirmations.domain_block.confirm": "مسدود کردن تمام دامنه",
   "confirmations.domain_block.message": "آیا جدی جدی می‌خواهید تمام دامنهٔ {domain} را مسدود کنید؟ در بیشتر موارد مسدود کردن یا خموشاندن چند حساب خاص کافی است و توصیه می‌شود. پس از این کار شما هیچ محتوایی را از این دامنه در خط زمانی عمومی یا آگاهی‌هایتان نخواهید دید. پی‌گیرانتان از این دامنه هم برداشته خواهند شد.",
+  "confirmations.edit.confirm": "ویرایش",
+  "confirmations.edit.message": "در صورت ویرایش، پیامی که در حال نوشتنش بودید از بین خواهد رفت. می‌خواهید ادامه دهید؟",
   "confirmations.logout.confirm": "خروج از حساب",
   "confirmations.logout.message": "مطمئنید می‌خواهید خارج شوید؟",
   "confirmations.mute.confirm": "خموش",
@@ -171,7 +173,7 @@
   "confirmations.redraft.message": "مطمئنید که می‌خواهید این فرسته را حذف کنید و از نو بنویسید؟ با این کار تقویت‌ها و پسندهای آن از دست می‌رود و پاسخ‌ها به آن بی‌مرجع می‌شود.",
   "confirmations.reply.confirm": "پاسخ",
   "confirmations.reply.message": "اگر الان پاسخ دهید، چیزی که در حال نوشتنش بودید پاک خواهد شد. می‌خواهید ادامه دهید؟",
-  "confirmations.unfollow.confirm": "ناپی‌گیری",
+  "confirmations.unfollow.confirm": "پی‌نگرفتن",
   "confirmations.unfollow.message": "مطمئنید که می‌خواهید به پی‌گیری از {name} پایان دهید؟",
   "conversation.delete": "حذف گفتگو",
   "conversation.mark_as_read": "علامت‌گذاری به عنوان خوانده شده",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "هنوز کسی را مسدود نکرده‌اید.",
   "empty_column.bookmarked_statuses": "هنوز هیچ فرستهٔ نشانه‌گذاری شده‌ای ندارید. هنگامی که فرسته‌ای را نشانه‌گذاری کنید، این‌جا نشان داده خواهد شد.",
   "empty_column.community": "خط زمانی محلّی خالی است. چیزی بنویسید تا چرخش بچرخد!",
-  "empty_column.direct": "هنوز هیچ پیام مستقیمی ندارید. هنگامی که چنین پیامی بگیرید یا بفرستید این‌جا نشان داده خواهد شد.",
+  "empty_column.direct": "هنوز هیچ اشاره خصوصی‌ای ندارید. هنگامی که چنین پیامی بگیرید یا بفرستید این‌جا نشان داده خواهد شد.",
   "empty_column.domain_blocks": "هنوز هیچ دامنه‌ای مسدود نشده است.",
   "empty_column.explore_statuses": "الآن چیزی پرطرفدار نیست. بعداً دوباره بررسی کنید!",
   "empty_column.favourited_statuses": "شما هنوز هیچ فرسته‌ای را نپسندیده‌اید. هنگامی که فرسته‌ای را بپسندید، این‌جا نشان داده خواهد شد.",
   "empty_column.favourites": "هنوز هیچ کسی این فرسته را نپسندیده است. هنگامی که کسی آن را بپسندد، این‌جا نشان داده خواهد شد.",
-  "empty_column.follow_recommendations": "ظاهرا هیچ پیشنهادی برای شما نمی‌توانیم تولید کنیم. می‌توانید از امکان جست‌وجو برای یافتن افرادی که ممکن است بشناسید و یا کاوش میان برچسب‌های داغ استفاده کنید.",
+  "empty_column.follow_recommendations": "به نظر نمی‌توان هیچ پیشنهادی برایتان ایجاد کرد. می‌توانید برای یافتن افرادی که ممکن است بشناسید از جست‌وجو یا کاوش برچسب‌های داغ استفاده کنید.",
   "empty_column.follow_requests": "شما هنوز هیچ درخواست پی‌گیری‌ای ندارید. هنگامی که چنین درخواستی بگیرید، این‌جا نشان داده خواهد شد.",
+  "empty_column.followed_tags": "شما هیچ برچسبی را پی‌نگرفتید. هنگامی که برچسبی را پی‌گیری کنید اینجا نمایان می‌شوند.",
   "empty_column.hashtag": "هنوز هیچ چیزی در این برچسب نیست.",
   "empty_column.home": "خط زمانی خانگیتان خالی است! برای پر کردنش، افراد بیشتری را پی بگیرید. {suggestions}",
   "empty_column.home.suggestions": "چند پیشنهاد را ببینید",
@@ -239,8 +242,8 @@
   "explore.suggested_follows": "برای شما",
   "explore.title": "کاوش",
   "explore.trending_links": "اخبار",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "هشتگ‌ها",
+  "explore.trending_statuses": "فرسته‌ها",
+  "explore.trending_tags": "برچسب‌ها",
   "filter_modal.added.context_mismatch_explanation": "این دستهٔ پالایه به بافتاری که در آن به این فرسته دسترسی دارید اعمال نمی‌شود. اگر می‌خواهید فرسته در این بافتار هم پالوده شود، باید پالایه را ویرایش کنید.",
   "filter_modal.added.context_mismatch_title": "بافتار نامطابق!",
   "filter_modal.added.expired_explanation": "این دستهٔ پالایه منقضی شده است. برای اعمالش باید تاریخ انقضا را عوض کنید.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "اجازه دهید",
   "follow_request.reject": "رد کنید",
   "follow_requests.unlocked_explanation": "با این که حسابتان قفل نیست، کارکنان {domain} فکر کردند که ممکن است بخواهید درخواست‌ها از این حساب‌ها را به صورت دستی بازبینی کنید.",
+  "followed_tags": "برچسب‌های پی‌گرفته",
   "footer.about": "درباره",
   "footer.directory": "فهرست نمایه‌ها",
   "footer.get_app": "گرفتن کاره",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "میان‌برهای صفحه‌کلید",
   "footer.privacy_policy": "سیاست محرمانگی",
   "footer.source_code": "نمایش کد مبدأ",
+  "footer.status": "وضعیت",
   "generic.saved": "ذخیره شده",
   "getting_started.heading": "آغاز کنید",
   "hashtag.column_header.tag_mode.all": "و {additional}",
@@ -281,8 +286,8 @@
   "hashtag.column_settings.tag_mode.any": "هرکدام از این‌ها",
   "hashtag.column_settings.tag_mode.none": "هیچ‌کدام از این‌ها",
   "hashtag.column_settings.tag_toggle": "افزودن برچسب‌هایی بیشتر به این ستون",
-  "hashtag.follow": "پی‌گیری برچسب",
-  "hashtag.unfollow": "ناپی‌گیری برچسب",
+  "hashtag.follow": "پی‌گرفتن برچسب",
+  "hashtag.unfollow": "پی‌نگرفتن برچسب",
   "home.column_settings.basic": "پایه‌ای",
   "home.column_settings.show_reblogs": "نمایش تقویت‌ها",
   "home.column_settings.show_replies": "نمایش پاسخ‌ها",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "برای تمرکز روی یک فرسته در یکی از ستون‌ها",
   "keyboard_shortcuts.compose": "تمرکز روی محیط نوشتن",
   "keyboard_shortcuts.description": "توضیح",
-  "keyboard_shortcuts.direct": "برای گشودن ستون پیغام‌های مستقیم",
+  "keyboard_shortcuts.direct": "باز کردن ستون اشاره‌های خصوصی",
   "keyboard_shortcuts.down": "پایین بردن در سیاهه",
   "keyboard_shortcuts.enter": "گشودن فرسته",
   "keyboard_shortcuts.favourite": "پسندیدن فرسته",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "نشانک‌ها",
   "navigation_bar.community_timeline": "خط زمانی محلّی",
   "navigation_bar.compose": "نوشتن فرستهٔ تازه",
-  "navigation_bar.direct": "پیام‌های مستقیم",
+  "navigation_bar.direct": "اشاره‌های خصوصی",
   "navigation_bar.discover": "گشت و گذار",
   "navigation_bar.domain_blocks": "دامنه‌های مسدود شده",
   "navigation_bar.edit_profile": "ویرایش نمایه",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "پسندیده‌ها",
   "navigation_bar.filters": "واژه‌های خموش",
   "navigation_bar.follow_requests": "درخواست‌های پی‌گیری",
+  "navigation_bar.followed_tags": "برچسب‌های پی‌گرفته",
   "navigation_bar.follows_and_followers": "پی‌گرفتگان و پی‌گیرندگان",
   "navigation_bar.lists": "سیاهه‌ها",
   "navigation_bar.logout": "خروج",
@@ -445,7 +451,7 @@
   "poll.total_votes": "{count, plural, one {# رأی} other {# رأی}}",
   "poll.vote": "رأی",
   "poll.voted": "شما به این جواب رأی دادید",
-  "poll.votes": "# رأی",
+  "poll.votes": "{votes, plural, one {# رأی} other {# رأی}}",
   "poll_button.add_poll": "افزودن نظرسنجی",
   "poll_button.remove_poll": "برداشتن نظرسنجی",
   "privacy.change": "تغییر محرمانگی فرسته",
@@ -509,22 +515,24 @@
   "report.thanks.take_action_actionable": "در حالی که ما این مورد را بررسی می‌کنیم، می‌توانید علیه ‎@{name} اقدام کنید:",
   "report.thanks.title": "نمی‌خواهید این را ببینید؟",
   "report.thanks.title_actionable": "ممنون بابت گزارش، ما آن را بررسی خواهیم کرد.",
-  "report.unfollow": "ناپی‌گیری ‎@{name}",
+  "report.unfollow": "پی‌نگرفتن ‎@{name}",
   "report.unfollow_explanation": "شما این حساب را پی‌گرفته‌اید، برای اینکه دیگر فرسته‌هایش را در خوراک خانه‌تان نبینید؛ آن را پی‌نگیرید.",
   "report_notification.attached_statuses": "{count, plural, one {{count} فرسته} other {{count} فرسته}} پیوست شده",
   "report_notification.categories.other": "دیگر",
   "report_notification.categories.spam": "هرزنامه",
   "report_notification.categories.violation": "تخطّی از قانون",
   "report_notification.open": "گشودن گزارش",
+  "search.no_recent_searches": "جست‌وجوی اخیری نیست",
   "search.placeholder": "جست‌وجو",
+  "search.quick_action.account_search": "نمایه‌های جور با {x}",
+  "search.quick_action.go_to_account": "برو به نمایه {x}",
+  "search.quick_action.go_to_hashtag": "برو به برچسب {x}",
+  "search.quick_action.open_url": "باز کردن پیوند در ماستودون",
+  "search.quick_action.status_search": "فرسته‌های جور با {x}",
   "search.search_or_paste": "جست‌وجو یا جایگذاری نشانی",
-  "search_popout.search_format": "راهنمای جست‌وجوی پیشرفته",
-  "search_popout.tips.full_text": "جست‌وجوی متنی ساده فرسته‌هایی که نوشته، پسندیده، تقویت‌کرده یا در آن‌ها نام‌برده شده‌اید را به علاوهٔ نام‌های کاربری، نام‌های نمایشی و برچسب‌ها برمی‌گرداند.",
-  "search_popout.tips.hashtag": "برچسب",
-  "search_popout.tips.status": "فرسته",
-  "search_popout.tips.text": "جست‌وجوی متنی ساده برای نام‌ها، نام‌های کاربری، و برچسب‌ها",
-  "search_popout.tips.user": "کاربر",
-  "search_results.accounts": "افراد",
+  "search_popout.quick_actions": "کنش‌های سریع",
+  "search_popout.recent": "جست‌وجوهای اخیر",
+  "search_results.accounts": "نمایه‌ها",
   "search_results.all": "همه",
   "search_results.hashtags": "برچسب‌ها",
   "search_results.nothing_found": "چیزی برای این عبارت جست‌وجو یافت نشد",
@@ -540,9 +548,9 @@
   "server_banner.server_stats": "آمار کارساز:",
   "sign_in_banner.create_account": "ایجاد حساب",
   "sign_in_banner.sign_in": "ورود",
-  "sign_in_banner.text": "برای پی‌گیری نمایه‌ها یا برچسب‌ها، هم‌رسانی و پاسخ به فرسته‌ها یا تعامل از حسابتان روی کارسازی دیگر، وارد شوید.",
+  "sign_in_banner.text": "برای پی‌گیری نمایه‌ها یا برچسب‌ها، پسندیدن، هم‌رسانی و یا پاسخ به فرسته‌ها وارد شوید. همچنین می‌توانید این کارها را با حسابتان در کارسازی دیگر انجام دهید.",
   "status.admin_account": "گشودن واسط مدیریت برای ‎@{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_domain": "گشودن واسط مدیریت برای ‎{domain}",
   "status.admin_status": "گشودن این فرسته در واسط مدیریت",
   "status.block": "مسدود کردن ‎@{name}",
   "status.bookmark": "نشانک",
@@ -551,7 +559,8 @@
   "status.copy": "رونوشت از پیوند فرسته",
   "status.delete": "حذف",
   "status.detailed_status": "نمایش کامل گفتگو",
-  "status.direct": "پیام مستقیم به ‎@{name}",
+  "status.direct": "خصوصی به @{name} اشاره کنید",
+  "status.direct_indicator": "اشاره خصوصی",
   "status.edit": "ویرایش",
   "status.edited": "ویرایش شده در {date}",
   "status.edited_x_times": "{count, plural, one {{count} مرتبه} other {{count} مرتبه}} ویرایش شد",
@@ -559,7 +568,7 @@
   "status.favourite": "پسندیدن",
   "status.filter": "پالایش این فرسته",
   "status.filtered": "پالوده",
-  "status.hide": "Hide post",
+  "status.hide": "نهفتن فرسته",
   "status.history.created": "توسط {name} در {date} ایجاد شد",
   "status.history.edited": "توسط {name} در {date} ویرایش شد",
   "status.load_more": "بار کردن بیش‌تر",
@@ -610,7 +619,7 @@
   "time_remaining.moments": "زمان باقی‌مانده",
   "time_remaining.seconds": "{number, plural, one {# ثانیه} other {# ثانیه}} باقی مانده",
   "timeline_hint.remote_resource_not_displayed": "{resource} از دیگر کارسازها نمایش داده نمی‌شوند.",
-  "timeline_hint.resources.followers": "پیگیرندگان",
+  "timeline_hint.resources.followers": "پی‌گیرندگان",
   "timeline_hint.resources.follows": "پی‌گرفتگان",
   "timeline_hint.resources.statuses": "فرسته‌های قدیمی‌تر",
   "trends.counter_by_accounts": "{count, plural, one {{counter} نفر} other {{counter} نفر}} در {days, plural, one {روز} other {{days} روز}} گذشته",
diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json
index 5f3f7a416..1069ef8e2 100644
--- a/app/javascript/mastodon/locales/fi.json
+++ b/app/javascript/mastodon/locales/fi.json
@@ -1,6 +1,6 @@
 {
   "about.blocks": "Moderoidut palvelimet",
-  "about.contact": "Yhteystiedot:",
+  "about.contact": "Ota yhteyttä:",
   "about.disclaimer": "Mastodon on vapaa avoimen lähdekoodin ohjelmisto ja Mastodon gGmbH:n tavaramerkki.",
   "about.domain_blocks.no_reason_available": "Syytä ei ole ilmoitettu",
   "about.domain_blocks.preamble": "Yleisesti Mastodonin avulla voidaan tarkastella minkä tahansa muun fediverse-palvelinten sisältöä ja vuorovaikuttaa eri palvelinten käyttäjien kanssa. Nämä ovat tälle palvelimelle määritetyt poikkeukset.",
@@ -9,7 +9,7 @@
   "about.domain_blocks.suspended.explanation": "Mitään tämän palvelimen tietoja ei käsitellä, tallenneta tai vaihdeta, mikä tekee vuorovaikutuksesta ja viestinnästä sen käyttäjien kanssa mahdotonta.",
   "about.domain_blocks.suspended.title": "Jäädytetty",
   "about.not_available": "Näitä tietoja ei ole julkaistu tällä palvelimella.",
-  "about.powered_by": "Hajautettu sosiaalinen media, tarjoaa {mastodon}",
+  "about.powered_by": "Hajautetun sosiaalisen median tarjoaa {mastodon}",
   "about.rules": "Palvelimen säännöt",
   "account.account_note_header": "Muistiinpano",
   "account.add_or_remove_from_list": "Lisää tai poista listoilta",
@@ -20,16 +20,16 @@
   "account.blocked": "Estetty",
   "account.browse_more_on_origin_server": "Selaile lisää alkuperäisellä palvelimella",
   "account.cancel_follow_request": "Peruuta seurantapyyntö",
-  "account.direct": "Yksityisviesti käyttäjälle @{name}",
-  "account.disable_notifications": "Lopeta @{name}:n julkaisuista ilmoittaminen",
-  "account.domain_blocked": "Verkko-osoite estetty",
+  "account.direct": "Mainitse @{name} yksityisesti",
+  "account.disable_notifications": "Lopeta ilmoittamasta minulle, kun @{name} julkaisee",
+  "account.domain_blocked": "Palvelu estetty",
   "account.edit_profile": "Muokkaa profiilia",
-  "account.enable_notifications": "Ilmoita @{name}:n julkaisuista",
+  "account.enable_notifications": "Ilmoita kun käyttäjä @{name} julkaisee viestin",
   "account.endorse": "Suosittele profiilissasi",
   "account.featured_tags.last_status_at": "Viimeisin viesti {date}",
   "account.featured_tags.last_status_never": "Ei viestejä",
   "account.featured_tags.title": "Käyttäjän {name} esillä olevat aihetunnisteet",
-  "account.follow": "Seuratut",
+  "account.follow": "Seuraa",
   "account.followers": "Seuraajat",
   "account.followers.empty": "Kukaan ei seuraa tätä käyttäjää vielä.",
   "account.followers_counter": "{count, plural, one {{counter} seuraaja} other {{counter} seuraajaa}}",
@@ -38,7 +38,7 @@
   "account.follows.empty": "Tämä käyttäjä ei vielä seuraa ketään.",
   "account.follows_you": "Seuraa sinua",
   "account.go_to_profile": "Avaa profiili",
-  "account.hide_reblogs": "Piilota käyttäjän @{name} buustaukset",
+  "account.hide_reblogs": "Piilota käyttäjän @{name} tehostukset",
   "account.joined_short": "Liittynyt",
   "account.languages": "Vaihda tilattuja kieliä",
   "account.link_verified_on": "Linkin omistus tarkistettiin {date}",
@@ -50,15 +50,15 @@
   "account.mute_notifications": "Mykistä käyttäjän @{name} ilmoitukset",
   "account.muted": "Mykistetty",
   "account.open_original_page": "Avaa alkuperäinen sivu",
-  "account.posts": "Viestit",
+  "account.posts": "Julkaisut",
   "account.posts_with_replies": "Viestit ja vastaukset",
   "account.report": "Ilmoita käyttäjästä @{name}",
   "account.requested": "Odottaa hyväksyntää. Peruuta seuraamispyyntö klikkaamalla",
   "account.requested_follow": "{name} on pyytänyt lupaa seurata sinua",
   "account.share": "Jaa käyttäjän @{name} profiili",
-  "account.show_reblogs": "Näytä buustaukset käyttäjältä @{name}",
-  "account.statuses_counter": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}}",
-  "account.unblock": "Salli @{name}",
+  "account.show_reblogs": "Näytä tehostukset käyttäjältä @{name}",
+  "account.statuses_counter": "{count, plural, one {{counter} viesti} other {{counter} viestiä}}",
+  "account.unblock": "Poista esto: @{name}",
   "account.unblock_domain": "Salli palvelu {domain}",
   "account.unblock_short": "Poista esto",
   "account.unendorse": "Poista suosittelu profiilistasi",
@@ -102,9 +102,9 @@
   "column.blocks": "Estetyt käyttäjät",
   "column.bookmarks": "Kirjanmerkit",
   "column.community": "Paikallinen aikajana",
-  "column.direct": "Yksityisviestit",
+  "column.direct": "Yksityiset maininnat",
   "column.directory": "Selaa profiileja",
-  "column.domain_blocks": "Estetytr verkkotunnukset",
+  "column.domain_blocks": "Estetyt palvelut",
   "column.favourites": "Suosikit",
   "column.follow_requests": "Seuraamispyynnöt",
   "column.home": "Koti",
@@ -149,30 +149,32 @@
   "compose_form.spoiler.unmarked": "Lisää sisältövaroitus",
   "compose_form.spoiler_placeholder": "Kirjoita varoituksesi tähän",
   "confirmation_modal.cancel": "Peruuta",
-  "confirmations.block.block_and_report": "Estä ja raportoi",
+  "confirmations.block.block_and_report": "Estä ja ilmianna",
   "confirmations.block.confirm": "Estä",
   "confirmations.block.message": "Haluatko varmasti estää käyttäjän {name}?",
   "confirmations.cancel_follow_request.confirm": "Peruuta pyyntö",
-  "confirmations.cancel_follow_request.message": "Haluatko varmasti peruuttaa pyyntösi seurata käyttäjää {name}?",
+  "confirmations.cancel_follow_request.message": "Haluatko varmasti peruuttaa pyyntösi seurata profiilia {name}?",
   "confirmations.delete.confirm": "Poista",
-  "confirmations.delete.message": "Haluatko varmasti poistaa tämän julkaisun?",
+  "confirmations.delete.message": "Haluatko varmasti poistaa tämän viestin?",
   "confirmations.delete_list.confirm": "Poista",
   "confirmations.delete_list.message": "Haluatko varmasti poistaa tämän listan kokonaan?",
   "confirmations.discard_edit_media.confirm": "Hylkää",
-  "confirmations.discard_edit_media.message": "Onko sinulla tallentamattomia muutoksia kuvaukseen tai esikatseluun, hylätäänkö ne silti?",
-  "confirmations.domain_block.confirm": "Estä koko palvelu",
+  "confirmations.discard_edit_media.message": "Sinulla on tallentamattomia muutoksia median kuvaukseen tai esikatseluun, hylätäänkö ne silti?",
+  "confirmations.domain_block.confirm": "Estä koko verkkotunnus",
   "confirmations.domain_block.message": "Haluatko aivan varmasti estää palvelun {domain} täysin? Useimmiten muutama kohdistettu esto tai mykistys on riittävä ja suositeltava toimenpide. Et näe kyseisen sisältöä kyseiseltä verkkoalueelta missään julkisissa aikajanoissa tai ilmoituksissa. Tälle verkkoalueelle kuuluvat seuraajasi poistetaan.",
+  "confirmations.edit.confirm": "Muokkaa",
+  "confirmations.edit.message": "Muokkaaminen nyt korvaa viestin, jota paraikaa työstät. Haluatko varmasti jatkaa?",
   "confirmations.logout.confirm": "Kirjaudu ulos",
-  "confirmations.logout.message": "Oletko varma, että haluat kirjautua ulos?",
+  "confirmations.logout.message": "Haluatko varmasti kirjautua ulos?",
   "confirmations.mute.confirm": "Mykistä",
   "confirmations.mute.explanation": "Tämä toiminto piilottaa heidän julkaisunsa sinulta – mukaan lukien ne, joissa heidät mainitaan – sallien heidän yhä nähdä julkaisusi ja seurata sinua.",
-  "confirmations.mute.message": "Haluatko varmasti mykistää käyttäjän {name}?",
+  "confirmations.mute.message": "Haluatko varmasti mykistää profiilin {name}?",
   "confirmations.redraft.confirm": "Poista & palauta muokattavaksi",
-  "confirmations.redraft.message": "Oletko varma että haluat poistaa tämän julkaisun ja tehdä siitä uuden luonnoksen? Suosikit ja buustaukset menetään, alkuperäisen julkaisusi vastaukset jäävät orvoiksi.",
+  "confirmations.redraft.message": "Oletko varma että haluat poistaa tämän julkaisun ja tehdä siitä uuden luonnoksen? Suosikit ja tehostukset menetään, alkuperäisen julkaisusi vastaukset jäävät orvoiksi.",
   "confirmations.reply.confirm": "Vastaa",
   "confirmations.reply.message": "Jos vastaat nyt, vastaus korvaa tällä hetkellä työstämäsi viestin. Oletko varma, että haluat jatkaa?",
   "confirmations.unfollow.confirm": "Lopeta seuraaminen",
-  "confirmations.unfollow.message": "Haluatko varmasti lakata seuraamasta käyttäjää {name}?",
+  "confirmations.unfollow.message": "Haluatko varmasti lakata seuraamasta profiilia {name}?",
   "conversation.delete": "Poista keskustelu",
   "conversation.mark_as_read": "Merkitse luetuksi",
   "conversation.open": "Näytä keskustelu",
@@ -208,27 +210,28 @@
   "emoji_button.search_results": "Hakutulokset",
   "emoji_button.symbols": "Symbolit",
   "emoji_button.travel": "Matkailu ja paikat",
-  "empty_column.account_suspended": "Tilin käyttäminen keskeytetty",
+  "empty_column.account_suspended": "Tili jäädytetty",
   "empty_column.account_timeline": "Ei viestejä täällä.",
   "empty_column.account_unavailable": "Profiilia ei löydy",
-  "empty_column.blocks": "Et ole vielä estänyt käyttäjiä.",
+  "empty_column.blocks": "Et ole estänyt käyttäjiä.",
   "empty_column.bookmarked_statuses": "Et ole vielä lisännyt viestejä kirjanmerkkeihisi. Kun lisäät yhden, se näkyy tässä.",
   "empty_column.community": "Paikallinen aikajana on tyhjä. Kirjoita jotain julkista, niin homma lähtee käyntiin!",
-  "empty_column.direct": "Sinulla ei ole vielä yksityisviestejä. Kun lähetät tai vastaanotat sellaisen, se näkyy tässä.",
+  "empty_column.direct": "Yksityisiä mainintoja ei vielä ole. Kun lähetät tai vastaanotat sellaisen, näet sen täältä.",
   "empty_column.domain_blocks": "Palveluita ei ole vielä estetty.",
-  "empty_column.explore_statuses": "Mikään ei ole nyt trendi. Tarkista myöhemmin!",
+  "empty_column.explore_statuses": "Mikään ei trendaa nyt. Tarkista myöhemmin uudelleen!",
   "empty_column.favourited_statuses": "Et ole vielä lisännyt viestejä kirjanmerkkeihisi. Kun lisäät yhden, se näkyy tässä.",
   "empty_column.favourites": "Kukaan ei ole vielä lisännyt tätä viestiä suosikkeihinsa. Kun joku tekee niin, näkyy kyseinen henkilö tässä.",
   "empty_column.follow_recommendations": "Näyttää siltä, että sinulle ei voi luoda ehdotuksia. Voit yrittää etsiä ihmisiä, jotka saatat tuntea tai tutkia trendaavia aihetunnisteita.",
   "empty_column.follow_requests": "Et ole vielä vastaanottanut seurauspyyntöjä. Saamasi pyynnöt näytetään täällä.",
-  "empty_column.hashtag": "Tällä hashtagilla ei ole vielä mitään.",
+  "empty_column.followed_tags": "Et ole vielä ottanut yhtään aihetunnistetta seurattavaksesi. Jos tai kun sitten teet niin, ne listautuvat tänne.",
+  "empty_column.hashtag": "Tällä aihetunnisteella ei ole vielä mitään.",
   "empty_column.home": "Kotisi aikajana on tyhjä! Seuraa lisää ihmisiä täyttääksesi sen. {suggestions}",
   "empty_column.home.suggestions": "Katso joitakin ehdotuksia",
   "empty_column.list": "Tässä luettelossa ei ole vielä mitään. Kun tämän luettelon jäsenet julkaisevat uusia viestejä, ne näkyvät täällä.",
   "empty_column.lists": "Sinulla ei ole vielä yhtään listaa. Kun luot sellaisen, näkyy se tässä.",
   "empty_column.mutes": "Et ole mykistänyt vielä yhtään käyttäjää.",
-  "empty_column.notifications": "Sinulla ei ole vielä ilmoituksia. Kun muut ihmiset ovat vuorovaikutuksessa kanssasi, näet sen täällä.",
-  "empty_column.public": "Täällä ei ole mitään! Kirjoita jotain julkisesti tai manuaalisesti seuraa muiden palvelimien käyttäjiä niin saat sisältöä",
+  "empty_column.notifications": "Sinulla ei ole vielä ilmoituksia. Kun keskustelet muille, näet sen täällä.",
+  "empty_column.public": "Täällä ei ole mitään! Kirjoita jotain julkisesti. Voit myös seurata muiden palvelimien käyttäjiä",
   "error.unexpected_crash.explanation": "Sivua ei voi näyttää oikein, johtuen bugista tai ongelmasta selaimen yhteensopivuudessa.",
   "error.unexpected_crash.explanation_addons": "Sivua ei voitu näyttää oikein. Tämä virhe johtuu todennäköisesti selaimen lisäosasta tai automaattisista käännöstyökaluista.",
   "error.unexpected_crash.next_steps": "Kokeile sivun päivitystä. Jos se ei auta, voi Mastodonin käyttö silti olla mahdollista eri selaimella tai natiivilla sovelluksella.",
@@ -247,7 +250,7 @@
   "filter_modal.added.expired_title": "Vanhentunut suodatin!",
   "filter_modal.added.review_and_configure": "Voit tarkastella tätä suodatinluokkaa ja määrittää sen tarkemmin siirtymällä {settings_link}.",
   "filter_modal.added.review_and_configure_title": "Suodattimen asetukset",
-  "filter_modal.added.settings_link": "asetukset sivu",
+  "filter_modal.added.settings_link": "asetukset-sivulle",
   "filter_modal.added.short_explanation": "Tämä viesti on lisätty seuraavaan suodatinluokkaan: {title}.",
   "filter_modal.added.title": "Suodatin lisätty!",
   "filter_modal.select_filter.context_mismatch": "ei sovellu tähän asiayhteyteen",
@@ -258,11 +261,12 @@
   "filter_modal.select_filter.title": "Suodata tämä viesti",
   "filter_modal.title.status": "Suodata viesti",
   "follow_recommendations.done": "Valmis",
-  "follow_recommendations.heading": "Seuraa ihmisiä, joilta haluaisit nähdä julkaisuja! Tässä on muutamia ehdotuksia.",
+  "follow_recommendations.heading": "Seuraa ihmisiä, joilta haluat nähdä viestejä! Tässä on muutamia ehdotuksia.",
   "follow_recommendations.lead": "Seuraamiesi julkaisut näkyvät aikajärjestyksessä kotisyötteessä. Älä pelkää seurata vahingossa, voit lopettaa seuraamisen yhtä helposti!",
   "follow_request.authorize": "Valtuuta",
   "follow_request.reject": "Hylkää",
-  "follow_requests.unlocked_explanation": "Vaikkei tiliäsi ole lukittu, palvelun {domain} ylläpito on arvioinut, että voi olla halukas tarkistamaan nämä seurauspyynnöt erikseen.",
+  "follow_requests.unlocked_explanation": "Vaikkei tiliäsi ole lukittu, on palvelun {domain} ylläpito arvioinut, että saatat olla halukas tarkistamaan nämä seurauspyynnöt erikseen.",
+  "followed_tags": "Seuratut aihetunnisteet",
   "footer.about": "Tietoja",
   "footer.directory": "Profiilihakemisto",
   "footer.get_app": "Hanki sovellus",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Pikanäppäimet",
   "footer.privacy_policy": "Tietosuojakäytäntö",
   "footer.source_code": "Näytä lähdekoodi",
+  "footer.status": "Tila",
   "generic.saved": "Tallennettu",
   "getting_started.heading": "Näin pääset alkuun",
   "hashtag.column_header.tag_mode.all": "ja {additional}",
@@ -284,32 +289,32 @@
   "hashtag.follow": "Seuraa aihetunnistetta",
   "hashtag.unfollow": "Lopeta aihetunnisteen seuraaminen",
   "home.column_settings.basic": "Perusasetukset",
-  "home.column_settings.show_reblogs": "Näytä buustaukset",
+  "home.column_settings.show_reblogs": "Näytä tehostukset",
   "home.column_settings.show_replies": "Näytä vastaukset",
   "home.hide_announcements": "Piilota ilmoitukset",
   "home.show_announcements": "Näytä ilmoitukset",
   "interaction_modal.description.favourite": "Kun sinulla on tili Mastodonissa, voit lisätä tämän viestin suosikkeihin ja tallentaa sen myöhempää käyttöä varten.",
-  "interaction_modal.description.follow": "Kun sinulla on tili Mastodonissa, voit seurata {name} saadaksesi hänen viestejä sinun kotisyötteeseen.",
+  "interaction_modal.description.follow": "Kun sinulla on tili Mastodonissa, voit seurata käyttäjää {name} saadaksesi hänen viestit kotisyötteeseesi.",
   "interaction_modal.description.reblog": "Kun sinulla on tili Mastodonissa, voit tehostaa viestiä ja jakaa sen omien seuraajiesi kanssa.",
   "interaction_modal.description.reply": "Kun sinulla on tili Mastodonissa, voit vastata tähän viestiin.",
   "interaction_modal.on_another_server": "Toisella palvelimella",
   "interaction_modal.on_this_server": "Tällä palvelimella",
   "interaction_modal.other_server_instructions": "Kopioi ja liitä tämä URL-osoite käyttämäsi Mastodon-sovelluksen hakukenttään tai Mastodon-palvelimen web-käyttöliittymään.",
   "interaction_modal.preamble": "Koska Mastodon on hajautettu, voit käyttää toisen Mastodon-palvelimen tai yhteensopivan alustan ylläpitämää tiliäsi, jos sinulla ei ole tiliä tällä palvelimella.",
-  "interaction_modal.title.favourite": "Suosikin {name} viesti",
+  "interaction_modal.title.favourite": "Aseta käyttäjän {name} viesti suosikiksi",
   "interaction_modal.title.follow": "Seuraa {name}",
-  "interaction_modal.title.reblog": "Tehosta {name} viestiä",
+  "interaction_modal.title.reblog": "Tehosta käyttäjän {name} viestiä",
   "interaction_modal.title.reply": "Vastaa käyttäjän {name} viestiin",
   "intervals.full.days": "{number, plural, one {# päivä} other {# päivää}}",
   "intervals.full.hours": "{number, plural, one {# tunti} other {# tuntia}}",
   "intervals.full.minutes": "{number, plural, one {# minuutti} other {# minuuttia}}",
   "keyboard_shortcuts.back": "Siirry takaisin",
   "keyboard_shortcuts.blocked": "Avaa estettyjen käyttäjien luettelo",
-  "keyboard_shortcuts.boost": "Buustaa viestiä",
+  "keyboard_shortcuts.boost": "Tehosta viestiä",
   "keyboard_shortcuts.column": "Kohdista sarakkeeseen",
   "keyboard_shortcuts.compose": "siirry tekstinsyöttöön",
   "keyboard_shortcuts.description": "Kuvaus",
-  "keyboard_shortcuts.direct": "avaa yksityisviesti sarake",
+  "keyboard_shortcuts.direct": "avataksesi yksityisten mainintojen sarakkeen",
   "keyboard_shortcuts.down": "Siirry listassa alaspäin",
   "keyboard_shortcuts.enter": "Avaa julkaisu",
   "keyboard_shortcuts.favourite": "Lisää suosikkeihin",
@@ -323,18 +328,18 @@
   "keyboard_shortcuts.mention": "Mainitse julkaisija",
   "keyboard_shortcuts.muted": "Avaa lista mykistetyistä käyttäjistä",
   "keyboard_shortcuts.my_profile": "Avaa profiilisi",
-  "keyboard_shortcuts.notifications": "Avaa ilmoitukset-sarake",
+  "keyboard_shortcuts.notifications": "Avaa ilmoitukset-valikko",
   "keyboard_shortcuts.open_media": "Avaa media",
   "keyboard_shortcuts.pinned": "Avaa lista kiinnitetyistä viesteistä",
   "keyboard_shortcuts.profile": "Avaa kirjoittajan profiili",
   "keyboard_shortcuts.reply": "Vastaa viestiin",
   "keyboard_shortcuts.requests": "Avaa lista seurauspyynnöistä",
   "keyboard_shortcuts.search": "siirry hakukenttään",
-  "keyboard_shortcuts.spoilers": "näyttääksesi/piilottaaksesi CW kentän",
-  "keyboard_shortcuts.start": "avaa \"Aloitus\" -sarake",
+  "keyboard_shortcuts.spoilers": "Näytä/piilota sisältövaroituskenttä",
+  "keyboard_shortcuts.start": "avaa \"Aloitus\"",
   "keyboard_shortcuts.toggle_hidden": "näytä/piilota sisältövaroituksella merkitty teksti",
   "keyboard_shortcuts.toggle_sensitivity": "näytä/piilota media",
-  "keyboard_shortcuts.toot": "Aloita uusi viesti",
+  "keyboard_shortcuts.toot": "Luo uusi julkaisu",
   "keyboard_shortcuts.unfocus": "Poistu teksti-/hakukentästä",
   "keyboard_shortcuts.up": "Siirry listassa ylöspäin",
   "lightbox.close": "Sulje",
@@ -352,7 +357,7 @@
   "lists.new.create": "Lisää lista",
   "lists.new.title_placeholder": "Uuden listan nimi",
   "lists.replies_policy.followed": "Jokainen seurattu käyttäjä",
-  "lists.replies_policy.list": "Luettelon jäsenet",
+  "lists.replies_policy.list": "Listan jäsenet",
   "lists.replies_policy.none": "Ei kukaan",
   "lists.replies_policy.title": "Näytä vastaukset:",
   "lists.search": "Etsi seuraamistasi henkilöistä",
@@ -370,8 +375,8 @@
   "navigation_bar.blocks": "Estetyt käyttäjät",
   "navigation_bar.bookmarks": "Kirjanmerkit",
   "navigation_bar.community_timeline": "Paikallinen aikajana",
-  "navigation_bar.compose": "Luo uusi viesti",
-  "navigation_bar.direct": "Yksityisviestit",
+  "navigation_bar.compose": "Julkaise",
+  "navigation_bar.direct": "Yksityiset maininnat",
   "navigation_bar.discover": "Löydä uutta",
   "navigation_bar.domain_blocks": "Estetyt palvelut",
   "navigation_bar.edit_profile": "Muokkaa profiilia",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Suosikit",
   "navigation_bar.filters": "Mykistetyt sanat",
   "navigation_bar.follow_requests": "Seuraamispyynnöt",
+  "navigation_bar.followed_tags": "Seuratut aihetunnisteet",
   "navigation_bar.follows_and_followers": "Seurattavat ja seuraajat",
   "navigation_bar.lists": "Listat",
   "navigation_bar.logout": "Kirjaudu ulos",
@@ -389,21 +395,21 @@
   "navigation_bar.public_timeline": "Yleinen aikajana",
   "navigation_bar.search": "Haku",
   "navigation_bar.security": "Turvallisuus",
-  "not_signed_in_indicator.not_signed_in": "Sinun täytyy kirjautua sisään päästäksesi käsiksi tähän resurssiin.",
-  "notification.admin.report": "{name} ilmoitti {target}",
-  "notification.admin.sign_up": "{name} rekisteröitynyt",
-  "notification.favourite": "{name} tykkäsi viestistäsi",
+  "not_signed_in_indicator.not_signed_in": "Sinun tulee kirjautua sisään nähdäksesi tämän.",
+  "notification.admin.report": "{name} teki ilmoituksen käytäjästä {target}",
+  "notification.admin.sign_up": "{name} rekisteröityi",
+  "notification.favourite": "{name} tykkäsi julkaisustasi",
   "notification.follow": "{name} seurasi sinua",
   "notification.follow_request": "{name} haluaa seurata sinua",
   "notification.mention": "{name} mainitsi sinut",
   "notification.own_poll": "Kyselysi on päättynyt",
   "notification.poll": "Kysely, johon osallistuit, on päättynyt",
-  "notification.reblog": "{name} buustasi julkaisusi",
-  "notification.status": "{name} julkaisi juuri",
+  "notification.reblog": "{name} tehosti viestiäsi",
+  "notification.status": "{name} julkaisi juuri viestin",
   "notification.update": "{name} muokkasi viestiä",
   "notifications.clear": "Tyhjennä ilmoitukset",
   "notifications.clear_confirmation": "Haluatko varmasti poistaa kaikki ilmoitukset pysyvästi?",
-  "notifications.column_settings.admin.report": "Uudet raportit:",
+  "notifications.column_settings.admin.report": "Uudet ilmoitukset:",
   "notifications.column_settings.admin.sign_up": "Uudet kirjautumiset:",
   "notifications.column_settings.alert": "Työpöytäilmoitukset",
   "notifications.column_settings.favourite": "Tykkäykset:",
@@ -415,7 +421,7 @@
   "notifications.column_settings.mention": "Maininnat:",
   "notifications.column_settings.poll": "Kyselyn tulokset:",
   "notifications.column_settings.push": "Push-ilmoitukset",
-  "notifications.column_settings.reblog": "Buustit:",
+  "notifications.column_settings.reblog": "Tehostukset:",
   "notifications.column_settings.show": "Näytä sarakkeessa",
   "notifications.column_settings.sound": "Äänimerkki",
   "notifications.column_settings.status": "Uudet julkaisut:",
@@ -423,7 +429,7 @@
   "notifications.column_settings.unread_notifications.highlight": "Korosta lukemattomat ilmoitukset",
   "notifications.column_settings.update": "Muokkaukset:",
   "notifications.filter.all": "Kaikki",
-  "notifications.filter.boosts": "Buustit",
+  "notifications.filter.boosts": "Tehostukset",
   "notifications.filter.favourites": "Suosikit",
   "notifications.filter.follows": "Seuraa",
   "notifications.filter.mentions": "Maininnat",
@@ -448,10 +454,10 @@
   "poll.votes": "{votes, plural, one {# ääni} other {# ääntä}}",
   "poll_button.add_poll": "Lisää kysely",
   "poll_button.remove_poll": "Poista kysely",
-  "privacy.change": "Muuta julkaisun näkyvyyttä",
-  "privacy.direct.long": "Julkaise vain mainituille käyttäjille",
+  "privacy.change": "Muuta viestin näkyvyyttä",
+  "privacy.direct.long": "Näkyvissä vain mainituille käyttäjille",
   "privacy.direct.short": "Vain mainitut henkilöt",
-  "privacy.private.long": "Julkaise vain seuraajille",
+  "privacy.private.long": "Näkyvissä vain seuraajille",
   "privacy.private.short": "Vain seuraajat",
   "privacy.public.long": "Näkyvissä kaikille",
   "privacy.public.short": "Julkinen",
@@ -468,32 +474,32 @@
   "relative_time.full.just_now": "juuri nyt",
   "relative_time.full.minutes": "{number, plural, one {# minuutti} other {# minuuttia}} sitten",
   "relative_time.full.seconds": "{number, plural, one {# sekunti} other {# sekuntia}} sitten",
-  "relative_time.hours": "{number} tuntia",
+  "relative_time.hours": "{number} t",
   "relative_time.just_now": "nyt",
   "relative_time.minutes": "{number} min",
-  "relative_time.seconds": "{number} sek",
+  "relative_time.seconds": "{number} s",
   "relative_time.today": "tänään",
   "reply_indicator.cancel": "Peruuta",
   "report.block": "Estä",
-  "report.block_explanation": "Et näe heidän viestejään, eivätkä he voi nähdä viestejäsi tai seurata sinua. He näkevät, että heidät on estetty.",
-  "report.categories.other": "Muu",
+  "report.block_explanation": "Et näe hänen viestejään, eikä hän voi nähdä viestejäsi tai seurata sinua. Hän näkevät, että olet estänyt hänet.",
+  "report.categories.other": "muu",
   "report.categories.spam": "Roskaposti",
-  "report.categories.violation": "Sisältö rikkoo yhden tai useamman palvelimen sääntöjä",
-  "report.category.subtitle": "Valitse paras osuma",
-  "report.category.title": "Kerro meille mitä tämän {type} kanssa tapahtuu",
+  "report.categories.violation": "Sisältö rikkoo yhtä tai useampaa palvelimen sääntöä",
+  "report.category.subtitle": "Valitse se, mikä sopii parhaiten",
+  "report.category.title": "Kerro meille, miksi ilmiannat tämän: {type} ",
   "report.category.title_account": "profiili",
-  "report.category.title_status": "viesti",
+  "report.category.title_status": "julkaisu",
   "report.close": "Valmis",
-  "report.comment.title": "Pitäisikö meidän tietää jotain muuta?",
+  "report.comment.title": "Olisiko jotain muuta, mitä meidän pitäisi tietää?",
   "report.forward": "Välitä kohteeseen {target}",
   "report.forward_hint": "Tämä tili on toisella palvelimella. Haluatko lähettää nimettömän raportin myös sinne?",
   "report.mute": "Mykistä",
-  "report.mute_explanation": "Et näe heidän viestejään. He voivat silti seurata sinua ja nähdä viestisi eivätkä tiedä, että heidät on mykistetty.",
+  "report.mute_explanation": "Et näe hänen viestejään. Hän voi silti seurata sinua ja nähdä viestisi. Hän ei tiedä, että on mykistetty.",
   "report.next": "Seuraava",
   "report.placeholder": "Lisäkommentit",
   "report.reasons.dislike": "En pidä siitä",
   "report.reasons.dislike_description": "Et halua nähdä sitä",
-  "report.reasons.other": "Se on jotain muuta",
+  "report.reasons.other": "Jotain muuta",
   "report.reasons.other_description": "Ongelma ei sovi muihin kategorioihin",
   "report.reasons.spam": "Se on roskapostia",
   "report.reasons.spam_description": "Haitalliset linkit, väärennetyt sitoutumiset tai toistuvat vastaukset",
@@ -501,34 +507,36 @@
   "report.reasons.violation_description": "Tiedät, että se rikkoo tiettyjä sääntöjä",
   "report.rules.subtitle": "Valitse kaikki jotka sopivat",
   "report.rules.title": "Mitä sääntöjä rikotaan?",
-  "report.statuses.subtitle": "Valitse kaikki jotka sopivat",
+  "report.statuses.subtitle": "Valitse kaikki sopivat",
   "report.statuses.title": "Onko olemassa yhtään viestiä, jotka tukevat tätä raporttia?",
   "report.submit": "Lähetä",
   "report.target": "Raportoidaan {target}",
   "report.thanks.take_action": "Tässä on vaihtoehtosi hallita näkemääsi Mastodonissa:",
   "report.thanks.take_action_actionable": "Sillä välin kun tarkistamme tätä, voit ryhtyä toimenpiteisiin käyttäjää @{name} vastaan:",
   "report.thanks.title": "Etkö halua nähdä tätä?",
-  "report.thanks.title_actionable": "Kiitos raportista, tutkimme asiaa.",
-  "report.unfollow": "Lopeta seuraaminen @{name}",
-  "report.unfollow_explanation": "Seuraat tätä tiliä. Jotta et enää näkisi heidän kirjoituksiaan, lopeta niiden seuraaminen.",
+  "report.thanks.title_actionable": "Kiitos ilmoituksesta, tarkistamme asian.",
+  "report.unfollow": "Lopeta käyttäjän @{name} seuraaminen",
+  "report.unfollow_explanation": "Seuraat tätä tiliä. Jotta et enää näe tilin viestejä, lopeta tilin seuraaminen.",
   "report_notification.attached_statuses": "{count, plural, one {{count} viesti} other {{count} viestiä}} liitteenä",
   "report_notification.categories.other": "Muu",
   "report_notification.categories.spam": "Roskaposti",
   "report_notification.categories.violation": "Sääntöjen rikkominen",
-  "report_notification.open": "Avaa raportti",
+  "report_notification.open": "Avaa ilmoitus",
+  "search.no_recent_searches": "Edellisiä hakuja ei ole",
   "search.placeholder": "Hae",
+  "search.quick_action.account_search": "Profiilit, jotka vastaavat \"{x}\"",
+  "search.quick_action.go_to_account": "Avaa profiili {x}",
+  "search.quick_action.go_to_hashtag": "Avaa aihetunniste {x}",
+  "search.quick_action.open_url": "Avaa URL-osoite Mastodonissa",
+  "search.quick_action.status_search": "Julkaisut, jotka vastaavat \"{x}\"",
   "search.search_or_paste": "Etsi tai kirjoita URL-osoite",
-  "search_popout.search_format": "Tarkennettu haku",
-  "search_popout.tips.full_text": "Tekstihaku listaa tilapäivitykset, jotka olet kirjoittanut, lisännyt suosikkeihisi, boostannut tai joissa sinut mainitaan, sekä tekstin sisältävät käyttäjänimet, nimimerkit ja aihetunnisteet.",
-  "search_popout.tips.hashtag": "aihetunnisteet",
-  "search_popout.tips.status": "julkaisu",
-  "search_popout.tips.text": "Tekstihaku listaa hakua vastaavat nimimerkit, käyttäjänimet ja aihetunnisteet",
-  "search_popout.tips.user": "käyttäjä",
-  "search_results.accounts": "Ihmiset",
+  "search_popout.quick_actions": "Pikatoiminnot",
+  "search_popout.recent": "Edelliset haut",
+  "search_results.accounts": "Profiilit",
   "search_results.all": "Kaikki",
   "search_results.hashtags": "Aihetunnisteet",
   "search_results.nothing_found": "Näille hakusanoille ei löytynyt mitään",
-  "search_results.statuses": "Julkaisut",
+  "search_results.statuses": "Viestit",
   "search_results.statuses_fts_disabled": "Viestien haku sisällön perusteella ei ole käytössä tällä Mastodon-palvelimella.",
   "search_results.title": "Etsi {q}",
   "search_results.total": "{count, number} {count, plural, one {tulos} other {tulosta}}",
@@ -540,18 +548,19 @@
   "server_banner.server_stats": "Palvelimen tilastot:",
   "sign_in_banner.create_account": "Luo tili",
   "sign_in_banner.sign_in": "Kirjaudu sisään",
-  "sign_in_banner.text": "Kirjaudu sisään seurataksesi profiileja tai aihetunnisteita, lisätäksesi suosikkeihin, jakaaksesi julkaisuja ja vastataksesi niihin tai ollaksesi vuorovaikutuksessa tililläsi toisella palvelimella.",
+  "sign_in_banner.text": "Kirjaudu sisään seurataksesi profiileja tai aihetunnisteita, lisätäksesi suosikkeihin, jakaaksesi julkaisuja ja vastataksesi niihin. Voit myös olla vuorovaikutuksessa tililtäsi toisella palvelimella.",
   "status.admin_account": "Avaa moderaattorinäkymä tilistä @{name}",
   "status.admin_domain": "Avaa palvelimen {domain} moderointitoiminnot",
-  "status.admin_status": "Avaa julkaisu moderointinäkymässä",
+  "status.admin_status": "Avaa viesti moderointinäkymässä",
   "status.block": "Estä @{name}",
   "status.bookmark": "Tallenna kirjanmerkki",
-  "status.cancel_reblog_private": "Peru buustaus",
-  "status.cannot_reblog": "Tätä viestiä ei voi buustata",
-  "status.copy": "Kopioi linkki julkaisuun",
+  "status.cancel_reblog_private": "Peru tehostus",
+  "status.cannot_reblog": "Tätä viestiä ei voi tehostaa",
+  "status.copy": "Kopioi linkki viestiin",
   "status.delete": "Poista",
   "status.detailed_status": "Yksityiskohtainen keskustelunäkymä",
-  "status.direct": "Yksityisviesti käyttäjälle @{name}",
+  "status.direct": "Mainitse @{name} yksityisesti",
+  "status.direct_indicator": "Yksityinen maininta",
   "status.edit": "Muokkaa",
   "status.edited": "Muokattu {date}",
   "status.edited_x_times": "Muokattu {count, plural, one {{count} kerran} other {{count} kertaa}}",
@@ -559,7 +568,7 @@
   "status.favourite": "Lisää suosikkeihin",
   "status.filter": "Suodata tämä viesti",
   "status.filtered": "Suodatettu",
-  "status.hide": "Piilota viesti",
+  "status.hide": "Piilota julkaisu",
   "status.history.created": "{name} luotu {date}",
   "status.history.edited": "{name} muokkasi {date}",
   "status.load_more": "Lataa lisää",
@@ -570,15 +579,15 @@
   "status.mute_conversation": "Mykistä keskustelu",
   "status.open": "Laajenna julkaisu",
   "status.pin": "Kiinnitä profiiliin",
-  "status.pinned": "Kiinnitetty viesti",
+  "status.pinned": "Kiinnitetty julkaisu",
   "status.read_more": "Näytä enemmän",
-  "status.reblog": "Buustaa",
-  "status.reblog_private": "Buustaa alkuperäiselle yleisölle",
-  "status.reblogged_by": "{name} buustasi",
-  "status.reblogs.empty": "Kukaan ei ole vielä buustannut tätä viestiä. Kun joku tekee niin, näkyy kyseinen henkilö tässä.",
+  "status.reblog": "Tehosta",
+  "status.reblog_private": "Tehosta alkuperäiselle yleisölle",
+  "status.reblogged_by": "{name} tehosti",
+  "status.reblogs.empty": "Kukaan ei ole vielä tehostanut tätä viestiä. Kun joku tekee niin, näkyy kyseinen henkilö tässä.",
   "status.redraft": "Poista ja palauta muokattavaksi",
   "status.remove_bookmark": "Poista kirjanmerkki",
-  "status.replied_to": "Vastasit käyttäjälle {name}",
+  "status.replied_to": "Vastaus käyttäjälle {name}",
   "status.reply": "Vastaa",
   "status.replyAll": "Vastaa ketjuun",
   "status.report": "Raportoi @{name}",
@@ -591,7 +600,7 @@
   "status.show_more_all": "Näytä lisää kaikista",
   "status.show_original": "Näytä alkuperäinen",
   "status.translate": "Käännä",
-  "status.translated_from_with": "Käännetty kielestä {lang} käyttäen palvelua {provider}",
+  "status.translated_from_with": "Käännetty kielestä {lang} käyttäen {provider}",
   "status.uncached_media_warning": "Ei saatavilla",
   "status.unmute_conversation": "Poista keskustelun mykistys",
   "status.unpin": "Irrota profiilista",
@@ -612,7 +621,7 @@
   "timeline_hint.remote_resource_not_displayed": "{resource} muilta palvelimilta ei näytetä.",
   "timeline_hint.resources.followers": "Seuraajia",
   "timeline_hint.resources.follows": "Seurattuja",
-  "timeline_hint.resources.statuses": "Vanhemmat julkaisut",
+  "timeline_hint.resources.statuses": "Vanhemmat viestit",
   "trends.counter_by_accounts": "{count, plural, one {{counter} henkilö} other {{counter} henkilöä}} viimeisten {days, plural, one {päivän} other {{days} päivän}}",
   "trends.trending_now": "Suosittua nyt",
   "ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.",
@@ -620,7 +629,7 @@
   "units.short.million": "{count} milj.",
   "units.short.thousand": "{count} t.",
   "upload_area.title": "Lataa raahaamalla ja pudottamalla tähän",
-  "upload_button.label": "Lisää mediaa",
+  "upload_button.label": "Lisää kuvia, video tai äänitiedosto",
   "upload_error.limit": "Tiedostolatauksien raja ylitetty.",
   "upload_error.poll": "Tiedon lataaminen ei ole sallittua kyselyissä.",
   "upload_form.audio_description": "Kuvaile sisältöä kuuroille ja kuulorajoitteisille",
@@ -628,7 +637,7 @@
   "upload_form.description_missing": "Kuvausta ei ole lisätty",
   "upload_form.edit": "Kuvaile",
   "upload_form.thumbnail": "Vaihda pikkukuva",
-  "upload_form.undo": "Peru",
+  "upload_form.undo": "Poista",
   "upload_form.video_description": "Kuvaile sisältöä kuuroille, kuulorajoitteisille, sokeille tai näkörajoitteisille",
   "upload_modal.analyzing_picture": "Analysoidaan kuvaa…",
   "upload_modal.apply": "Käytä",
diff --git a/app/javascript/mastodon/locales/fo.json b/app/javascript/mastodon/locales/fo.json
index 1841df058..29a2f7bc2 100644
--- a/app/javascript/mastodon/locales/fo.json
+++ b/app/javascript/mastodon/locales/fo.json
@@ -20,7 +20,7 @@
   "account.blocked": "Bannað/ur",
   "account.browse_more_on_origin_server": "Kaga meira á upprunaligu vangamyndina",
   "account.cancel_follow_request": "Strika fylgjaraumbøn",
-  "account.direct": "Beinleiðis boð @{name}",
+  "account.direct": "Umrøð @{name} privat",
   "account.disable_notifications": "Ikki boða mær frá, tá @{name} skrivar",
   "account.domain_blocked": "Økisnavn bannað",
   "account.edit_profile": "Broyt vanga",
@@ -102,7 +102,7 @@
   "column.blocks": "Bannaðir brúkarar",
   "column.bookmarks": "Bókamerki",
   "column.community": "Lokal tíðarlinja",
-  "column.direct": "Beinleiðis boð",
+  "column.direct": "Privatar umrøður",
   "column.directory": "Blaða gjøgnum vangar",
   "column.domain_blocks": "Bannað økisnøvn",
   "column.favourites": "Dámd",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Tú hevur broytingar í miðlalýsingini ella undansýningini, sum ikki eru goymdar. Vilt tú kortini vraka?",
   "confirmations.domain_block.confirm": "Banna heilum økisnavni",
   "confirmations.domain_block.message": "Ert tú púra, púra vís/ur í, at tú vilt banna øllum {domain}? Í flestu førum er nóg mikið og betri, bert at banna ella doyva onkrum ávísum. Tú fert eingi evni at síggja frá økisnavninum á nakrari almennari tíðarrás ella í tínum fráboðanum. Tínir fylgjarar undir økisnavninum verða eisini strikaðir.",
+  "confirmations.edit.confirm": "Rætta",
+  "confirmations.edit.message": "Rættingar, sum verða gjørdar nú, skriva yvir boðini, sum tú ert í holt við. Ert tú vís/ur í, at tú vilt halda fram?",
   "confirmations.logout.confirm": "Rita út",
   "confirmations.logout.message": "Ert tú vís/ur í, at tú vilt útrita teg?",
   "confirmations.mute.confirm": "Doyv",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Tú hevur enn ikki bannað nakran brúkara.",
   "empty_column.bookmarked_statuses": "Tú hevur enn einki goymt uppslag. Tú tú goymir eitt uppslag, kemur tað her.",
   "empty_column.community": "Lokala tíðarlinjan er tóm. Skriva okkurt alment fyri at fáa boltin á rull!",
-  "empty_column.direct": "Tú hevur eingi beinleiðis boð enn. Tá tú sendir ella móttekur eini beinleiðis boð, so síggjast tey her.",
+  "empty_column.direct": "Tú hevur ongar privatar umrøður enn. Tá tú sendir ella móttekur eina privata umrøðu, so verður hon sjónlig her.",
   "empty_column.domain_blocks": "Enn eru eingi blokeraði domenir.",
   "empty_column.explore_statuses": "Einki rák er beint nú. Royn aftur seinni!",
   "empty_column.favourited_statuses": "Tú hevur ongar yndispostar enn. Tá tú gevur einum posti yndismerki, so sært tú hann her.",
   "empty_column.favourites": "Eingin hevur givið hesum postinum yndismerki enn. Tá onkur ger, so sæst tað her.",
   "empty_column.follow_recommendations": "Tað sær út sum, at eingi uppskot kundu fáast fram til tín. Tú kanst royna at brúka leiting fyri at finna fólk, sum tú kanska kennir, ella at kanna frámerki, sum eru vælumtókt í løtuni.",
   "empty_column.follow_requests": "Tú hevur ongar umbønir um at verða fylgd/ur enn. Tá tú fær eina, so sæst hon her.",
+  "empty_column.followed_tags": "Tú hevur ikki fylgt nøkrum frámerki enn. Tá tú ger tað, so síggjast tey her.",
   "empty_column.hashtag": "Einki er í hesum frámerkinum enn.",
   "empty_column.home": "Heima-tíðarlinjan hjá tær er tóm! Fylg fleiri fyri at fylla hana. {suggestions}",
   "empty_column.home.suggestions": "Síggj nøkur uppskot",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Veit myndugleika",
   "follow_request.reject": "Nokta",
   "follow_requests.unlocked_explanation": "Sjálvt um konta tín ikki er læst, so hugsa {domain} starvsfólkini, at tú kanska hevur hug at kanna umbønir um at fylgja frá hesum kontum við hond.",
+  "followed_tags": "Fylgd frámerki",
   "footer.about": "Um",
   "footer.directory": "Vangaskrá",
   "footer.get_app": "Heinta appina",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Knappasnarvegir",
   "footer.privacy_policy": "Privatlívspolitikkur",
   "footer.source_code": "Vís keldukotuna",
+  "footer.status": "Støða",
   "generic.saved": "Goymt",
   "getting_started.heading": "At byrja",
   "hashtag.column_header.tag_mode.all": "og {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Fá teig í miðdepilin",
   "keyboard_shortcuts.compose": "Fá skriviøkið í miðdeplin",
   "keyboard_shortcuts.description": "Frágreiðing",
-  "keyboard_shortcuts.direct": "fyri at lata teig við beinleiðis boðum upp",
+  "keyboard_shortcuts.direct": "at lata teigin við privatum umrøðum upp",
   "keyboard_shortcuts.down": "Flyt niðureftir listanum",
   "keyboard_shortcuts.enter": "Opna uppslag",
   "keyboard_shortcuts.favourite": "Dáma uppslag",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Goymd",
   "navigation_bar.community_timeline": "Lokal tíðarlinja",
   "navigation_bar.compose": "Skriva nýggjan post",
-  "navigation_bar.direct": "Beinleiðis boð",
+  "navigation_bar.direct": "Privatar umrøður",
   "navigation_bar.discover": "Uppdaga",
   "navigation_bar.domain_blocks": "Bannað økisnøvn",
   "navigation_bar.edit_profile": "Broyt vanga",
@@ -379,13 +384,14 @@
   "navigation_bar.favourites": "Dámd",
   "navigation_bar.filters": "Doyvd orð",
   "navigation_bar.follow_requests": "Umbønir um at fylgja",
+  "navigation_bar.followed_tags": "Fylgd frámerki",
   "navigation_bar.follows_and_followers": "Fylgd og fylgjarar",
   "navigation_bar.lists": "Listar",
   "navigation_bar.logout": "Rita út",
   "navigation_bar.mutes": "Doyvdir brúkarar",
   "navigation_bar.personal": "Persónligt",
   "navigation_bar.pins": "Festir postar",
-  "navigation_bar.preferences": "Sertokki",
+  "navigation_bar.preferences": "Stillingar",
   "navigation_bar.public_timeline": "Felags tíðarlinja",
   "navigation_bar.search": "Leita",
   "navigation_bar.security": "Trygd",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Ruskpostur",
   "report_notification.categories.violation": "Brotin regla",
   "report_notification.open": "Opna melding",
+  "search.no_recent_searches": "Ongar nýggjar leitingar",
   "search.placeholder": "Leita",
+  "search.quick_action.account_search": "Vangar, ið samsvara {x}",
+  "search.quick_action.go_to_account": "Far til vanga {x}",
+  "search.quick_action.go_to_hashtag": "Far til frámerki {x}",
+  "search.quick_action.open_url": "Lat URL upp í Mastodon",
+  "search.quick_action.status_search": "Postar, ið samsvara {x}",
   "search.search_or_paste": "Leita ella set URL inn",
-  "search_popout.search_format": "Framkomið leiti-forsnið",
-  "search_popout.tips.full_text": "Einfaldur tekstur gevur aftur postar, sum tú hevur skrivað, yndismerkt, stimbrað ella er nevnd/ur í, umframt samsvarandi brúkaranøvn, víst nøvn og frámerki.",
-  "search_popout.tips.hashtag": "frámerki",
-  "search_popout.tips.status": "postur",
-  "search_popout.tips.text": "Einfaldur tekstur gevur aftur víst nøvn, brúkaranøvn og frámerki",
-  "search_popout.tips.user": "brúkari",
-  "search_results.accounts": "Fólk",
+  "search_popout.quick_actions": "Skjótar atgerðir",
+  "search_popout.recent": "Nýggjar leitingar",
+  "search_results.accounts": "Vangar",
   "search_results.all": "Alt",
   "search_results.hashtags": "Frámerki",
   "search_results.nothing_found": "Hesi leitiorð góvu ongi úrslit",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Ambætarahagtøl:",
   "sign_in_banner.create_account": "Stovna kontu",
   "sign_in_banner.sign_in": "Rita inn",
-  "sign_in_banner.text": "Innrita fyri at fylgja vangum og frámerkjum, seta yndismerki á, deila og svara postum, ella at brúka kontuna til at samvirka á einum øðrum ambætara.",
+  "sign_in_banner.text": "Innrita fyri at fylgja vangum og frámerkjum, seta yndismerki á, deila og svara postum. Tú kanst eisini brúka kontuna til at samvirka á einum øðrum ambætara.",
   "status.admin_account": "Lat kjakleiðaramarkamót upp fyri @{name}",
   "status.admin_domain": "Lat umsjónarmarkamót upp fyri {domain}",
   "status.admin_status": "Lat hendan postin upp í kjakleiðaramarkamótinum",
@@ -551,7 +559,8 @@
   "status.copy": "Kopiera leinki til postin",
   "status.delete": "Strika",
   "status.detailed_status": "Útgreinað samrøðusýni",
-  "status.direct": "Beinleiðis boð @{name}",
+  "status.direct": "Umrøð @{name} privat",
+  "status.direct_indicator": "Privat umrøða",
   "status.edit": "Rætta",
   "status.edited": "Rættað {date}",
   "status.edited_x_times": "Rættað {count, plural, one {{count} ferð} other {{count} ferð}}",
diff --git a/app/javascript/mastodon/locales/fr-QC.json b/app/javascript/mastodon/locales/fr-QC.json
index 89c3e7644..ff85f43b7 100644
--- a/app/javascript/mastodon/locales/fr-QC.json
+++ b/app/javascript/mastodon/locales/fr-QC.json
@@ -20,7 +20,7 @@
   "account.blocked": "Bloqué·e",
   "account.browse_more_on_origin_server": "Parcourir davantage sur le profil original",
   "account.cancel_follow_request": "Retirer cette demande d'abonnement",
-  "account.direct": "Message direct @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Ne plus me notifier quand @{name} publie",
   "account.domain_blocked": "Domaine bloqué",
   "account.edit_profile": "Modifier le profil",
@@ -102,7 +102,7 @@
   "column.blocks": "Comptes bloqués",
   "column.bookmarks": "Signets",
   "column.community": "Fil local",
-  "column.direct": "Messages directs",
+  "column.direct": "Private mentions",
   "column.directory": "Parcourir les profils",
   "column.domain_blocks": "Domaines bloqués",
   "column.favourites": "Favoris",
@@ -128,7 +128,7 @@
   "compose.language.search": "Rechercher des langues…",
   "compose_form.direct_message_warning_learn_more": "En savoir plus",
   "compose_form.encryption_warning": "Les publications sur Mastodon ne sont pas chiffrées de bout en bout. Veuillez ne partager aucune information sensible sur Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "Ce message n'apparaîtra pas dans les listes de hashtags, car il n'est pas public. Seuls les messages publics peuvent apparaître dans les recherches par hashtags.",
   "compose_form.lock_disclaimer": "Votre compte n’est pas {locked}. Tout le monde peut vous suivre et voir vos publications privés.",
   "compose_form.lock_disclaimer.lock": "verrouillé",
   "compose_form.placeholder": "À quoi pensez-vous?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Vous avez des modifications non enregistrées de la description ou de l'aperçu du média, voulez-vous quand même les supprimer?",
   "confirmations.domain_block.confirm": "Bloquer ce domaine entier",
   "confirmations.domain_block.message": "Voulez-vous vraiment, vraiment bloquer {domain} en entier? Dans la plupart des cas, quelques blocages ou masquages ciblés sont suffisants et préférables. Vous ne verrez plus de contenu provenant de ce domaine, ni dans vos fils publics, ni dans vos notifications. Vos abonné·e·s utilisant ce domaine seront retiré·e·s.",
+  "confirmations.edit.confirm": "Éditer",
+  "confirmations.edit.message": "Modifier maintenant écrasera votre message en cours de rédaction. Voulez-vous vraiment continuer ?",
   "confirmations.logout.confirm": "Se déconnecter",
   "confirmations.logout.message": "Voulez-vous vraiment vous déconnecter?",
   "confirmations.mute.confirm": "Masquer",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Vous n’avez bloqué aucun compte pour le moment.",
   "empty_column.bookmarked_statuses": "Vous n'avez pas de publications parmi vos signets. Lorsque vous en ajouterez une, elle apparaîtra ici.",
   "empty_column.community": "Le fil 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.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Il n’y a aucun domaine bloqué pour le moment.",
   "empty_column.explore_statuses": "Rien n'est en tendance présentement. Revenez plus tard!",
   "empty_column.favourited_statuses": "Vous n’avez pas encore de publications favorites. Lorsque vous en ajouterez une, elle apparaîtra ici.",
   "empty_column.favourites": "Personne n’a encore ajouté cette publication à ses favoris. Lorsque quelqu’un le fera, elle apparaîtra ici.",
   "empty_column.follow_recommendations": "Il semble qu’aucune suggestion n’ait pu être générée pour vous. Vous pouvez essayer d’utiliser la recherche pour découvrir des personnes que vous pourriez connaître ou explorer les hashtags populaires.",
   "empty_column.follow_requests": "Vous n’avez pas encore de demande d'abonnement. Lorsque vous en recevrez une, elle apparaîtra ici.",
+  "empty_column.followed_tags": "Vous n'avez pas encore suivi d'hashtags. Lorsque vous le ferez, ils apparaîtront ici.",
   "empty_column.hashtag": "Il n’y a pas encore de contenu associé à ce hashtag.",
   "empty_column.home": "Votre fil d'accueil est vide! Suivez plus de personnes pour la remplir. {suggestions}",
   "empty_column.home.suggestions": "Voir quelques suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autoriser",
   "follow_request.reject": "Rejeter",
   "follow_requests.unlocked_explanation": "Même si votre compte n’est pas privé, l’équipe de {domain} a pensé que vous pourriez vouloir peut-être consulter manuellement les demandes d'abonnement de ces comptes.",
+  "followed_tags": "Hashtags suivis",
   "footer.about": "À propos",
   "footer.directory": "Annuaire des profils",
   "footer.get_app": "Télécharger l’application",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Raccourcis clavier",
   "footer.privacy_policy": "Politique de confidentialité",
   "footer.source_code": "Voir le code source",
+  "footer.status": "État",
   "generic.saved": "Sauvegardé",
   "getting_started.heading": "Pour commencer",
   "hashtag.column_header.tag_mode.all": "et {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Se concentrer sur une colonne",
   "keyboard_shortcuts.compose": "Se concentrer sur la zone de rédaction",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "pour ouvrir la colonne de messages directs",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Descendre dans la liste",
   "keyboard_shortcuts.enter": "Ouvrir cette publication",
   "keyboard_shortcuts.favourite": "Ajouter publication aux favoris",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Signets",
   "navigation_bar.community_timeline": "Fil local",
   "navigation_bar.compose": "Rédiger un nouveau message",
-  "navigation_bar.direct": "Messages directs",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Découvrir",
   "navigation_bar.domain_blocks": "Domaines bloqués",
   "navigation_bar.edit_profile": "Modifier le profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoris",
   "navigation_bar.filters": "Mots masqués",
   "navigation_bar.follow_requests": "Demandes d'abonnements",
+  "navigation_bar.followed_tags": "Hashtags suivis",
   "navigation_bar.follows_and_followers": "Abonnements et abonnés",
   "navigation_bar.lists": "Listes",
   "navigation_bar.logout": "Se déconnecter",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Infraction aux règles du serveur",
   "report_notification.open": "Ouvrir le signalement",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Rechercher",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Rechercher ou saisir un URL",
-  "search_popout.search_format": "Recherche avancée",
-  "search_popout.tips.full_text": "Un texte normal retourne les publications que vous avez écrites, ajoutées à vos favoris, partagées, ou vous mentionnant, ainsi que les identifiants, les noms affichés, et les hashtags des personnes et publications correspondantes.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "publication",
-  "search_popout.tips.text": "Une simple entrée de texte renvoie les noms affichés, les identifiants et les hashtags correspondants",
-  "search_popout.tips.user": "compte",
-  "search_results.accounts": "Personnes",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Tout",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Aucun résultat avec ces mots-clés",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Statistiques du serveur:",
   "sign_in_banner.create_account": "Créer un compte",
   "sign_in_banner.sign_in": "Se connecter",
-  "sign_in_banner.text": "Connectez-vous pour suivre les profils ou les hashtags, ajouter aux favoris, partager et répondre aux publications, ou interagir depuis votre compte sur un autre serveur.",
+  "sign_in_banner.text": "Identifiez-vous pour suivre des profils ou des hashtags, ajouter des favoris, partager et répondre à des messages. Vous pouvez également interagir depuis votre compte sur un autre serveur.",
   "status.admin_account": "Ouvrir l’interface de modération pour @{name}",
   "status.admin_domain": "Ouvrir l’interface de modération pour {domain}",
   "status.admin_status": "Ouvrir ce message dans l’interface de modération",
@@ -551,7 +559,8 @@
   "status.copy": "Copier un lien vers cette publication",
   "status.delete": "Supprimer",
   "status.detailed_status": "Vue détaillée de la conversation",
-  "status.direct": "Envoyer un message direct à @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modifier",
   "status.edited": "Modifiée le {date}",
   "status.edited_x_times": "Modifiée {count, plural, one {{count} fois} other {{count} fois}}",
@@ -559,7 +568,7 @@
   "status.favourite": "Ajouter aux favoris",
   "status.filter": "Filtrer cette publication",
   "status.filtered": "Filtrée",
-  "status.hide": "Masquer la publication",
+  "status.hide": "Masquer le message",
   "status.history.created": "créé par {name} {date}",
   "status.history.edited": "modifié par {name} {date}",
   "status.load_more": "Charger plus",
diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json
index d9c77089a..75b5b9bf7 100644
--- a/app/javascript/mastodon/locales/fr.json
+++ b/app/javascript/mastodon/locales/fr.json
@@ -6,7 +6,7 @@
   "about.domain_blocks.preamble": "Mastodon vous permet généralement de visualiser le contenu et d'interagir avec les utilisateur⋅rice⋅s de n'importe quel autre serveur dans le fédiverse. Voici les exceptions qui ont été faites sur ce serveur en particulier.",
   "about.domain_blocks.silenced.explanation": "Vous ne verrez généralement pas les profils et le contenu de ce serveur, à moins que vous ne les recherchiez explicitement ou que vous ne choisissiez de les suivre.",
   "about.domain_blocks.silenced.title": "Limité",
-  "about.domain_blocks.suspended.explanation": "Aucune donnée de ce serveur ne sera traitée, enregistrée ou échangée, rendant impossible toute interaction ou communication avec les utilisateurs de ce serveur.",
+  "about.domain_blocks.suspended.explanation": "Aucune donnée de ce serveur ne sera traitée, enregistrée ou échangée, rendant impossible toute interaction ou communication avec les comptes de ce serveur.",
   "about.domain_blocks.suspended.title": "Suspendu",
   "about.not_available": "Cette information n'a pas été rendue disponible sur ce serveur.",
   "about.powered_by": "Réseau social décentralisé propulsé par {mastodon}",
@@ -20,7 +20,7 @@
   "account.blocked": "Bloqué·e",
   "account.browse_more_on_origin_server": "Parcourir davantage sur le profil original",
   "account.cancel_follow_request": "Retirer la demande d’abonnement",
-  "account.direct": "Envoyer un message direct à @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Ne plus me notifier quand @{name} publie quelque chose",
   "account.domain_blocked": "Domaine bloqué",
   "account.edit_profile": "Modifier le profil",
@@ -45,7 +45,7 @@
   "account.locked_info": "Ce compte est privé. Son ou sa propriétaire approuve manuellement qui peut le suivre.",
   "account.media": "Médias",
   "account.mention": "Mentionner @{name}",
-  "account.moved_to": "{name} a indiqué que son nouveau compte est maintenant  :",
+  "account.moved_to": "{name} a indiqué que son nouveau compte est maintenant :",
   "account.mute": "Masquer @{name}",
   "account.mute_notifications": "Masquer les notifications de @{name}",
   "account.muted": "Masqué·e",
@@ -71,7 +71,7 @@
   "admin.dashboard.monthly_retention": "Taux de rétention des utilisateur·rice·s par mois après inscription",
   "admin.dashboard.retention.average": "Moyenne",
   "admin.dashboard.retention.cohort": "Mois d'inscription",
-  "admin.dashboard.retention.cohort_size": "Nouveaux utilisateurs",
+  "admin.dashboard.retention.cohort_size": "Nouveaux comptes",
   "alert.rate_limited.message": "Veuillez réessayer après {retry_time, time, medium}.",
   "alert.rate_limited.title": "Nombre de requêtes limité",
   "alert.unexpected.message": "Une erreur inattendue s’est produite.",
@@ -96,13 +96,13 @@
   "closed_registrations.other_server_instructions": "Puisque Mastodon est décentralisé, vous pouvez créer un compte sur un autre serveur et interagir quand même avec celui-ci.",
   "closed_registrations_modal.description": "Créer un compte sur {domain} est actuellement impossible, néanmoins souvenez-vous que vous n'avez pas besoin d'un compte spécifiquement sur {domain} pour utiliser Mastodon.",
   "closed_registrations_modal.find_another_server": "Trouver un autre serveur",
-  "closed_registrations_modal.preamble": "Mastodon est décentralisé : peu importe où vous créez votre votre, vous serez en mesure de suivre et d'interagir avec quiconque sur ce serveur. Vous pouvez même l'héberger !",
+  "closed_registrations_modal.preamble": "Mastodon est décentralisé : peu importe où vous créez votre compte, vous serez en mesure de suivre et d'interagir avec quiconque sur ce serveur. Vous pouvez même l'héberger !",
   "closed_registrations_modal.title": "Inscription sur Mastodon",
   "column.about": "À propos",
   "column.blocks": "Comptes bloqués",
   "column.bookmarks": "Signets",
   "column.community": "Fil public local",
-  "column.direct": "Messages directs",
+  "column.direct": "Private mentions",
   "column.directory": "Parcourir les profils",
   "column.domain_blocks": "Domaines bloqués",
   "column.favourites": "Favoris",
@@ -128,8 +128,8 @@
   "compose.language.search": "Rechercher des langues …",
   "compose_form.direct_message_warning_learn_more": "En savoir plus",
   "compose_form.encryption_warning": "Les messages sur Mastodon ne sont pas chiffrés de bout en bout. Ne partagez aucune information sensible sur Mastodon.",
-  "compose_form.hashtag_warning": "Ce message n'apparaîtra pas dans les listes de hashtags, car il n'est pas public. Seuls les messages publics peuvent appaître dans les recherches par hashtags.",
-  "compose_form.lock_disclaimer": "Votre compte n’est pas {locked}. Tout le monde peut vous suivre et voir vos messages privés.",
+  "compose_form.hashtag_warning": "Ce message n'apparaîtra pas dans les listes de hashtags, car il n'est pas public. Seuls les messages publics peuvent apparaître dans les recherches par hashtags.",
+  "compose_form.lock_disclaimer": "Votre compte n’est pas {locked}. Tout le monde peut vous suivre pour voir vos messages réservés à vos abonné⋅e⋅s.",
   "compose_form.lock_disclaimer.lock": "verrouillé",
   "compose_form.placeholder": "Qu’avez-vous en tête ?",
   "compose_form.poll.add_option": "Ajouter un choix",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Vous avez des modifications non enregistrées de la description ou de l'aperçu du média, les supprimer quand même ?",
   "confirmations.domain_block.confirm": "Bloquer tout le domaine",
   "confirmations.domain_block.message": "Voulez-vous vraiment, vraiment bloquer {domain} en entier ? Dans la plupart des cas, quelques blocages ou masquages ciblés sont suffisants et préférables. Vous ne verrez plus de contenu provenant de ce domaine, ni dans vos fils publics, ni dans vos notifications. Vos abonné·e·s utilisant ce domaine seront retiré·e·s.",
+  "confirmations.edit.confirm": "Éditer",
+  "confirmations.edit.message": "Modifier maintenant écrasera votre message en cours de rédaction. Voulez-vous vraiment continuer ?",
   "confirmations.logout.confirm": "Se déconnecter",
   "confirmations.logout.message": "Voulez-vous vraiment vous déconnecter ?",
   "confirmations.mute.confirm": "Masquer",
@@ -170,7 +172,7 @@
   "confirmations.redraft.confirm": "Supprimer et ré-écrire",
   "confirmations.redraft.message": "Êtes-vous sûr·e de vouloir effacer ce statut pour le réécrire ? Ses partages ainsi que ses mises en favori seront perdus et ses réponses seront orphelines.",
   "confirmations.reply.confirm": "Répondre",
-  "confirmations.reply.message": "Répondre maintenant écrasera le message que vous rédigez actuellement. Voulez-vous vraiment continuer ?",
+  "confirmations.reply.message": "Répondre maintenant écrasera votre message en cours de rédaction. Voulez-vous vraiment continuer ?",
   "confirmations.unfollow.confirm": "Ne plus suivre",
   "confirmations.unfollow.message": "Voulez-vous vraiment vous désabonner de {name} ?",
   "conversation.delete": "Supprimer la conversation",
@@ -185,12 +187,12 @@
   "directory.recently_active": "Actif·ve·s récemment",
   "disabled_account_banner.account_settings": "Paramètres du compte",
   "disabled_account_banner.text": "Votre compte {disabledAccount} est actuellement désactivé.",
-  "dismissable_banner.community_timeline": "Voici les messages publics les plus récents des personnes dont les comptes sont hébergés par {domain}.",
+  "dismissable_banner.community_timeline": "Voici les messages publics les plus récents des comptes hébergés par {domain}.",
   "dismissable_banner.dismiss": "Rejeter",
   "dismissable_banner.explore_links": "On parle actuellement de ces nouvelles sur ce serveur, ainsi que sur d'autres serveurs du réseau décentralisé.",
   "dismissable_banner.explore_statuses": "Ces publications depuis les serveurs du réseau décentralisé, dont celui-ci, sont actuellement en train de gagner de l'ampleur sur ce serveur.",
   "dismissable_banner.explore_tags": "Ces hashtags sont actuellement en train de gagner de l'ampleur parmi les personnes sur les serveurs du réseau décentralisé dont celui-ci.",
-  "dismissable_banner.public_timeline": "Voici les publications publiques les plus récentes des personnes de ce serveur et des autres du réseau décentralisé que ce serveur connait.",
+  "dismissable_banner.public_timeline": "Voici les messages publics les plus récents des personnes de cette instance et des autres instances du réseau décentralisé connues par ce serveur.",
   "embed.instructions": "Intégrez ce message à votre site en copiant le code ci-dessous.",
   "embed.preview": "Il apparaîtra comme cela :",
   "emoji_button.activity": "Activités",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Vous n’avez bloqué aucun compte pour le moment.",
   "empty_column.bookmarked_statuses": "Vous n'avez pas de message en marque-page. Lorsque vous en ajouterez un, il apparaîtra ici.",
   "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.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Il n’y a aucun domaine bloqué pour le moment.",
   "empty_column.explore_statuses": "Rien n'est en tendance pour le moment. Revenez plus tard !",
   "empty_column.favourited_statuses": "Vous n’avez pas encore de message en favori. Lorsque vous en ajouterez un, il apparaîtra ici.",
   "empty_column.favourites": "Personne n’a encore ajouté ce message à ses favoris. Lorsque quelqu’un le fera, il apparaîtra ici.",
   "empty_column.follow_recommendations": "Il semble qu’aucune suggestion n’ait pu être générée pour vous. Vous pouvez essayer d’utiliser la recherche pour découvrir des personnes que vous pourriez connaître ou explorer les hashtags tendance.",
   "empty_column.follow_requests": "Vous n’avez pas encore de demande de suivi. Lorsque vous en recevrez une, elle apparaîtra ici.",
+  "empty_column.followed_tags": "Vous n'avez pas encore suivi d'hashtags. Lorsque vous le ferez, ils apparaîtront 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.suggestions": "Voir quelques suggestions",
@@ -245,7 +248,7 @@
   "filter_modal.added.context_mismatch_title": "Incompatibilité du contexte !",
   "filter_modal.added.expired_explanation": "Cette catégorie de filtre a expiré, vous devrez modifier la date d'expiration pour qu'elle soit appliquée.",
   "filter_modal.added.expired_title": "Filtre expiré !",
-  "filter_modal.added.review_and_configure": "Pour passer en revue et approfondir la configuration de cette catégorie de filtre, aller sur le {settings_link}.",
+  "filter_modal.added.review_and_configure": "Pour examiner et affiner la configuration de cette catégorie de filtre, allez à {settings_link}.",
   "filter_modal.added.review_and_configure_title": "Paramètres du filtre",
   "filter_modal.added.settings_link": "page des paramètres",
   "filter_modal.added.short_explanation": "Ce message a été ajouté à la catégorie de filtre suivante : {title}.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Accepter",
   "follow_request.reject": "Rejeter",
   "follow_requests.unlocked_explanation": "Même si votre compte n’est pas privé, l’équipe de {domain} a pensé que vous pourriez vouloir consulter manuellement les demandes de suivi de ces comptes.",
+  "followed_tags": "Hashtags suivis",
   "footer.about": "À propos",
   "footer.directory": "Annuaire des profils",
   "footer.get_app": "Télécharger l’application",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Raccourcis clavier",
   "footer.privacy_policy": "Politique de confidentialité",
   "footer.source_code": "Voir le code source",
+  "footer.status": "État",
   "generic.saved": "Sauvegardé",
   "getting_started.heading": "Pour commencer",
   "hashtag.column_header.tag_mode.all": "et {additional}",
@@ -288,17 +293,17 @@
   "home.column_settings.show_replies": "Afficher les réponses",
   "home.hide_announcements": "Masquer les annonces",
   "home.show_announcements": "Afficher les annonces",
-  "interaction_modal.description.favourite": "Avec un compte Mastodon, vous pouvez ajouter ce post aux favoris pour informer l'auteur que vous l'appréciez et le sauvegarder pour plus tard.",
+  "interaction_modal.description.favourite": "Avec un compte Mastodon, vous pouvez ajouter ce message à vos favoris pour informer l'auteur⋅rice que vous l'appréciez et pour le sauvegarder pour plus tard.",
   "interaction_modal.description.follow": "Avec un compte Mastodon, vous pouvez suivre {name} et recevoir leurs posts dans votre fil d'actualité.",
-  "interaction_modal.description.reblog": "Avec un compte sur Mastodon, vous pouvez booster ce message pour le partager avec vos propres abonnés.",
+  "interaction_modal.description.reblog": "Avec un compte sur Mastodon, vous pouvez partager ce message pour le faire découvrir à vos propres abonné⋅e⋅s.",
   "interaction_modal.description.reply": "Avec un compte sur Mastodon, vous pouvez répondre à ce message.",
   "interaction_modal.on_another_server": "Sur un autre serveur",
   "interaction_modal.on_this_server": "Sur ce serveur",
-  "interaction_modal.other_server_instructions": "Copiez et collez cette URL dans le champ de recherche de votre application Mastodon préférée ou l'interface web de votre serveur Mastodon.",
-  "interaction_modal.preamble": "Puisque Mastodon est décentralisé, vous pouvez utiliser votre compte existant hébergé par un autre serveur Mastodon ou une plateforme compatible si vous n'avez pas de compte sur celui-ci.",
-  "interaction_modal.title.favourite": "Ajouter de post de {name} aux favoris",
+  "interaction_modal.other_server_instructions": "Copiez et collez cette URL dans le champ de recherche de votre application Mastodon préférée ou de l'interface web de votre serveur Mastodon.",
+  "interaction_modal.preamble": "Mastodon étant décentralisé, vous pouvez utiliser votre compte existant sur un autre serveur Mastodon, ou sur une autre plateforme compatible, si vous n'avez pas de compte ici.",
+  "interaction_modal.title.favourite": "Ajouter le message de {name} aux favoris",
   "interaction_modal.title.follow": "Suivre {name}",
-  "interaction_modal.title.reblog": "Partager la publication de {name}",
+  "interaction_modal.title.reblog": "Partager le message de {name}",
   "interaction_modal.title.reply": "Répondre au message de {name}",
   "intervals.full.days": "{number, plural, one {# jour} other {# jours}}",
   "intervals.full.hours": "{number, plural, one {# heure} other {# heures}}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Se placer dans une colonne",
   "keyboard_shortcuts.compose": "Se placer dans la zone de rédaction",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "pour ouvrir la colonne des messages directs",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Descendre dans la liste",
   "keyboard_shortcuts.enter": "Ouvrir le message",
   "keyboard_shortcuts.favourite": "Ajouter le message aux favoris",
@@ -362,7 +367,7 @@
   "media_gallery.toggle_visible": "{number, plural, one {Cacher l’image} other {Cacher les images}}",
   "missing_indicator.label": "Non trouvé",
   "missing_indicator.sublabel": "Ressource introuvable",
-  "moved_to_account_banner.text": "Votre compte {disabledAccount} est actuellement désactivé parce que vous avez déplacé vers {movedToAccount}.",
+  "moved_to_account_banner.text": "Votre compte {disabledAccount} est actuellement désactivé parce que vous l'avez déplacé à {movedToAccount}.",
   "mute_modal.duration": "Durée",
   "mute_modal.hide_notifications": "Masquer les notifications de cette personne ?",
   "mute_modal.indefinite": "Indéfinie",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Marque-pages",
   "navigation_bar.community_timeline": "Fil public local",
   "navigation_bar.compose": "Rédiger un nouveau message",
-  "navigation_bar.direct": "Messages directs",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Découvrir",
   "navigation_bar.domain_blocks": "Domaines bloqués",
   "navigation_bar.edit_profile": "Modifier le profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoris",
   "navigation_bar.filters": "Mots masqués",
   "navigation_bar.follow_requests": "Demandes d’abonnement",
+  "navigation_bar.followed_tags": "Hashtags suivis",
   "navigation_bar.follows_and_followers": "Abonnements et abonnés",
   "navigation_bar.lists": "Listes",
   "navigation_bar.logout": "Déconnexion",
@@ -392,7 +398,7 @@
   "not_signed_in_indicator.not_signed_in": "Vous devez vous connecter pour accéder à cette ressource.",
   "notification.admin.report": "{name} a signalé {target}",
   "notification.admin.sign_up": "{name} s'est inscrit",
-  "notification.favourite": "{name} a aimé votre publication",
+  "notification.favourite": "{name} a ajouté votre message à ses favoris",
   "notification.follow": "{name} vous suit",
   "notification.follow_request": "{name} a demandé à vous suivre",
   "notification.mention": "{name} vous a mentionné·e :",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Infraction aux règles du serveur",
   "report_notification.open": "Ouvrir le signalement",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Rechercher",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Rechercher ou saisir une URL",
-  "search_popout.search_format": "Recherche avancée",
-  "search_popout.tips.full_text": "Un texte normal retourne les messages que vous avez écrits, ajoutés à vos favoris, partagés, ou vous mentionnant, ainsi que les identifiants, les noms affichés, et les hashtags des personnes et messages correspondants.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "message",
-  "search_popout.tips.text": "Un texte simple renvoie les noms affichés, les identifiants et les hashtags correspondants",
-  "search_popout.tips.user": "utilisateur",
-  "search_results.accounts": "Comptes",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Tous les résultats",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Aucun résultat avec ces mots-clefs",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Statistiques du serveur :",
   "sign_in_banner.create_account": "Créer un compte",
   "sign_in_banner.sign_in": "Se connecter",
-  "sign_in_banner.text": "Connectez-vous pour suivre les profils ou les hashtags, ajouter aux favoris, partager et répondre aux messages, ou interagir depuis votre compte sur un autre serveur.",
+  "sign_in_banner.text": "Identifiez-vous pour suivre des profils ou des hashtags, ajouter des favoris, partager et répondre à des messages. Vous pouvez également interagir depuis votre compte sur un autre serveur.",
   "status.admin_account": "Ouvrir l’interface de modération pour @{name}",
   "status.admin_domain": "Ouvrir l’interface de modération pour {domain}",
   "status.admin_status": "Ouvrir ce message dans l’interface de modération",
@@ -551,7 +559,8 @@
   "status.copy": "Copier le lien vers le message",
   "status.delete": "Supprimer",
   "status.detailed_status": "Vue détaillée de la conversation",
-  "status.direct": "Envoyer un message direct à @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Éditer",
   "status.edited": "Édité le {date}",
   "status.edited_x_times": "Edité {count, plural, one {{count} fois} other {{count} fois}}",
@@ -559,7 +568,7 @@
   "status.favourite": "Ajouter aux favoris",
   "status.filter": "Filtrer ce message",
   "status.filtered": "Filtré",
-  "status.hide": "Masquer la publication",
+  "status.hide": "Masquer le message",
   "status.history.created": "créé par {name} {date}",
   "status.history.edited": "édité par {name} {date}",
   "status.load_more": "Charger plus",
@@ -612,7 +621,7 @@
   "timeline_hint.remote_resource_not_displayed": "{resource} des autres serveurs ne sont pas affichés.",
   "timeline_hint.resources.followers": "Les abonnés",
   "timeline_hint.resources.follows": "Les abonnements",
-  "timeline_hint.resources.statuses": "Les messages plus anciens",
+  "timeline_hint.resources.statuses": "Messages plus anciens",
   "trends.counter_by_accounts": "{count, plural, one {{counter} personne} other {{counter} personnes}} au cours {days, plural, one {des dernières 24h} other {des {days} derniers jours}}",
   "trends.trending_now": "Tendance en ce moment",
   "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.",
@@ -624,7 +633,7 @@
   "upload_error.limit": "Taille maximale d'envoi de fichier dépassée.",
   "upload_error.poll": "L’envoi de fichiers n’est pas autorisé avec les sondages.",
   "upload_form.audio_description": "Décrire pour les personnes ayant des difficultés d’audition",
-  "upload_form.description": "Décrire pour les malvoyants",
+  "upload_form.description": "Décrire pour les malvoyant·e·s",
   "upload_form.description_missing": "Description manquante",
   "upload_form.edit": "Décrire",
   "upload_form.thumbnail": "Changer la vignette",
diff --git a/app/javascript/mastodon/locales/fy.json b/app/javascript/mastodon/locales/fy.json
index 9cbf32e5c..d4dfe35a2 100644
--- a/app/javascript/mastodon/locales/fy.json
+++ b/app/javascript/mastodon/locales/fy.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blokkearre",
   "account.browse_more_on_origin_server": "Mear op it orizjinele profyl besjen",
   "account.cancel_follow_request": "Folchfersyk annulearje",
-  "account.direct": "@{name} in direkt berjocht stjoere",
+  "account.direct": "Privee fermelde @{name}",
   "account.disable_notifications": "Jou gjin melding mear wannear @{name} in berjocht pleatst",
   "account.domain_blocked": "Domein blokkearre",
   "account.edit_profile": "Profyl bewurkje",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokkearre brûkers",
   "column.bookmarks": "Blêdwizers",
   "column.community": "Lokale tiidline",
-  "column.direct": "Direkte berjochten",
+  "column.direct": "Priveefermeldingen",
   "column.directory": "Profilen trochsykje",
   "column.domain_blocks": "Blokkearre domeinen",
   "column.favourites": "Favoriten",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Jo hawwe net-bewarre wizigingen yn de mediabeskriuwing of foarfertoaning, wolle jo dizze dochs fuortsmite?",
   "confirmations.domain_block.confirm": "Hide entire domain",
   "confirmations.domain_block.message": "Binne jo echt wis dat jo alles fan {domain} negearje wolle? Yn de measte gefallen is it blokkearjen of negearjen fan in pear spesifike persoanen genôch en better. Jo sille gjin berjochten fan dizze server op iepenbiere tiidlinen sjen of yn jo meldingen. Jo folgers fan dizze server wurde fuortsmiten.",
+  "confirmations.edit.confirm": "Bewurkje",
+  "confirmations.edit.message": "Troch no te bewurkjen sil it berjocht dat jo no oan it skriuwen binne oerskreaun wurde. Wolle jo trochgean?",
   "confirmations.logout.confirm": "Ofmelde",
   "confirmations.logout.message": "Bisto wis datsto ôfmelde wolst?",
   "confirmations.mute.confirm": "Negearje",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Do hast noch gjin brûkers blokkearre.",
   "empty_column.bookmarked_statuses": "Jo hawwe noch gjin berjochten oan jo blêdwizers tafoege. Wannear’t jo der ien oan jo blêdwizers tafoegje, falt dizze hjir te sjen.",
   "empty_column.community": "De lokale tiidline is noch leech. Pleats in iepenbier berjocht om de spits ôf te biten!",
-  "empty_column.direct": "Jo hawwe noch gjin direkte berjochten. Wannear’t jo der ien ferstjoere of ûntfang, komt dizze hjir te stean.",
+  "empty_column.direct": "Jo hawwe noch gjin priveefermeldingen. Wannear’t jo der ien ferstjoere of ûntfange, komt dizze hjir te stean.",
   "empty_column.domain_blocks": "Der binne noch gjin blokkearre domeinen.",
   "empty_column.explore_statuses": "Op dit stuit binne der gjin trends. Kom letter werom!",
   "empty_column.favourited_statuses": "Jo hawwe noch gjin favorite berjochten. Wannear’t jo ien as favoryt markearje, falt dizze hjir te sjen.",
   "empty_column.favourites": "Net ien hat dit berjocht noch as favoryt markearre. Wannear’t ien dit docht, falt dat hjir te sjen.",
   "empty_column.follow_recommendations": "It liket der op dat der gjin oanrekommandaasjes foar jo oanmakke wurde kinne. Jo kinne probearje te sykjen nei minsken dy’t jo miskien kinne, sykje op hashtags, de lokale en globale tiidlinen besjen of de brûkersgids trochblêdzje.",
   "empty_column.follow_requests": "Jo hawwe noch gjin folchfersiken ûntfongen. Wannear’t jo der ien ûntfange, falt dat hjir te sjen.",
+  "empty_column.followed_tags": "Jo folgje noch gjin hashtags. As jo dat wol dogge, wurde se hjir toand.",
   "empty_column.hashtag": "Der is noch neat te finen ûnder dizze hashtag.",
   "empty_column.home": "Dizze tiidline is leech! Folgje mear minsken om it te foljen. {suggestions}",
   "empty_column.home.suggestions": "Suggestjes besjen",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Goedkarre",
   "follow_request.reject": "Wegerje",
   "follow_requests.unlocked_explanation": "Ek al is jo account net besletten, de meiwurkers fan {domain} tinke dat jo miskien de folgjende folchfersiken hânmjittich kontrolearje.",
+  "followed_tags": "Folge hashtags",
   "footer.about": "Oer",
   "footer.directory": "Profylmap",
   "footer.get_app": "App downloade",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Fluchtoetsen",
   "footer.privacy_policy": "Privacybelied",
   "footer.source_code": "Boarnekoade besjen",
+  "footer.status": "Steat",
   "generic.saved": "Bewarre",
   "getting_started.heading": "Uteinsette",
   "hashtag.column_header.tag_mode.all": "en {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "to focus a status in one of the columns",
   "keyboard_shortcuts.compose": "to focus the compose textarea",
   "keyboard_shortcuts.description": "Omskriuwing",
-  "keyboard_shortcuts.direct": "Direkte berjochten toane",
+  "keyboard_shortcuts.direct": "om de kolom priveefermeldingen te iepenjen",
   "keyboard_shortcuts.down": "to move down in the list",
   "keyboard_shortcuts.enter": "Berjocht iepenje",
   "keyboard_shortcuts.favourite": "As favoryt markearje",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Blêdwizers",
   "navigation_bar.community_timeline": "Lokale tiidline",
   "navigation_bar.compose": "Nij berjocht skriuwe",
-  "navigation_bar.direct": "Direkte berjochten",
+  "navigation_bar.direct": "Priveefermeldingen",
   "navigation_bar.discover": "Untdekke",
   "navigation_bar.domain_blocks": "Blokkearre domeinen",
   "navigation_bar.edit_profile": "Profyl bewurkje",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoriten",
   "navigation_bar.filters": "Negearre wurden",
   "navigation_bar.follow_requests": "Folchfersiken",
+  "navigation_bar.followed_tags": "Folge hashtags",
   "navigation_bar.follows_and_followers": "Folgers en folgjenden",
   "navigation_bar.lists": "Listen",
   "navigation_bar.logout": "Ofmelde",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Skeinde regels",
   "report_notification.open": "Rapport iepenje",
+  "search.no_recent_searches": "Gjin resinte sykopdrachten",
   "search.placeholder": "Sykje",
+  "search.quick_action.account_search": "Accounts dy’t oerienkomme mei {x}",
+  "search.quick_action.go_to_account": "Gean nei account {x}",
+  "search.quick_action.go_to_hashtag": "Gean nei hashtag {x}",
+  "search.quick_action.open_url": "URL yn Mastodon iepenje",
+  "search.quick_action.status_search": "Berjochten dy’t oerienkomme mei {x}",
   "search.search_or_paste": "Sykje of fier URL yn",
-  "search_popout.search_format": "Avansearre sykje",
-  "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": "Brûk gewoane tekst om te sykjen op werjeftenammen, brûkersnammen en hashtags",
-  "search_popout.tips.user": "brûker",
-  "search_results.accounts": "Minsken",
+  "search_popout.quick_actions": "Flugge aksjes",
+  "search_popout.recent": "Resinte sykopdrachten",
+  "search_results.accounts": "Profilen",
   "search_results.all": "Alles",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Dizze syktermen leverje gjin resultaat op",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Serverstatistiken:",
   "sign_in_banner.create_account": "Account registrearje",
   "sign_in_banner.sign_in": "Oanmelde",
-  "sign_in_banner.text": "Wannear’t jo in account op dizze server hawwe, kinne jo oanmelde om minsken of hashtags te folgjen, op berjochten te reagearjen of om dizze te dielen. Wannear’t jo in account op in oare server hawwe, kinne jo dêr oanmelde en dêr ynteraksje mei minsken op dizze server hawwe.",
+  "sign_in_banner.text": "Meld jo oan, om profilen of hashtags te folgjen, berjochten favoryt te meitsjen, te dielen en te beäntwurdzjen of om fan jo account út op in oare server mei oaren ynteraksje te hawwen.",
   "status.admin_account": "Moderaasje-omjouwing fan @{name} iepenje",
   "status.admin_domain": "Moderaasje-omjouwing fan {domain} iepenje",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Fuortsmite",
   "status.detailed_status": "Detaillearre petearoersjoch",
-  "status.direct": "@{name} in direkt berjocht stjoere",
+  "status.direct": "Privee fermelde @{name}",
+  "status.direct_indicator": "Priveefermelding",
   "status.edit": "Bewurkje",
   "status.edited": "Bewurke op {date}",
   "status.edited_x_times": "{count, plural, one {{count} kear} other {{count} kearen}} bewurke",
diff --git a/app/javascript/mastodon/locales/ga.json b/app/javascript/mastodon/locales/ga.json
index b5e2fbd8c..6645483a5 100644
--- a/app/javascript/mastodon/locales/ga.json
+++ b/app/javascript/mastodon/locales/ga.json
@@ -20,7 +20,7 @@
   "account.blocked": "Bactha",
   "account.browse_more_on_origin_server": "Brabhsáil níos mó ar an phróifíl bhunaidh",
   "account.cancel_follow_request": "Éirigh as iarratas leanta",
-  "account.direct": "Seol teachtaireacht dhíreach chuig @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Éirigh as ag cuir mé in eol nuair bpostálann @{name}",
   "account.domain_blocked": "Ainm fearainn bactha",
   "account.edit_profile": "Cuir an phróifíl in eagar",
@@ -102,7 +102,7 @@
   "column.blocks": "Cuntais choiscthe",
   "column.bookmarks": "Leabharmharcanna",
   "column.community": "Amlíne áitiúil",
-  "column.direct": "Teachtaireachtaí dhíreacha",
+  "column.direct": "Private mentions",
   "column.directory": "Brabhsáil próifílí",
   "column.domain_blocks": "Fearainn bhactha",
   "column.favourites": "Toghanna",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Tá athruithe neamhshlánaithe don tuarascáil gné nó réamhamharc agat, faigh réidh dóibh ar aon nós?",
   "confirmations.domain_block.confirm": "Bac fearann go hiomlán",
   "confirmations.domain_block.message": "An bhfuil tú iontach cinnte gur mhaith leat bac an t-ainm fearainn {domain} in iomlán? I bhformhór na gcásanna, is leor agus is fearr cúpla baic a cur i bhfeidhm nó cúpla úsáideoirí a balbhú. Ní fheicfidh tú ábhair ón t-ainm fearainn sin in amlíne ar bith, nó i d'fhógraí. Scaoilfear do leantóirí ón ainm fearainn sin.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Logáil amach",
   "confirmations.logout.message": "An bhfuil tú cinnte gur mhaith leat logáil amach?",
   "confirmations.mute.confirm": "Balbhaigh",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Níl aon úsáideoir bactha agat fós.",
   "empty_column.bookmarked_statuses": "Níl aon phostáil leabharmharcaithe agat fós. Nuair a dhéanann tú leabharmharc, beidh sé le feiceáil anseo.",
   "empty_column.community": "Tá an amlíne áitiúil folamh. Foilsigh rud éigin go poiblí le tús a chur le cúrsaí!",
-  "empty_column.direct": "Níl aon teachtaireacht dírithe agat fós. Nuair a sheolann tú nó nuair a fhaigheann tú ceann, feicfear anseo í.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Níl aon fearainn bhactha ann go fóill.",
   "empty_column.explore_statuses": "Níl rud ar bith ag treochtáil faoi láthair. Tar ar ais ar ball!",
   "empty_column.favourited_statuses": "Níor roghnaigh tú postáil ar bith fós. Nuair a roghnaigh tú ceann, beidh sí le feiceáil anseo.",
   "empty_column.favourites": "Níor thogh éinne an phostáil seo fós. Nuair a thoghfaidh duine éigin í, taispeánfar anseo é sin.",
   "empty_column.follow_recommendations": "Is cosúil nár fhéadfaí moltaí a ghineadh. D'fhéadfá cuardach a úsáid le teacht ar dhaoine a bhfuil aithne agat orthu, nó iniúchadh ar haischlibeanna atá ag treochtáil a dhéanamh.",
   "empty_column.follow_requests": "Níl aon phostáil leabharmharcaithe agat fós. Nuair a dhéanann tú leabharmharc, feicfear anseo é.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Níl rud ar bith faoin haischlib seo go fóill.",
   "empty_column.home": "Tá d'amlíne baile folamh! B'fhiú duit cúpla duine eile a leanúint lena líonadh! {suggestions}",
   "empty_column.home.suggestions": "Féach ar roinnt moltaí",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Ceadaigh",
   "follow_request.reject": "Diúltaigh",
   "follow_requests.unlocked_explanation": "Cé nach bhfuil do chuntas faoi ghlas, cheap foireann {domain} gur mhaith leat súil siar ar iarratais leanúnaí as na cuntais seo.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "Maidir le",
   "footer.directory": "Eolaire próifílí",
   "footer.get_app": "Faigh an aip",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Aicearraí méarchláir",
   "footer.privacy_policy": "Polasaí príobháideachais",
   "footer.source_code": "Féach ar an gcód foinseach",
+  "footer.status": "Status",
   "generic.saved": "Sábháilte",
   "getting_started.heading": "Ag tosú amach",
   "hashtag.column_header.tag_mode.all": "agus {additional}",
@@ -350,7 +355,7 @@
   "lists.edit": "Cuir an liosta in eagar",
   "lists.edit.submit": "Athraigh teideal",
   "lists.new.create": "Cruthaigh liosta",
-  "lists.new.title_placeholder": "New list title",
+  "lists.new.title_placeholder": "Teideal liosta nua",
   "lists.replies_policy.followed": "Úsáideoir ar bith atá á leanúint",
   "lists.replies_policy.list": "Baill an liosta",
   "lists.replies_policy.none": "Duine ar bith",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Leabharmharcanna",
   "navigation_bar.community_timeline": "Amlíne áitiúil",
   "navigation_bar.compose": "Cum postáil nua",
-  "navigation_bar.direct": "Teachtaireachtaí dhíreacha",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Faigh amach",
   "navigation_bar.domain_blocks": "Fearainn bhactha",
   "navigation_bar.edit_profile": "Cuir an phróifíl in eagar",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Toghanna",
   "navigation_bar.filters": "Focail bhalbhaithe",
   "navigation_bar.follow_requests": "Iarratais leanúnaí",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Ag leanúint agus do do leanúint",
   "navigation_bar.lists": "Liostaí",
   "navigation_bar.logout": "Logáil Amach",
@@ -408,7 +414,7 @@
   "notifications.column_settings.alert": "Fógraí deisce",
   "notifications.column_settings.favourite": "Toghanna:",
   "notifications.column_settings.filter_bar.advanced": "Taispeáin na catagóirí go léir",
-  "notifications.column_settings.filter_bar.category": "Quick filter bar",
+  "notifications.column_settings.filter_bar.category": "Barra scagaire tapa",
   "notifications.column_settings.filter_bar.show_bar": "Taispeáin barra scagaire",
   "notifications.column_settings.follow": "Leantóirí nua:",
   "notifications.column_settings.follow_request": "Iarratais leanúnaí nua:",
@@ -437,15 +443,15 @@
   "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
   "notifications_permission_banner.enable": "Ceadaigh fógraí ar an deasc",
   "notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.",
-  "notifications_permission_banner.title": "Never miss a thing",
+  "notifications_permission_banner.title": "Ná caill aon rud go deo",
   "picture_in_picture.restore": "Cuir é ar ais",
   "poll.closed": "Dúnta",
   "poll.refresh": "Athnuaigh",
-  "poll.total_people": "{count, plural, one {# person} other {# people}}",
-  "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
+  "poll.total_people": "{count, plural, one {# duine} other {# duine}}",
+  "poll.total_votes": "{count, plural, one {# vóta} other {# vóta}}",
   "poll.vote": "Vótáil",
-  "poll.voted": "You voted for this answer",
-  "poll.votes": "{votes, plural, one {# vote} other {# votes}}",
+  "poll.voted": "Vótáil tú don freagra seo",
+  "poll.votes": "{votes, plural, one {# vóta} other {# vóta}}",
   "poll_button.add_poll": "Cruthaigh suirbhé",
   "poll_button.remove_poll": "Bain suirbhé",
   "privacy.change": "Adjust status privacy",
@@ -485,7 +491,7 @@
   "report.category.title_status": "postáil",
   "report.close": "Déanta",
   "report.comment.title": "Is there anything else you think we should know?",
-  "report.forward": "Forward to {target}",
+  "report.forward": "Seol ar aghaidh chun {target}",
   "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
   "report.mute": "Balbhaigh",
   "report.mute_explanation": "Ní fheicfidh tú a postálacha. Is féidir an té seo tú a leanúint agus do phostálacha a fheiceáil, agus ní fhios go bhfuil iad balbhaithe.",
@@ -516,31 +522,33 @@
   "report_notification.categories.spam": "Turscar",
   "report_notification.categories.violation": "Sárú rialach",
   "report_notification.open": "Oscail tuairisc",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Cuardaigh",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Cuardaigh nó cuir URL isteach",
-  "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": "haischlib",
-  "search_popout.tips.status": "postáil",
-  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
-  "search_popout.tips.user": "úsáideoir",
-  "search_results.accounts": "Daoine",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Uile",
   "search_results.hashtags": "Haischlibeanna",
   "search_results.nothing_found": "Could not find anything for these search terms",
   "search_results.statuses": "Postálacha",
   "search_results.statuses_fts_disabled": "Searching posts by their content is not enabled on this Mastodon server.",
-  "search_results.title": "Search for {q}",
+  "search_results.title": "Cuardaigh ar thóir {q}",
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
   "server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
-  "server_banner.active_users": "active users",
+  "server_banner.active_users": "úsáideoirí gníomhacha",
   "server_banner.administered_by": "Administered by:",
   "server_banner.introduction": "{domain} is part of the decentralized social network powered by {mastodon}.",
   "server_banner.learn_more": "Tuilleadh eolais",
-  "server_banner.server_stats": "Server stats:",
+  "server_banner.server_stats": "Staitisticí freastalaí:",
   "sign_in_banner.create_account": "Cruthaigh cuntas",
   "sign_in_banner.sign_in": "Sinigh isteach",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,19 +559,20 @@
   "status.copy": "Copy link to status",
   "status.delete": "Scrios",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Seol teachtaireacht dhíreach chuig @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Cuir in eagar",
   "status.edited": "Curtha in eagar in {date}",
   "status.edited_x_times": "Curtha in eagar {count, plural, one {{count} uair amháin} two {{count} uair} few {{count} uair} many {{count} uair} other {{count} uair}}",
   "status.embed": "Leabaigh",
   "status.favourite": "Rogha",
   "status.filter": "Déan scagadh ar an bpostáil seo",
-  "status.filtered": "Filtered",
+  "status.filtered": "Scagtha",
   "status.hide": "Cuir postáil i bhfolach",
-  "status.history.created": "{name} created {date}",
+  "status.history.created": "Chruthaigh {name} {date}",
   "status.history.edited": "Curtha in eagar ag {name} in {date}",
   "status.load_more": "Lódáil a thuilleadh",
-  "status.media_hidden": "Media hidden",
+  "status.media_hidden": "Cuirtear meáin i bhfolach",
   "status.mention": "Luaigh @{name}",
   "status.more": "Tuilleadh",
   "status.mute": "Balbhaigh @{name}",
@@ -596,24 +605,24 @@
   "status.unmute_conversation": "Díbhalbhaigh comhrá",
   "status.unpin": "Díphionnáil de do phróifíl",
   "subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
-  "subscribed_languages.save": "Save changes",
+  "subscribed_languages.save": "Sábháil athruithe",
   "subscribed_languages.target": "Change subscribed languages for {target}",
   "suggestions.dismiss": "Dismiss suggestion",
-  "suggestions.header": "You might be interested in…",
+  "suggestions.header": "Seans go mbeidh suim agat i…",
   "tabs_bar.federated_timeline": "Cónasctha",
   "tabs_bar.home": "Baile",
   "tabs_bar.local_timeline": "Áitiúil",
   "tabs_bar.notifications": "Fógraí",
-  "time_remaining.days": "{number, plural, one {# day} other {# days}} left",
-  "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left",
-  "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
+  "time_remaining.days": "{number, plural, one {# lá} other {# lá}} fágtha",
+  "time_remaining.hours": "{number, plural, one {# uair} other {# uair}} fágtha",
+  "time_remaining.minutes": "{number, plural, one {# nóiméad} other {# nóiméad}} fágtha",
   "time_remaining.moments": "Moments remaining",
-  "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
+  "time_remaining.seconds": "{number, plural, one {# soicind} other {# soicind}} fágtha",
   "timeline_hint.remote_resource_not_displayed": "{resource} from other servers are not displayed.",
   "timeline_hint.resources.followers": "Leantóirí",
   "timeline_hint.resources.follows": "Cuntais leanta",
   "timeline_hint.resources.statuses": "Postáilí níos sine",
-  "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {{days} days}}",
+  "trends.counter_by_accounts": "{count, plural, one {{counter} duine} other {{counter} duine}} le {days, plural, one {lá} other {{days} lá}} anuas",
   "trends.trending_now": "Ag treochtáil anois",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
   "units.short.billion": "{count}B",
diff --git a/app/javascript/mastodon/locales/gd.json b/app/javascript/mastodon/locales/gd.json
index 995bb585d..ca3b8b4ab 100644
--- a/app/javascript/mastodon/locales/gd.json
+++ b/app/javascript/mastodon/locales/gd.json
@@ -20,7 +20,7 @@
   "account.blocked": "’Ga bhacadh",
   "account.browse_more_on_origin_server": "Rùraich barrachd dheth air a’ phròifil thùsail",
   "account.cancel_follow_request": "Cuir d’ iarrtas leantainn dhan dàrna taobh",
-  "account.direct": "Cuir teachdaireachd dhìreach gu @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Na cuir brath thugam tuilleadh nuair a chuireas @{name} post ris",
   "account.domain_blocked": "Chaidh an àrainn a bhacadh",
   "account.edit_profile": "Deasaich a’ phròifil",
@@ -54,7 +54,7 @@
   "account.posts_with_replies": "Postaichean ’s freagairtean",
   "account.report": "Dèan gearan mu @{name}",
   "account.requested": "A’ feitheamh air aontachadh. Briog airson sgur dhen iarrtas leantainn",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "Dh’iarr {name} ’gad leantainn",
   "account.share": "Co-roinn a’ phròifil aig @{name}",
   "account.show_reblogs": "Seall na brosnachaidhean o @{name}",
   "account.statuses_counter": "{count, plural, one {{counter} phost} two {{counter} phost} few {{counter} postaichean} other {{counter} post}}",
@@ -102,7 +102,7 @@
   "column.blocks": "Cleachdaichean bacte",
   "column.bookmarks": "Comharran-lìn",
   "column.community": "Loidhne-ama ionadail",
-  "column.direct": "Teachdaireachdan dìreach",
+  "column.direct": "Private mentions",
   "column.directory": "Rùraich sna pròifilean",
   "column.domain_blocks": "Àrainnean bacte",
   "column.favourites": "Na h-annsachdan",
@@ -128,7 +128,7 @@
   "compose.language.search": "Lorg cànan…",
   "compose_form.direct_message_warning_learn_more": "Barrachd fiosrachaidh",
   "compose_form.encryption_warning": "Chan eil crioptachadh ceann gu ceann air postaichean Mhastodon. Na co-roinn fiosrachadh dìomhair idir le Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "Cha nochd am post seo fon taga hais o nach eil e poblach. Cha ghabh ach postaichean poblach a lorg a-rèir an tagaichean hais.",
   "compose_form.lock_disclaimer": "Chan eil an cunntas agad {locked}. ’S urrainn do dhuine sam bith ’gad leantainn is na postaichean agad a tha ag amas air an luchd-leantainn agad a-mhàin a shealltainn.",
   "compose_form.lock_disclaimer.lock": "glaiste",
   "compose_form.placeholder": "Dè tha air d’ aire?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Tha atharraichean gun sàbhaladh agad ann an tuairisgeul no ro-shealladh a’ mheadhain, a bheil thu airson an tilgeil air falbh co-dhiù?",
   "confirmations.domain_block.confirm": "Bac an àrainn uile gu lèir",
   "confirmations.domain_block.message": "A bheil thu cinnteach dha-rìribh gu bheil thu airson an àrainn {domain} a bhacadh uile gu lèir? Mar as trice, foghnaidh gun dèan thu bacadh no mùchadh no dhà gu sònraichte agus bhiodh sin na b’ fheàrr. Chan fhaic thu susbaint on àrainn ud air loidhne-ama phoblach sam bith no am measg nam brathan agad. Thèid an luchd-leantainn agad on àrainn ud a thoirt air falbh.",
+  "confirmations.edit.confirm": "Deasaich",
+  "confirmations.edit.message": "Ma nì thu deasachadh an-dràsta, thèid seo a sgrìobhadh thairis air an teachdaireachd a tha thu a’ sgrìobhadh an-dràsta. A bheil thu cinnteach gu bheil thu airson leantainn air adhart?",
   "confirmations.logout.confirm": "Clàraich a-mach",
   "confirmations.logout.message": "A bheil thu cinnteach gu bheil thu airson clàradh a-mach?",
   "confirmations.mute.confirm": "Mùch",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Cha do bhac thu cleachdaiche sam bith fhathast.",
   "empty_column.bookmarked_statuses": "Chan eil comharra-lìn ri post agad fhathast. Nuair a nì thu comharra-lìn de dh’fhear, nochdaidh e an-seo.",
   "empty_column.community": "Tha an loidhne-ama ionadail falamh. Sgrìobh rudeigin gu poblach airson toiseach-tòiseachaidh a dhèanamh!",
-  "empty_column.direct": "Chan eil teachdaireachd dhìreach agad fhathast. Nuair a chuireas no a gheibh thu tè, nochdaidh i an-seo.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Cha deach àrainn sam bith a bhacadh fhathast.",
   "empty_column.explore_statuses": "Chan eil dad a’ treandadh an-dràsta fhèin. Thoir sùil a-rithist an ceann greis!",
   "empty_column.favourited_statuses": "Chan eil annsachd air post agad fhathast. Nuair a nì thu annsachd de dh’fhear, nochdaidh e an-seo.",
   "empty_column.favourites": "Chan eil am post seo ’na annsachd aig duine sam bith fhathast. Nuair a nì daoine annsachd dheth, nochdaidh iad an-seo.",
   "empty_column.follow_recommendations": "Chan urrainn dhuinn dad a mholadh dhut. Cleachd gleus an luirg feuch an lorg thu daoine air a bheil thu eòlach no rùraich na tagaichean-hais a tha a’ treandadh.",
   "empty_column.follow_requests": "Chan eil iarrtas leantainn agad fhathast. Nuair a gheibh thu fear, nochdaidh e an-seo.",
+  "empty_column.followed_tags": "Cha do lean thu taga hais sam bith fhathast. Nuair a leanas tu, nochdaidh iad an-seo.",
   "empty_column.hashtag": "Chan eil dad san taga hais seo fhathast.",
   "empty_column.home": "Tha loidhne-ama na dachaigh agad falamh! Lean barrachd dhaoine gus a lìonadh. {suggestions}",
   "empty_column.home.suggestions": "Faic moladh no dhà",
@@ -236,11 +239,11 @@
   "errors.unexpected_crash.copy_stacktrace": "Cuir lethbhreac dhen stacktrace air an stòr-bhòrd",
   "errors.unexpected_crash.report_issue": "Dèan aithris air an duilgheadas",
   "explore.search_results": "Toraidhean an luirg",
-  "explore.suggested_follows": "For you",
+  "explore.suggested_follows": "Dhut-sa",
   "explore.title": "Rùraich",
-  "explore.trending_links": "News",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "Hashtags",
+  "explore.trending_links": "Naidheachdan",
+  "explore.trending_statuses": "Postaichean",
+  "explore.trending_tags": "Tagaichean hais",
   "filter_modal.added.context_mismatch_explanation": "Chan eil an roinn-seòrsa criathraidh iom seo chaidh dhan cho-theacs san do dh’inntrig thu am post seo. Ma tha thu airson am post a chriathradh sa cho-theacs seo cuideachd, feumaidh tu a’ chriathrag a dheasachadh.",
   "filter_modal.added.context_mismatch_title": "Co-theacsa neo-iomchaidh!",
   "filter_modal.added.expired_explanation": "Dh’fhalbh an ùine air an roinn-seòrsa criathraidh seo agus feumaidh tu an ceann-là crìochnachaidh atharrachadh mus cuir thu an sàs i.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Ùghdarraich",
   "follow_request.reject": "Diùlt",
   "follow_requests.unlocked_explanation": "Ged nach eil an cunntas agad glaiste, tha sgioba {domain} dhen bheachd gum b’ fheàirrde thu lèirmheas a dhèanamh air na h-iarrtasan leantainn o na cunntasan seo a làimh.",
+  "followed_tags": "Tagaichean hais ’gan leantainn",
   "footer.about": "Mu dhèidhinn",
   "footer.directory": "Eòlaire nam pròifil",
   "footer.get_app": "Faigh an aplacaid",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Ath-ghoiridean a’ mheur-chlàir",
   "footer.privacy_policy": "Poileasaidh prìobhaideachd",
   "footer.source_code": "Seall am bun-tùs",
+  "footer.status": "Staid",
   "generic.saved": "Chaidh a shàbhaladh",
   "getting_started.heading": "Toiseach",
   "hashtag.column_header.tag_mode.all": "agus {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Cuir am fòcas air colbh",
   "keyboard_shortcuts.compose": "Cuir am fòcas air raon teacsa an sgrìobhaidh",
   "keyboard_shortcuts.description": "Tuairisgeul",
-  "keyboard_shortcuts.direct": "Fosgail colbh nan teachdaireachdan dìreach",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Gluais sìos air an liosta",
   "keyboard_shortcuts.enter": "Fosgail post",
   "keyboard_shortcuts.favourite": "Cuir post ris na h-annsachdan",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Comharran-lìn",
   "navigation_bar.community_timeline": "Loidhne-ama ionadail",
   "navigation_bar.compose": "Sgrìobh post ùr",
-  "navigation_bar.direct": "Teachdaireachdan dìreach",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Rùraich",
   "navigation_bar.domain_blocks": "Àrainnean bacte",
   "navigation_bar.edit_profile": "Deasaich a’ phròifil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Na h-annsachdan",
   "navigation_bar.filters": "Faclan mùchte",
   "navigation_bar.follow_requests": "Iarrtasan leantainn",
+  "navigation_bar.followed_tags": "Tagaichean hais ’gan leantainn",
   "navigation_bar.follows_and_followers": "Dàimhean leantainn",
   "navigation_bar.lists": "Liostaichean",
   "navigation_bar.logout": "Clàraich a-mach",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spama",
   "report_notification.categories.violation": "Briseadh riaghailte",
   "report_notification.open": "Fosgail an gearan",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Lorg",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Dèan lorg no cuir a-steach URL",
-  "search_popout.search_format": "Fòrmat adhartach an luirg",
-  "search_popout.tips.full_text": "Bheir teacsa sìmplidh dhut na postaichean a sgrìobh thu, a tha nan annsachdan dhut, a bhrosnaich thu no san deach iomradh a thoirt ort cho math ri ainmean-cleachdaiche, ainmean taisbeanaidh agus tagaichean hais a mhaidsicheas.",
-  "search_popout.tips.hashtag": "taga hais",
-  "search_popout.tips.status": "post",
-  "search_popout.tips.text": "Bheir teacsa sìmplidh dhut na h-ainmean-cleachdaiche, ainmean taisbeanaidh agus tagaichean hais a mhaidsicheas",
-  "search_popout.tips.user": "cleachdaiche",
-  "search_results.accounts": "Daoine",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Na h-uile",
   "search_results.hashtags": "Tagaichean hais",
   "search_results.nothing_found": "Cha do lorg sinn dad dha na h-abairtean-luirg seo",
@@ -540,9 +548,9 @@
   "server_banner.server_stats": "Stadastaireachd an fhrithealaiche:",
   "sign_in_banner.create_account": "Cruthaich cunntas",
   "sign_in_banner.sign_in": "Clàraich a-steach",
-  "sign_in_banner.text": "Clàraich a-steach a leantainn phròifilean no thagaichean hais, a’ cur postaichean ris na h-annsachdan ’s ’gan co-roinneadh is freagairt dhaibh no gabh gnìomh le cunntas o fhrithealaiche eile.",
+  "sign_in_banner.text": "Clàraich a-steach a leantainn phròifilean no thagaichean hais, a’ cur postaichean ris na h-annsachdan ’s ’gan co-roinneadh is freagairt dhaibh. ’S urrainn dhut gnìomh a ghabhail le cunntas o fhrithealaiche eile cuideachd.",
   "status.admin_account": "Fosgail eadar-aghaidh na maorsainneachd dha @{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_domain": "Fosgail eadar-aghaidh na maorsainneachd dha {domain}",
   "status.admin_status": "Fosgail am post seo ann an eadar-aghaidh na maorsainneachd",
   "status.block": "Bac @{name}",
   "status.bookmark": "Cuir ris na comharran-lìn",
@@ -551,7 +559,8 @@
   "status.copy": "Dèan lethbhreac dhen cheangal dhan phost",
   "status.delete": "Sguab às",
   "status.detailed_status": "Mion-shealladh a’ chòmhraidh",
-  "status.direct": "Cuir teachdaireachd dhìreach gu @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Deasaich",
   "status.edited": "Air a dheasachadh {date}",
   "status.edited_x_times": "Chaidh a dheasachadh {count, plural, one {{counter} turas} two {{counter} thuras} few {{counter} tursan} other {{counter} turas}}",
@@ -559,7 +568,7 @@
   "status.favourite": "Cuir ris na h-annsachdan",
   "status.filter": "Criathraich am post seo",
   "status.filtered": "Criathraichte",
-  "status.hide": "Hide post",
+  "status.hide": "Falaich am post",
   "status.history.created": "Chruthaich {name} {date} e",
   "status.history.edited": "Dheasaich {name} {date} e",
   "status.load_more": "Luchdaich barrachd dheth",
@@ -591,7 +600,7 @@
   "status.show_more_all": "Seall barrachd dhen a h-uile",
   "status.show_original": "Seall an tionndadh tùsail",
   "status.translate": "Eadar-theangaich",
-  "status.translated_from_with": "Air eaar-theangachadh o {lang} le {provider}",
+  "status.translated_from_with": "Air eadar-theangachadh o {lang} le {provider}",
   "status.uncached_media_warning": "Chan eil seo ri fhaighinn",
   "status.unmute_conversation": "Dì-mhùch an còmhradh",
   "status.unpin": "Dì-phrìnich on phròifil",
diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json
index d430a5a8d..20279e71f 100644
--- a/app/javascript/mastodon/locales/gl.json
+++ b/app/javascript/mastodon/locales/gl.json
@@ -20,7 +20,7 @@
   "account.blocked": "Bloqueada",
   "account.browse_more_on_origin_server": "Busca máis no perfil orixinal",
   "account.cancel_follow_request": "Retirar solicitude de seguimento",
-  "account.direct": "Mensaxe directa a @{name}",
+  "account.direct": "Mencionar de xeito privado a @{name}",
   "account.disable_notifications": "Deixar de notificarme cando @{name} publica",
   "account.domain_blocked": "Dominio agochado",
   "account.edit_profile": "Editar perfil",
@@ -102,7 +102,7 @@
   "column.blocks": "Usuarias bloqueadas",
   "column.bookmarks": "Marcadores",
   "column.community": "Cronoloxía local",
-  "column.direct": "Mensaxes directas",
+  "column.direct": "Mencións privadas",
   "column.directory": "Procurar perfís",
   "column.domain_blocks": "Dominios agochados",
   "column.favourites": "Favoritas",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Tes cambios sen gardar para a vista previa ou descrición do multimedia, descartamos os cambios?",
   "confirmations.domain_block.confirm": "Agochar dominio enteiro",
   "confirmations.domain_block.message": "Tes a certeza de querer bloquear todo de {domain}? Na meirande parte dos casos uns bloqueos ou silenciados específicos son suficientes. Non verás máis o contido deste dominio en ningunha cronoloxía pública ou nas túas notificacións. As túas seguidoras deste dominio serán eliminadas.",
+  "confirmations.edit.confirm": "Editar",
+  "confirmations.edit.message": "Ao editar sobrescribirás a mensaxe que estás a compor. Tes a certeza de que queres continuar?",
   "confirmations.logout.confirm": "Pechar sesión",
   "confirmations.logout.message": "Desexas pechar a sesión?",
   "confirmations.mute.confirm": "Acalar",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Aínda non bloqueaches a ningún usuaria.",
   "empty_column.bookmarked_statuses": "Aínda non marcaches ningunha publicación. Cando o fagas, aparecerán aquí.",
   "empty_column.community": "A cronoloxía local está baleira. Escribe algo de xeito público para espallalo!",
-  "empty_column.direct": "Aínda non tes mensaxes directas. Cando envíes ou recibas unha, amosarase aquí.",
+  "empty_column.direct": "Aínda non tes mencións privadas. Cando envíes ou recibas unha, aparecerá aquí.",
   "empty_column.domain_blocks": "Aínda non hai dominios agochados.",
   "empty_column.explore_statuses": "Non hai temas en voga. Volve máis tarde!",
   "empty_column.favourited_statuses": "Aínda non tes publicacións favoritas. Cando che guste algunha, aparecerá aquí.",
   "empty_column.favourites": "A ninguén lle gustou esta publicación polo momento. Cando a alguén lle guste, aparecerá aquí.",
   "empty_column.follow_recommendations": "Semella que non temos suxestións para ti. Podes utilizar a busca para atopar persoas que coñezas ou explorar os cancelos en voga.",
   "empty_column.follow_requests": "Non tes peticións de seguimento. Cando recibas unha, amosarase aquí.",
+  "empty_column.followed_tags": "Aínda non seguiches ningún cancelo. Cando o fagas aparecerán aquí.",
   "empty_column.hashtag": "Aínda non hai nada con este cancelo.",
   "empty_column.home": "A túa cronoloxía inicial está baleira! Segue a outras usuarias para enchela. {suggestions}",
   "empty_column.home.suggestions": "Ver suxestións",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autorizar",
   "follow_request.reject": "Rexeitar",
   "follow_requests.unlocked_explanation": "Malia que a túa conta non é privada, a administración de {domain} pensou que quizabes terías que revisar de xeito manual as solicitudes de seguiminto.",
+  "followed_tags": "Cancelos seguidos",
   "footer.about": "Acerca de",
   "footer.directory": "Directorio de perfís",
   "footer.get_app": "Descarga a app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Atallos do teclado",
   "footer.privacy_policy": "Política de privacidade",
   "footer.source_code": "Ver código fonte",
+  "footer.status": "Estado",
   "generic.saved": "Gardado",
   "getting_started.heading": "Primeiros pasos",
   "hashtag.column_header.tag_mode.all": "e {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Destacar unha columna",
   "keyboard_shortcuts.compose": "Por o cursor na área de escritura",
   "keyboard_shortcuts.description": "Descrición",
-  "keyboard_shortcuts.direct": "para abrir a columna de mensaxes directas",
+  "keyboard_shortcuts.direct": "para abrir a columna de mencións privadas",
   "keyboard_shortcuts.down": "Para mover cara abaixo na listaxe",
   "keyboard_shortcuts.enter": "Para abrir publicación",
   "keyboard_shortcuts.favourite": "Marcar como favorita",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Marcadores",
   "navigation_bar.community_timeline": "Cronoloxía local",
   "navigation_bar.compose": "Escribir unha nova publicación",
-  "navigation_bar.direct": "Mensaxes directas",
+  "navigation_bar.direct": "Mencións privadas",
   "navigation_bar.discover": "Descubrir",
   "navigation_bar.domain_blocks": "Dominios agochados",
   "navigation_bar.edit_profile": "Editar perfil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoritas",
   "navigation_bar.filters": "Palabras silenciadas",
   "navigation_bar.follow_requests": "Peticións de seguimento",
+  "navigation_bar.followed_tags": "Cancelos seguidos",
   "navigation_bar.follows_and_followers": "Seguindo e seguidoras",
   "navigation_bar.lists": "Listaxes",
   "navigation_bar.logout": "Pechar sesión",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Faltou ás regras",
   "report_notification.open": "Abrir a denuncia",
+  "search.no_recent_searches": "Non hai buscas recentes",
   "search.placeholder": "Procurar",
+  "search.quick_action.account_search": "Perfís coincidentes {x}",
+  "search.quick_action.go_to_account": "Ir ao perfil {x}",
+  "search.quick_action.go_to_hashtag": "Ir ao cancelo {x}",
+  "search.quick_action.open_url": "Abrir URL en Mastodon",
+  "search.quick_action.status_search": "Publicacións coincidentes {x}",
   "search.search_or_paste": "Busca ou insire URL",
-  "search_popout.search_format": "Formato de procura avanzada",
-  "search_popout.tips.full_text": "Texto simple devolve publicacións que ti escribiches, promoveches, marcaches como favoritas, ou foches mencionada, así como nomes de usuaria coincidentes, nomes públicos e cancelos.",
-  "search_popout.tips.hashtag": "cancelo",
-  "search_popout.tips.status": "publicación",
-  "search_popout.tips.text": "Texto simple devolve coincidencias con nomes públicos, nomes de usuaria e cancelos",
-  "search_popout.tips.user": "usuaria",
-  "search_results.accounts": "Persoas",
+  "search_popout.quick_actions": "Accións rápidas",
+  "search_popout.recent": "Buscas recentes",
+  "search_results.accounts": "Perfís",
   "search_results.all": "Todo",
   "search_results.hashtags": "Cancelos",
   "search_results.nothing_found": "Non atopamos nada con estes termos de busca",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Estatísticas do servidor:",
   "sign_in_banner.create_account": "Crear conta",
   "sign_in_banner.sign_in": "Acceder",
-  "sign_in_banner.text": "Inicia sesión para seguir perfís ou etiquetas, marcar como favorita, responder a publicacións ou interactuar con outro servidor desde a túa conta.",
+  "sign_in_banner.text": "Inicia sesión para seguir perfís ou cancelos, marcar como favorita e responder a publicacións. Tamén podes interactuar con outro servidor desde a túa conta.",
   "status.admin_account": "Abrir interface de moderación para @{name}",
   "status.admin_domain": "Abrir interface de moderación para {domain}",
   "status.admin_status": "Abrir esta publicación na interface de moderación",
@@ -551,7 +559,8 @@
   "status.copy": "Copiar ligazón á publicación",
   "status.delete": "Eliminar",
   "status.detailed_status": "Vista detallada da conversa",
-  "status.direct": "Mensaxe directa a @{name}",
+  "status.direct": "Mencionar de xeito privado a @{name}",
+  "status.direct_indicator": "Mención privada",
   "status.edit": "Editar",
   "status.edited": "Editado {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} vez} other {{count} veces}}",
@@ -624,12 +633,12 @@
   "upload_error.limit": "Límite máximo do ficheiro a subir excedido.",
   "upload_error.poll": "Non se poden subir ficheiros nas enquisas.",
   "upload_form.audio_description": "Describir para persoas con problemas auditivos",
-  "upload_form.description": "Describir para persoas con problemas visuais",
+  "upload_form.description": "Describir para persoas cegas ou con problemas visuais",
   "upload_form.description_missing": "Sen descrición",
   "upload_form.edit": "Editar",
   "upload_form.thumbnail": "Cambiar a miniatura",
   "upload_form.undo": "Eliminar",
-  "upload_form.video_description": "Describir para persoas con problemas visuais ou auditivos",
+  "upload_form.video_description": "Describe para persoas con problemas visuais ou auditivos",
   "upload_modal.analyzing_picture": "Estase a analizar a imaxe…",
   "upload_modal.apply": "Aplicar",
   "upload_modal.applying": "Aplicando…",
diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json
index eb558a863..17b97ca74 100644
--- a/app/javascript/mastodon/locales/he.json
+++ b/app/javascript/mastodon/locales/he.json
@@ -12,7 +12,7 @@
   "about.powered_by": "רשת חברתית מבוזרת המופעלת על ידי {mastodon}",
   "about.rules": "כללי השרת",
   "account.account_note_header": "הערה",
-  "account.add_or_remove_from_list": "הוסף או הסר מהרשימות",
+  "account.add_or_remove_from_list": "הוספה או הסרה מרשימות",
   "account.badges.bot": "בוט",
   "account.badges.group": "קבוצה",
   "account.block": "חסמי את @{name}",
@@ -20,7 +20,7 @@
   "account.blocked": "לחסום",
   "account.browse_more_on_origin_server": "ראה יותר בפרופיל המקורי",
   "account.cancel_follow_request": "משיכת בקשת מעקב",
-  "account.direct": "הודעה ישירה ל@{name}",
+  "account.direct": "הודעה פרטית אל @{name}",
   "account.disable_notifications": "הפסק לשלוח לי התראות כש@{name} מפרסמים",
   "account.domain_blocked": "הדומיין חסום",
   "account.edit_profile": "עריכת פרופיל",
@@ -40,7 +40,7 @@
   "account.go_to_profile": "מעבר לפרופיל",
   "account.hide_reblogs": "להסתיר הידהודים מאת @{name}",
   "account.joined_short": "תאריך הצטרפות",
-  "account.languages": "שנה שפת הרשמה",
+  "account.languages": "שנה רישום לשפות",
   "account.link_verified_on": "בעלות על הקישור הזה נבדקה לאחרונה ב{date}",
   "account.locked_info": "החשבון הזה הוגדר כנעול. צריך לקבל אישור כדי לעקוב אחריו.",
   "account.media": "מדיה",
@@ -72,8 +72,8 @@
   "admin.dashboard.retention.average": "ממוצע",
   "admin.dashboard.retention.cohort": "חודש רישום",
   "admin.dashboard.retention.cohort_size": "משתמשים חדשים",
-  "alert.rate_limited.message": "נא לנסות אחרי {retry_time, time, medium}.",
-  "alert.rate_limited.title": "חלה הגבלת קצב",
+  "alert.rate_limited.message": " נא לנסות שוב אחרי {retry_time, time, medium}.",
+  "alert.rate_limited.title": "חלה הגבלה על קצב התעבורה",
   "alert.unexpected.message": "אירעה שגיאה בלתי צפויה.",
   "alert.unexpected.title": "אופס!",
   "announcement.announcement": "הכרזה",
@@ -81,7 +81,7 @@
   "audio.hide": "השתק",
   "autosuggest_hashtag.per_week": "{count} לשבוע",
   "boost_modal.combo": "ניתן להקיש {combo} כדי לדלג בפעם הבאה",
-  "bundle_column_error.copy_stacktrace": "העתקת הודעת התקלה",
+  "bundle_column_error.copy_stacktrace": "העתקת הודעת שגיאה",
   "bundle_column_error.error.body": "הדף המבוקש אינו זמין. זה עשוי להיות באג בקוד או בעייה בתאימות הדפדפן.",
   "bundle_column_error.error.title": "הו, לא!",
   "bundle_column_error.network.body": "קרתה תקלה בעת טעינת העמוד. זו עשויה להיות תקלה זמנית בשרת או בחיבור האינטרנט שלך.",
@@ -89,11 +89,11 @@
   "bundle_column_error.retry": "לנסות שוב",
   "bundle_column_error.return": "חזרה לדף הבית",
   "bundle_column_error.routing.body": "העמוד המבוקש לא נמצא. האם ה־URL נכון?",
-  "bundle_column_error.routing.title": "404",
+  "bundle_column_error.routing.title": "שגיאה 404: הדף לא נמצא",
   "bundle_modal_error.close": "לסגור",
   "bundle_modal_error.message": "משהו השתבש בעת טעינת הרכיב הזה.",
   "bundle_modal_error.retry": "לנסות שוב",
-  "closed_registrations.other_server_instructions": "מכיוון שמסטודון הוא רשת מבוזרת, ניתן ליצור חשבון על שרת נוסף ועדיין לקיים קשר עם משתמשים בשרת זה.",
+  "closed_registrations.other_server_instructions": "מכיוון שמסטודון היא רשת מבוזרת, ניתן ליצור חשבון על שרת נוסף ועדיין לקיים קשר עם משתמשים בשרת זה.",
   "closed_registrations_modal.description": "יצירת חשבון על שרת {domain} איננה אפשרית כרגע, אבל זכרו שאינכן זקוקות לחשבון על {domain} כדי להשתמש במסטודון.",
   "closed_registrations_modal.find_another_server": "חיפוש שרת אחר",
   "closed_registrations_modal.preamble": "מסטודון הוא רשת מבוזרת, כך שלא משנה היכן החשבון שלך, קיימת האפשרות לעקוב ולתקשר עם משתמשים בשרת הזה. אפשר אפילו להריץ שרת בעצמך!",
@@ -102,7 +102,7 @@
   "column.blocks": "משתמשים חסומים",
   "column.bookmarks": "סימניות",
   "column.community": "פיד שרת מקומי",
-  "column.direct": "הודעות ישירות",
+  "column.direct": "הודעות פרטיות",
   "column.directory": "עיין בפרופילים",
   "column.domain_blocks": "קהילות (שמות מתחם) מוסתרות",
   "column.favourites": "חיבובים",
@@ -153,7 +153,7 @@
   "confirmations.block.confirm": "לחסום",
   "confirmations.block.message": "האם את/ה בטוח/ה שברצונך למחוק את \"{name}\"?",
   "confirmations.cancel_follow_request.confirm": "ויתור על בקשה",
-  "confirmations.cancel_follow_request.message": "האם באמת לוותר על בקשת המעקב אחרי {name}?",
+  "confirmations.cancel_follow_request.message": "לבטל את בקשת המעקב אחרי {name}?",
   "confirmations.delete.confirm": "למחוק",
   "confirmations.delete.message": "בטוח/ה שאת/ה רוצה למחוק את ההודעה?",
   "confirmations.delete_list.confirm": "למחוק",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "יש לך שינויים לא שמורים לתיאור המדיה. להשליך אותם בכל זאת?",
   "confirmations.domain_block.confirm": "חסמו לגמרי את שם המתחם (דומיין)",
   "confirmations.domain_block.message": "בטוחה שברצונך באמת לחסום את קהילת {domain}? ברב המקרים השתקה וחסימה של מספר משתמשים עשוייה להספיק. לא תראי תוכל מכלל שם המתחם בפידים הציבוריים או בהתראות שלך. העוקבים שלך מהקהילה הזאת יוסרו",
+  "confirmations.edit.confirm": "עריכה",
+  "confirmations.edit.message": "עריכה תדרוס את ההודעה שכבר התחלת לכתוב. האם להמשיך?",
   "confirmations.logout.confirm": "התנתקות",
   "confirmations.logout.message": "האם אתם בטוחים שאתם רוצים להתנתק?",
   "confirmations.mute.confirm": "להשתיק",
@@ -170,7 +172,7 @@
   "confirmations.redraft.confirm": "מחיקה ועריכה מחדש",
   "confirmations.redraft.message": "בטוחה שאת רוצה למחוק ולהתחיל טיוטה חדשה? חיבובים והדהודים יאבדו, ותגובות להודעה המקורית ישארו יתומות.",
   "confirmations.reply.confirm": "תגובה",
-  "confirmations.reply.message": "תגובה עכשיו תדרוס את ההודעה שכבר התחלתים לכתוב. האם אתם בטוחים שברצונכם להמשיך?",
+  "confirmations.reply.message": "תגובה עכשיו תמחק את ההודעה שכבר התחלת לכתוב. להמשיך?",
   "confirmations.unfollow.confirm": "הפסקת מעקב",
   "confirmations.unfollow.message": "להפסיק מעקב אחרי {name}?",
   "conversation.delete": "מחיקת שיחה",
@@ -187,9 +189,9 @@
   "disabled_account_banner.text": "חשבונך {disabledAccount} אינו פעיל כרגע.",
   "dismissable_banner.community_timeline": "אלו הם החצרוצים הציבוריים האחרונים מהמשתמשים על שרת {domain}.",
   "dismissable_banner.dismiss": "בטל",
-  "dismissable_banner.explore_links": "אלו סיפורי החדשות האחרונים שמדוברים על ידי משתמשים בשרת זה ואחרים ברשת המבוזרת כרגע.",
-  "dismissable_banner.explore_statuses": "החצרוצים האלו, משרת זה ואחרים ברשת המבוזרת, כרגע צוברים חשיפה.",
-  "dismissable_banner.explore_tags": "התגיות האלו, משרת זה ואחרים ברשת המבוזרת, כרגע צוברות חשיפה.",
+  "dismissable_banner.explore_links": "אלו הקישורים האחרונים ששותפו על ידי משתמשים ששרת זה רואה ברשת המבוזרת כרגע.",
+  "dismissable_banner.explore_statuses": "החצרוצים האלו, משרת זה ואחרים ברשת המבוזרת, צוברים חשיפה כעת.",
+  "dismissable_banner.explore_tags": "התגיות האלו, משרת זה ואחרים ברשת המבוזרת, צוברות חשיפה כעת.",
   "dismissable_banner.public_timeline": "אלו הם החצרוצים הציבוריים האחרונים מהמשתמשים משרת זה ואחרים ברשת המבוזרת ששרת זה יודע עליהן.",
   "embed.instructions": "ניתן להטמיע את ההודעה הזו באתרך ע\"י העתקת הקוד שלהלן.",
   "embed.preview": "דוגמא כיצד זה יראה:",
@@ -221,6 +223,7 @@
   "empty_column.favourites": "עוד לא חיבבו את ההודעה הזו. כאשר זה יקרה, החיבובים יופיעו כאן.",
   "empty_column.follow_recommendations": "נראה שלא ניתן לייצר המלצות עבורך. נסה/י להשתמש בחיפוש כדי למצוא אנשים מוכרים או לבדוק את הנושאים החמים.",
   "empty_column.follow_requests": "אין לך שום בקשות מעקב עדיין. לכשיתקבלו כאלה, הן תופענה כאן.",
+  "empty_column.followed_tags": "עוד לא עקבת אחרי תגיות. כשיהיו לך תגיות נעקבות, ההודעות יופיעו פה.",
   "empty_column.hashtag": "אין כלום בהאשתג הזה עדיין.",
   "empty_column.home": "פיד הבית ריק ! אפשר לבקר ב{public} או להשתמש בחיפוש כדי להתחיל ולהכיר משתמשים/ות אחרים/ות. {suggestions}",
   "empty_column.home.suggestions": "ראה/י כמה הצעות",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "הרשאה",
   "follow_request.reject": "דחיה",
   "follow_requests.unlocked_explanation": "למרות שחשבונך אינו נעול, צוות {domain} חושב שאולי כדאי לוודא את בקשות המעקב האלה ידנית.",
+  "followed_tags": "התגיות שהחשבון שלך עוקב אחריהן",
   "footer.about": "אודות",
   "footer.directory": "מדריך פרופילים",
   "footer.get_app": "להתקנת היישומון",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "קיצורי מקלדת",
   "footer.privacy_policy": "מדיניות פרטיות",
   "footer.source_code": "צפיה בקוד המקור",
+  "footer.status": "מצב",
   "generic.saved": "נשמר",
   "getting_started.heading": "בואו נתחיל",
   "hashtag.column_header.tag_mode.all": "ו- {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "להתמקד בחצרוץ באחד מהטורים",
   "keyboard_shortcuts.compose": "להתמקד בתיבת חיבור החצרוצים",
   "keyboard_shortcuts.description": "תיאור",
-  "keyboard_shortcuts.direct": "לפתיחת טור הודעות ישירות",
+  "keyboard_shortcuts.direct": "לפתוח עמודת שיחות פרטיות",
   "keyboard_shortcuts.down": "לנוע במורד הרשימה",
   "keyboard_shortcuts.enter": "פתח הודעה",
   "keyboard_shortcuts.favourite": "לחבב",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "סימניות",
   "navigation_bar.community_timeline": "פיד שרת מקומי",
   "navigation_bar.compose": "צור הודעה חדשה",
-  "navigation_bar.direct": "הודעות ישירות",
+  "navigation_bar.direct": "הודעות פרטיות",
   "navigation_bar.discover": "גלה",
   "navigation_bar.domain_blocks": "קהילות (שמות מתחם) חסומות",
   "navigation_bar.edit_profile": "עריכת פרופיל",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "חיבובים",
   "navigation_bar.filters": "מילים מושתקות",
   "navigation_bar.follow_requests": "בקשות מעקב",
+  "navigation_bar.followed_tags": "תגיות שהחשבון שלך עוקב אחריהן",
   "navigation_bar.follows_and_followers": "נעקבים ועוקבים",
   "navigation_bar.lists": "רשימות",
   "navigation_bar.logout": "התנתקות",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "ספאם (דואר זבל)",
   "report_notification.categories.violation": "הפרת כלל",
   "report_notification.open": "פתח דו\"ח",
+  "search.no_recent_searches": "לא נמצאו חיפושים אחרונים",
   "search.placeholder": "חיפוש",
+  "search.quick_action.account_search": "פרופילים המכילים {x}",
+  "search.quick_action.go_to_account": "לצפיה בפרופיל {x}",
+  "search.quick_action.go_to_hashtag": "לצפיה בתגית {x}",
+  "search.quick_action.open_url": "לפתיחת {x} במסטודון",
+  "search.quick_action.status_search": "הודעות המכילות {x}",
   "search.search_or_paste": "חפש או הזן קישור",
-  "search_popout.search_format": "מבנה חיפוש מתקדם",
-  "search_popout.tips.full_text": "טקסט פשוט מחזיר פוסטים שכתבת, חיבבת, הידהדת או שאוזכרת בהם, כמו גם שמות משתמשים, שמות להצגה ותגיות מתאימים.",
-  "search_popout.tips.hashtag": "תגית",
-  "search_popout.tips.status": "הודעה",
-  "search_popout.tips.text": "טקסט פשוט מחזיר כינויים, שמות משתמש והאשתגים",
-  "search_popout.tips.user": "משתמש(ת)",
-  "search_results.accounts": "אנשים",
+  "search_popout.quick_actions": "פעולות זריזות",
+  "search_popout.recent": "חיפושים אחרונים",
+  "search_results.accounts": "פרופילים",
   "search_results.all": "כל התוצאות",
   "search_results.hashtags": "תגיות",
   "search_results.nothing_found": "לא נמצא דבר עבור תנאי חיפוש אלה",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "סטטיסטיקות שרת:",
   "sign_in_banner.create_account": "יצירת חשבון",
   "sign_in_banner.sign_in": "התחברות",
-  "sign_in_banner.text": "יש להתחבר כדי לעקוב אחרי משתמשים או תגיות, לחבב, לשתף ולענות לחצרוצים, או לנהל תקשורת מהחשבון שלך על שרת אחר.",
+  "sign_in_banner.text": "יש להתחבר כדי לעקוב אחרי משתמשים או תגיות, לחבב, לשתף ולענות להודעות. ניתן גם לתקשר מהחשבון שלך עם שרת אחר.",
   "status.admin_account": "פתח/י ממשק ניהול עבור @{name}",
   "status.admin_domain": "פתיחת ממשק ניהול עבור {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "העתק/י קישור להודעה זו",
   "status.delete": "מחיקה",
   "status.detailed_status": "תצוגת שיחה מפורטת",
-  "status.direct": "הודעה ישירה ל@{name}",
+  "status.direct": "הודעה פרטית אל @{name}",
+  "status.direct_indicator": "הודעה פרטית",
   "status.edit": "עריכה",
   "status.edited": "נערך ב{date}",
   "status.edited_x_times": "נערך {count, plural, one {פעם {count}} other {{count} פעמים}}",
@@ -559,7 +568,7 @@
   "status.favourite": "חיבוב",
   "status.filter": "סנן הודעה זו",
   "status.filtered": "סונן",
-  "status.hide": "הסתר הודעה",
+  "status.hide": "הסתרת חיצרוץ",
   "status.history.created": "{name} יצר/ה {date}",
   "status.history.edited": "{name} ערך/ה {date}",
   "status.load_more": "עוד",
diff --git a/app/javascript/mastodon/locales/hi.json b/app/javascript/mastodon/locales/hi.json
index 70edbbe5e..186de8129 100644
--- a/app/javascript/mastodon/locales/hi.json
+++ b/app/javascript/mastodon/locales/hi.json
@@ -20,7 +20,7 @@
   "account.blocked": "ब्लॉक",
   "account.browse_more_on_origin_server": "मूल प्रोफ़ाइल पर अधिक ब्राउज़ करें",
   "account.cancel_follow_request": "फॉलो रिक्वेस्ट वापस लें",
-  "account.direct": "प्रत्यक्ष संदेश @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "@{name} पोस्ट के लिए मुझे सूचित मत करो",
   "account.domain_blocked": "छिपा हुआ डोमेन",
   "account.edit_profile": "प्रोफ़ाइल संपादित करें",
@@ -54,7 +54,7 @@
   "account.posts_with_replies": "टूट्स एवं जवाब",
   "account.report": "रिपोर्ट @{name}",
   "account.requested": "मंजूरी का इंतजार। फॉलो रिक्वेस्ट को रद्द करने के लिए क्लिक करें",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name} ने आपको फॉलो करने के लिए अनुरोध किया है",
   "account.share": "@{name} की प्रोफाइल शेयर करे",
   "account.show_reblogs": "@{name} के बूस्ट दिखाए",
   "account.statuses_counter": "{count, plural, one {{counter} भोंपू} other {{counter} भोंपू}}",
@@ -102,7 +102,7 @@
   "column.blocks": "ब्लॉक्ड यूज़र्स",
   "column.bookmarks": "पुस्तकचिह्न:",
   "column.community": "लोकल टाइम्लाइन",
-  "column.direct": "सीधा संदेश",
+  "column.direct": "निजी संदेश",
   "column.directory": "प्रोफाइल्स खोजें",
   "column.domain_blocks": "छुपे डोमेन्स",
   "column.favourites": "पसंदीदा",
@@ -128,7 +128,7 @@
   "compose.language.search": "भाषाएँ खोजें...",
   "compose_form.direct_message_warning_learn_more": "और जानें",
   "compose_form.encryption_warning": "मास्टोडॉन पर पोस्ट एन्ड-टू-एन्ड एन्क्रिप्टेड नहीं है। कोई भी व्यक्तिगत जानकारी मास्टोडॉन पर मत भेजें।",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "ये पोस्ट किसी भी हैशटैग में लिस्ट नहीं किया जाएगा क्योंकि ये पब्लिक नहीं है। सिर्फ पब्लिक पोस्ट ही हैशटैग से खोजे जा सकते हैं।",
   "compose_form.lock_disclaimer": "आपका खाता {locked} नहीं है। आपको केवल फॉलोवर्स को दिखाई दिए जाने वाले पोस्ट देखने के लिए कोई भी फॉलो कर सकता है।",
   "compose_form.lock_disclaimer.lock": "लॉक्ड",
   "compose_form.placeholder": "What is on your mind?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "लिस्ट में जोड़ें",
   "confirmations.domain_block.confirm": "संपूर्ण डोमेन छिपाएं",
   "confirmations.domain_block.message": "क्या आप वास्तव में, वास्तव में आप पूरे {domain} को ब्लॉक करना चाहते हैं? ज्यादातर मामलों में कुछ लक्षित ब्लॉक या म्यूट पर्याप्त और बेहतर हैं। आप किसी भी सार्वजनिक समय-सीमा या अपनी सूचनाओं में उस डोमेन की सामग्री नहीं देखेंगे। उस डोमेन से आपके फॉलोवर्स को हटा दिया जाएगा।",
+  "confirmations.edit.confirm": "संशोधित करें",
+  "confirmations.edit.message": "अभी संपादन किया तो वो संदेश मिट जायेगा जिसे आप लिख रहे थे। क्या आप जारी रखना चाहते हैं?",
   "confirmations.logout.confirm": "लॉग आउट करें",
   "confirmations.logout.message": "आप सुनिश्चित हैं कि लॉगआउट करना चाहते हैं?",
   "confirmations.mute.confirm": "शांत",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "आप अभी तक किसी भी यूजर के द्वारा ब्लॉक्ड नहीं हो।",
   "empty_column.bookmarked_statuses": "आपके पास अभी तक कोई बुकमार्क नहीं है। जब आप एक बुकमार्क करते हैं, तो यह यहां दिखाई देगा।",
   "empty_column.community": "लोकल टाइम्लाइन खाली है, कुछ देखने के लिये सार्वजनिक रूप से कुछ लिखें!",
-  "empty_column.direct": "आपके पास अभी तक कोई सीधा संदेश नहीं है, जब आप भेजेंगे या प्राप्त करेंगे तो ये यहाँ दिखेगा |",
+  "empty_column.direct": "अभी तक आपको कोई निजी संदेश नहीं मिला है। जब भी आप निजी संदेश भेजेंगे या पाएंगे, तो वो यहां पर दिखेगा।",
   "empty_column.domain_blocks": "अभी तक कोई छुपा हुआ डोमेन नहीं है।",
   "empty_column.explore_statuses": "कुछ भी अभी ट्रैंडिंग नहीं है, कुछ देर बाद जांचे!",
   "empty_column.favourited_statuses": "आपके पास अभी कोई भी चहिता टूट नहीं है. जब आप किसी टूट को पसंद (स्टार) करेंगे, तब वो यहाँ दिखेगा।",
   "empty_column.favourites": "अभी तक किसी ने भी इस टूट को पसंद (स्टार) नहीं किया है. जब भी कोई इसे पसंद करेगा, उनका नाम यहाँ दिखेगा।",
   "empty_column.follow_recommendations": "ऐसा लगता है कि आपके लिए कोई सुझाव जेनरेट नहीं किया जा सका. आप उन लोगों को खोजने के लिए सर्च का उपयोग करने का प्रयास कर सकते हैं जिन्हें आप जानते हैं या ट्रेंडिंग हैशटैग का पता लगा सकते हैं।",
   "empty_column.follow_requests": "अभी तक किसी ने भी आपका अनुसरण करने की विनती नहीं की है. जब भी कोई आपको विनती भेजेगा, वो यहाँ दिखेगी.",
+  "empty_column.followed_tags": "आपने किसी हैशटैग को फॉलो नहीं किया है। जैसे ही आप फॉलो करेंगे, आपके फॉलो किए गए हैशटैग यहां दिखेंगे।",
   "empty_column.hashtag": "यह हैशटैग अभी तक खाली है।",
   "empty_column.home": "आपकी मुख्य कालक्रम अभी खली है. अन्य उपयोगकर्ताओं से मिलने के लिए और अपनी गतिविधियां शुरू करने के लिए या तो {public} पर जाएं या खोज का उपयोग करें।",
   "empty_column.home.suggestions": "कुछ सुझाव देखिए",
@@ -236,11 +239,11 @@
   "errors.unexpected_crash.copy_stacktrace": "स्टैकट्रेस को क्लिपबोर्ड पर कॉपी करें",
   "errors.unexpected_crash.report_issue": "समस्या सूचित करें",
   "explore.search_results": "सर्च रिजल्ट्स",
-  "explore.suggested_follows": "For you",
+  "explore.suggested_follows": "आपके लिए",
   "explore.title": "एक्स्प्लोर",
-  "explore.trending_links": "News",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "Hashtags",
+  "explore.trending_links": "समाचार",
+  "explore.trending_statuses": "पोस्ट्स",
+  "explore.trending_tags": "हैशटैग्स",
   "filter_modal.added.context_mismatch_explanation": "यह फ़िल्टर श्रेणी उस संदर्भ पर लागू नहीं होती जिसमें आपने इस पोस्ट को एक्सेस किया है। यदि आप चाहते हैं कि इस संदर्भ में भी पोस्ट को फ़िल्टर किया जाए, तो आपको फ़िल्टर को एडिट करना होगा।",
   "filter_modal.added.context_mismatch_title": "कंटेंट मिसमैच!",
   "filter_modal.added.expired_explanation": "यह फ़िल्टर श्रेणी समाप्त हो गई है, इसे लागू करने के लिए आपको समाप्ति तिथि बदलनी होगी।",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "अधिकार दें",
   "follow_request.reject": "अस्वीकार करें",
   "follow_requests.unlocked_explanation": "हालाँकि आपका खाता लॉक नहीं है, फिर भी {domain} डोमेन स्टाफ ने सोचा कि आप इन खातों के मैन्युअल अनुरोधों की समीक्षा करना चाहते हैं।",
+  "followed_tags": "फॉलो किए गए हैशटैग्स",
   "footer.about": "अबाउट",
   "footer.directory": "प्रोफाइल्स डायरेक्टरी",
   "footer.get_app": "अप्प प्राप्त करें",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "कीबोर्ड शॉर्टकट",
   "footer.privacy_policy": "प्राइवेसी पालिसी",
   "footer.source_code": "सोर्स कोड देखें",
+  "footer.status": "स्टेटस",
   "generic.saved": "सेव्ड",
   "getting_started.heading": "पहले कदम रखें",
   "hashtag.column_header.tag_mode.all": "और {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "to focus a status in one of the columns",
   "keyboard_shortcuts.compose": "कंपोज़ टेक्स्ट-एरिया पर ध्यान केंद्रित करने के लिए",
   "keyboard_shortcuts.description": "विवरण",
-  "keyboard_shortcuts.direct": "डायरेक्ट मैसेज कॉलम खोलने के लिए",
+  "keyboard_shortcuts.direct": "निजी संदेश खोलने के लिए",
   "keyboard_shortcuts.down": "सूची में शामिल करने के लिए",
   "keyboard_shortcuts.enter": "स्टेटस खोलने के लिए",
   "keyboard_shortcuts.favourite": "पसंदीदा के लिए",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "पुस्तकचिह्न:",
   "navigation_bar.community_timeline": "लोकल टाइम्लाइन",
   "navigation_bar.compose": "नया टूट् लिखें",
-  "navigation_bar.direct": "प्रत्यक्ष संदेश",
+  "navigation_bar.direct": "निजी संदेश",
   "navigation_bar.discover": "खोजें",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "प्रोफ़ाइल संपादित करें",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "पसंदीदा",
   "navigation_bar.filters": "वारित शब्द",
   "navigation_bar.follow_requests": "अनुसरण करने के अनुरोध",
+  "navigation_bar.followed_tags": "हैशटैग को फॉलो करें",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "सूचियाँ",
   "navigation_bar.logout": "बाहर जाए",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "कोई हालिया खोज नहीं",
   "search.placeholder": "खोजें",
+  "search.quick_action.account_search": "प्रोफ़ाइल मिले {x}",
+  "search.quick_action.go_to_account": "प्रोफ़ाइल पर जाएँ {x}",
+  "search.quick_action.go_to_hashtag": "हैशटैग पर जाएं {x}",
+  "search.quick_action.open_url": "URL मॅस्टोडॉन में खोलें",
+  "search.quick_action.status_search": "पोस्ट मिलें {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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": "हैशटैग",
-  "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_popout.quick_actions": "त्वरित क्रियाएं",
+  "search_popout.recent": "हालिया खोजें",
+  "search_results.accounts": "प्रोफ़ाइल",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "निजी संदेश @{name} से",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json
index 5d38728b1..47d2bfb92 100644
--- a/app/javascript/mastodon/locales/hr.json
+++ b/app/javascript/mastodon/locales/hr.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blokirano",
   "account.browse_more_on_origin_server": "Pogledajte više na izvornom profilu",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Pošalji poruku @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Nemoj me obavjestiti kada @{name} napravi objavu",
   "account.domain_blocked": "Domena je blokirana",
   "account.edit_profile": "Uredi profil",
@@ -65,7 +65,7 @@
   "account.unfollow": "Prestani pratiti",
   "account.unmute": "Poništi utišavanje @{name}",
   "account.unmute_notifications": "Ne utišavaj obavijesti od @{name}",
-  "account.unmute_short": "Unmute",
+  "account.unmute_short": "Poništi utišavanje",
   "account_note.placeholder": "Kliknite za dodavanje bilješke",
   "admin.dashboard.daily_retention": "User retention rate by day after sign-up",
   "admin.dashboard.monthly_retention": "User retention rate by month after sign-up",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokirani korisnici",
   "column.bookmarks": "Knjižne oznake",
   "column.community": "Lokalna vremenska crta",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Pregledavanje profila",
   "column.domain_blocks": "Blokirane domene",
   "column.favourites": "Favoriti",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Postoje nespremljene promjene u opisu medija ili u pretpregledu, svejedno ih odbaciti?",
   "confirmations.domain_block.confirm": "Blokiraj cijelu domenu",
   "confirmations.domain_block.message": "Jeste li zaista, zaista sigurni da želite blokirati cijelu domenu {domain}? U većini slučajeva dovoljno je i preferirano nekoliko ciljanih blokiranja ili utišavanja. Nećete vidjeti sadržaj s te domene ni u kojim javnim vremenskim crtama ili Vašim obavijestima. Vaši pratitelji s te domene bit će uklonjeni.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Odjavi se",
   "confirmations.logout.message": "Jeste li sigurni da se želite odjaviti?",
   "confirmations.mute.confirm": "Utišaj",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Još niste blokirali nikoga.",
   "empty_column.bookmarked_statuses": "Još nemaš niti jedan označeni toot. Kada označiš jedan, prikazad će se ovdje.",
   "empty_column.community": "Lokalna vremenska crta je prazna. Napišite nešto javno da biste pokrenuli stvari!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Još nema blokiranih domena.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "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_recommendations": "Čini se da se ne postoje sugestije generirane za tebe. Možeš pokušati koristiti pretragu kako bi pronašao osobe koje poznaš ili istraži popularne hashtagove.",
   "empty_column.follow_requests": "Nemaš niti jedan zahtjev za praćenjem. Ako ga dobiješ, prikazat će se ovdje.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Još ne postoji ništa s ovim hashtagom.",
   "empty_column.home": "Vaša početna vremenska crta je prazna! Posjetite {public} ili koristite tražilicu kako biste započeli i upoznali druge korisnike.",
   "empty_column.home.suggestions": "Pogledajte neke prijedloge",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autoriziraj",
   "follow_request.reject": "Odbij",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Preuzmi aplikaciju",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Tipkovni prečaci",
   "footer.privacy_policy": "Pravila o zaštiti privatnosti",
   "footer.source_code": "Prikaz izvornog koda",
+  "footer.status": "Status",
   "generic.saved": "Spremljeno",
   "getting_started.heading": "Počnimo",
   "hashtag.column_header.tag_mode.all": "i {additional}",
@@ -364,14 +369,14 @@
   "missing_indicator.sublabel": "This resource could not be found",
   "moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.",
   "mute_modal.duration": "Trajanje",
-  "mute_modal.hide_notifications": "Hide notifications from this user?",
+  "mute_modal.hide_notifications": "Sakrij obavijesti ovog korisnika?",
   "mute_modal.indefinite": "Indefinite",
   "navigation_bar.about": "About",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Lokalna vremenska crta",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Istraživanje",
   "navigation_bar.domain_blocks": "Blokirane domene",
   "navigation_bar.edit_profile": "Uredi profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoriti",
   "navigation_bar.filters": "Utišane riječi",
   "navigation_bar.follow_requests": "Zahtjevi za praćenje",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Praćeni i pratitelji",
   "navigation_bar.lists": "Liste",
   "navigation_bar.logout": "Odjavi se",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Traži",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "Format naprednog pretraživanja",
-  "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": "korisnik",
-  "search_results.accounts": "Ljudi",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Stvori račun",
   "sign_in_banner.sign_in": "Prijavi se",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Obriši",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Uredi",
   "status.edited": "Uređeno {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json
index 7c91ff568..4cd7ebde7 100644
--- a/app/javascript/mastodon/locales/hu.json
+++ b/app/javascript/mastodon/locales/hu.json
@@ -11,7 +11,7 @@
   "about.not_available": "Ez az információ nem lett közzétéve ezen a kiszolgálón.",
   "about.powered_by": "Decentralizált közösségi média a {mastodon} segítségével",
   "about.rules": "Kiszolgáló szabályai",
-  "account.account_note_header": "Jegyzet",
+  "account.account_note_header": "Feljegyzés",
   "account.add_or_remove_from_list": "Hozzáadás vagy eltávolítás a listákról",
   "account.badges.bot": "Bot",
   "account.badges.group": "Csoport",
@@ -20,7 +20,7 @@
   "account.blocked": "Letiltva",
   "account.browse_more_on_origin_server": "Böngéssz tovább az eredeti profilon",
   "account.cancel_follow_request": "Követési kérés visszavonása",
-  "account.direct": "Közvetlen üzenet @{name} számára",
+  "account.direct": "@{name} személyes említése",
   "account.disable_notifications": "Ne figyelmeztessen, ha @{name} bejegyzést tesz közzé",
   "account.domain_blocked": "Letiltott domain",
   "account.edit_profile": "Profil szerkesztése",
@@ -34,7 +34,7 @@
   "account.followers.empty": "Ezt a felhasználót még senki sem követi.",
   "account.followers_counter": "{count, plural, one {{counter} Követő} other {{counter} Követő}}",
   "account.following": "Követve",
-  "account.following_counter": "{count, plural, one {{counter} Követett} other {{counter} Követett}}",
+  "account.following_counter": "{count, plural, one {{counter} követett} other {{counter} követett}}",
   "account.follows.empty": "Ez a felhasználó még senkit sem követ.",
   "account.follows_you": "Követ téged",
   "account.go_to_profile": "Ugrás a profilhoz",
@@ -53,8 +53,8 @@
   "account.posts": "Bejegyzések",
   "account.posts_with_replies": "Bejegyzések és válaszok",
   "account.report": "@{name} jelentése",
-  "account.requested": "Jóváhagysára vár. Kattints a követési kérés visszavonásához",
-  "account.requested_follow": "{name} kérte, hogy követhessen téged",
+  "account.requested": "Jóváhagysára vár. Kattintás a követési kérés törléséhez",
+  "account.requested_follow": "{name} kérte, hogy követhessen",
   "account.share": "@{name} profiljának megosztása",
   "account.show_reblogs": "@{name} megtolásainak mutatása",
   "account.statuses_counter": "{count, plural, one {{counter} Bejegyzés} other {{counter} Bejegyzés}}",
@@ -66,12 +66,12 @@
   "account.unmute": "@{name} némításának feloldása",
   "account.unmute_notifications": "@{name} némított értesítéseinek feloldása",
   "account.unmute_short": "Némitás feloldása",
-  "account_note.placeholder": "Kattints jegyzet hozzáadásához",
+  "account_note.placeholder": "Kattintás jegyzet hozzáadásához",
   "admin.dashboard.daily_retention": "Napi regisztráció utáni felhasználómegtartási arány",
   "admin.dashboard.monthly_retention": "Havi regisztráció utáni felhasználómegtartási arány",
   "admin.dashboard.retention.average": "Átlag",
   "admin.dashboard.retention.cohort": "Regisztráció hónapja",
-  "admin.dashboard.retention.cohort_size": "Új felhasználók",
+  "admin.dashboard.retention.cohort_size": "Új felhasználó",
   "alert.rate_limited.message": "Próbáld újra {retry_time, time, medium} után.",
   "alert.rate_limited.title": "Adatforgalom korlátozva",
   "alert.unexpected.message": "Váratlan hiba történt.",
@@ -96,13 +96,13 @@
   "closed_registrations.other_server_instructions": "Mivel a Mastdon decentralizált, létrehozhatsz egy fiókot egy másik kiszolgálón és mégis kapcsolódhatsz ehhez.",
   "closed_registrations_modal.description": "Fiók létrehozása a {domain} kiszolgálón jelenleg nem lehetséges, de jó, ha tudod, hogy nem szükséges fiókkal rendelkezni pont a {domain} kiszolgálón, hogy használhasd a Mastodont.",
   "closed_registrations_modal.find_another_server": "Másik kiszolgáló keresése",
-  "closed_registrations_modal.preamble": "A Mastodon decentralizált, így teljesen mindegy, hol hozod létre a fiókodat, követhetsz és kapcsolódhatsz bárkivel ezen a kiszolgálón is. Saját magad is üzemeltethetsz kiszolgálót!",
+  "closed_registrations_modal.preamble": "A Mastodon nem központosított, így teljesen mindegy, hol történik a fiók létrehozása, követhető bárki és kapcsolatba lehet lépni bárkivel ezen a kiszolgálón is. Saját magunk is üzemeltethetünk kiszolgálót!",
   "closed_registrations_modal.title": "Regisztráció a Mastodonra",
   "column.about": "Névjegy",
   "column.blocks": "Letiltott felhasználók",
   "column.bookmarks": "Könyvjelzők",
   "column.community": "Helyi idővonal",
-  "column.direct": "Közvetlen üzenetek",
+  "column.direct": "Személyes említések",
   "column.directory": "Profilok böngészése",
   "column.domain_blocks": "Letiltott tartománynevek",
   "column.favourites": "Kedvencek",
@@ -131,7 +131,7 @@
   "compose_form.hashtag_warning": "Ez a bejegyzésed nem fog megjelenni semmilyen hashtag alatt, mivel nem nyilvános. Csak a nyilvános bejegyzések kereshetők hashtaggel.",
   "compose_form.lock_disclaimer": "A fiókod nincs {locked}. Bárki követni tud, hogy megtekintse a kizárólag követőknek szánt bejegyzéseket.",
   "compose_form.lock_disclaimer.lock": "lezárva",
-  "compose_form.placeholder": "Mi jár a fejedben?",
+  "compose_form.placeholder": "Mire gondolunk éppen?",
   "compose_form.poll.add_option": "Lehetőség hozzáadása",
   "compose_form.poll.duration": "Szavazás időtartama",
   "compose_form.poll.option_placeholder": "{number}. lehetőség",
@@ -145,23 +145,25 @@
   "compose_form.sensitive.hide": "{count, plural, one {Média kényesnek jelölése} other {Média kényesnek jelölése}}",
   "compose_form.sensitive.marked": "{count, plural, one {A médiát kényesnek jelölték} other {A médiát kényesnek jelölték}}",
   "compose_form.sensitive.unmarked": "{count, plural, one {A médiát nem jelölték kényesnek} other {A médiát nem jelölték kényesnek}}",
-  "compose_form.spoiler.marked": "Tartalmi figyelmeztetés törlése",
+  "compose_form.spoiler.marked": "Tartalmi figyelmeztetés eltávolítása",
   "compose_form.spoiler.unmarked": "Tartalmi figyelmeztetés hozzáadása",
-  "compose_form.spoiler_placeholder": "Írd ide a figyelmeztetést",
-  "confirmation_modal.cancel": "Mégse",
+  "compose_form.spoiler_placeholder": "A figyelmeztetés beírása ide",
+  "confirmation_modal.cancel": "Mégsem",
   "confirmations.block.block_and_report": "Letiltás és jelentés",
   "confirmations.block.confirm": "Letiltás",
   "confirmations.block.message": "Biztos, hogy letiltod: {name}?",
   "confirmations.cancel_follow_request.confirm": "Kérés visszavonása",
-  "confirmations.cancel_follow_request.message": "Biztos, hogy visszavonod a(z) {name} felhasználóra vonatkozó követési kérésedet?",
+  "confirmations.cancel_follow_request.message": "Biztosan visszavonásra kerüljön {name} felhasználóra vonatkozó követési kérés?",
   "confirmations.delete.confirm": "Törlés",
   "confirmations.delete.message": "Biztos, hogy törölni szeretnéd ezt a bejegyzést?",
   "confirmations.delete_list.confirm": "Törlés",
   "confirmations.delete_list.message": "Biztos, hogy véglegesen törölni szeretnéd ezt a listát?",
   "confirmations.discard_edit_media.confirm": "Elvetés",
-  "confirmations.discard_edit_media.message": "Elmentetlen változtatásaid vannak a média leírásában vagy előnézetében. Eldobjuk őket?",
-  "confirmations.domain_block.confirm": "Teljes domain elrejtése",
+  "confirmations.discard_edit_media.message": "Elmentetlen változtatások vannak a média leírásában vagy előnézetében. Elvetésre kerüljenek?",
+  "confirmations.domain_block.confirm": "Teljes tartomány tiltása",
   "confirmations.domain_block.message": "Biztos, hogy le szeretnéd tiltani a teljes {domain} domaint? A legtöbb esetben néhány célzott tiltás vagy némítás elegendő, és kívánatosabb megoldás. Semmilyen tartalmat nem fogsz látni ebből a domainből se az idővonalakon, se az értesítésekben. Az ebben a domainben lévő követőidet is eltávolítjuk.",
+  "confirmations.edit.confirm": "Szerkesztés",
+  "confirmations.edit.message": "A szerkesztés felülírja a most összeállítás alatt álló üzenetet. Folytatás?",
   "confirmations.logout.confirm": "Kijelentkezés",
   "confirmations.logout.message": "Biztos, hogy kijelentkezel?",
   "confirmations.mute.confirm": "Némítás",
@@ -176,21 +178,21 @@
   "conversation.delete": "Beszélgetés törlése",
   "conversation.mark_as_read": "Megjelölés olvasottként",
   "conversation.open": "Beszélgetés megtekintése",
-  "conversation.with": "{names}-el/al",
+  "conversation.with": "Velük: {names}",
   "copypaste.copied": "Másolva",
   "copypaste.copy": "Másolás",
   "directory.federated": "Az ismert fediverzumból",
-  "directory.local": "Csak innen: {domain}",
+  "directory.local": "Csak {domain} tartományból",
   "directory.new_arrivals": "Új csatlakozók",
   "directory.recently_active": "Nemrég aktív",
   "disabled_account_banner.account_settings": "Fiókbeállítások",
-  "disabled_account_banner.text": "A(z) {disabledAccount} fiókod jelenleg le van tiltva.",
-  "dismissable_banner.community_timeline": "Ezek a legfrissebb nyilvános bejegyzések, amelyeket a(z) {domain} kiszolgáló fiókjait használó emberek tették közzé.",
-  "dismissable_banner.dismiss": "Eltüntetés",
-  "dismissable_banner.explore_links": "Jelenleg ezekről a hírekről beszélgetnek az ezen és a decentralizált hálózat többi kiszolgálóján lévő emberek.",
-  "dismissable_banner.explore_statuses": "Jelenleg ezek a bejegyzések hódítanak teret ezen és a decentralizált hálózat egyéb kiszolgálóin.",
-  "dismissable_banner.explore_tags": "Jelenleg ezek a hashtagek hódítanak teret ezen és a decentralizált hálózat többi kiszolgálóján lévő emberek körében.",
-  "dismissable_banner.public_timeline": "Ezek a legfrissebb bejegyzések azoktól, akik a decentralizált hálózat más kiszolgálóin vannak, és ez a kiszolgáló tud róluk.",
+  "disabled_account_banner.text": "{disabledAccount} fiók jelenleg letilzásra került.",
+  "dismissable_banner.community_timeline": "Ezek a legfrissebb nyilvános bejegyzések, amelyeket {domain} tartományban levő kiszolgáló fiókjait használó emberek tettek közzé.",
+  "dismissable_banner.dismiss": "Elvetés",
+  "dismissable_banner.explore_links": "Jelenleg ezekről a hírekről beszélgetnek az ezen és a központosítás nélküli hálózat többi kiszolgálóján lévő emberek.",
+  "dismissable_banner.explore_statuses": "Jelenleg ezek a bejegyzések hódítanak teret ezen és a központosítás nélküli hálózat egyéb kiszolgálóin.",
+  "dismissable_banner.explore_tags": "Jelenleg ezek a #címke elemek hódítanak teret ezen és a központosítás nélküli hálózat többi kiszolgálóján lévő emberek körében.",
+  "dismissable_banner.public_timeline": "Ezek a legfrissebb bejegyzések azoktól, akik a központosítás nélküli hálózat más kiszolgálóin vannak és ez a kiszolgáló tud róluk.",
   "embed.instructions": "Ágyazd be ezt a bejegyzést a weboldaladba az alábbi kód kimásolásával.",
   "embed.preview": "Így fog kinézni:",
   "emoji_button.activity": "Tevékenység",
@@ -208,79 +210,82 @@
   "emoji_button.search_results": "Keresési találatok",
   "emoji_button.symbols": "Szimbólumok",
   "emoji_button.travel": "Utazás és Helyek",
-  "empty_column.account_suspended": "Fiók felfüggesztve",
+  "empty_column.account_suspended": "A fiók felfüggesztésre került",
   "empty_column.account_timeline": "Itt nincs bejegyzés!",
   "empty_column.account_unavailable": "A profil nem érhető el",
-  "empty_column.blocks": "Még senkit sem tiltottál le.",
+  "empty_column.blocks": "Még senki sem került letiltásra.",
   "empty_column.bookmarked_statuses": "Még nincs egyetlen könyvjelzőzött bejegyzésed sem. Ha könyvjelzőzöl egyet, itt fog megjelenni.",
   "empty_column.community": "A helyi idővonal üres. Tégy közzé valamit nyilvánosan, hogy elindítsd az eseményeket!",
-  "empty_column.direct": "Még nincs egy közvetlen üzeneted sem. Ha küldesz vagy kapsz egyet, itt fog megjelenni.",
-  "empty_column.domain_blocks": "Még nem rejtettél el egyetlen domaint sem.",
+  "empty_column.direct": "Még nincs egy személyes említésed sem. Küldéskor vagy fogadáskor itt fognak megjelenni.",
+  "empty_column.domain_blocks": "Még nem lett letiltva egyetlen tartomány sem.",
   "empty_column.explore_statuses": "Jelenleg semmi sem felkapott. Nézz vissza később!",
   "empty_column.favourited_statuses": "Még nincs egyetlen kedvenc bejegyzésed sem. Ha kedvencnek jelölsz egyet, itt fog megjelenni.",
   "empty_column.favourites": "Még senki sem jelölte ezt a bejegyzést kedvencnek. Ha valaki mégis megteszi, itt fogjuk mutatni.",
   "empty_column.follow_recommendations": "Úgy tűnik, neked nem tudunk javaslatokat adni. Próbáld a keresést használni olyanok megtalálására, akiket ismerhetsz, vagy fedezd fel a felkapott hastageket.",
-  "empty_column.follow_requests": "Még nincs egy követési kérésed sem. Ha kapsz egyet, itt fogjuk feltüntetni.",
-  "empty_column.hashtag": "Jelenleg nem található semmi ezzel a hashtaggel.",
-  "empty_column.home": "A saját idővonalad üres! Látogasd meg a {public} oldalt vagy használd a keresőt, hogy megismerj másokat.",
+  "empty_column.follow_requests": "Még nincs egy követési kérés sem. Fogadáskor itt jelenik meg.",
+  "empty_column.followed_tags": "Még egy #címke sincs követve. Ezek ekkor itt jelennek meg.",
+  "empty_column.hashtag": "Jelenleg nem található semmi ezzel a #címkével.",
+  "empty_column.home": "A saját idővonal üres! További emberek követése a kitöltéshez. {suggestions}",
   "empty_column.home.suggestions": "Nézzünk pár javaslatot",
   "empty_column.list": "A lista jelenleg üres. Ha a listatagok bejegyzést tesznek közzé, itt fog megjelenni.",
-  "empty_column.lists": "Még nem hoztál létre listát. Ha csinálsz egyet, itt látszik majd.",
-  "empty_column.mutes": "Még egy felhasználót sem némítottál le.",
-  "empty_column.notifications": "Jelenleg nincsenek értesítéseid. Lépj kapcsolatba másokkal, hogy elindítsd a beszélgetést.",
-  "empty_column.public": "Jelenleg itt nincs semmi! Írj valamit nyilvánosan vagy kövess más kiszolgálón levő felhasználókat, hogy megtöltsd.",
+  "empty_column.lists": "Még nincs egyetlen lista sem. A létrehozáskor itt jelenik meg.",
+  "empty_column.mutes": "Még nincs egyetlen némított felhasználót sem.",
+  "empty_column.notifications": "Jelenleg nincsenek értesítések. Más emberekkel kapcsolatba lépés után ez itt lesz látható.",
+  "empty_column.public": "Jelenleg itt nincs semmi! Írjunk valamit nyilvánosan vagy kövessünk más kiszolgálón levő felhasználókat a megjelenéshez.",
   "error.unexpected_crash.explanation": "Egy hiba vagy böngésző inkompatibilitás miatt ez az oldal nem jeleníthető meg rendesen.",
-  "error.unexpected_crash.explanation_addons": "Ezt az oldalt nem lehet helyesen megjeleníteni. Ezt a hibát valószínűleg egy böngésző beépülő vagy egy automatikus fordító okozza.",
-  "error.unexpected_crash.next_steps": "Próbáld frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy appon keresztül még mindig használhatod a Mastodont.",
-  "error.unexpected_crash.next_steps_addons": "Próbáld letiltani őket és frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy appon keresztül még mindig használhatod a Mastodont.",
+  "error.unexpected_crash.explanation_addons": "Ezt az oldalt nem lehet helyesen megjeleníteni. Ezt a hibát valószínűleg egy böngésző kiegészítő vagy egy automatikus fordító okozza.",
+  "error.unexpected_crash.next_steps": "Próbáljuk meg frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy appon keresztül még mindig használható a Mastodon.",
+  "error.unexpected_crash.next_steps_addons": "Próbáljuk meg letiltani őket és frissíteni az oldalt. Ha ez nem segít, egy másik böngészőn vagy alkalmazáson keresztül még mindig használható a Mastodon.",
   "errors.unexpected_crash.copy_stacktrace": "Veremkiíratás vágólapra másolása",
   "errors.unexpected_crash.report_issue": "Probléma jelentése",
   "explore.search_results": "Keresési találatok",
-  "explore.suggested_follows": "Neked",
+  "explore.suggested_follows": "Nekem",
   "explore.title": "Felfedezés",
   "explore.trending_links": "Hírek",
   "explore.trending_statuses": "Bejegyzések",
-  "explore.trending_tags": "Hashtagek",
-  "filter_modal.added.context_mismatch_explanation": "Ez a szűrőkategória nem érvényes abban a környezetben, amelyből elérted ezt a bejegyzést. Ha ebben a környezetben is szűrni szeretnéd a bejegyzést, akkor szerkesztened kell a szűrőt.",
+  "explore.trending_tags": "#Címkék",
+  "filter_modal.added.context_mismatch_explanation": "Ez a szűrőkategória nem érvényes abban a környezetben, amelyből ez a bejegyzés elérésre kerül. Ha ebben a környezetben is szűrni szeretnénk a bejegyzést, akkor szerkeszteni kell a szűrőt.",
   "filter_modal.added.context_mismatch_title": "Környezeti eltérés.",
-  "filter_modal.added.expired_explanation": "Ez a szűrőkategória elévült, a használatához módosítanod kell az elévülési dátumot.",
-  "filter_modal.added.expired_title": "Elévült szűrő.",
-  "filter_modal.added.review_and_configure": "A szűrőkategória felülvizsgálatához és további beállításához ugorjon a {settings_link} oldalra.",
+  "filter_modal.added.expired_explanation": "Ez a szűrőkategória elévült, a használatához módosítani kell a lejárati dátumot.",
+  "filter_modal.added.expired_title": "A szűrő lejárt!",
+  "filter_modal.added.review_and_configure": "A szűrőkategória felülvizsgálatához és további beállításához ugorás {settings_link} oldalra.",
   "filter_modal.added.review_and_configure_title": "Szűrőbeállítások",
   "filter_modal.added.settings_link": "beállítások oldal",
   "filter_modal.added.short_explanation": "A következő bejegyzés hozzá lett adva a következő szűrőkategóriához: {title}.",
-  "filter_modal.added.title": "Szűrő hozzáadva.",
+  "filter_modal.added.title": "A szűrő hozzáadásra került.",
   "filter_modal.select_filter.context_mismatch": "nem érvényes erre a környezetre",
-  "filter_modal.select_filter.expired": "elévült",
+  "filter_modal.select_filter.expired": "lejárt",
   "filter_modal.select_filter.prompt_new": "Új kategória: {name}",
   "filter_modal.select_filter.search": "Keresés vagy létrehozás",
-  "filter_modal.select_filter.subtitle": "Válassz egy meglévő kategóriát, vagy hozz létre egy újat",
+  "filter_modal.select_filter.subtitle": "Létező kategória használata vagy új létrehozása",
   "filter_modal.select_filter.title": "E bejegyzés szűrése",
   "filter_modal.title.status": "Egy bejegyzés szűrése",
   "follow_recommendations.done": "Kész",
   "follow_recommendations.heading": "Kövesd azokat, akiknek a bejegyzéseit látni szeretnéd! Itt van néhány javaslat.",
   "follow_recommendations.lead": "Az általad követettek bejegyzései a saját idővonaladon fognak megjelenni időrendi sorrendben. Ne félj attól, hogy hibázol! A követést bármikor, ugyanilyen könnyen visszavonhatod!",
-  "follow_request.authorize": "Engedélyezés",
+  "follow_request.authorize": "Hitelesítés",
   "follow_request.reject": "Elutasítás",
   "follow_requests.unlocked_explanation": "Bár a fiókod nincs zárolva, a(z) {domain} csapata úgy gondolta, hogy talán kézzel szeretnéd ellenőrizni a fiók követési kéréseit.",
+  "followed_tags": "Követett #címkék",
   "footer.about": "Névjegy",
-  "footer.directory": "Profilok",
-  "footer.get_app": "Töltsd le az appot",
-  "footer.invite": "Mások meghívása",
+  "footer.directory": "Profiltár",
+  "footer.get_app": "Alkalmazás beszerzése",
+  "footer.invite": "Emberek meghívása",
   "footer.keyboard_shortcuts": "Billentyűparancsok",
   "footer.privacy_policy": "Adatvédelmi szabályzat",
   "footer.source_code": "Forráskód megtekintése",
+  "footer.status": "Állapot",
   "generic.saved": "Elmentve",
   "getting_started.heading": "Első lépések",
   "hashtag.column_header.tag_mode.all": "és {additional}",
   "hashtag.column_header.tag_mode.any": "vagy {additional}",
   "hashtag.column_header.tag_mode.none": "{additional} nélkül",
   "hashtag.column_settings.select.no_options_message": "Nincs javaslat",
-  "hashtag.column_settings.select.placeholder": "Addj meg hashtageket…",
+  "hashtag.column_settings.select.placeholder": "#Címkék megadása…",
   "hashtag.column_settings.tag_mode.all": "Mindegyik",
   "hashtag.column_settings.tag_mode.any": "Bármelyik",
   "hashtag.column_settings.tag_mode.none": "Egyik sem",
-  "hashtag.column_settings.tag_toggle": "Új címkék felvétele ehhez az oszlophoz",
+  "hashtag.column_settings.tag_toggle": "További címkék felvétele ehhez az oszlophoz",
   "hashtag.follow": "Hashtag követése",
   "hashtag.unfollow": "Hashtag követésének megszüntetése",
   "home.column_settings.basic": "Alapvető",
@@ -288,14 +293,14 @@
   "home.column_settings.show_replies": "Válaszok megjelenítése",
   "home.hide_announcements": "Közlemények elrejtése",
   "home.show_announcements": "Közlemények megjelenítése",
-  "interaction_modal.description.favourite": "Egy Mastodon fiókkal kedvencnek jelölheted ezt a bejegyzést, tudatva a szerzővel, hogy értékeled és elteszed későbbre.",
+  "interaction_modal.description.favourite": "Egy Mastodon fiókkal kedvencnek jelölhető ez a bejegyzés, tudatva a szerzővel, hogy értékeljük és eltesszük későbbre.",
   "interaction_modal.description.follow": "Egy Mastodon fiókkal bekövetheted {name} fiókot, hogy lásd a bejegyzéseit a saját hírfolyamodban.",
-  "interaction_modal.description.reblog": "Egy Mastodon fiókkal megtolhatod ezt a bejegyzést, hogy megoszd a saját követőiddel.",
-  "interaction_modal.description.reply": "Egy Mastodon fiókkal válaszolhatsz erre a bejegyzésre.",
+  "interaction_modal.description.reblog": "Egy Mastodon fiókkal megtolható ez a bejegyzés a saját követőkkel megosztáshoz.",
+  "interaction_modal.description.reply": "Egy Mastodon fiókkal válaszolhatunk erre a bejegyzésre.",
   "interaction_modal.on_another_server": "Másik kiszolgálón",
   "interaction_modal.on_this_server": "Ezen a kiszolgálón",
-  "interaction_modal.other_server_instructions": "Másold és illeszd be ezt a webcímet a kedvenc Mastodon alkalmazásod vagy a Mastodon-kiszolgálód webes felületének keresőmezőjébe.",
-  "interaction_modal.preamble": "Mivel a Mastodon decentralizált, használhatod egy másik Mastodon kiszolgálón, vagy kompatibilis szolgáltatáson lévő fiókodat, ha ezen a kiszolgálón nincs fiókod.",
+  "interaction_modal.other_server_instructions": "Másoljuk és illesszük be ezt a webcímet a kedvenc Mastodon alkalmazásd vagy a Mastodon kiszolgáló webes felületének keresőmezőjébe.",
+  "interaction_modal.preamble": "Mivel a Mastodon nem központosított, használható egy másik Mastodon kiszolgálón vagy kompatibilis szolgáltatáson lévő fiók, ha ezen a kiszolgálón nincs saját fiók.",
   "interaction_modal.title.favourite": "{name} bejegyzésének megjelölése kedvencként",
   "interaction_modal.title.follow": "{name} követése",
   "interaction_modal.title.reblog": "{name} bejegyzésének megtolása",
@@ -303,47 +308,47 @@
   "intervals.full.days": "{number, plural, one {# nap} other {# nap}}",
   "intervals.full.hours": "{number, plural, one {# óra} other {# óra}}",
   "intervals.full.minutes": "{number, plural, one {# perc} other {# perc}}",
-  "keyboard_shortcuts.back": "visszafelé navigálás",
-  "keyboard_shortcuts.blocked": "letiltott felhasználók listájának megnyitása",
+  "keyboard_shortcuts.back": "Navigálás vissza",
+  "keyboard_shortcuts.blocked": "Letiltott felhasználók listájának megnyitása",
   "keyboard_shortcuts.boost": "Bejegyzés megtolása",
   "keyboard_shortcuts.column": "Fókuszálás egy oszlopra",
-  "keyboard_shortcuts.compose": "fókuszálás a szerkesztési szövegdobozra",
+  "keyboard_shortcuts.compose": "Szerkesztési terület fókuszálása",
   "keyboard_shortcuts.description": "Leírás",
-  "keyboard_shortcuts.direct": "közvetlen üzenetek megnyitása",
-  "keyboard_shortcuts.down": "lefele navigálás a listában",
+  "keyboard_shortcuts.direct": "személyes említések oszlop megnyitása",
+  "keyboard_shortcuts.down": "Mozgás lefelé a listában",
   "keyboard_shortcuts.enter": "Bejegyzés megnyitása",
   "keyboard_shortcuts.favourite": "Bejegyzés kedvencnek jelölése",
-  "keyboard_shortcuts.favourites": "kedvenc lista megnyitása",
+  "keyboard_shortcuts.favourites": "Kedvencek lista megnyitása",
   "keyboard_shortcuts.federated": "föderációs idővonal megnyitása",
   "keyboard_shortcuts.heading": "Billentyűparancsok",
-  "keyboard_shortcuts.home": "saját idővonal megnyitása",
+  "keyboard_shortcuts.home": "Saját idővonal megnyitása",
   "keyboard_shortcuts.hotkey": "Gyorsbillentyű",
   "keyboard_shortcuts.legend": "jelmagyarázat megjelenítése",
   "keyboard_shortcuts.local": "helyi idővonal megnyitása",
-  "keyboard_shortcuts.mention": "szerző megemlítése",
+  "keyboard_shortcuts.mention": "Szerző megemlítése",
   "keyboard_shortcuts.muted": "némított felhasználók listájának megnyitása",
-  "keyboard_shortcuts.my_profile": "profilod megnyitása",
-  "keyboard_shortcuts.notifications": "értesítések megnyitása",
-  "keyboard_shortcuts.open_media": "média megnyitása",
+  "keyboard_shortcuts.my_profile": "Saját profil megnyitása",
+  "keyboard_shortcuts.notifications": "Értesítések oszlop megnyitása",
+  "keyboard_shortcuts.open_media": "Média megnyitása",
   "keyboard_shortcuts.pinned": "Kitűzött bejegyzések listájának megnyitása",
-  "keyboard_shortcuts.profile": "szerző profiljának megnyitása",
+  "keyboard_shortcuts.profile": "Szerző profil megnyitása",
   "keyboard_shortcuts.reply": "Válasz bejegyzésre",
-  "keyboard_shortcuts.requests": "követési kérések listájának megnyitása",
-  "keyboard_shortcuts.search": "fókuszálás a keresőre",
+  "keyboard_shortcuts.requests": "Követési kérések lista megnyitása",
+  "keyboard_shortcuts.search": "Keresősáv fókuszálása",
   "keyboard_shortcuts.spoilers": "Tartalmi figyelmeztetés mező megjelenítése/elrejtése",
-  "keyboard_shortcuts.start": "\"Első lépések\" megnyitása",
-  "keyboard_shortcuts.toggle_hidden": "Tartalmi figyelmeztetéssel ellátott szöveg megjelenítése/elrejtése",
+  "keyboard_shortcuts.start": "\"Első lépések\" oszlop megnyitása",
+  "keyboard_shortcuts.toggle_hidden": "Tartalmi figyelmeztetéssel mögötti szöveg megjelenítése/elrejtése",
   "keyboard_shortcuts.toggle_sensitivity": "Média megjelenítése/elrejtése",
   "keyboard_shortcuts.toot": "Új bejegyzés írása",
   "keyboard_shortcuts.unfocus": "Szerkesztés/keresés fókuszból való kivétele",
-  "keyboard_shortcuts.up": "felfelé mozdítás a listában",
+  "keyboard_shortcuts.up": "Mozgás felfelé a listában",
   "lightbox.close": "Bezárás",
-  "lightbox.compress": "Képnézet összecsukása",
-  "lightbox.expand": "Képnézet kinagyítása",
+  "lightbox.compress": "Képnéző doboz összezárása",
+  "lightbox.expand": "Képnéző doboz kinyitása",
   "lightbox.next": "Következő",
   "lightbox.previous": "Előző",
-  "limited_account_hint.action": "Mindenképpen mutassa a profilt",
-  "limited_account_hint.title": "Ezt a profilt a(z) {domain} moderátorai elrejtették.",
+  "limited_account_hint.action": "Profil megjelenítése mindenképpen",
+  "limited_account_hint.title": "Ezt a profilt {domain} moderátorai elrejtették.",
   "lists.account.add": "Hozzáadás a listához",
   "lists.account.remove": "Eltávolítás a listából",
   "lists.delete": "Lista törlése",
@@ -354,31 +359,32 @@
   "lists.replies_policy.followed": "Bármely követett felhasználó",
   "lists.replies_policy.list": "A lista tagjai",
   "lists.replies_policy.none": "Senki",
-  "lists.replies_policy.title": "Nekik mutassuk a válaszokat:",
+  "lists.replies_policy.title": "Válaszok megjelenítése:",
   "lists.search": "Keresés a követett személyek között",
-  "lists.subheading": "Listáid",
+  "lists.subheading": "Saját listák",
   "load_pending": "{count, plural, one {# új elem} other {# új elem}}",
   "loading_indicator.label": "Betöltés...",
   "media_gallery.toggle_visible": "{number, plural, one {Kép elrejtése} other {Képek elrejtése}}",
   "missing_indicator.label": "Nincs találat",
   "missing_indicator.sublabel": "Ez az erőforrás nem található",
-  "moved_to_account_banner.text": "A(z) {disabledAccount} fiókod jelenleg le van tiltva, mert átköltöztél ide: {movedToAccount}.",
+  "moved_to_account_banner.text": "{disabledAccount} fiók jelenleg le van tiltva, mert más {movedToAccount} fiókba került át.",
   "mute_modal.duration": "Időtartam",
-  "mute_modal.hide_notifications": "Rejtsük el a felhasználótól származó értesítéseket?",
+  "mute_modal.hide_notifications": "Értesítések elrejtése ettől a felhasználótól?",
   "mute_modal.indefinite": "Határozatlan",
   "navigation_bar.about": "Névjegy",
   "navigation_bar.blocks": "Letiltott felhasználók",
   "navigation_bar.bookmarks": "Könyvjelzők",
   "navigation_bar.community_timeline": "Helyi idővonal",
   "navigation_bar.compose": "Új bejegyzés írása",
-  "navigation_bar.direct": "Közvetlen üzenetek",
+  "navigation_bar.direct": "Személyes említések",
   "navigation_bar.discover": "Felfedezés",
-  "navigation_bar.domain_blocks": "Rejtett domainek",
+  "navigation_bar.domain_blocks": "Letiltott tartományok",
   "navigation_bar.edit_profile": "Profil szerkesztése",
   "navigation_bar.explore": "Felfedezés",
   "navigation_bar.favourites": "Kedvencek",
   "navigation_bar.filters": "Némított szavak",
   "navigation_bar.follow_requests": "Követési kérelmek",
+  "navigation_bar.followed_tags": "Követett #címkék",
   "navigation_bar.follows_and_followers": "Követettek és követők",
   "navigation_bar.lists": "Listák",
   "navigation_bar.logout": "Kijelentkezés",
@@ -389,15 +395,15 @@
   "navigation_bar.public_timeline": "Föderációs idővonal",
   "navigation_bar.search": "Keresés",
   "navigation_bar.security": "Biztonság",
-  "not_signed_in_indicator.not_signed_in": "Az erőforrás eléréséhez be kell jelentkezned.",
+  "not_signed_in_indicator.not_signed_in": "Az erőforrás eléréséhez be kell jelentkezni.",
   "notification.admin.report": "{name} jelentette: {target}",
   "notification.admin.sign_up": "{name} regisztrált",
   "notification.favourite": "{name} kedvencnek jelölte a bejegyzésedet",
   "notification.follow": "{name} követ téged",
   "notification.follow_request": "{name} követni szeretne téged",
   "notification.mention": "{name} megemlített",
-  "notification.own_poll": "A szavazásod véget ért",
-  "notification.poll": "Egy szavazás, melyben részt vettél, véget ért",
+  "notification.own_poll": "A szavazás véget ért",
+  "notification.poll": "Egy általam részt vett szavazás véget ért",
   "notification.reblog": "{name} megtolta a bejegyzésedet",
   "notification.status": "{name} bejegyzést tett közzé",
   "notification.update": "{name} szerkesztett egy bejegyzést",
@@ -407,47 +413,47 @@
   "notifications.column_settings.admin.sign_up": "Új regisztrálók:",
   "notifications.column_settings.alert": "Asztali értesítések",
   "notifications.column_settings.favourite": "Kedvencek:",
-  "notifications.column_settings.filter_bar.advanced": "Minden kategória megjelenítése",
-  "notifications.column_settings.filter_bar.category": "Gyorskereső mező",
-  "notifications.column_settings.filter_bar.show_bar": "Szűrősáv mutatása",
+  "notifications.column_settings.filter_bar.advanced": "Összes kategória megjelenítése",
+  "notifications.column_settings.filter_bar.category": "Gyorsszűrő sáv",
+  "notifications.column_settings.filter_bar.show_bar": "Szűrősáv megjelenítése",
   "notifications.column_settings.follow": "Új követők:",
   "notifications.column_settings.follow_request": "Új követési kérelmek:",
   "notifications.column_settings.mention": "Megemlítések:",
-  "notifications.column_settings.poll": "Szavazás eredménye:",
+  "notifications.column_settings.poll": "Szavazási eredmények:",
   "notifications.column_settings.push": "Push értesítések",
   "notifications.column_settings.reblog": "Megtolások:",
-  "notifications.column_settings.show": "Oszlopban mutatás",
+  "notifications.column_settings.show": "Megjelenítés oszlopban",
   "notifications.column_settings.sound": "Hang lejátszása",
   "notifications.column_settings.status": "Új bejegyzések:",
   "notifications.column_settings.unread_notifications.category": "Olvasatlan értesítések",
   "notifications.column_settings.unread_notifications.highlight": "Olvasatlan értesítések kiemelése",
   "notifications.column_settings.update": "Szerkesztések:",
-  "notifications.filter.all": "Mind",
+  "notifications.filter.all": "Összes",
   "notifications.filter.boosts": "Megtolások",
-  "notifications.filter.favourites": "Kedvencnek jelölések",
+  "notifications.filter.favourites": "Kedvencek",
   "notifications.filter.follows": "Követések",
   "notifications.filter.mentions": "Megemlítések",
   "notifications.filter.polls": "Szavazások eredményei",
   "notifications.filter.statuses": "Frissítések azoktól, akiket követsz",
   "notifications.grant_permission": "Engedély megadása.",
   "notifications.group": "{count} értesítés",
-  "notifications.mark_as_read": "Minden értesítés olvasottnak jelölése",
-  "notifications.permission_denied": "Nem tudjuk engedélyezni az asztali értesítéseket, mert az engedélyt megtagadták.",
-  "notifications.permission_denied_alert": "Az asztali értesítések nem engedélyezhetőek, mert az engedélyt megtagadták a böngészőben",
-  "notifications.permission_required": "Az asztali értesítések nem elérhetőek, mert a szükséges engedélyt nem adtad meg.",
+  "notifications.mark_as_read": "Összes értesítés megjelölése olvasottként",
+  "notifications.permission_denied": "Az asztali értesítések nem érhetők el a korábban elutasított böngésző engedély kérelem miatt",
+  "notifications.permission_denied_alert": "Az asztali értesítések nem engedélyezhetők a korábban elutasított böngésző engedély miatt",
+  "notifications.permission_required": "Az asztali értesítések nem érhetők, mivel a szükséges engedély nem lett megadva.",
   "notifications_permission_banner.enable": "Asztali értesítések engedélyezése",
-  "notifications_permission_banner.how_to_control": "Ahhoz, hogy értesítéseket kapj akkor, amikor a Mastodon nincs megnyitva, engedélyezd az asztali értesítéseket. Pontosan be tudod állítani, hogy milyen interakciókról értesülj a fenti {icon} gombon keresztül, ha egyszer már engedélyezted őket.",
-  "notifications_permission_banner.title": "Soha ne mulassz el semmit",
-  "picture_in_picture.restore": "Visszarakás",
+  "notifications_permission_banner.how_to_control": "Bezárt Mastononnál értesések fogadásához engedélyezni kell az asztali értesítéseket. Pontosan lehet vezérelni, hogy milyen interakciókról érkezzen értesítés fenti {icon} gombon keresztül, ha már lorábban megtörtént az engedélyezés.",
+  "notifications_permission_banner.title": "Soha ne mulasszunk el semmit",
+  "picture_in_picture.restore": "Visszahelyezés",
   "poll.closed": "Lezárva",
   "poll.refresh": "Frissítés",
   "poll.total_people": "{count, plural, one {# személy} other {# személy}}",
   "poll.total_votes": "{count, plural, one {# szavazat} other {# szavazat}}",
   "poll.vote": "Szavazás",
-  "poll.voted": "Erre a válaszra szavaztál",
+  "poll.voted": "Megtörtént a szavazás erre a kérdésre",
   "poll.votes": "{votes, plural, one {# szavazat} other {# szavazat}}",
   "poll_button.add_poll": "Új szavazás",
-  "poll_button.remove_poll": "Szavazás törlése",
+  "poll_button.remove_poll": "Szavazás eltávolítása",
   "privacy.change": "Bejegyzés láthatóságának módosítása",
   "privacy.direct.long": "Csak a megemlített felhasználóknak látható",
   "privacy.direct.short": "Csak megemlítetteknek",
@@ -460,7 +466,7 @@
   "privacy_policy.last_updated": "Utoljára frissítve: {date}",
   "privacy_policy.title": "Adatvédelmi szabályzat",
   "refresh": "Frissítés",
-  "regeneration_indicator.label": "Töltődik…",
+  "regeneration_indicator.label": "A betöltés folyamatban van…",
   "regeneration_indicator.sublabel": "A saját idővonalad épp készül!",
   "relative_time.days": "{number}n",
   "relative_time.full.days": "{number, plural, one {# napja} other {# napja}}",
@@ -475,7 +481,7 @@
   "relative_time.today": "ma",
   "reply_indicator.cancel": "Mégsem",
   "report.block": "Letiltás",
-  "report.block_explanation": "Nem fogod látni a bejegyzéseit. Nem fogja tudni megnézni a bejegyzéseidet és nem fog tudni követni sem. Azt is meg fogja tudni mondani, hogy letiltottad.",
+  "report.block_explanation": "A bejegyzéseik nem áthatók. Nem nézheti meg a saját bejegyzéseimet és nem tudni követni sem. Azt is meg fogja tudni mondani, hogy letiltották.",
   "report.categories.other": "Egyéb",
   "report.categories.spam": "Kéretlen üzenet",
   "report.categories.violation": "A tartalom a kiszolgáló egy vagy több szabályát sérti",
@@ -486,11 +492,11 @@
   "report.close": "Kész",
   "report.comment.title": "Van valami, amiről tudnunk kellene?",
   "report.forward": "Továbbítás: {target}",
-  "report.forward_hint": "Ez a fiók egy másik kiszolgálóról van. Oda is elküldöd a jelentés egy anonimizált másolatát?",
+  "report.forward_hint": "Ez a fiók egy másik kiszolgálóról van. Oda is elküldésre kerüljön a jelentés egy anonimizált másolata?",
   "report.mute": "Némítás",
   "report.mute_explanation": "Nem fogod látni a bejegyzéseit. Továbbra is fog tudni követni, és látni fogja a bejegyzéseidet, és nem fogja tudni, hogy némítottad.",
   "report.next": "Következő",
-  "report.placeholder": "További megjegyzések",
+  "report.placeholder": "További hozzászólások",
   "report.reasons.dislike": "Nem tetszik",
   "report.reasons.dislike_description": "Ezt nem szeretném látni",
   "report.reasons.other": "Valami más",
@@ -506,7 +512,7 @@
   "report.submit": "Küldés",
   "report.target": "{target} jelentése",
   "report.thanks.take_action": "Itt vannak a beállítások, melyek szabályozzák, hogy mit látsz a Mastodonon:",
-  "report.thanks.take_action_actionable": "Míg átnézzük, a következőket teheted @{name} ellen:",
+  "report.thanks.take_action_actionable": "Míg átnézzük, a következőket lehet tenni @{name} ellen:",
   "report.thanks.title": "Nem akarod ezt látni?",
   "report.thanks.title_actionable": "Köszönjük, hogy jelentetted, megnézzük.",
   "report.unfollow": "@{name} követésének leállítása",
@@ -516,33 +522,35 @@
   "report_notification.categories.spam": "Kéretlen üzenet",
   "report_notification.categories.violation": "Szabálysértés",
   "report_notification.open": "Bejelentés megnyitása",
+  "search.no_recent_searches": "Nincsenek keresési előzmények",
   "search.placeholder": "Keresés",
-  "search.search_or_paste": "Keresés vagy URL beillesztése",
-  "search_popout.search_format": "Speciális keresés",
-  "search_popout.tips.full_text": "Egyszerű szöveg, mely általad írt, kedvencnek jelölt vagy megtolt bejegyzéseket, rólad szóló megemlítéseket, felhasználói neveket, megjelenített neveket, hashtageket ad majd vissza.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "bejegyzés",
-  "search_popout.tips.text": "Egyszerű szöveg. Illeszkedő megjelenített nevet, felhasználói nevet, hashtageket ad majd vissza",
-  "search_popout.tips.user": "felhasználó",
-  "search_results.accounts": "Emberek",
+  "search.quick_action.account_search": "Profilok a következő keresésre: {x}",
+  "search.quick_action.go_to_account": "Ugrás a következő profilhoz: {x}",
+  "search.quick_action.go_to_hashtag": "Ugrás a következő hashtaghez: {x}",
+  "search.quick_action.open_url": "Webcím megnyitása a Mastodonon",
+  "search.quick_action.status_search": "Bejegyzések a következő keresésre: {x}",
+  "search.search_or_paste": "Keresés vagy webcím beillesztése",
+  "search_popout.quick_actions": "Gyors műveletek",
+  "search_popout.recent": "Legutóbbi keresések",
+  "search_results.accounts": "Profilok",
   "search_results.all": "Összes",
-  "search_results.hashtags": "Hashtagek",
+  "search_results.hashtags": "#Címkék",
   "search_results.nothing_found": "Nincs találat ezekre a keresési kifejezésekre",
   "search_results.statuses": "Bejegyzések",
   "search_results.statuses_fts_disabled": "Ezen a Mastodon szerveren nem engedélyezett a bejegyzések tartalom szerinti keresése.",
   "search_results.title": "Keresés erre: {q}",
   "search_results.total": "{count, number} {count, plural, one {találat} other {találat}}",
-  "server_banner.about_active_users": "Az elmúlt 30 napban ezt a kiszolgálót használók száma (Havi aktív felhasználók)",
+  "server_banner.about_active_users": "Az elmúlt 30 napban ezt a kiszolgálót használó emberek (Havi aktív felhasználók)",
   "server_banner.active_users": "aktív felhasználó",
   "server_banner.administered_by": "Adminisztrátor:",
-  "server_banner.introduction": "{domain} része egy decentralizált közösségi hálónak, melyet a {mastodon} hajt meg.",
-  "server_banner.learn_more": "Tudj meg többet",
-  "server_banner.server_stats": "Kiszolgálóstatisztika:",
+  "server_banner.introduction": "{domain} része egy központ nélküliközösségi hálónak, melyet a {mastodon} hajt meg.",
+  "server_banner.learn_more": "További információ",
+  "server_banner.server_stats": "Szerver statisztika:",
   "sign_in_banner.create_account": "Fiók létrehozása",
   "sign_in_banner.sign_in": "Bejelentkezés",
-  "sign_in_banner.text": "Jelentkezz be profilok vagy hashtagek követéséhez, bejegyzések megosztásához, megválaszolásához, vagy kommunikálj a fiókodból más kiszolgálókkal.",
+  "sign_in_banner.text": "Jelentkezzünk be profilok vagy hashtagek követéséhez, kedvencnek jelöléséhez, bejegyzések megosztásához, megválaszolásához. A fiókból más kiszolgálókon is kommunikálhatunk.",
   "status.admin_account": "Moderációs felület megnyitása @{name} fiókhoz",
-  "status.admin_domain": "A következő moderációs felületének megnyitása: @{domain}",
+  "status.admin_domain": "Moderációs felület megnyitása {domain} esetében",
   "status.admin_status": "Bejegyzés megnyitása a moderációs felületen",
   "status.block": "@{name} letiltása",
   "status.bookmark": "Könyvjelzőzés",
@@ -551,7 +559,8 @@
   "status.copy": "Link másolása bejegyzésbe",
   "status.delete": "Törlés",
   "status.detailed_status": "Részletes beszélgetési nézet",
-  "status.direct": "Közvetlen üzenet @{name} számára",
+  "status.direct": "@{name} személyes említése",
+  "status.direct_indicator": "Személyes említés",
   "status.edit": "Szerkesztés",
   "status.edited": "Szerkesztve: {date}",
   "status.edited_x_times": "{count, plural, one {{count} alkalommal} other {{count} alkalommal}} szerkesztve",
@@ -589,19 +598,19 @@
   "status.show_less_all": "Kevesebbet mindenhol",
   "status.show_more": "Többet",
   "status.show_more_all": "Többet mindenhol",
-  "status.show_original": "Eredeti mutatása",
+  "status.show_original": "Eredeti megjelenítése",
   "status.translate": "Fordítás",
   "status.translated_from_with": "{lang} nyelvről fordítva {provider} szolgáltatással",
   "status.uncached_media_warning": "Nem érhető el",
   "status.unmute_conversation": "Beszélgetés némításának feloldása",
   "status.unpin": "Kitűzés eltávolítása a profilodról",
-  "subscribed_languages.lead": "A változtatás után csak a kiválasztott nyelvű bejegyzések fognak megjelenni a kezdőlapon és az idővonalakon. Ha egy sincs kiválasztva, akkor minden nyelven megjelennek a bejegyzések.",
+  "subscribed_languages.lead": "A változtatás után csak a kiválasztott nyelvű bejegyzések fognak megjelenni a kezdőoldalon és az idővonalakon. Ha egy sincs kiválasztva, akkor az összes nyelvű bejegyzések megjelennek.",
   "subscribed_languages.save": "Változások mentése",
-  "subscribed_languages.target": "Feliratkozott nyelvek módosítása a következőnél: {target}",
+  "subscribed_languages.target": "Feliratkozott nyelvek módosítása {target} esetében",
   "suggestions.dismiss": "Javaslat elvetése",
-  "suggestions.header": "Esetleg érdekelhet…",
-  "tabs_bar.federated_timeline": "Föderációs",
-  "tabs_bar.home": "Kezdőlap",
+  "suggestions.header": "Esetleg érdeklődésre tarthat számot…",
+  "tabs_bar.federated_timeline": "Összekapcsolt",
+  "tabs_bar.home": "Kezdőoldal",
   "tabs_bar.local_timeline": "Helyi",
   "tabs_bar.notifications": "Értesítések",
   "time_remaining.days": "{number, plural, one {# nap} other {# nap}} van hátra",
@@ -615,29 +624,29 @@
   "timeline_hint.resources.statuses": "Régi bejegyzések",
   "trends.counter_by_accounts": "{count, plural, one {{counter} ember} other {{counter} ember}} az elmúlt {days, plural,one {napban} other {{days} napban}}",
   "trends.trending_now": "Most felkapott",
-  "ui.beforeunload": "A piszkozatod el fog veszni, ha elhagyod a Mastodont.",
+  "ui.beforeunload": "A vázlat elveszik a Mastodon elhagyásakor.",
   "units.short.billion": "{count}Mrd",
   "units.short.million": "{count}M",
   "units.short.thousand": "{count}K",
-  "upload_area.title": "Húzd ide a feltöltéshez",
-  "upload_button.label": "Média hozzáadása",
-  "upload_error.limit": "Túllépted a fájlfeltöltési korlátot.",
+  "upload_area.title": "Húzás a feltöltéshez",
+  "upload_button.label": "Képek, videó vagy audió fájl hozzáadása",
+  "upload_error.limit": "A fájlfeltöltési korlát elérésre került.",
   "upload_error.poll": "Szavazásnál nem lehet fájlt feltölteni.",
-  "upload_form.audio_description": "Írja le a hallássérültek számára",
-  "upload_form.description": "Leírás látáskorlátozottak számára",
+  "upload_form.audio_description": "Leírás be a siket vagy hallássérült embereknek",
+  "upload_form.description": "Leírás be vak vagy gyengénlátó embereknek",
   "upload_form.description_missing": "Nincs leírás megadva",
   "upload_form.edit": "Szerkesztés",
-  "upload_form.thumbnail": "Előnézet megváltoztatása",
+  "upload_form.thumbnail": "Bélyegkép megváltoztatása",
   "upload_form.undo": "Törlés",
-  "upload_form.video_description": "Írja le a hallás- vagy látássérültek számára",
+  "upload_form.video_description": "Leírás be a siket, hallássérült, vak vagy gyengénlátó embereknek",
   "upload_modal.analyzing_picture": "Kép elemzése…",
-  "upload_modal.apply": "Alkalmaz",
+  "upload_modal.apply": "Alkalmazás",
   "upload_modal.applying": "Alkalmazás…",
   "upload_modal.choose_image": "Kép kiválasztása",
   "upload_modal.description_placeholder": "A gyors, barna róka átugrik a lusta kutya fölött",
   "upload_modal.detect_text": "Szöveg felismerése a képről",
   "upload_modal.edit_media": "Média szerkesztése",
-  "upload_modal.hint": "Kattints vagy húzd a kört az előnézetben arra a fókuszpontra, mely minden megjelenített bélyegképen látható kell, legyen.",
+  "upload_modal.hint": "Kattintás vagy kör húzása az előnézetben arra a fókuszpontra, mely minden megjelenített bélyegképen láthatónak kell lenni.",
   "upload_modal.preparing_ocr": "OCR előkészítése…",
   "upload_modal.preview_label": "Előnézet ({ratio})",
   "upload_progress.label": "Feltöltés...",
diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json
index 137983cbc..e77641312 100644
--- a/app/javascript/mastodon/locales/hy.json
+++ b/app/javascript/mastodon/locales/hy.json
@@ -1,16 +1,16 @@
 {
-  "about.blocks": "Moderated servers",
-  "about.contact": "Contact:",
-  "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
+  "about.blocks": "Մոդերացուող սպասարկիչներ",
+  "about.contact": "Կապ՝",
+  "about.disclaimer": "Մաստոդոնը ազատ, բաց ելակոդով ծրագրակազմ է, յայտնի Mastodon gGmbH ապրանքանշանով։",
   "about.domain_blocks.no_reason_available": "Reason not available",
   "about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",
   "about.domain_blocks.silenced.explanation": "You will generally not see profiles and content from this server, unless you explicitly look it up or opt into it by following.",
-  "about.domain_blocks.silenced.title": "Limited",
+  "about.domain_blocks.silenced.title": "Սահմանափակ",
   "about.domain_blocks.suspended.explanation": "No data from this server will be processed, stored or exchanged, making any interaction or communication with users from this server impossible.",
-  "about.domain_blocks.suspended.title": "Suspended",
-  "about.not_available": "This information has not been made available on this server.",
-  "about.powered_by": "Decentralized social media powered by {mastodon}",
-  "about.rules": "Server rules",
+  "about.domain_blocks.suspended.title": "Սպասող",
+  "about.not_available": "Այս տեղեկութիւնը տեսանելի չի այս սերուերում։",
+  "about.powered_by": "Ապակենտրոն սոց. ցանց սեղծուած {mastodon}-ի կողմից",
+  "about.rules": "Սերուերի կանոնները",
   "account.account_note_header": "Նշում",
   "account.add_or_remove_from_list": "Աւելացնել կամ հեռացնել ցանկերից",
   "account.badges.bot": "Բոտ",
@@ -20,14 +20,14 @@
   "account.blocked": "Արգելափակուած է",
   "account.browse_more_on_origin_server": "Դիտել աւելին իրական պրոֆիլում",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Նամակ գրել @{name} -ին",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Ծանուցումները անջատել @{name} գրառումների համար",
   "account.domain_blocked": "Տիրոյթը արգելափակուած է",
   "account.edit_profile": "Խմբագրել անձնական էջը",
   "account.enable_notifications": "Ծանուցել ինձ @{name} գրառումների մասին",
   "account.endorse": "Ցուցադրել անձնական էջում",
-  "account.featured_tags.last_status_at": "Last post on {date}",
-  "account.featured_tags.last_status_never": "No posts",
+  "account.featured_tags.last_status_at": "Վերջին գրառումը եղել է՝ {date}",
+  "account.featured_tags.last_status_never": "Գրառումներ չկան",
   "account.featured_tags.title": "{name}'s featured hashtags",
   "account.follow": "Հետեւել",
   "account.followers": "Հետեւողներ",
@@ -37,9 +37,9 @@
   "account.following_counter": "{count, plural, one {{counter} Հետեւած} other {{counter} Հետեւած}}",
   "account.follows.empty": "Այս օգտատէրը դեռ ոչ մէկի չի հետեւում։",
   "account.follows_you": "Հետեւում է քեզ",
-  "account.go_to_profile": "Go to profile",
+  "account.go_to_profile": "Գնալ անձնական հաշիւ",
   "account.hide_reblogs": "Թաքցնել @{name}֊ի տարածածները",
-  "account.joined_short": "Joined",
+  "account.joined_short": "Միացել է",
   "account.languages": "Change subscribed languages",
   "account.link_verified_on": "Սոյն յղման տիրապետումը ստուգուած է՝ {date}֊ին",
   "account.locked_info": "Սոյն հաշուի գաղտնիութեան մակարդակը նշուած է որպէս՝ փակ։ Հաշուի տէրն ընտրում է, թէ ով կարող է հետեւել իրեն։",
@@ -49,12 +49,12 @@
   "account.mute": "Լռեցնել @{name}֊ին",
   "account.mute_notifications": "Անջատել ծանուցումները @{name}֊ից",
   "account.muted": "Լռեցուած",
-  "account.open_original_page": "Open original page",
+  "account.open_original_page": "Բացել իրական էջը",
   "account.posts": "Գրառումներ",
   "account.posts_with_replies": "Գրառումներ եւ պատասխաններ",
   "account.report": "Բողոքել @{name}֊ի մասին",
   "account.requested": "Հաստատման կարիք ունի։ Սեղմիր՝ հետեւելու հայցը չեղարկելու համար։",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name}-ը ցանկանում է հետեւել քեզ",
   "account.share": "Կիսուել @{name}֊ի էջով",
   "account.show_reblogs": "Ցուցադրել @{name}֊ի տարածածները",
   "account.statuses_counter": "{count, plural, one {{counter} Գրառում} other {{counter} Գրառումներ}}",
@@ -78,16 +78,16 @@
   "alert.unexpected.title": "Վա՜յ",
   "announcement.announcement": "Յայտարարութիւններ",
   "attachments_list.unprocessed": "(unprocessed)",
-  "audio.hide": "Hide audio",
+  "audio.hide": "Թաքցնել աուդիոն",
   "autosuggest_hashtag.per_week": "շաբաթը՝ {count}",
   "boost_modal.combo": "Կարող ես սեղմել {combo}՝ սա յաջորդ անգամ բաց թողնելու համար",
   "bundle_column_error.copy_stacktrace": "Copy error report",
   "bundle_column_error.error.body": "The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.",
-  "bundle_column_error.error.title": "Oh, no!",
+  "bundle_column_error.error.title": "Օ՜, ոչ։",
   "bundle_column_error.network.body": "There was an error when trying to load this page. This could be due to a temporary problem with your internet connection or this server.",
-  "bundle_column_error.network.title": "Network error",
+  "bundle_column_error.network.title": "Ցանցի սխալ",
   "bundle_column_error.retry": "Կրկին փորձել",
-  "bundle_column_error.return": "Go back home",
+  "bundle_column_error.return": "Վերադառնալ տուն",
   "bundle_column_error.routing.body": "The requested page could not be found. Are you sure the URL in the address bar is correct?",
   "bundle_column_error.routing.title": "404",
   "bundle_modal_error.close": "Փակել",
@@ -95,14 +95,14 @@
   "bundle_modal_error.retry": "Կրկին փորձել",
   "closed_registrations.other_server_instructions": "Since Mastodon is decentralized, you can create an account on another server and still interact with this one.",
   "closed_registrations_modal.description": "Creating an account on {domain} is currently not possible, but please keep in mind that you do not need an account specifically on {domain} to use Mastodon.",
-  "closed_registrations_modal.find_another_server": "Find another server",
+  "closed_registrations_modal.find_another_server": "Գտնել այլ սերուերում",
   "closed_registrations_modal.preamble": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
   "closed_registrations_modal.title": "Signing up on Mastodon",
-  "column.about": "About",
+  "column.about": "Մասին",
   "column.blocks": "Արգելափակուած օգտատէրեր",
   "column.bookmarks": "Էջանիշեր",
   "column.community": "Տեղական հոսք",
-  "column.direct": "Հասցէագրուած",
+  "column.direct": "Private mentions",
   "column.directory": "Զննել անձնական էջերը",
   "column.domain_blocks": "Թաքցուած տիրոյթները",
   "column.favourites": "Հաւանածներ",
@@ -124,8 +124,8 @@
   "community.column_settings.local_only": "Միայն տեղական",
   "community.column_settings.media_only": "Միայն մեդիա",
   "community.column_settings.remote_only": "Միայն հեռակայ",
-  "compose.language.change": "Change language",
-  "compose.language.search": "Search languages...",
+  "compose.language.change": "Փոխել լեզուն",
+  "compose.language.search": "Որոնել լեզուներ",
   "compose_form.direct_message_warning_learn_more": "Իմանալ աւելին",
   "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
   "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
@@ -138,8 +138,8 @@
   "compose_form.poll.remove_option": "Հեռացնել այս տարբերակը",
   "compose_form.poll.switch_to_multiple": "Հարցումը դարձնել բազմակի ընտրութեամբ",
   "compose_form.poll.switch_to_single": "Հարցումը դարձնել եզակի ընտրութեամբ",
-  "compose_form.publish": "Publish",
-  "compose_form.publish_form": "Publish",
+  "compose_form.publish": "Հրապարակել",
+  "compose_form.publish_form": "Հրապարակել",
   "compose_form.publish_loud": "Հրապարակե՜լ",
   "compose_form.save_changes": "Պահպանել փոփոխութիւնները",
   "compose_form.sensitive.hide": "Նշել մեդիան որպէս դիւրազգաց",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Թաքցնել ամբողջ տիրույթը",
   "confirmations.domain_block.message": "Հաստատ֊հաստա՞տ վստահ ես, որ ուզում ես արգելափակել ամբողջ {domain} տիրոյթը։ Սովորաբար մի երկու թիրախաւորուած արգելափակում կամ լռեցում բաւական է ու նախընտրելի։",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Ելք",
   "confirmations.logout.message": "Համոզո՞ւած ես, որ ուզում ես դուրս գալ",
   "confirmations.mute.confirm": "Լռեցնել",
@@ -177,8 +179,8 @@
   "conversation.mark_as_read": "Նշել որպէս ընթերցուած",
   "conversation.open": "Դիտել խօսակցութիւնը",
   "conversation.with": "{names}-ի հետ",
-  "copypaste.copied": "Copied",
-  "copypaste.copy": "Copy",
+  "copypaste.copied": "Պատճէնուած է",
+  "copypaste.copy": "Պատճէնել",
   "directory.federated": "Յայտնի դաշնեզերքից",
   "directory.local": "{domain} տիրոյթից միայն",
   "directory.new_arrivals": "Նորեկներ",
@@ -194,7 +196,7 @@
   "embed.instructions": "Այս գրառումը քո կայքում ներդնելու համար կարող ես պատճէնել ներքեւի կոդը։",
   "embed.preview": "Ահա, թէ ինչ տեսք կունենայ այն՝",
   "emoji_button.activity": "Զբաղմունքներ",
-  "emoji_button.clear": "Clear",
+  "emoji_button.clear": "Մաքրել",
   "emoji_button.custom": "Յատուկ",
   "emoji_button.flags": "Դրօշներ",
   "emoji_button.food": "Կերուխում",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Դու դեռ ոչ մէկի չես արգելափակել։",
   "empty_column.bookmarked_statuses": "Դու դեռ չունես որեւէ էջանշուած գրառում։ Երբ էջանշես, դրանք կը երեւան այստեղ։",
   "empty_column.community": "Տեղական հոսքը դատարկ է։ Հրապարակային մի բան գրի՛ր շարժիչը գործարկելու համար։",
-  "empty_column.direct": "Դու դեռ չունես ոչ մի հասցէագրուած հաղորդագրութիւն։ Երբ ուղարկես կամ ստանաս որեւէ անձնական նամակ, այն կերեւայ այստեղ։",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Թաքցուած տիրոյթներ դեռ չկան։",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "Դու դեռ չունես որեւէ հաւանած գրառում։ Երբ հաւանես, դրանք կերեւան այստեղ։",
   "empty_column.favourites": "Այս գրառումը ոչ մէկ դեռ չի հաւանել։ Հաւանողները կերեւան այստեղ, երբ հաւանեն։",
   "empty_column.follow_recommendations": "Կարծես քեզ համար ոչ մի առաջարկ չի գեներացուել։ Օգտագործիր որոնման դաշտը մարդկանց փնտրելու համար կամ բացայայտիր յայտնի պիտակներով։",
   "empty_column.follow_requests": "Դու դեռ չունես որեւէ հետեւելու յայտ։ Բոլոր նման յայտերը կը յայտնուեն այստեղ։",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Այս պիտակով դեռ ոչինչ չկայ։",
   "empty_column.home": "Քո հիմնական հոսքը դատարկ է։ Այցելի՛ր {public}ը կամ օգտուիր որոնումից՝ այլ մարդկանց հանդիպելու համար։",
   "empty_column.home.suggestions": "Տեսնել որոշ առաջարկներ",
@@ -236,24 +239,24 @@
   "errors.unexpected_crash.copy_stacktrace": "Պատճենել սթաքթրեյսը սեղմատախտակին",
   "errors.unexpected_crash.report_issue": "Զեկուցել խնդրի մասին",
   "explore.search_results": "Որոնման արդիւնքներ",
-  "explore.suggested_follows": "For you",
+  "explore.suggested_follows": "Քեզ համար",
   "explore.title": "Բացայայտել",
-  "explore.trending_links": "News",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "Hashtags",
+  "explore.trending_links": "Նորութիւններ",
+  "explore.trending_statuses": "Գրառումներ",
+  "explore.trending_tags": "Պիտակներ",
   "filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.",
   "filter_modal.added.context_mismatch_title": "Context mismatch!",
   "filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.",
   "filter_modal.added.expired_title": "Expired filter!",
   "filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.",
   "filter_modal.added.review_and_configure_title": "Filter settings",
-  "filter_modal.added.settings_link": "settings page",
+  "filter_modal.added.settings_link": "կարգաւորումների էջ",
   "filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.",
   "filter_modal.added.title": "Filter added!",
   "filter_modal.select_filter.context_mismatch": "does not apply to this context",
   "filter_modal.select_filter.expired": "expired",
   "filter_modal.select_filter.prompt_new": "New category: {name}",
-  "filter_modal.select_filter.search": "Search or create",
+  "filter_modal.select_filter.search": "Որոնել կամ ստեղծել",
   "filter_modal.select_filter.subtitle": "Use an existing category or create a new one",
   "filter_modal.select_filter.title": "Filter this post",
   "filter_modal.title.status": "Filter a post",
@@ -263,13 +266,15 @@
   "follow_request.authorize": "Վաւերացնել",
   "follow_request.reject": "Մերժել",
   "follow_requests.unlocked_explanation": "Այս հարցումը ուղարկուած է հաշուից, որի համար {domain}-ի անձնակազմը միացրել է ձեռքով ստուգում։",
-  "footer.about": "About",
+  "followed_tags": "Followed hashtags",
+  "footer.about": "Մասին",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
-  "footer.invite": "Invite people",
-  "footer.keyboard_shortcuts": "Keyboard shortcuts",
+  "footer.invite": "Հրաւիրել մարդկանց",
+  "footer.keyboard_shortcuts": "Ստեղնաշարի կարճատներ",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Պահպանուած է",
   "getting_started.heading": "Ինչպէս սկսել",
   "hashtag.column_header.tag_mode.all": "եւ {additional}",
@@ -281,8 +286,8 @@
   "hashtag.column_settings.tag_mode.any": "Ցանկացածը",
   "hashtag.column_settings.tag_mode.none": "Ոչ մեկը",
   "hashtag.column_settings.tag_toggle": "Ներառել լրացուցիչ պիտակները այս սիւնակում ",
-  "hashtag.follow": "Follow hashtag",
-  "hashtag.unfollow": "Unfollow hashtag",
+  "hashtag.follow": "Հետեւել պիտակին",
+  "hashtag.unfollow": "Չհետեւել պիտակին",
   "home.column_settings.basic": "Հիմնական",
   "home.column_settings.show_reblogs": "Ցուցադրել տարածածները",
   "home.column_settings.show_replies": "Ցուցադրել պատասխանները",
@@ -297,9 +302,9 @@
   "interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favourite Mastodon app or the web interface of your Mastodon server.",
   "interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.",
   "interaction_modal.title.favourite": "Favourite {name}'s post",
-  "interaction_modal.title.follow": "Follow {name}",
-  "interaction_modal.title.reblog": "Boost {name}'s post",
-  "interaction_modal.title.reply": "Reply to {name}'s post",
+  "interaction_modal.title.follow": "Հետեւել {name}-ին",
+  "interaction_modal.title.reblog": "Տարածել {name}-ի գրառումը",
+  "interaction_modal.title.reply": "Պատասխանել {name}-ի գրառմանը",
   "intervals.full.days": "{number, plural, one {# օր} other {# օր}}",
   "intervals.full.hours": "{number, plural, one {# ժամ} other {# ժամ}}",
   "intervals.full.minutes": "{number, plural, one {# րոպէ} other {# րոպէ}}",
@@ -366,12 +371,12 @@
   "mute_modal.duration": "Տեւողութիւն",
   "mute_modal.hide_notifications": "Թաքցնե՞լ ծանուցումներն այս օգտատիրոջից։",
   "mute_modal.indefinite": "Անժամկէտ",
-  "navigation_bar.about": "About",
+  "navigation_bar.about": "Մասին",
   "navigation_bar.blocks": "Արգելափակուած օգտատէրեր",
   "navigation_bar.bookmarks": "Էջանիշեր",
   "navigation_bar.community_timeline": "Տեղական հոսք",
   "navigation_bar.compose": "Ստեղծել նոր գրառում",
-  "navigation_bar.direct": "Հասցէագրուած նամակներ",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Բացայայտել",
   "navigation_bar.domain_blocks": "Թաքցուած տիրոյթներ",
   "navigation_bar.edit_profile": "Խմբագրել անձնական էջը",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Հաւանածներ",
   "navigation_bar.filters": "Լռեցուած բառեր",
   "navigation_bar.follow_requests": "Հետեւելու հայցեր",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Հետեւածներ եւ հետեւողներ",
   "navigation_bar.lists": "Ցանկեր",
   "navigation_bar.logout": "Դուրս գալ",
@@ -387,7 +393,7 @@
   "navigation_bar.pins": "Ամրացուած գրառումներ",
   "navigation_bar.preferences": "Նախապատուութիւններ",
   "navigation_bar.public_timeline": "Դաշնային հոսք",
-  "navigation_bar.search": "Search",
+  "navigation_bar.search": "Որոնել",
   "navigation_bar.security": "Անվտանգութիւն",
   "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.",
   "notification.admin.report": "{name} reported {target}",
@@ -509,38 +515,40 @@
   "report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:",
   "report.thanks.title": "Don't want to see this?",
   "report.thanks.title_actionable": "Thanks for reporting, we'll look into this.",
-  "report.unfollow": "Unfollow @{name}",
+  "report.unfollow": "Չհետեւել {name}-ին",
   "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.",
   "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached",
-  "report_notification.categories.other": "Other",
-  "report_notification.categories.spam": "Spam",
+  "report_notification.categories.other": "Այլ",
+  "report_notification.categories.spam": "Սպամ",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Փնտրել",
-  "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "Փնտրելու առաջադէմ ձեւ",
-  "search_popout.tips.full_text": "Պարզ տեքստը վերադարձնում է գրառումներդ, հաւանածներդ, տարածածներդ, որտեղ ես նշուած եղել, ինչպէս նաեւ նման օգտանուններ, անուններ եւ պիտակներ։",
-  "search_popout.tips.hashtag": "պիտակ",
-  "search_popout.tips.status": "գրառում",
-  "search_popout.tips.text": "Հասարակ տեքստը կը վերադարձնի համընկնող անուններ, օգտանուններ ու պիտակներ",
-  "search_popout.tips.user": "օգտատէր",
-  "search_results.accounts": "Մարդիկ",
-  "search_results.all": "All",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
+  "search.search_or_paste": "Որոնել կամ դնել URL",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
+  "search_results.all": "Բոլորը",
   "search_results.hashtags": "Պիտակներ",
   "search_results.nothing_found": "Could not find anything for these search terms",
   "search_results.statuses": "Գրառումներ",
   "search_results.statuses_fts_disabled": "Այս հանգոյցում միացուած չէ ըստ բովանդակութեան գրառում փնտրելու հնարաւորութիւնը։",
-  "search_results.title": "Search for {q}",
+  "search_results.title": "Որոնել {q}-ն",
   "search_results.total": "{count, number} {count, plural, one {արդիւնք} other {արդիւնք}}",
   "server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
-  "server_banner.active_users": "active users",
-  "server_banner.administered_by": "Administered by:",
+  "server_banner.active_users": "ակտիւ մարդիկ",
+  "server_banner.administered_by": "Կառաւարող",
   "server_banner.introduction": "{domain} is part of the decentralized social network powered by {mastodon}.",
-  "server_banner.learn_more": "Learn more",
-  "server_banner.server_stats": "Server stats:",
-  "sign_in_banner.create_account": "Create account",
-  "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "server_banner.learn_more": "Իմանալ աւելին",
+  "server_banner.server_stats": "Սերուերի վիճակը",
+  "sign_in_banner.create_account": "Ստեղծել հաշիւ",
+  "sign_in_banner.sign_in": "Մուտք",
+  "sign_in_banner.text": "Մտէք, որ կարողանաք հետեւել հաշիւներին կամ պիտակներին, հաւանել, տարածել կամ պատասխանել գրառումներին։ Նաեւ շփուել այլ հանգոյցների հետ։",
   "status.admin_account": "Բացել @{name} օգտատիրոջ մոդերացիայի դիմերէսը։",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Բացել այս գրառումը մոդերատորի դիմերէսի մէջ",
@@ -551,17 +559,18 @@
   "status.copy": "Պատճէնել գրառման յղումը",
   "status.delete": "Ջնջել",
   "status.detailed_status": "Շղթայի ընդլայնուած դիտում",
-  "status.direct": "Նամակ գրել {name} -ին",
-  "status.edit": "Edit",
-  "status.edited": "Edited {date}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
+  "status.edit": "Խմբագրել",
+  "status.edited": "Խմբագրուել է՝ {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
   "status.embed": "Ներդնել",
   "status.favourite": "Հաւանել",
   "status.filter": "Filter this post",
   "status.filtered": "Զտուած",
-  "status.hide": "Hide post",
-  "status.history.created": "{name} created {date}",
-  "status.history.edited": "{name} edited {date}",
+  "status.hide": "Թաքցնել գրառումը",
+  "status.history.created": "{name}-ը ստեղծել է՝ {date}",
+  "status.history.edited": "{name}-ը խմբագրել է՝ {date}",
   "status.load_more": "Բեռնել աւելին",
   "status.media_hidden": "մեդիաբովանդակութիւնը թաքցուած է",
   "status.mention": "Նշել @{name}֊ին",
@@ -578,25 +587,25 @@
   "status.reblogs.empty": "Այս գրառումը ոչ մէկ դեռ չի տարածել։ Տարածողները կերեւան այստեղ, երբ տարածեն։",
   "status.redraft": "Ջնջել եւ վերակազմել",
   "status.remove_bookmark": "Հեռացնել էջանիշերից",
-  "status.replied_to": "Replied to {name}",
+  "status.replied_to": "Պատասխանել է {name}-ին",
   "status.reply": "Պատասխանել",
   "status.replyAll": "Պատասխանել շղթային",
   "status.report": "Բողոքել @{name}֊ից",
   "status.sensitive_warning": "Կասկածելի բովանդակութիւն",
   "status.share": "Կիսուել",
-  "status.show_filter_reason": "Show anyway",
+  "status.show_filter_reason": "Ցոյց տալ բոլոր դէպքերում",
   "status.show_less": "Պակաս",
   "status.show_less_all": "Թաքցնել բոլոր նախազգուշացնումները",
   "status.show_more": "Աւելին",
   "status.show_more_all": "Ցուցադրել բոլոր նախազգուշացնումները",
-  "status.show_original": "Show original",
-  "status.translate": "Translate",
+  "status.show_original": "Ցոյց տալ բնօրինակը",
+  "status.translate": "Թարգմանել",
   "status.translated_from_with": "Translated from {lang} using {provider}",
   "status.uncached_media_warning": "Անհասանելի",
   "status.unmute_conversation": "Ապալռեցնել խօսակցութիւնը",
   "status.unpin": "Հանել անձնական էջից",
   "subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
-  "subscribed_languages.save": "Save changes",
+  "subscribed_languages.save": "Պահպանել փոփոխութիւնները",
   "subscribed_languages.target": "Change subscribed languages for {target}",
   "suggestions.dismiss": "Անտեսել առաջարկը",
   "suggestions.header": "Միգուցէ քեզ հետաքրքրի…",
@@ -641,7 +650,7 @@
   "upload_modal.preparing_ocr": "Գրաճանաչման նախապատրաստում…",
   "upload_modal.preview_label": "Նախադիտում ({ratio})",
   "upload_progress.label": "Վերբեռնվում է…",
-  "upload_progress.processing": "Processing…",
+  "upload_progress.processing": "Մշակուում է...",
   "video.close": "Փակել  տեսագրութիւնը",
   "video.download": "Ներբեռնել նիշքը",
   "video.exit_fullscreen": "Անջատել լիաէկրան դիտումը",
diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json
index 7ed8a02ab..6e8a33d37 100644
--- a/app/javascript/mastodon/locales/id.json
+++ b/app/javascript/mastodon/locales/id.json
@@ -1,7 +1,7 @@
 {
   "about.blocks": "Server yang dimoderasi",
   "about.contact": "Hubungi:",
-  "about.disclaimer": "Mastodon addalah perangkat lunak bebas dan sumber terbuka, dan adalah merek dagang dari Mastodon gGmbH.",
+  "about.disclaimer": "Mastodon adalah perangkat lunak bebas dan sumber terbuka, dan adalah merek dagang dari Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Alasan tidak tersedia",
   "about.domain_blocks.preamble": "Mastodon umumnya mengizinkan Anda untuk melihat konten dan berinteraksi dengan pengguna dari server lain di fediverse. Ini adalah pengecualian yang dibuat untuk beberapa server.",
   "about.domain_blocks.silenced.explanation": "Anda secara umum tidak melihat profil dan konten dari server ini, kecuali jika Anda mencarinya atau memilihnya dengan mengikuti secara eksplisit.",
@@ -20,7 +20,7 @@
   "account.blocked": "Terblokir",
   "account.browse_more_on_origin_server": "Lihat lebih lanjut di profil asli",
   "account.cancel_follow_request": "Batalkan permintaan ikut",
-  "account.direct": "Pesan Langsung @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Berhenti memberitahu saya ketika @{name} memposting",
   "account.domain_blocked": "Domain diblokir",
   "account.edit_profile": "Ubah profil",
@@ -102,7 +102,7 @@
   "column.blocks": "Pengguna yang diblokir",
   "column.bookmarks": "Markah",
   "column.community": "Linimasa Lokal",
-  "column.direct": "Pesan langsung",
+  "column.direct": "Private mentions",
   "column.directory": "Jelajahi profil",
   "column.domain_blocks": "Domain tersembunyi",
   "column.favourites": "Favorit",
@@ -111,7 +111,7 @@
   "column.lists": "List",
   "column.mutes": "Pengguna yang dibisukan",
   "column.notifications": "Notifikasi",
-  "column.pins": "Pinned toot",
+  "column.pins": "Kiriman tersemat",
   "column.public": "Linimasa gabungan",
   "column_back_button.label": "Kembali",
   "column_header.hide_settings": "Sembunyikan pengaturan",
@@ -126,14 +126,14 @@
   "community.column_settings.remote_only": "Hanya jarak jauh",
   "compose.language.change": "Ganti bahasa",
   "compose.language.search": "Telusuri bahasa...",
-  "compose_form.direct_message_warning_learn_more": "Pelajari selengkapnya",
-  "compose_form.encryption_warning": "Kiriman di Mastodon tidak dienkripsi end-to-end. Jangan bagikan informasi sensitif melalui Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.direct_message_warning_learn_more": "Pelajari lebih lanjut",
+  "compose_form.encryption_warning": "Kiriman di Mastodon tidak dienkripsi secara end-to-end. Jangan bagikan informasi sensitif melalui Mastodon.",
+  "compose_form.hashtag_warning": "Kiriman ini tidak akan didaftarkan di bawah tagar apapun selama tidak diatur ke publik. Hanya kiriman publik yang dapat dicari dengan tagar.",
   "compose_form.lock_disclaimer": "Akun Anda tidak {locked}. Semua orang dapat mengikuti Anda untuk melihat kiriman khusus untuk pengikut Anda.",
   "compose_form.lock_disclaimer.lock": "terkunci",
   "compose_form.placeholder": "Apa yang ada di pikiran Anda?",
   "compose_form.poll.add_option": "Tambahkan pilihan",
-  "compose_form.poll.duration": "Durasi polling",
+  "compose_form.poll.duration": "Durasi japat",
   "compose_form.poll.option_placeholder": "Pilihan {number}",
   "compose_form.poll.remove_option": "Hapus opsi ini",
   "compose_form.poll.switch_to_multiple": "Ubah japat menjadi pilihan ganda",
@@ -145,8 +145,8 @@
   "compose_form.sensitive.hide": "{count, plural, other {Tandai media sebagai sensitif}}",
   "compose_form.sensitive.marked": "{count, plural, other {Media ini ditandai sebagai sensitif}}",
   "compose_form.sensitive.unmarked": "{count, plural, other {Media ini tidak ditandai sebagai sensitif}}",
-  "compose_form.spoiler.marked": "Teks disembunyikan dibalik peringatan",
-  "compose_form.spoiler.unmarked": "Teks tidak tersembunyi",
+  "compose_form.spoiler.marked": "Hapus peringatan tentang isi konten",
+  "compose_form.spoiler.unmarked": "Tambahkan peringatan tentang isi konten",
   "compose_form.spoiler_placeholder": "Peringatan konten",
   "confirmation_modal.cancel": "Batal",
   "confirmations.block.block_and_report": "Blokir & Laporkan",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Anda belum menyimpan perubahan deskripsi atau pratinjau media, buang saja?",
   "confirmations.domain_block.confirm": "Sembunyikan keseluruhan domain",
   "confirmations.domain_block.message": "Apakah Anda benar-benar yakin untuk memblokir keseluruhan {domain}? Dalam kasus tertentu beberapa pemblokiran atau penyembunyian lebih baik.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Keluar",
   "confirmations.logout.message": "Apakah Anda yakin ingin keluar?",
   "confirmations.mute.confirm": "Bisukan",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Anda belum memblokir siapa pun.",
   "empty_column.bookmarked_statuses": "Anda belum memiliki kiriman termarkah. Saat Anda menandainya sebagai markah, mereka akan muncul di sini.",
   "empty_column.community": "Linimasa lokal masih kosong. Tulis sesuatu secara publik dan buat roda berputar!",
-  "empty_column.direct": "Anda belum memiliki pesan langsung. Ketika Anda mengirim atau menerimanya, maka akan muncul di sini.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Tidak ada topik tersembunyi.",
   "empty_column.explore_statuses": "Tidak ada yang sedang tren pada saat ini. Periksa lagi nanti!",
   "empty_column.favourited_statuses": "Anda belum memiliki kiriman favorit. Ketika Anda mengirim atau menerimanya, mereka akan muncul di sini.",
   "empty_column.favourites": "Belum ada yang memfavoritkan toot ini. Ketika seseorang melakukannya, mereka akan muncul di sini.",
   "empty_column.follow_recommendations": "Sepertinya tak ada saran yang dibuat untuk Anda. Anda dapat mencoba menggunakan pencarian untuk menemukan orang yang Anda ketahui atau menjelajahi tagar yang sedang tren.",
   "empty_column.follow_requests": "Anda belum memiliki permintaan mengikuti. Ketika Anda menerimanya, maka itu akan muncul di sini.",
+  "empty_column.followed_tags": "Anda belum mengikuti tagar apapun. Saat Anda mulai melakukannya, mereka akan muncul di sini.",
   "empty_column.hashtag": "Tidak ada apa pun dalam hashtag ini.",
   "empty_column.home": "Linimasa anda kosong! Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.",
   "empty_column.home.suggestions": "Lihat beberapa saran",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Izinkan",
   "follow_request.reject": "Tolak",
   "follow_requests.unlocked_explanation": "Meskipun akun Anda tidak dikunci, staf {domain} menyarankan Anda untuk meninjau permintaan mengikuti dari akun-akun ini secara manual.",
+  "followed_tags": "Tagar yang diikuti",
   "footer.about": "Tentang",
   "footer.directory": "Direktori profil",
   "footer.get_app": "Dapatkan aplikasi",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Pintasan papan ketik",
   "footer.privacy_policy": "Kebijakan privasi",
   "footer.source_code": "Lihat kode sumber",
+  "footer.status": "Status",
   "generic.saved": "Disimpan",
   "getting_started.heading": "Mulai",
   "hashtag.column_header.tag_mode.all": "dan {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Fokus kolom",
   "keyboard_shortcuts.compose": "untuk fokus ke area penulisan",
   "keyboard_shortcuts.description": "Deskripsi",
-  "keyboard_shortcuts.direct": "untuk membuka kolom pesan langsung",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "untuk pindah ke bawah dalam sebuah daftar",
   "keyboard_shortcuts.enter": "Buka kiriman",
   "keyboard_shortcuts.favourite": "untuk memfavoritkan",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Markah",
   "navigation_bar.community_timeline": "Linimasa lokal",
   "navigation_bar.compose": "Tulis toot baru",
-  "navigation_bar.direct": "Pesan langsung",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Temukan",
   "navigation_bar.domain_blocks": "Domain tersembunyi",
   "navigation_bar.edit_profile": "Ubah profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favorit",
   "navigation_bar.filters": "Kata yang dibisukan",
   "navigation_bar.follow_requests": "Permintaan mengikuti",
+  "navigation_bar.followed_tags": "Tagar yang diikuti",
   "navigation_bar.follows_and_followers": "Ikuti dan pengikut",
   "navigation_bar.lists": "Daftar",
   "navigation_bar.logout": "Keluar",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Pelanggaran peraturan",
   "report_notification.open": "Buka laporan",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Pencarian",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Cari atau ketik URL",
-  "search_popout.search_format": "Format pencarian mahir",
-  "search_popout.tips.full_text": "Teks simpel memberikan kiriman yang Anda telah tulis, favorit, boost, atau status yang menyebut Anda, serta nama pengguna, nama yang ditampilkan, dan tagar yang cocok.",
-  "search_popout.tips.hashtag": "tagar",
-  "search_popout.tips.status": "kiriman",
-  "search_popout.tips.text": "Teks sederhana menampilkan nama yang ditampilkan, nama pengguna, dan tagar yang cocok",
-  "search_popout.tips.user": "pengguna",
-  "search_results.accounts": "Orang",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Semua",
   "search_results.hashtags": "Tagar",
   "search_results.nothing_found": "Tidak dapat menemukan apa pun untuk istilah-istilah pencarian ini",
@@ -540,9 +548,9 @@
   "server_banner.server_stats": "Statistik server:",
   "sign_in_banner.create_account": "Buat akun",
   "sign_in_banner.sign_in": "Masuk",
-  "sign_in_banner.text": "Masuk untuk mengikuti profil atau tagar, favorit, bagikan, dan balas ke kiriman, atau berinteraksi dari akun Anda di server yang lain.",
+  "sign_in_banner.text": "Masuk untuk mengikuti profil atau tagar, memfavoritkan, membagi, dan membalas kiriman. Anda juga dapat berinteraksi dari akun Anda di server yang berbeda.",
   "status.admin_account": "Buka antarmuka moderasi untuk @{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_domain": "Buka antarmuka moderasi untuk {domain}",
   "status.admin_status": "Buka kiriman ini dalam antar muka moderasi",
   "status.block": "Blokir @{name}",
   "status.bookmark": "Markah",
@@ -551,7 +559,8 @@
   "status.copy": "Salin tautan ke kiriman",
   "status.delete": "Hapus",
   "status.detailed_status": "Tampilan detail percakapan",
-  "status.direct": "Pesan langsung @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Diedit {date}",
   "status.edited_x_times": "Diedit {count, plural, other {{count} kali}}",
diff --git a/app/javascript/mastodon/locales/ig.json b/app/javascript/mastodon/locales/ig.json
index 68ba6e91e..41280827e 100644
--- a/app/javascript/mastodon/locales/ig.json
+++ b/app/javascript/mastodon/locales/ig.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocked",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Domain blocked",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Ojiarụ egbochiri",
   "column.bookmarks": "Ebenrụtụakā",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "Favourites",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "Mute",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
   "empty_column.favourites": "No one has favourited this post yet. When someone does, they will show up here.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Iwu nzuzu",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Mbido",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Ebenrụtụakā",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new post",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Ndepụta",
   "navigation_bar.logout": "Logout",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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": "ojiarụ",
-  "search_results.accounts": "People",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Hichapụ",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json
index e57d37787..7a83ec301 100644
--- a/app/javascript/mastodon/locales/io.json
+++ b/app/javascript/mastodon/locales/io.json
@@ -20,7 +20,7 @@
   "account.blocked": "Restriktita",
   "account.browse_more_on_origin_server": "Videz pluse che originala profilo",
   "account.cancel_follow_request": "Desendez sequodemando",
-  "account.direct": "Direct Message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Cesez avizar me kande @{name} postas",
   "account.domain_blocked": "Domain hidden",
   "account.edit_profile": "Modifikar profilo",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokusita uzeri",
   "column.bookmarks": "Libromarki",
   "column.community": "Lokala tempolineo",
-  "column.direct": "Direta mesaji",
+  "column.direct": "Private mentions",
   "column.directory": "Videz profili",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favorati",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Vu havas nesparita chanji di mediodeskript o prevido, vu volas jus efacar?",
   "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.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Ekirez",
   "confirmations.logout.message": "Ka tu certe volas ekirar?",
   "confirmations.mute.confirm": "Silencigez",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Vu ne restriktis irga uzanti til nun.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "La lokala tempolineo esas vakua. Skribez ulo publike por iniciar la agiveso!",
-  "empty_column.direct": "Vu ne havas irga direta mesaji til nun. Kande vu sendas o ganas, ol montresos hike.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
   "empty_column.explore_statuses": "Nulo esas tendenca nun. Videz itere pose!",
   "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_recommendations": "Semblas tale nula sugestato povas facesar por vu. Vu povas probar trovar personi quon vu forsan konocas o exploras tendenca hashtagi.",
   "empty_column.follow_requests": "Vu ne havas irga sequodemandi til nun. Kande vu ganas talo, ol montresos hike.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Esas ankore nulo en ta gretovorto.",
   "empty_column.home": "Vua hemtempolineo esas vakua! Sequez plu multa personi por plenigar lu. {suggestions}",
   "empty_column.home.suggestions": "Videz ula sugestati",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Yurizar",
   "follow_request.reject": "Refuzar",
   "follow_requests.unlocked_explanation": "Quankam vua konto ne klefklozesis, la {domain} laborero pensas ke vu forsan volas kontralar sequodemandi de ca konti manuale.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Sparesis",
   "getting_started.heading": "Debuto",
   "hashtag.column_header.tag_mode.all": "e {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "to focus a status in one of the columns",
   "keyboard_shortcuts.compose": "to focus the compose textarea",
   "keyboard_shortcuts.description": "Deskripto",
-  "keyboard_shortcuts.direct": "apertar kolumno di direta mesaji",
+  "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",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Libromarki",
   "navigation_bar.community_timeline": "Lokala tempolineo",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direta mesaji",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Deskovrez",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Modifikar profilo",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favorati",
   "navigation_bar.filters": "Silencigita vorti",
   "navigation_bar.follow_requests": "Demandi di sequado",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Sequati e sequanti",
   "navigation_bar.lists": "Listi",
   "navigation_bar.logout": "Ekirar",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spamo",
   "report_notification.categories.violation": "Regulnesequo",
   "report_notification.open": "Apertez raporto",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Serchez",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "Avancata trovformato",
-  "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": "hashtago",
-  "search_popout.tips.status": "status",
-  "search_popout.tips.text": "Simpla textoretrovenuri quo parigas trovnomi, uzantonomi e hashtagi",
-  "search_popout.tips.user": "uzanto",
-  "search_results.accounts": "Personi",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Omna",
   "search_results.hashtags": "Hashtagi",
   "search_results.nothing_found": "Ne povas ganar irgo per ca trovvorti",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Servilstatistiko:",
   "sign_in_banner.create_account": "Kreez konto",
   "sign_in_banner.sign_in": "Enirez",
-  "sign_in_banner.text": "Enirez por sequar profili o hashtagi, favorizar, partigar e respondizar posti, o interagar de vua konto de diferanta servilo.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Apertez jerintervizajo por @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Efacar",
   "status.detailed_status": "Detala konversvido",
-  "status.direct": "Direta mesajigez @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modifikez",
   "status.edited": "Modifikesis ye {date}",
   "status.edited_x_times": "Modifikesis {count, plural, one {{count} foyo} other {{count} foyi}}",
diff --git a/app/javascript/mastodon/locales/is.json b/app/javascript/mastodon/locales/is.json
index f5271f245..b9d650473 100644
--- a/app/javascript/mastodon/locales/is.json
+++ b/app/javascript/mastodon/locales/is.json
@@ -20,7 +20,7 @@
   "account.blocked": "Útilokaður",
   "account.browse_more_on_origin_server": "Skoða nánari upplýsingar á notandasniðinu",
   "account.cancel_follow_request": "Taka fylgjendabeiðni til baka",
-  "account.direct": "Bein skilaboð til @{name}",
+  "account.direct": "Minnast einslega á @{name}",
   "account.disable_notifications": "Hætta að láta mig vita þegar @{name} sendir inn",
   "account.domain_blocked": "Lén útilokað",
   "account.edit_profile": "Breyta notandasniði",
@@ -102,7 +102,7 @@
   "column.blocks": "Útilokaðir notendur",
   "column.bookmarks": "Bókamerki",
   "column.community": "Staðvær tímalína",
-  "column.direct": "Bein skilaboð",
+  "column.direct": "Minnst einslega á",
   "column.directory": "Skoða notendasnið",
   "column.domain_blocks": "Útilokuð lén",
   "column.favourites": "Eftirlæti",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Þú ert með óvistaðar breytingar á lýsingu myndefnis eða forskoðunar, henda þeim samt?",
   "confirmations.domain_block.confirm": "Útiloka allt lénið",
   "confirmations.domain_block.message": "Ertu alveg algjörlega viss um að þú viljir loka á allt {domain}? Í flestum tilfellum er vænlegra að nota færri en markvissari útilokanir eða að þagga niður tiltekna aðila. Þú munt ekki sjá efni frá þessu léni í neinum opinberum tímalínum eða í tilkynningunum þínum. Fylgjendur þínir frá þessu léni verða fjarlægðir.",
+  "confirmations.edit.confirm": "Breyta",
+  "confirmations.edit.message": "Ef þú breytir núna verður skrifað yfir skilaboðin sem þú ert að semja núna. Ertu viss um að þú viljir halda áfram?",
   "confirmations.logout.confirm": "Skrá út",
   "confirmations.logout.message": "Ertu viss um að þú viljir skrá þig út?",
   "confirmations.mute.confirm": "Þagga",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Þú hefur ekki ennþá útilokað neina notendur.",
   "empty_column.bookmarked_statuses": "Þú ert ekki ennþá með neinar bókamerktar færslur. Þegar þú bókamerkir færslu, mun það birtast hér.",
   "empty_column.community": "Staðværa tímalínan er tóm. Skrifaðu eitthvað opinberlega til að láta boltann fara að rúlla!",
-  "empty_column.direct": "Þú átt ennþá engin bein skilaboð. Þegar þú sendir eða tekur á móti slíkum skilaboðum, munu þau birtast hér.",
+  "empty_column.direct": "Þú ert ekki ennþá með neitt þar sem minnst er einslega á einhvern. Þegar þú sendir eða tekur við slíku, mun það birtast hér.",
   "empty_column.domain_blocks": "Það eru ennþá engin útilokuð lén.",
   "empty_column.explore_statuses": "Ekkert er á uppleið í augnablikinu. Athugaðu aftur síðar!",
   "empty_column.favourited_statuses": "Þú ert ekki ennþá með neinar eftirlætisfærslur. Þegar þú setur færslu í eftirlæti, munu þau birtast hér.",
   "empty_column.favourites": "Enginn hefur ennþá sett þessa færslu í eftirlæti. Þegar einhver gerir það, mun það birtast hér.",
   "empty_column.follow_recommendations": "Það lítur út fyrir að ekki hafi verið hægt að útbúa neinar tillögur fyrir þig. Þú getur reynt að leita að fólki sem þú gætir þekkt eða skoðað myllumerki sem eru í umræðunni.",
   "empty_column.follow_requests": "Þú átt ennþá engar beiðnir um að fylgja þér. Þegar þú færð slíkar beiðnir, munu þær birtast hér.",
+  "empty_column.followed_tags": "Þú ert ekki ennþá farin/n að fylgjast með myllumerkjum. Þegar þú gerir það, munu þau birtast hér.",
   "empty_column.hashtag": "Það er ekkert ennþá undir þessu myllumerki.",
   "empty_column.home": "Heimatímalínan þín er tóm! Fylgstu með fleira fólki til að fylla hana. {suggestions}",
   "empty_column.home.suggestions": "Skoðaðu nokkrar tillögur",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Heimila",
   "follow_request.reject": "Hafna",
   "follow_requests.unlocked_explanation": "Jafnvel þótt aðgangurinn þinn sé ekki læstur, hafa umsjónarmenn {domain} ímyndað sér að þú gætir viljað yfirfara handvirkt fylgjendabeiðnir frá þessum notendum.",
+  "followed_tags": "Myllumerki sem fylgst er með",
   "footer.about": "Nánari upplýsingar",
   "footer.directory": "Notandasniðamappa",
   "footer.get_app": "Ná í forritið",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Flýtileiðir á lyklaborði",
   "footer.privacy_policy": "Persónuverndarstefna",
   "footer.source_code": "Skoða frumkóða",
+  "footer.status": "Staða",
   "generic.saved": "Vistað",
   "getting_started.heading": "Komast í gang",
   "hashtag.column_header.tag_mode.all": "og {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Setja virkni í dálk",
   "keyboard_shortcuts.compose": "Setja virkni á textainnsetningarreit",
   "keyboard_shortcuts.description": "Lýsing",
-  "keyboard_shortcuts.direct": "að opna dálk með beinum skilaboðum",
+  "keyboard_shortcuts.direct": "til að opna dálk þar sem minnst er einslega á e-n",
   "keyboard_shortcuts.down": "Fara neðar í listanum",
   "keyboard_shortcuts.enter": "Opna færslu",
   "keyboard_shortcuts.favourite": "Eftirlætisfærsla",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bókamerki",
   "navigation_bar.community_timeline": "Staðvær tímalína",
   "navigation_bar.compose": "Semja nýja færslu",
-  "navigation_bar.direct": "Bein skilaboð",
+  "navigation_bar.direct": "Minnst einslega á",
   "navigation_bar.discover": "Uppgötva",
   "navigation_bar.domain_blocks": "Útilokuð lén",
   "navigation_bar.edit_profile": "Breyta notandasniði",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Eftirlæti",
   "navigation_bar.filters": "Þögguð orð",
   "navigation_bar.follow_requests": "Beiðnir um að fylgjast með",
+  "navigation_bar.followed_tags": "Myllumerki sem fylgst er með",
   "navigation_bar.follows_and_followers": "Fylgist með og fylgjendur",
   "navigation_bar.lists": "Listar",
   "navigation_bar.logout": "Útskráning",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Ruslpóstur",
   "report_notification.categories.violation": "Brot á reglum",
   "report_notification.open": "Opin kæra",
+  "search.no_recent_searches": "Engar nýlegar leitir",
   "search.placeholder": "Leita",
+  "search.quick_action.account_search": "Notandasnið sem samsvara {x}",
+  "search.quick_action.go_to_account": "Fara á notandasnið {x}",
+  "search.quick_action.go_to_hashtag": "Fara á myllumerkið {x}",
+  "search.quick_action.open_url": "Opna slóð í Mastodon",
+  "search.quick_action.status_search": "Færslur sem samsvara {x}",
   "search.search_or_paste": "Leita eða líma slóð",
-  "search_popout.search_format": "Snið ítarlegrar leitar",
-  "search_popout.tips.full_text": "Einfaldur texti skilar færslum sem þú hefur skrifað, sett í eftirlæti, endurbirt eða verið minnst á þig í, ásamt samsvarandi birtingarnöfnum, notendanöfnum og myllumerkjum.",
-  "search_popout.tips.hashtag": "myllumerki",
-  "search_popout.tips.status": "færsla",
-  "search_popout.tips.text": "Einfaldur texti skilar samsvarandi birtingarnöfnum, notendanöfnum og myllumerkjum",
-  "search_popout.tips.user": "notandi",
-  "search_results.accounts": "Fólk",
+  "search_popout.quick_actions": "Flýtiaðgerðir",
+  "search_popout.recent": "Nýlegar leitir",
+  "search_results.accounts": "Notendasnið",
   "search_results.all": "Allt",
   "search_results.hashtags": "Myllumerki",
   "search_results.nothing_found": "Gat ekki fundið neitt sem samsvarar þessum leitarorðum",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Tölfræði þjóns:",
   "sign_in_banner.create_account": "Búa til notandaaðgang",
   "sign_in_banner.sign_in": "Skrá inn",
-  "sign_in_banner.text": "Skráðu þig inn til að fylgjast með notendum eða myllumerkjum, svara færslum, deila þeim eða setja í eftirlæti, eða eiga í samskiptum á aðgangnum þínum á öðrum netþjónum.",
+  "sign_in_banner.text": "Skráðu þig inn til að fylgjast með notendum eða myllumerkjum, svara færslum, deila þeim eða setja í eftirlæti. Þú getur einnig átt í samskiptum á aðgangnum þínum á öðrum netþjónum.",
   "status.admin_account": "Opna umsjónarviðmót fyrir @{name}",
   "status.admin_domain": "Opna umsjónarviðmót fyrir @{domain}",
   "status.admin_status": "Opna þessa færslu í umsjónarviðmótinu",
@@ -551,7 +559,8 @@
   "status.copy": "Afrita tengil í færslu",
   "status.delete": "Eyða",
   "status.detailed_status": "Nákvæm spjallþráðasýn",
-  "status.direct": "Bein skilaboð @{name}",
+  "status.direct": "Minnast einslega á @{name}",
+  "status.direct_indicator": "Minnast einslega á",
   "status.edit": "Breyta",
   "status.edited": "Breytt {date}",
   "status.edited_x_times": "Breytt {count, plural, one {{count} sinni} other {{count} sinnum}}",
diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json
index 080bfafbd..ab1cc02af 100644
--- a/app/javascript/mastodon/locales/it.json
+++ b/app/javascript/mastodon/locales/it.json
@@ -5,7 +5,7 @@
   "about.domain_blocks.no_reason_available": "Motivo non disponibile",
   "about.domain_blocks.preamble": "Mastodon, generalmente, ti consente di visualizzare i contenuti e interagire con gli utenti da qualsiasi altro server nel fediverso. Queste sono le eccezioni che sono state fatte su questo particolare server.",
   "about.domain_blocks.silenced.explanation": "Generalmente non vedrai i profili e i contenuti di questo server, a meno che tu non lo cerchi esplicitamente o che tu scelga di seguirlo.",
-  "about.domain_blocks.silenced.title": "Silenziato",
+  "about.domain_blocks.silenced.title": "Limitato",
   "about.domain_blocks.suspended.explanation": "Nessun dato proveniente da questo server verrà elaborato, conservato o scambiato, rendendo impossibile qualsiasi interazione o comunicazione con gli utenti da questo server.",
   "about.domain_blocks.suspended.title": "Sospeso",
   "about.not_available": "Queste informazioni non sono state rese disponibili su questo server.",
@@ -20,7 +20,7 @@
   "account.blocked": "Bloccato",
   "account.browse_more_on_origin_server": "Sfoglia di più sul profilo originale",
   "account.cancel_follow_request": "Annulla la richiesta di seguire",
-  "account.direct": "Messaggio diretto a @{name}",
+  "account.direct": "Menziona privatamente @{name}",
   "account.disable_notifications": "Smetti di avvisarmi quando @{name} pubblica un post",
   "account.domain_blocked": "Dominio bloccato",
   "account.edit_profile": "Modifica profilo",
@@ -102,7 +102,7 @@
   "column.blocks": "Utenti bloccati",
   "column.bookmarks": "Segnalibri",
   "column.community": "Cronologia locale",
-  "column.direct": "Messaggi diretti",
+  "column.direct": "Menzioni private",
   "column.directory": "Sfoglia profili",
   "column.domain_blocks": "Domini bloccati",
   "column.favourites": "Preferiti",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Hai delle modifiche non salvate alla descrizione o anteprima del media, scartarle comunque?",
   "confirmations.domain_block.confirm": "Blocca l'intero dominio",
   "confirmations.domain_block.message": "Sei davvero sicuro di voler bloccare l'intero {domain}? In gran parte dei casi, è sufficiente e preferibile bloccare o silenziare alcuni profili. Non visualizzerai i contenuti da quel dominio in alcuna cronologia pubblica o tra le tue notifiche. I tuoi seguaci da quel dominio saranno rimossi.",
+  "confirmations.edit.confirm": "Modifica",
+  "confirmations.edit.message": "Modificare ora sovrascriverà il messaggio che stai correntemente componendo. Sei sicuro di voler procedere?",
   "confirmations.logout.confirm": "Disconnettiti",
   "confirmations.logout.message": "Sei sicuro di volerti disconnettere?",
   "confirmations.mute.confirm": "Silenzia",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Non hai ancora bloccato alcun utente.",
   "empty_column.bookmarked_statuses": "Non hai ancora salvato nei segnalibri alcun post. Quando lo farai, apparirà qui.",
   "empty_column.community": "La cronologia locale è vuota. Scrivi qualcosa pubblicamente per dare inizio alla festa!",
-  "empty_column.direct": "Non hai ancora alcun messaggio diretto. Quando ne invierai o riceverai uno, apparirà qui.",
+  "empty_column.direct": "Non hai ancora alcuna menzione privata. Quando ne invierai o riceverai una, apparirà qui.",
   "empty_column.domain_blocks": "Ancora nessun dominio bloccato.",
   "empty_column.explore_statuses": "Nulla è in tendenza al momento. Ricontrolla più tardi!",
   "empty_column.favourited_statuses": "Non hai ancora alcun post preferito. Quando ne salverai uno tra i preferiti, apparirà qui.",
   "empty_column.favourites": "Nessuno ha ancora messo questo post tra i preferiti. Quando qualcuno lo farà, apparirà qui.",
   "empty_column.follow_recommendations": "Sembra che non sia stato possibile generare alcun suggerimento per te. Puoi provare a utilizzare la ricerca per cercare persone che potresti conoscere, o a esplorare gli hashtag in tendenza.",
   "empty_column.follow_requests": "Non hai ancora alcuna richiesta di seguirti. Quando ne riceverai una, apparirà qui.",
+  "empty_column.followed_tags": "Non hai ancora seguito alcun hashtag. Quando lo farai, appariranno qui.",
   "empty_column.hashtag": "Non c'è ancora nulla in questo hashtag.",
   "empty_column.home": "La cronologia della tua home è vuota! Segui altre persone per riempirla. {suggestions}",
   "empty_column.home.suggestions": "Vedi alcuni suggerimenti",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autorizza",
   "follow_request.reject": "Rifiuta",
   "follow_requests.unlocked_explanation": "Anche se il tuo profilo non è privato, lo staff di {domain} ha pensato che potresti voler revisionare manualmente le richieste di seguirti da questi profili.",
+  "followed_tags": "Hashtag seguiti",
   "footer.about": "Info",
   "footer.directory": "Cartella dei profili",
   "footer.get_app": "Scarica l'app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Scorciatoie da tastiera",
   "footer.privacy_policy": "Politica sulla privacy",
   "footer.source_code": "Visualizza il codice sorgente",
+  "footer.status": "Stato",
   "generic.saved": "Salvato",
   "getting_started.heading": "Per iniziare",
   "hashtag.column_header.tag_mode.all": "e {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Focalizza alla colonna",
   "keyboard_shortcuts.compose": "Focalizza l'area di composizione testuale",
   "keyboard_shortcuts.description": "Descrizione",
-  "keyboard_shortcuts.direct": "per aprire la colonna dei messaggi diretti",
+  "keyboard_shortcuts.direct": "per aprire la colonna menzioni private",
   "keyboard_shortcuts.down": "Scorri in basso nell'elenco",
   "keyboard_shortcuts.enter": "Apre il post",
   "keyboard_shortcuts.favourite": "Salva il post tra i preferiti",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Segnalibri",
   "navigation_bar.community_timeline": "Cronologia locale",
   "navigation_bar.compose": "Componi nuovo toot",
-  "navigation_bar.direct": "Messaggi diretti",
+  "navigation_bar.direct": "Menzioni private",
   "navigation_bar.discover": "Scopri",
   "navigation_bar.domain_blocks": "Domini bloccati",
   "navigation_bar.edit_profile": "Modifica il profilo",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Preferiti",
   "navigation_bar.filters": "Parole silenziate",
   "navigation_bar.follow_requests": "Richieste di seguirti",
+  "navigation_bar.followed_tags": "Hashtag seguiti",
   "navigation_bar.follows_and_followers": "Seguiti e seguaci",
   "navigation_bar.lists": "Liste",
   "navigation_bar.logout": "Disconnettiti",
@@ -470,7 +476,7 @@
   "relative_time.full.seconds": "{number, plural, one {# secondo} other {# secondi}} fa",
   "relative_time.hours": "{number}h",
   "relative_time.just_now": "ora",
-  "relative_time.minutes": "{number} minuti",
+  "relative_time.minutes": "{number}m",
   "relative_time.seconds": "{number}s",
   "relative_time.today": "oggi",
   "reply_indicator.cancel": "Annulla",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Violazione delle regole",
   "report_notification.open": "Apri segnalazione",
+  "search.no_recent_searches": "Nessuna ricerca recente",
   "search.placeholder": "Cerca",
+  "search.quick_action.account_search": "Profili corrispondenti a {x}",
+  "search.quick_action.go_to_account": "Vai al profilo {x}",
+  "search.quick_action.go_to_hashtag": "Vai all'hashtag {x}",
+  "search.quick_action.open_url": "Apri URL in Mastodon",
+  "search.quick_action.status_search": "Post corrispondenti a {x}",
   "search.search_or_paste": "Cerca o incolla URL",
-  "search_popout.search_format": "Formato di ricerca avanzato",
-  "search_popout.tips.full_text": "Restituisce in testo semplice i post che hai scritto, salvato tra i preferiti, rebloggato o in cui sei stato menzionato, nonché nomi utenti, nomi visualizzati e hashtag corrispondenti.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "post",
-  "search_popout.tips.text": "Restituisce in testo semplice i nomi utente, i nomi visualizzati e gli hashtag corrispondenti",
-  "search_popout.tips.user": "utente",
-  "search_results.accounts": "Persone",
+  "search_popout.quick_actions": "Azioni rapide",
+  "search_popout.recent": "Ricerche recenti",
+  "search_results.accounts": "Profili",
   "search_results.all": "Tutto",
   "search_results.hashtags": "Hashtag",
   "search_results.nothing_found": "Impossibile trovare qualcosa per questi termini di ricerca",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Statistiche del server:",
   "sign_in_banner.create_account": "Crea un profilo",
   "sign_in_banner.sign_in": "Accedi",
-  "sign_in_banner.text": "Accedi per seguire profili o hashtag, salvare tra i preferiti, condividere e rispondere ai post, o interagire dal tuo profilo su un server differente.",
+  "sign_in_banner.text": "Accedi per seguire profili o hashtag, segnare messaggi come preferiti, condividere e rispondere ai messaggi. Puoi anche interagire dal tuo account su un server diverso.",
   "status.admin_account": "Apri interfaccia di moderazione per @{name}",
   "status.admin_domain": "Apri l'interfaccia di moderazione per {domain}",
   "status.admin_status": "Apri questo post nell'interfaccia di moderazione",
@@ -551,7 +559,8 @@
   "status.copy": "Copia link al post",
   "status.delete": "Elimina",
   "status.detailed_status": "Vista conversazione dettagliata",
-  "status.direct": "Messaggio diretto a @{name}",
+  "status.direct": "Menziona privatamente @{name}",
+  "status.direct_indicator": "Menzione privata",
   "status.edit": "Modifica",
   "status.edited": "Modificato il {date}",
   "status.edited_x_times": "Modificato {count, plural, one {{count} volta} other {{count} volte}}",
diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json
index 2a24e6934..01eac6a10 100644
--- a/app/javascript/mastodon/locales/ja.json
+++ b/app/javascript/mastodon/locales/ja.json
@@ -20,7 +20,7 @@
   "account.blocked": "ブロック済み",
   "account.browse_more_on_origin_server": "リモートで表示",
   "account.cancel_follow_request": "フォローリクエストの取り消し",
-  "account.direct": "@{name}さんにダイレクトメッセージ",
+  "account.direct": "@{name}さんに非公開でメンション",
   "account.disable_notifications": "@{name}さんの投稿時の通知を停止",
   "account.domain_blocked": "ドメインブロック中",
   "account.edit_profile": "プロフィール編集",
@@ -45,7 +45,7 @@
   "account.locked_info": "このアカウントは承認制アカウントです。相手が承認するまでフォローは完了しません。",
   "account.media": "メディア",
   "account.mention": "@{name}さんにメンション",
-  "account.moved_to": "{name} さんの新しいアカウント:",
+  "account.moved_to": "{name}さんがアカウントを引っ越しました:",
   "account.mute": "@{name}さんをミュート",
   "account.mute_notifications": "@{name}さんからの通知を受け取らない",
   "account.muted": "ミュート済み",
@@ -54,7 +54,7 @@
   "account.posts_with_replies": "投稿と返信",
   "account.report": "@{name}さんを通報",
   "account.requested": "フォロー承認待ちです。クリックしてキャンセル",
-  "account.requested_follow": "{name} さんがあなたにフォローリクエストしました",
+  "account.requested_follow": "{name}さんがあなたにフォローリクエストしました",
   "account.share": "@{name}さんのプロフィールを共有する",
   "account.show_reblogs": "@{name}さんからのブーストを表示",
   "account.statuses_counter": "{counter} 投稿",
@@ -98,11 +98,11 @@
   "closed_registrations_modal.find_another_server": "別のサーバーを探す",
   "closed_registrations_modal.preamble": "Mastodonは分散型なのでどのサーバーでアカウントを作成してもこのサーバーのユーザーを誰でもフォローして交流することができます。また自分でホスティングすることもできます!",
   "closed_registrations_modal.title": "Mastodonでアカウントを作成",
-  "column.about": "About",
+  "column.about": "概要",
   "column.blocks": "ブロックしたユーザー",
   "column.bookmarks": "ブックマーク",
   "column.community": "ローカルタイムライン",
-  "column.direct": "ダイレクトメッセージ",
+  "column.direct": "非公開の返信",
   "column.directory": "ディレクトリ",
   "column.domain_blocks": "ブロックしたドメイン",
   "column.favourites": "お気に入り",
@@ -120,10 +120,6 @@
   "column_header.pin": "ピン留めする",
   "column_header.show_settings": "設定を表示",
   "column_header.unpin": "ピン留めを外す",
-  "column.heading": "その他",
-  "column.subheading": "その他のオプション",
-  "column_subheading.lists": "リスト",
-  "column_subheading.navigation": "ナビゲーション",
   "column_subheading.settings": "設定",
   "community.column_settings.local_only": "ローカルのみ表示",
   "community.column_settings.media_only": "メディアのみ表示",
@@ -132,7 +128,7 @@
   "compose.language.search": "言語を検索...",
   "compose_form.direct_message_warning_learn_more": "もっと詳しく",
   "compose_form.encryption_warning": "Mastodonの投稿はエンドツーエンド暗号化に対応していません。安全に送受信されるべき情報をMastodonで共有しないでください。",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "この投稿は公開設定ではないのでハッシュタグの一覧に表示されません。公開投稿だけがハッシュタグで検索できます。",
   "compose_form.lock_disclaimer": "あなたのアカウントは{locked}になっていません。誰でもあなたをフォローすることができ、フォロワー限定の投稿を見ることができます。",
   "compose_form.lock_disclaimer.lock": "承認制",
   "compose_form.placeholder": "今なにしてる?",
@@ -166,6 +162,8 @@
   "confirmations.discard_edit_media.message": "メディアの説明またはプレビューに保存されていない変更があります。それでも破棄しますか?",
   "confirmations.domain_block.confirm": "ドメイン全体をブロック",
   "confirmations.domain_block.message": "本当に{domain}全体を非表示にしますか? 多くの場合は個別にブロックやミュートするだけで充分であり、また好ましいです。公開タイムラインにそのドメインのコンテンツが表示されなくなり、通知も届かなくなります。そのドメインのフォロワーはアンフォローされます。",
+  "confirmations.edit.confirm": "編集",
+  "confirmations.edit.message": "今編集すると現在作成中のメッセージが上書きされます。本当に実行しますか?",
   "confirmations.logout.confirm": "ログアウト",
   "confirmations.logout.message": "本当にログアウトしますか?",
   "confirmations.mute.confirm": "ミュート",
@@ -218,13 +216,14 @@
   "empty_column.blocks": "まだ誰もブロックしていません。",
   "empty_column.bookmarked_statuses": "まだ何もブックマーク登録していません。ブックマーク登録するとここに表示されます。",
   "empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!",
-  "empty_column.direct": "ダイレクトメッセージはまだありません。ダイレクトメッセージをやりとりすると、ここに表示されます。",
+  "empty_column.direct": "非公開の返信はまだありません。非公開でやりとりをするとここに表示されます。",
   "empty_column.domain_blocks": "ブロックしているドメインはありません。",
   "empty_column.explore_statuses": "まだ何もありません。後で確認してください。",
   "empty_column.favourited_statuses": "まだ何もお気に入り登録していません。お気に入り登録するとここに表示されます。",
   "empty_column.favourites": "まだ誰もお気に入り登録していません。お気に入り登録されるとここに表示されます。",
   "empty_column.follow_recommendations": "おすすめを生成できませんでした。検索を使って知り合いを探したり、トレンドハッシュタグを見てみましょう。",
   "empty_column.follow_requests": "まだフォローリクエストを受けていません。フォローリクエストを受けるとここに表示されます。",
+  "empty_column.followed_tags": "まだハッシュタグをフォローしていません。フォローするとここに表示されます。",
   "empty_column.hashtag": "このハッシュタグはまだ使われていません。",
   "empty_column.home": "ホームタイムラインはまだ空っぽです。誰かフォローして埋めてみましょう。 {suggestions}",
   "empty_column.home.suggestions": "おすすめを見る",
@@ -267,13 +266,15 @@
   "follow_request.authorize": "許可",
   "follow_request.reject": "拒否",
   "follow_requests.unlocked_explanation": "あなたのアカウントは承認制ではありませんが、{domain}のスタッフはこれらのアカウントからのフォローリクエストの確認が必要であると判断しました。",
+  "followed_tags": "フォロー中のハッシュタグ",
   "footer.about": "概要",
   "footer.directory": "ディレクトリ",
-  "footer.get_app": "アプリをダウンロードする",
+  "footer.get_app": "アプリを入手",
   "footer.invite": "新規ユーザーの招待",
   "footer.keyboard_shortcuts": "キーボードショートカット",
   "footer.privacy_policy": "プライバシーポリシー",
   "footer.source_code": "ソースコードを表示",
+  "footer.status": "ステータス",
   "generic.saved": "保存しました",
   "getting_started.heading": "スタート",
   "hashtag.column_header.tag_mode.all": "と{additional}",
@@ -313,7 +314,7 @@
   "keyboard_shortcuts.column": "左からn番目のカラムの最新に移動",
   "keyboard_shortcuts.compose": "投稿の入力欄に移動",
   "keyboard_shortcuts.description": "説明",
-  "keyboard_shortcuts.direct": "ダイレクトメッセージのカラムを開く",
+  "keyboard_shortcuts.direct": "非公開の返信のカラムを開く",
   "keyboard_shortcuts.down": "カラム内一つ下に移動",
   "keyboard_shortcuts.enter": "投稿の詳細を表示",
   "keyboard_shortcuts.favourite": "お気に入り",
@@ -375,7 +376,7 @@
   "navigation_bar.bookmarks": "ブックマーク",
   "navigation_bar.community_timeline": "ローカルタイムライン",
   "navigation_bar.compose": "投稿の新規作成",
-  "navigation_bar.direct": "ダイレクトメッセージ",
+  "navigation_bar.direct": "非公開の返信",
   "navigation_bar.discover": "見つける",
   "navigation_bar.domain_blocks": "ブロックしたドメイン",
   "navigation_bar.edit_profile": "プロフィールを編集",
@@ -383,6 +384,7 @@
   "navigation_bar.favourites": "お気に入り",
   "navigation_bar.filters": "フィルター設定",
   "navigation_bar.follow_requests": "フォローリクエスト",
+  "navigation_bar.followed_tags": "フォロー中のハッシュタグ",
   "navigation_bar.follows_and_followers": "フォロー・フォロワー",
   "navigation_bar.lists": "リスト",
   "navigation_bar.logout": "ログアウト",
@@ -520,15 +522,17 @@
   "report_notification.categories.spam": "スパム",
   "report_notification.categories.violation": "ルール違反",
   "report_notification.open": "通報を開く",
+  "search.no_recent_searches": "検索履歴はありません",
   "search.placeholder": "検索",
+  "search.quick_action.account_search": "{x} に該当するプロフィール",
+  "search.quick_action.go_to_account": "{x} のプロフィールを見る",
+  "search.quick_action.go_to_hashtag": "{x} に該当するハッシュタグ",
+  "search.quick_action.open_url": "MastodonでURLを開く",
+  "search.quick_action.status_search": "{x} に該当する投稿",
   "search.search_or_paste": "検索またはURLを入力",
-  "search_popout.search_format": "高度な検索フォーマット",
-  "search_popout.tips.full_text": "表示名やユーザー名、ハッシュタグのほか、あなたの投稿やお気に入り、ブーストした投稿、返信に一致する単純なテキスト。",
-  "search_popout.tips.hashtag": "ハッシュタグ",
-  "search_popout.tips.status": "投稿",
-  "search_popout.tips.text": "表示名やユーザー名、ハッシュタグに一致する単純なテキスト",
-  "search_popout.tips.user": "ユーザー",
-  "search_results.accounts": "人々",
+  "search_popout.quick_actions": "クイック操作",
+  "search_popout.recent": "最近の検索",
+  "search_results.accounts": "ユーザー",
   "search_results.all": "すべて",
   "search_results.hashtags": "ハッシュタグ",
   "search_results.nothing_found": "この検索条件では何も見つかりませんでした",
@@ -555,7 +559,8 @@
   "status.copy": "投稿へのリンクをコピー",
   "status.delete": "削除",
   "status.detailed_status": "詳細な会話ビュー",
-  "status.direct": "@{name}さんにダイレクトメッセージ",
+  "status.direct": "@{name}さんに非公開で投稿",
+  "status.direct_indicator": "非公開の返信",
   "status.edit": "編集",
   "status.edited": "{date}に編集",
   "status.edited_x_times": "{count}回編集",
@@ -628,12 +633,12 @@
   "upload_error.limit": "アップロードできる上限を超えています。",
   "upload_error.poll": "アンケートではファイルをアップロードできません。",
   "upload_form.audio_description": "聴き取りが難しいユーザーへの説明",
-  "upload_form.description": "視覚障害者向けの説明",
+  "upload_form.description": "視覚的に閲覧が難しいユーザーへの説明",
   "upload_form.description_missing": "説明を追加していません",
   "upload_form.edit": "説明",
   "upload_form.thumbnail": "サムネイルを変更",
   "upload_form.undo": "削除",
-  "upload_form.video_description": "視聴が難しいユーザーへの説明",
+  "upload_form.video_description": "聴き取りや視覚的に閲覧が難しいユーザーへの説明",
   "upload_modal.analyzing_picture": "画像を解析中…",
   "upload_modal.apply": "適用",
   "upload_modal.applying": "適用中...",
diff --git a/app/javascript/mastodon/locales/ka.json b/app/javascript/mastodon/locales/ka.json
index 64e726321..35e808ac1 100644
--- a/app/javascript/mastodon/locales/ka.json
+++ b/app/javascript/mastodon/locales/ka.json
@@ -20,7 +20,7 @@
   "account.blocked": "დაიბლოკა",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "პირდაპირი წერილი @{name}-ს",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "დომენი დამალულია",
   "account.edit_profile": "პროფილის ცვლილება",
@@ -102,7 +102,7 @@
   "column.blocks": "დაბლოკილი მომხმარებლები",
   "column.bookmarks": "Bookmarks",
   "column.community": "ლოკალური თაიმლაინი",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "დამალული დომენები",
   "column.favourites": "ფავორიტები",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "მთელი დომენის დამალვა",
   "confirmations.domain_block.message": "ნაღდად, ნაღდად, დარწმუნებული ხართ, გსურთ დაბლოკოთ მთელი {domain}? უმეტეს შემთხვევაში რამდენიმე გამიზნული ბლოკი ან გაჩუმება საკმარისი და უკეთესია. კონტენტს ამ დომენიდან ვერ იხილავთ ვერც ერთ ღია თაიმლაინზე ან თქვენს შეტყობინებებში. ამ დომენიდან არსებული მიმდევრები ამოიშლება.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "გაჩუმება",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "ლოკალური თაიმლაინი ცარიელია. დაწერეთ რაიმე ღიად ან ქენით რაიმე სხვა!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "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_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "ამ ჰეშტეგში ჯერ არაფერია.",
   "empty_column.home": "თქვენი სახლის თაიმლაინი ცარიელია! ესტუმრეთ {public}-ს ან დასაწყისისთვის გამოიყენეთ ძებნა, რომ შეხვდეთ სხვა მომხმარებლებს.",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "ავტორიზაცია",
   "follow_request.reject": "უარყოფა",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "დაწყება",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "ლოკალური თაიმლაინი",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "აღმოაჩინე",
   "navigation_bar.domain_blocks": "დამალული დომენები",
   "navigation_bar.edit_profile": "შეცვალე პროფილი",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "ფავორიტები",
   "navigation_bar.filters": "გაჩუმებული სიტყვები",
   "navigation_bar.follow_requests": "დადევნების მოთხოვნები",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "სიები",
   "navigation_bar.logout": "გასვლა",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "ძებნა",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "დეტალური ძებნის ფორმა",
-  "search_popout.tips.full_text": "მარტივი ტექსტი აბრუნებს სტატუსებს რომლებიც შექმენით, აქციეთ ფავორიტად, დაბუსტეთ, ან რაშიც ასახელეთ, ასევე ემთხვევა მომხმარებლის სახელებს, დისპლეი სახელებს, და ჰეშტეგებს.",
-  "search_popout.tips.hashtag": "ჰეშტეგი",
-  "search_popout.tips.status": "სტატუსი",
-  "search_popout.tips.text": "მარტივი ტექსტი აბრუნებს დამთხვეულ დისპლეი სახელებს, მომხმარებლის სახელებს და ჰეშტეგებს",
-  "search_popout.tips.user": "მომხმარებელი",
-  "search_results.accounts": "ხალხი",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "ჰეშტეგები",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "წაშლა",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "პირდაპირი წერილი @{name}-ს",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/kab.json b/app/javascript/mastodon/locales/kab.json
index 609c29540..4460e4b65 100644
--- a/app/javascript/mastodon/locales/kab.json
+++ b/app/javascript/mastodon/locales/kab.json
@@ -1,6 +1,6 @@
 {
   "about.blocks": "Moderated servers",
-  "about.contact": "Contact:",
+  "about.contact": "Anermis:",
   "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Reason not available",
   "about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",
@@ -10,7 +10,7 @@
   "about.domain_blocks.suspended.title": "Suspended",
   "about.not_available": "This information has not been made available on this server.",
   "about.powered_by": "Decentralized social media powered by {mastodon}",
-  "about.rules": "Server rules",
+  "about.rules": "Ilugan n uqeddac",
   "account.account_note_header": "Tazmilt",
   "account.add_or_remove_from_list": "Rnu neɣ kkes seg tebdarin",
   "account.badges.bot": "Aṛubut",
@@ -20,7 +20,7 @@
   "account.blocked": "Yettusewḥel",
   "account.browse_more_on_origin_server": "Snirem ugar deg umeɣnu aneẓli",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Izen usrid i @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Ḥbes ur iyi-d-ttazen ara ilɣa mi ara d-isuffeɣ @{name}",
   "account.domain_blocked": "Taɣult yeffren",
   "account.edit_profile": "Ẓreg amaɣnu",
@@ -83,7 +83,7 @@
   "boost_modal.combo": "Tzemreḍ ad tetekkiḍ ɣef {combo} akken ad tessurfeḍ aya tikelt-nniḍen",
   "bundle_column_error.copy_stacktrace": "Nɣel tuccḍa n uneqqis",
   "bundle_column_error.error.body": "The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.",
-  "bundle_column_error.error.title": "Oh, no!",
+  "bundle_column_error.error.title": "Uh, ala !",
   "bundle_column_error.network.body": "There was an error when trying to load this page. This could be due to a temporary problem with your internet connection or this server.",
   "bundle_column_error.network.title": "Tuccḍa deg uẓeṭṭa",
   "bundle_column_error.retry": "Ɛreḍ tikelt-nniḍen",
@@ -102,7 +102,7 @@
   "column.blocks": "Imiḍanen yettusḥebsen",
   "column.bookmarks": "Ticraḍ",
   "column.community": "Tasuddemt tadigant",
-  "column.direct": "Iznan usriden",
+  "column.direct": "Private mentions",
   "column.directory": "Inig deg imaɣnuten",
   "column.domain_blocks": "Taɣulin yeffren",
   "column.favourites": "Ismenyifen",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Ffer taɣult meṛṛa",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Ffeɣ",
   "confirmations.logout.message": "D tidet tebɣiḍ ad teffɣeḍ?",
   "confirmations.mute.confirm": "Sgugem",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Ur tesḥebseḍ ula yiwen n umseqdac ar tura.",
   "empty_column.bookmarked_statuses": "Ulac tijewwaqin i terniḍ ɣer yismenyifen-ik ar tura. Ticki terniḍ yiwet, ad d-tettwasken da.",
   "empty_column.community": "Tasuddemt tazayezt tadigant n yisallen d tilemt. Aru ihi kra akken ad tt-teččareḍ!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Ulac kra n taɣult yettwaffren ar tura.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "Ulac ula yiwet n tjewwaqt deg yismenyifen-ik ar tura. Ticki Tella-d yiwet, ad d-ban da.",
   "empty_column.favourites": "Ula yiwen ur yerri tajewwaqt-agi deg yismenyifen-is. Melmi i d-yella waya, ad d-yettwasken da.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "Ulac ɣur-k ula yiwen n usuter n teḍfeṛt. Ticki teṭṭfeḍ-d yiwen ad d-yettwasken da.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Ar tura ulac kra n ugbur yesɛan assaɣ ɣer uhacṭag-agi.",
   "empty_column.home": "Tasuddemt tagejdant n yisallen d tilemt! Ẓer {public} neɣ nadi ad tafeḍ imseqdacen-nniḍen ad ten-ḍefṛeḍ.",
   "empty_column.home.suggestions": "Ẓer kra n yisumar",
@@ -238,9 +241,9 @@
   "explore.search_results": "Igemmaḍ n unadi",
   "explore.suggested_follows": "For you",
   "explore.title": "Snirem",
-  "explore.trending_links": "News",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "Hashtags",
+  "explore.trending_links": "Isallen",
+  "explore.trending_statuses": "Tisuffiɣin",
+  "explore.trending_tags": "Ihacṭagen",
   "filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.",
   "filter_modal.added.context_mismatch_title": "Context mismatch!",
   "filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Ssireg",
   "follow_request.reject": "Agi",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "Γef",
   "footer.directory": "Akaram n imaγnuten",
   "footer.get_app": "Awi-d asnas",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Inegzumen n unasiw",
   "footer.privacy_policy": "Tasertit tabaḍnit",
   "footer.source_code": "Wali tangalt taɣbalut",
+  "footer.status": "Status",
   "generic.saved": "Yettwasekles",
   "getting_started.heading": "Bdu",
   "hashtag.column_header.tag_mode.all": "d {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Ticraḍ",
   "navigation_bar.community_timeline": "Tasuddemt tadigant",
   "navigation_bar.compose": "Aru tajewwiqt tamaynut",
-  "navigation_bar.direct": "Iznan usridden",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Ẓer",
   "navigation_bar.domain_blocks": "Tiɣula yeffren",
   "navigation_bar.edit_profile": "Ẓreg amaɣnu",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Ismenyifen",
   "navigation_bar.filters": "Awalen i yettwasgugmen",
   "navigation_bar.follow_requests": "Isuturen n teḍfeṛt",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Imeḍfaṛen akked wid i teṭṭafaṛeḍ",
   "navigation_bar.lists": "Tibdarin",
   "navigation_bar.logout": "Ffeɣ",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Aspam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Ldi aneqqis",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Nadi",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Nadi neɣ senṭeḍ URL",
-  "search_popout.search_format": "Anadi yenneflin",
-  "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": "ahacṭag",
-  "search_popout.tips.status": "addad",
-  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
-  "search_popout.tips.user": "amseqdac",
-  "search_results.accounts": "Medden",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Akk",
   "search_results.hashtags": "Ihacṭagen",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Snulfu-d amiḍan",
   "sign_in_banner.sign_in": "Qqen",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Nɣel assaɣ ɣer tasuffeɣt",
   "status.delete": "Kkes",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Izen usrid i @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Ẓreg",
   "status.edited": "Tettwaẓreg deg {date}",
   "status.edited_x_times": "Tettwaẓreg {count, plural, one {{count} n tikkelt} other {{count} n tikkal}}",
diff --git a/app/javascript/mastodon/locales/kk.json b/app/javascript/mastodon/locales/kk.json
index d1f5faf6d..a2aea1459 100644
--- a/app/javascript/mastodon/locales/kk.json
+++ b/app/javascript/mastodon/locales/kk.json
@@ -1,57 +1,57 @@
 {
   "about.blocks": "Модерацияланған серверлер",
   "about.contact": "Байланыс:",
-  "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
-  "about.domain_blocks.no_reason_available": "Reason not available",
-  "about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",
-  "about.domain_blocks.silenced.explanation": "You will generally not see profiles and content from this server, unless you explicitly look it up or opt into it by following.",
-  "about.domain_blocks.silenced.title": "Limited",
-  "about.domain_blocks.suspended.explanation": "No data from this server will be processed, stored or exchanged, making any interaction or communication with users from this server impossible.",
-  "about.domain_blocks.suspended.title": "Suspended",
-  "about.not_available": "This information has not been made available on this server.",
-  "about.powered_by": "Decentralized social media powered by {mastodon}",
-  "about.rules": "Server rules",
+  "about.disclaimer": "Mastodon деген тегін, бастапқы коды ашық бағдарламалық жасақтама және Mastodon gGmbH-тің сауда маркасы.",
+  "about.domain_blocks.no_reason_available": "Себеп қолжетімсіз",
+  "about.domain_blocks.preamble": "Mastodon әдетте сізге Fediverse'тің кез келген серверінің қолданушыларының контентін көріп, олармен байланысуға мүмкіндік береді. Осы белгілі серверде жасалған ережеден тыс жағдайлар міне.",
+  "about.domain_blocks.silenced.explanation": "Сіз бұл сервердің профильдері мен контентін іздегенше немесе жазылмағанша, оларды әдетте көрмейсіз.",
+  "about.domain_blocks.silenced.title": "Шектеулі",
+  "about.domain_blocks.suspended.explanation": "Бұл сервердің деректері өңделмейді, сақталмайды және айырбасталмайды, сондықтан бұл сервердің қолданушыларымен кез келген әрекеттесу немесе байланыс мүмкін емес.",
+  "about.domain_blocks.suspended.title": "Тоқтатылған",
+  "about.not_available": "Бұл ақпарат бұл серверде қолжетімді емес.",
+  "about.powered_by": "{mastodon} негізіндегі орталықсыз әлеуметтік желі",
+  "about.rules": "Сервер ережелері",
   "account.account_note_header": "Жазба",
   "account.add_or_remove_from_list": "Тізімге қосу немесе жою",
   "account.badges.bot": "Бот",
   "account.badges.group": "Топ",
-  "account.block": "Бұғаттау @{name}",
-  "account.block_domain": "Домендегі барлығын бұғатта {domain}",
-  "account.blocked": "Бұғатталды",
-  "account.browse_more_on_origin_server": "Толығырақ оригинал профилінде қара",
+  "account.block": "@{name} дегенді бұғаттау",
+  "account.block_domain": "{domain} доменін бұғаттау",
+  "account.blocked": "Бұғатталған",
+  "account.browse_more_on_origin_server": "Бастапқы профильден шолу",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Жеке хат @{name}",
-  "account.disable_notifications": "Stop notifying me when @{name} posts",
-  "account.domain_blocked": "Домен жабық",
+  "account.direct": "Privately mention @{name}",
+  "account.disable_notifications": "@{name} постары туралы ескертпеу",
+  "account.domain_blocked": "Домен бұғатталған",
   "account.edit_profile": "Профильді өңдеу",
-  "account.enable_notifications": "Notify me when @{name} posts",
-  "account.endorse": "Профильде рекомендеу",
+  "account.enable_notifications": "@{name} постары туралы ескерту",
+  "account.endorse": "Профильде ұсыну",
   "account.featured_tags.last_status_at": "Last post on {date}",
-  "account.featured_tags.last_status_never": "No posts",
-  "account.featured_tags.title": "{name}'s featured hashtags",
+  "account.featured_tags.last_status_never": "Пост жоқ",
+  "account.featured_tags.title": "{name} таңдаулы хэштегтері",
   "account.follow": "Жазылу",
-  "account.followers": "Оқырмандар",
-  "account.followers.empty": "Әлі ешкім жазылмаған.",
-  "account.followers_counter": "{count, plural, one {{counter} Оқырман} other {{counter} Оқырман}}",
-  "account.following": "Following",
-  "account.following_counter": "{count, plural, one {{counter} Жазылым} other {{counter} Жазылым}}",
-  "account.follows.empty": "Ешкімге жазылмапты.",
-  "account.follows_you": "Сізге жазылыпты",
-  "account.go_to_profile": "Go to profile",
-  "account.hide_reblogs": "@{name} атты қолданушының әрекеттерін жасыру",
-  "account.joined_short": "Joined",
-  "account.languages": "Change subscribed languages",
+  "account.followers": "Жазылушы",
+  "account.followers.empty": "Бұл қолданушыға әлі ешкім жазылмаған.",
+  "account.followers_counter": "{count, plural, one {{counter} жазылушы} other {{counter} жазылушы}}",
+  "account.following": "Жазылым",
+  "account.following_counter": "{count, plural, one {{counter} жазылым} other {{counter} жазылым}}",
+  "account.follows.empty": "Бұл қолданушы әлі ешкімге жазылмаған.",
+  "account.follows_you": "Сізге жазылған",
+  "account.go_to_profile": "Профиліне өту",
+  "account.hide_reblogs": "@{name} бустарын жасыру",
+  "account.joined_short": "Қосылған",
+  "account.languages": "Жазылған тілдерді өзгерту",
   "account.link_verified_on": "Сілтеме меншігі расталған күн {date}",
   "account.locked_info": "Бұл қолданушы өзі туралы мәліметтерді жасырған. Тек жазылғандар ғана көре алады.",
   "account.media": "Медиа",
-  "account.mention": "Аталым @{name}",
+  "account.mention": "@{name} дегенді атау",
   "account.moved_to": "{name} has indicated that their new account is now:",
-  "account.mute": "Үнсіз қылу @{name}",
-  "account.mute_notifications": "@{name} туралы ескертпелерді жасыру",
-  "account.muted": "Үнсіз",
-  "account.open_original_page": "Open original page",
-  "account.posts": "Жазбалар",
-  "account.posts_with_replies": "Жазбалар мен жауаптар",
+  "account.mute": "@{name} дегенді елемеу",
+  "account.mute_notifications": "@{name} хабарландыруларын елемеу",
+  "account.muted": "Еленбейді",
+  "account.open_original_page": "Бастапқы бетті ашу",
+  "account.posts": "Пост",
+  "account.posts_with_replies": "Постар мен жауаптар",
   "account.report": "Шағымдану @{name}",
   "account.requested": "Растауын күтіңіз. Жазылудан бас тарту үшін басыңыз",
   "account.requested_follow": "{name} has requested to follow you",
@@ -102,7 +102,7 @@
   "column.blocks": "Бұғатталғандар",
   "column.bookmarks": "Бетбелгілер",
   "column.community": "Жергілікті желі",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Профильдерді аралау",
   "column.domain_blocks": "Жасырылған домендер",
   "column.favourites": "Таңдаулылар",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Бұл доменді бұғатта",
   "confirmations.domain_block.message": "Бұл домендегі {domain} жазбаларды шынымен бұғаттайсыз ба? Кейде үнсіз қылып тастау да жеткілікті.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Шығу",
   "confirmations.logout.message": "Шығатыныңызға сенімдісіз бе?",
   "confirmations.mute.confirm": "Үнсіз қылу",
@@ -178,7 +180,7 @@
   "conversation.open": "Пікірталасты қарау",
   "conversation.with": "{names} атты",
   "copypaste.copied": "Copied",
-  "copypaste.copy": "Copy",
+  "copypaste.copy": "Көшіру",
   "directory.federated": "Танымал желіден",
   "directory.local": "Тек {domain} доменінен",
   "directory.new_arrivals": "Жаңадан келгендер",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Ешкімді бұғаттамағансыз.",
   "empty_column.bookmarked_statuses": "Ешқандай жазба Бетбелгілер тізіміне қосылмапты. Қосылғаннан кейін осында жинала бастайды.",
   "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.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Бұғатталған домен жоқ.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "Ешқандай жазба 'Таңдаулылар' тізіміне қосылмапты. Қосылғаннан кейін осында жинала бастайды.",
   "empty_column.favourites": "Бұл постты әлі ешкім 'Таңдаулылар' тізіміне қоспапты. Біреу бастағаннан кейін осында көрінетін болады.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "Әлі ешқандай жазылуға сұранымдар келмеді. Жаңа сұранымдар осында көрінетін болады.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Бұндай хэштегпен әлі ешкім жазбапты.",
   "empty_column.home": "Әлі ешкімге жазылмапсыз. Бәлкім {public} жазбаларын қарап немесе іздеуді қолданып көрерсіз.",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Авторизация",
   "follow_request.reject": "Қабылдамау",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Сақталды",
   "getting_started.heading": "Желіде",
   "hashtag.column_header.tag_mode.all": "және {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Бетбелгілер",
   "navigation_bar.community_timeline": "Жергілікті желі",
   "navigation_bar.compose": "Жаңа жазба бастау",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "шарлау",
   "navigation_bar.domain_blocks": "Жабық домендер",
   "navigation_bar.edit_profile": "Профиль түзету",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Таңдаулылар",
   "navigation_bar.filters": "Үнсіз сөздер",
   "navigation_bar.follow_requests": "Жазылуға сұранғандар",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Жазылымдар және оқырмандар",
   "navigation_bar.lists": "Тізімдер",
   "navigation_bar.logout": "Шығу",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Іздеу",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "Кеңейтілген іздеу форматы",
-  "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, bоosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
-  "search_popout.tips.hashtag": "хэштег",
-  "search_popout.tips.status": "статус",
-  "search_popout.tips.text": "Simple text returns matching display names, usernames аnd hashtags",
-  "search_popout.tips.user": "қолданушы",
-  "search_results.accounts": "Адамдар",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Хэштегтер",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "@{name} үшін модерация интерфейсін аш",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Бұл жазбаны модерация интерфейсінде аш",
@@ -551,7 +559,8 @@
   "status.copy": "Жазба сілтемесін көшір",
   "status.delete": "Өшіру",
   "status.detailed_status": "Толық пікірталас көрінісі",
-  "status.direct": "Хат жіберу @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/kn.json b/app/javascript/mastodon/locales/kn.json
index c6f8322df..04edb8522 100644
--- a/app/javascript/mastodon/locales/kn.json
+++ b/app/javascript/mastodon/locales/kn.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocked",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Domain hidden",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Favourites",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "Mute",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "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_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lists",
   "navigation_bar.logout": "Logout",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json
index a94801ad8..7d7659569 100644
--- a/app/javascript/mastodon/locales/ko.json
+++ b/app/javascript/mastodon/locales/ko.json
@@ -1,8 +1,8 @@
 {
   "about.blocks": "제한된 서버들",
   "about.contact": "연락처:",
-  "about.disclaimer": "마스토돈은 자유 오픈소스 소프트웨어이며, Mastodon gGmbH의 상표입니다",
-  "about.domain_blocks.no_reason_available": "이유 비공개",
+  "about.disclaimer": "Mastodon은 자유 오픈소스 소프트웨어이며, Mastodon gGmbH의 상표입니다",
+  "about.domain_blocks.no_reason_available": "사유를 밝히지 않음",
   "about.domain_blocks.preamble": "마스토돈은 일반적으로 연합우주에 있는 어떤 서버의 사용자와도 게시물을 보고 응답을 할 수 있도록 허용합니다. 다음 항목들은 특정한 서버에 대해 만들어 진 예외사항입니다.",
   "about.domain_blocks.silenced.explanation": "명시적으로 찾아보거나 팔로우를 하기 전까지는, 이 서버에 있는 프로필이나 게시물 등을 일반적으로 볼 수 없습니다.",
   "about.domain_blocks.silenced.title": "제한됨",
@@ -20,7 +20,7 @@
   "account.blocked": "차단됨",
   "account.browse_more_on_origin_server": "원본 프로필에서 더 탐색하기",
   "account.cancel_follow_request": "팔로우 요청 취소",
-  "account.direct": "@{name}에게 다이렉트 메시지",
+  "account.direct": "@{name} 님에게 개인적으로 멘션",
   "account.disable_notifications": "@{name} 의 게시물 알림 끄기",
   "account.domain_blocked": "도메인 차단됨",
   "account.edit_profile": "프로필 편집",
@@ -66,12 +66,12 @@
   "account.unmute": "@{name} 뮤트 해제",
   "account.unmute_notifications": "@{name}의 알림 뮤트 해제",
   "account.unmute_short": "뮤트 해제",
-  "account_note.placeholder": "클릭해서 노트 추가",
+  "account_note.placeholder": "클릭하여 노트 추가",
   "admin.dashboard.daily_retention": "가입 후 일별 사용자 유지율",
   "admin.dashboard.monthly_retention": "가입 후 월별 사용자 유지율",
   "admin.dashboard.retention.average": "평균",
   "admin.dashboard.retention.cohort": "가입한 달",
-  "admin.dashboard.retention.cohort_size": "새로운 사용자",
+  "admin.dashboard.retention.cohort_size": "새 사용자",
   "alert.rate_limited.message": "{retry_time, time, medium}에 다시 시도해 주세요.",
   "alert.rate_limited.title": "빈도 제한됨",
   "alert.unexpected.message": "예상하지 못한 에러가 발생했습니다.",
@@ -102,7 +102,7 @@
   "column.blocks": "차단한 사용자",
   "column.bookmarks": "북마크",
   "column.community": "로컬 타임라인",
-  "column.direct": "다이렉트 메시지",
+  "column.direct": "개인적인 멘션",
   "column.directory": "프로필 둘러보기",
   "column.domain_blocks": "차단한 도메인",
   "column.favourites": "좋아요",
@@ -123,7 +123,7 @@
   "column_subheading.settings": "설정",
   "community.column_settings.local_only": "로컬만",
   "community.column_settings.media_only": "미디어만",
-  "community.column_settings.remote_only": "원격만",
+  "community.column_settings.remote_only": "원격지만",
   "compose.language.change": "언어 변경",
   "compose.language.search": "언어 검색...",
   "compose_form.direct_message_warning_learn_more": "더 알아보기",
@@ -138,14 +138,14 @@
   "compose_form.poll.remove_option": "이 항목 삭제",
   "compose_form.poll.switch_to_multiple": "다중 선택이 가능한 투표로 변경",
   "compose_form.poll.switch_to_single": "단일 선택 투표로 변경",
-  "compose_form.publish": "뿌우",
-  "compose_form.publish_form": "뿌우",
+  "compose_form.publish": "게시",
+  "compose_form.publish_form": "게시",
   "compose_form.publish_loud": "{publish}!",
   "compose_form.save_changes": "변경사항 저장",
   "compose_form.sensitive.hide": "미디어를 민감함으로 설정하기",
   "compose_form.sensitive.marked": "미디어가 열람주의로 설정되어 있습니다",
   "compose_form.sensitive.unmarked": "미디어가 열람주의로 설정 되어 있지 않습니다",
-  "compose_form.spoiler.marked": "콘텐츠 경고 제거",
+  "compose_form.spoiler.marked": "열람주의 제거",
   "compose_form.spoiler.unmarked": "열람 주의 문구 추가",
   "compose_form.spoiler_placeholder": "경고 문구를 여기에 작성하세요",
   "confirmation_modal.cancel": "취소",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "미디어 설명이나 미리보기에 대한 저장하지 않은 변경사항이 있습니다. 버리시겠습니까?",
   "confirmations.domain_block.confirm": "도메인 전체를 차단",
   "confirmations.domain_block.message": "정말로 {domain} 전체를 차단하시겠습니까? 대부분의 경우 개별 차단이나 뮤트로 충분합니다. 모든 공개 타임라인과 알림에서 해당 도메인에서 작성된 콘텐츠를 보지 못합니다. 해당 도메인에 속한 팔로워와의 관계가 사라집니다.",
+  "confirmations.edit.confirm": "수정",
+  "confirmations.edit.message": "지금 수정하면 작성 중인 메시지를 덮어쓰게 됩니다. 정말 진행합니까?",
   "confirmations.logout.confirm": "로그아웃",
   "confirmations.logout.message": "정말로 로그아웃 하시겠습니까?",
   "confirmations.mute.confirm": "뮤트",
@@ -170,7 +172,7 @@
   "confirmations.redraft.confirm": "삭제하고 다시 쓰기",
   "confirmations.redraft.message": "정말로 이 게시물을 삭제하고 다시 쓰시겠습니까? 해당 게시물에 대한 부스트와 좋아요를 잃게 되고 원본에 대한 답장은 연결 되지 않습니다.",
   "confirmations.reply.confirm": "답글",
-  "confirmations.reply.message": "답글을 달기 위해 현재 작성 중인 메시지가 덮어 씌워집니다. 진행하시겠습니까?",
+  "confirmations.reply.message": "지금 답장하면 작성 중인 메시지를 덮어쓰게 됩니다. 정말 진행합니까?",
   "confirmations.unfollow.confirm": "팔로우 해제",
   "confirmations.unfollow.message": "정말로 {name} 님을 팔로우 해제하시겠습니까?",
   "conversation.delete": "대화 삭제",
@@ -192,7 +194,7 @@
   "dismissable_banner.explore_tags": "이 해시태그들은 이 서버와 분산화된 네트워크의 다른 서버에서 사람들의 인기를 끌고 있는 것들입니다.",
   "dismissable_banner.public_timeline": "이 게시물들은 이 서버와 이 서버가 알고있는 분산화된 네트워크의 다른 서버에서 사람들이 게시한 최근 공개 게시물들입니다.",
   "embed.instructions": "아래의 코드를 복사하여 대화를 원하는 곳으로 공유하세요.",
-  "embed.preview": "이렇게 표시됩니다:",
+  "embed.preview": "여기처럼 보여집니다.",
   "emoji_button.activity": "활동",
   "emoji_button.clear": "지우기",
   "emoji_button.custom": "사용자 지정",
@@ -209,21 +211,22 @@
   "emoji_button.symbols": "기호",
   "emoji_button.travel": "여행과 장소",
   "empty_column.account_suspended": "계정 정지됨",
-  "empty_column.account_timeline": "여긴 게시물이 없어요!",
+  "empty_column.account_timeline": "이곳에는 게시물이 없습니다!",
   "empty_column.account_unavailable": "프로필 사용 불가",
   "empty_column.blocks": "아직 아무도 차단하지 않았습니다.",
   "empty_column.bookmarked_statuses": "아직 북마크에 저장한 게시물이 없습니다. 게시물을 북마크 지정하면 여기에 나타납니다.",
   "empty_column.community": "로컬 타임라인에 아무것도 없습니다. 아무거나 적어 보세요!",
-  "empty_column.direct": "아직 다이렉트 메시지가 없습니다. 다이렉트 메시지를 보내거나 받은 경우, 여기에 표시 됩니다.",
+  "empty_column.direct": "개인적인 멘션이 없습니다. 보내거나 받으면 여기에 표시됩니다.",
   "empty_column.domain_blocks": "아직 차단한 도메인이 없습니다.",
   "empty_column.explore_statuses": "아직 유행하는 것이 없습니다. 나중에 다시 확인하세요!",
   "empty_column.favourited_statuses": "아직 마음에 들어한 게시물이 없습니다. 게시물을 좋아요 하면 여기에 나타납니다.",
   "empty_column.favourites": "아직 아무도 이 게시물을 마음에 들어하지 않았습니다. 누군가 좋아요를 하면 여기에 나타납니다.",
-  "empty_column.follow_recommendations": "나를 위한 추천을 만들 수 없는 것 같습니다. 알 수도 있는 사람을 검색하거나 유행하는 해시태그를 둘러볼 수 있습니다.",
+  "empty_column.follow_recommendations": "당신을 위한 제안이 생성될 수 없는 것 같습니다. 알 수도 있는 사람을 검색하거나 유행하는 해시태그를 둘러볼 수 있습니다.",
   "empty_column.follow_requests": "아직 팔로우 요청이 없습니다. 요청을 받았을 때 여기에 나타납니다.",
+  "empty_column.followed_tags": "아직 아무 해시태그도 팔로우하고 있지 않습니다. 해시태그를 팔로우하면, 여기에 표시됩니다.",
   "empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.",
   "empty_column.home": "당신의 홈 타임라인은 비어있습니다! 더 많은 사람들을 팔로우 하여 채워보세요. {suggestions}",
-  "empty_column.home.suggestions": "몇몇의 제안 보기",
+  "empty_column.home.suggestions": "몇몇 제안 보기",
   "empty_column.list": "리스트에 아직 아무것도 없습니다. 리스트의 누군가가 게시물을 올리면 여기에 나타납니다.",
   "empty_column.lists": "아직 리스트가 없습니다. 리스트를 만들면 여기에 나타납니다.",
   "empty_column.mutes": "아직 아무도 뮤트하지 않았습니다.",
@@ -258,11 +261,12 @@
   "filter_modal.select_filter.title": "이 게시물을 필터",
   "filter_modal.title.status": "게시물 필터",
   "follow_recommendations.done": "완료",
-  "follow_recommendations.heading": "게시물을 받아 볼 사람들을 팔로우 하세요! 여기 몇몇의 추천이 있습니다.",
+  "follow_recommendations.heading": "게시물을 받아 볼 사람을 팔로우하세요! 여기 몇몇 추천이 있습니다.",
   "follow_recommendations.lead": "당신이 팔로우 하는 사람들의 게시물이 시간순으로 정렬되어 당신의 홈 피드에 표시될 것입니다. 실수를 두려워 하지 마세요, 언제든지 쉽게 팔로우 취소를 할 수 있습니다!",
   "follow_request.authorize": "허가",
   "follow_request.reject": "거부",
   "follow_requests.unlocked_explanation": "귀하의 계정이 잠긴 계정이 아닐지라도, {domain} 스태프는 이 계정들의 팔로우 요청을 수동으로 처리해 주시면 좋겠다고 생각했습니다.",
+  "followed_tags": "팔로우 중인 해시태그",
   "footer.about": "정보",
   "footer.directory": "프로필 책자",
   "footer.get_app": "앱 다운로드하기",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "키보드 단축키",
   "footer.privacy_policy": "개인정보 정책",
   "footer.source_code": "소스코드 보기",
+  "footer.status": "상태",
   "generic.saved": "저장됨",
   "getting_started.heading": "시작하기",
   "hashtag.column_header.tag_mode.all": "및 {additional}",
@@ -278,7 +283,7 @@
   "hashtag.column_settings.select.no_options_message": "추천할 내용이 없습니다",
   "hashtag.column_settings.select.placeholder": "해시태그를 입력하세요…",
   "hashtag.column_settings.tag_mode.all": "모두",
-  "hashtag.column_settings.tag_mode.any": "아무것이든",
+  "hashtag.column_settings.tag_mode.any": "어느것이든",
   "hashtag.column_settings.tag_mode.none": "이것들을 제외하고",
   "hashtag.column_settings.tag_toggle": "추가 해시태그를 이 컬럼에 추가합니다",
   "hashtag.follow": "해시태그 팔로우",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "해당 컬럼에 포커스",
   "keyboard_shortcuts.compose": "작성창에 포커스",
   "keyboard_shortcuts.description": "설명",
-  "keyboard_shortcuts.direct": "다이렉트 메시지 컬럼 열기",
+  "keyboard_shortcuts.direct": "개인적인 멘션 컬럼 열기",
   "keyboard_shortcuts.down": "리스트에서 아래로 이동",
   "keyboard_shortcuts.enter": "게시물 열기",
   "keyboard_shortcuts.favourite": "관심글 지정",
@@ -352,9 +357,9 @@
   "lists.new.create": "리스트 추가",
   "lists.new.title_placeholder": "새 리스트의 이름",
   "lists.replies_policy.followed": "팔로우 한 사용자 누구나",
-  "lists.replies_policy.list": "리스트의 구성원들",
-  "lists.replies_policy.none": "아무도 없음",
-  "lists.replies_policy.title": "답글 표시:",
+  "lists.replies_policy.list": "리스트의 구성원",
+  "lists.replies_policy.none": "고르지 않음",
+  "lists.replies_policy.title": "답글을 볼 대상",
   "lists.search": "팔로우 중인 사람들 중에서 찾기",
   "lists.subheading": "리스트",
   "load_pending": "{count}개의 새 항목",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "북마크",
   "navigation_bar.community_timeline": "로컬 타임라인",
   "navigation_bar.compose": "새 게시물 작성",
-  "navigation_bar.direct": "다이렉트 메시지",
+  "navigation_bar.direct": "개인적인 멘션",
   "navigation_bar.discover": "발견하기",
   "navigation_bar.domain_blocks": "차단한 도메인",
   "navigation_bar.edit_profile": "프로필 편집",
@@ -379,13 +384,14 @@
   "navigation_bar.favourites": "좋아요",
   "navigation_bar.filters": "뮤트한 단어",
   "navigation_bar.follow_requests": "팔로우 요청",
+  "navigation_bar.followed_tags": "팔로우 중인 해시태그",
   "navigation_bar.follows_and_followers": "팔로우와 팔로워",
   "navigation_bar.lists": "리스트",
   "navigation_bar.logout": "로그아웃",
   "navigation_bar.mutes": "뮤트한 사용자",
   "navigation_bar.personal": "개인용",
   "navigation_bar.pins": "고정된 게시물",
-  "navigation_bar.preferences": "사용자 설정",
+  "navigation_bar.preferences": "환경설정",
   "navigation_bar.public_timeline": "연합 타임라인",
   "navigation_bar.search": "검색",
   "navigation_bar.security": "보안",
@@ -395,24 +401,24 @@
   "notification.favourite": "{name} 님이 당신의 게시물을 마음에 들어합니다",
   "notification.follow": "{name} 님이 나를 팔로우했습니다",
   "notification.follow_request": "{name} 님이 팔로우 요청을 보냈습니다",
-  "notification.mention": "{name} 님이 언급하였습니다",
-  "notification.own_poll": "내 투표가 끝났습니다",
-  "notification.poll": "당신이 참여 한 투표가 종료되었습니다",
+  "notification.mention": "{name}님의 멘션",
+  "notification.own_poll": "투표를 마쳤습니다.",
+  "notification.poll": "참여했던 투표가 끝났습니다.",
   "notification.reblog": "{name} 님이 부스트했습니다",
   "notification.status": "{name} 님이 방금 게시물을 올렸습니다",
   "notification.update": "{name} 님이 게시물을 수정했습니다",
-  "notifications.clear": "알림 지우기",
+  "notifications.clear": "알림 비우기",
   "notifications.clear_confirmation": "정말로 알림을 삭제하시겠습니까?",
   "notifications.column_settings.admin.report": "새 신고:",
   "notifications.column_settings.admin.sign_up": "새로운 가입:",
   "notifications.column_settings.alert": "데스크탑 알림",
   "notifications.column_settings.favourite": "좋아요:",
-  "notifications.column_settings.filter_bar.advanced": "카테고리의 모든 종류를 표시",
-  "notifications.column_settings.filter_bar.category": "퀵 필터 바",
-  "notifications.column_settings.filter_bar.show_bar": "필터 막대 표시",
+  "notifications.column_settings.filter_bar.advanced": "모든 범주 표시하기",
+  "notifications.column_settings.filter_bar.category": "빠른 필터 막대",
+  "notifications.column_settings.filter_bar.show_bar": "필터 막대 보이기",
   "notifications.column_settings.follow": "새 팔로워:",
   "notifications.column_settings.follow_request": "새 팔로우 요청:",
-  "notifications.column_settings.mention": "답글:",
+  "notifications.column_settings.mention": "멘션:",
   "notifications.column_settings.poll": "투표 결과:",
   "notifications.column_settings.push": "푸시 알림",
   "notifications.column_settings.reblog": "부스트:",
@@ -456,7 +462,7 @@
   "privacy.public.long": "모두가 볼 수 있음",
   "privacy.public.short": "공개",
   "privacy.unlisted.long": "모두가 볼 수 있지만, 발견하기 기능에서는 제외됨",
-  "privacy.unlisted.short": "타임라인에 비표시",
+  "privacy.unlisted.short": "미등재",
   "privacy_policy.last_updated": "{date}에 마지막으로 업데이트됨",
   "privacy_policy.title": "개인정보 정책",
   "refresh": "새로고침",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "스팸",
   "report_notification.categories.violation": "규칙 위반",
   "report_notification.open": "신고 열기",
+  "search.no_recent_searches": "최근 검색 기록이 없습니다",
   "search.placeholder": "검색",
+  "search.quick_action.account_search": "{x}에 맞는 프로필",
+  "search.quick_action.go_to_account": "{x} 프로필로 이동",
+  "search.quick_action.go_to_hashtag": "{x} 해시태그로 이동",
+  "search.quick_action.open_url": "마스토돈에서 URL 열기",
+  "search.quick_action.status_search": "{x}에 맞는 게시물",
   "search.search_or_paste": "검색하거나 URL 붙여넣기",
-  "search_popout.search_format": "고급 검색 방법",
-  "search_popout.tips.full_text": "단순한 텍스트 검색은 당신이 작성했거나, 관심글로 지정했거나, 부스트했거나, 멘션을 받은 게시글, 그리고 사용자명, 표시되는 이름, 해시태그를 반환합니다.",
-  "search_popout.tips.hashtag": "해시태그",
-  "search_popout.tips.status": "게시물",
-  "search_popout.tips.text": "단순한 텍스트 검색은 관계된 프로필 이름, 사용자명 그리고 해시태그를 표시합니다",
-  "search_popout.tips.user": "사용자",
-  "search_results.accounts": "사람",
+  "search_popout.quick_actions": "빠른 작업",
+  "search_popout.recent": "최근 검색",
+  "search_results.accounts": "프로필",
   "search_results.all": "전부",
   "search_results.hashtags": "해시태그",
   "search_results.nothing_found": "검색어에 대한 결과를 찾을 수 없습니다",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "서버 통계:",
   "sign_in_banner.create_account": "계정 생성",
   "sign_in_banner.sign_in": "로그인",
-  "sign_in_banner.text": "로그인을 통해 프로필이나 해시태그를 팔로우하거나 마음에 들어하거나 공유하고 답글을 달 수 있습니다, 혹은 다른 서버에 있는 본인의 계정을 통해 참여할 수도 있습니다.",
+  "sign_in_banner.text": "로그인을 통해 프로필이나 해시태그를 팔로우하거나 마음에 들어하거나 공유하고 답글을 달 수 있습니다. 다른 서버에 있는 본인의 계정을 통해 참여할 수도 있습니다.",
   "status.admin_account": "@{name}에 대한 중재 화면 열기",
   "status.admin_domain": "{domain}에 대한 중재 화면 열기",
   "status.admin_status": "중재 화면에서 이 게시물 열기",
@@ -551,7 +559,8 @@
   "status.copy": "게시물 링크 복사",
   "status.delete": "삭제",
   "status.detailed_status": "대화 자세히 보기",
-  "status.direct": "@{name}에게 다이렉트 메시지",
+  "status.direct": "@{name} 님에게 개인적으로 멘션",
+  "status.direct_indicator": "개인적인 멘션",
   "status.edit": "수정",
   "status.edited": "{date}에 편집됨",
   "status.edited_x_times": "{count}번 수정됨",
@@ -564,7 +573,7 @@
   "status.history.edited": "{name} 님이 {date}에 수정함",
   "status.load_more": "더 보기",
   "status.media_hidden": "미디어 숨겨짐",
-  "status.mention": "@{name} 님에게 글 쓰기",
+  "status.mention": "@{name}님에게 멘션",
   "status.more": "자세히",
   "status.mute": "@{name} 님을 뮤트하기",
   "status.mute_conversation": "이 대화를 뮤트",
@@ -582,16 +591,16 @@
   "status.reply": "답장",
   "status.replyAll": "글타래에 답장",
   "status.report": "{name} 님을 신고하기",
-  "status.sensitive_warning": "민감한 미디어",
+  "status.sensitive_warning": "민감한 내용",
   "status.share": "공유",
   "status.show_filter_reason": "그냥 표시하기",
-  "status.show_less": "숨기기",
+  "status.show_less": "접기",
   "status.show_less_all": "모두 접기",
-  "status.show_more": "더 보기",
+  "status.show_more": "펼치기",
   "status.show_more_all": "모두 펼치기",
   "status.show_original": "원본 보기",
   "status.translate": "번역",
-  "status.translated_from_with": "{lang}에서 {provider}를 사용해 번역됨",
+  "status.translated_from_with": "{provider}에 의해 {lang}에서 번역됨",
   "status.uncached_media_warning": "사용할 수 없음",
   "status.unmute_conversation": "이 대화의 뮤트 해제하기",
   "status.unpin": "고정 해제",
@@ -614,7 +623,7 @@
   "timeline_hint.resources.follows": "팔로우",
   "timeline_hint.resources.statuses": "이전 게시물",
   "trends.counter_by_accounts": "이전 {days}일 동안 {counter} 명의 사용자",
-  "trends.trending_now": "지금 유행중",
+  "trends.trending_now": "지금 유행 중",
   "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.",
   "units.short.billion": "{count}B",
   "units.short.million": "{count}B",
diff --git a/app/javascript/mastodon/locales/ku.json b/app/javascript/mastodon/locales/ku.json
index 7dbb34591..12124a612 100644
--- a/app/javascript/mastodon/locales/ku.json
+++ b/app/javascript/mastodon/locales/ku.json
@@ -20,7 +20,7 @@
   "account.blocked": "Astengkirî",
   "account.browse_more_on_origin_server": "Li pelên resen bêtir bigere",
   "account.cancel_follow_request": "Daxwaza şopandinê vekişîne",
-  "account.direct": "Peyamekê bişîne @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Êdî min agahdar neke gava @{name} diweşîne",
   "account.domain_blocked": "Navper hate astengkirin",
   "account.edit_profile": "Profîlê serrast bike",
@@ -102,7 +102,7 @@
   "column.blocks": "Bikarhênerên astengkirî",
   "column.bookmarks": "Şûnpel",
   "column.community": "Demnameya herêmî",
-  "column.direct": "Peyamên rasterast",
+  "column.direct": "Private mentions",
   "column.directory": "Li profîlan bigere",
   "column.domain_blocks": "Navperên astengkirî",
   "column.favourites": "Bijarte",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Guhertinên neqedandî di danasîna an pêşdîtina medyayê de hene, wan bi her awayî bavêje?",
   "confirmations.domain_block.confirm": "Tevahiya navperê asteng bike",
   "confirmations.domain_block.message": "Tu pê bawerî ku tu dixwazî tevahiya {domain} asteng bikî? Di gelek rewşan de astengkirin an jî bêdengkirin têrê dike û tê hilbijartin. Tu nikarî naveroka vê navperê di demnameyê an jî agahdariyên xwe de bibînî. Şopînerên te yê di vê navperê wê werin jêbirin.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Derkeve",
   "confirmations.logout.message": "Ma tu dixwazî ku derkevî?",
   "confirmations.mute.confirm": "Bêdeng bike",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Te tu bikarhêner asteng nekiriye.",
   "empty_column.bookmarked_statuses": "Hîn tu peyamên te yên şûnpelkirî tune ne. Dema ku tu yekî şûnpel bikî, ew ê li vir xuya bibe.",
   "empty_column.community": "Demnameya herêmî vala ye. Tiştek ji raya giştî re binivsînin da ku rûpel biherike!",
-  "empty_column.direct": "Hîn peyamên te yên rasterast tune ne. Dema ku tu yekî bişînî an jî wergirî, ew ê li vir xuya bibe.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Hîn tu navperên ku hatine astengkirin tune ne.",
   "empty_column.explore_statuses": "Tiştek niha di rojevê de tune. Paşê vegere!",
   "empty_column.favourited_statuses": "Hîn tu peyamên te yên bijare tunene. Gava ku te yekî bijart, ew ê li vir xûya bike.",
   "empty_column.favourites": "Hîn tu kes vê peyamê nebijartiye. Gava ku hin kes bijartin, ew ê li vir xûya bikin.",
   "empty_column.follow_recommendations": "Wusa dixuye ku ji bo we tu pêşniyar nehatine çêkirin. Hûn dikarin lêgerînê bikarbînin da ku li kesên ku hûn nas dikin bigerin an hashtagên trendî bigerin.",
   "empty_column.follow_requests": "Hê jî daxwaza şopandinê tunne ye. Dema daxwazek hat, yê li vir were nîşan kirin.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Di vê hashtagê de hêj tiştekî tune.",
   "empty_column.home": "Rojeva demnameya te vala ye! Ji bona tijîkirinê bêtir mirovan bişopîne. {suggestions}",
   "empty_column.home.suggestions": "Hinek pêşniyaran bibîne",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Mafê bide",
   "follow_request.reject": "Nepejirîne",
   "follow_requests.unlocked_explanation": "Tevlî ku ajimêra te ne kilît kiriye, karmendên {domain} digotin qey tu dixwazî ku pêşdîtina daxwazên şopandinê bi destan bike.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "Derbar",
   "footer.directory": "Pelrêça profîlan",
   "footer.get_app": "Bernamokê bistîne",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Kurteriyên klavyeyê",
   "footer.privacy_policy": "Peymana nepeniyê",
   "footer.source_code": "Koda çavkanî nîşan bide",
+  "footer.status": "Status",
   "generic.saved": "Tomarkirî",
   "getting_started.heading": "Destpêkirin",
   "hashtag.column_header.tag_mode.all": "û {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Stûna balkişandinê",
   "keyboard_shortcuts.compose": "Bal bikşîne cîhê nivîsê/textarea",
   "keyboard_shortcuts.description": "Danasîn",
-  "keyboard_shortcuts.direct": "ji bo vekirina stûnê peyamên rasterast",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Di lîsteyê de dakêşe jêr",
   "keyboard_shortcuts.enter": "Şandiyê veke",
   "keyboard_shortcuts.favourite": "Şandiya bijarte",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Şûnpel",
   "navigation_bar.community_timeline": "Demnameya herêmî",
   "navigation_bar.compose": "Şandiyeke nû binivsîne",
-  "navigation_bar.direct": "Peyamên rasterast",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Vekolê",
   "navigation_bar.domain_blocks": "Navperên astengkirî",
   "navigation_bar.edit_profile": "Profîlê serrast bike",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Bijarte",
   "navigation_bar.filters": "Peyvên bêdengkirî",
   "navigation_bar.follow_requests": "Daxwazên şopandinê",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Şopandin û şopîner",
   "navigation_bar.lists": "Lîste",
   "navigation_bar.logout": "Derkeve",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Nexwestî (Spam)",
   "report_notification.categories.violation": "Binpêkirina rêzîkê",
   "report_notification.open": "Ragihandinê veke",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Bigere",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Bigere yan jî girêdanê pêve bike",
-  "search_popout.search_format": "Dirûva lêgerîna pêşketî",
-  "search_popout.tips.full_text": "Nivîsên hêsan, şandiyên ku te nivîsandiye, bijare kiriye, bilind kiriye an jî yên behsa te kirine û her wiha navê bikarhêneran, navên xûya dike û hashtagan vedigerîne.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "şandî",
-  "search_popout.tips.text": "Nivîsên hêsan, navên xûya ên ku li hev hatî, bikarhêner û hashtagan vedigerîne",
-  "search_popout.tips.user": "bikarhêner",
-  "search_results.accounts": "Mirov",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Hemû",
   "search_results.hashtags": "Hashtag",
   "search_results.nothing_found": "Ji bo van peyvên lêgerînê tiştek nehate dîtin",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Amarên rajekar:",
   "sign_in_banner.create_account": "Ajimêr biafirîne",
   "sign_in_banner.sign_in": "Têkeve",
-  "sign_in_banner.text": "Têkeve ji bo şopandina profîlan an hashtagan, bijartekirin, parvekirin û bersivdana şandiyan, an ji ajimêrê xwe li ser rajekarek cuda têkilî deyine.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Ji bo @{name} navrûya venihêrtinê veke",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Vê şandîyê di navrûya venihêrtinê de veke",
@@ -551,7 +559,8 @@
   "status.copy": "Girêdanê jê bigire bo weşankirinê",
   "status.delete": "Jê bibe",
   "status.detailed_status": "Dîtina axaftina berfireh",
-  "status.direct": "Peyama rasterast @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Serrast bike",
   "status.edited": "Di {date} de hate serrastkirin",
   "status.edited_x_times": "{count, plural, one {{count} car} other {{count} car}} hate serrastkirin",
diff --git a/app/javascript/mastodon/locales/kw.json b/app/javascript/mastodon/locales/kw.json
index f56974d2b..b20f93e54 100644
--- a/app/javascript/mastodon/locales/kw.json
+++ b/app/javascript/mastodon/locales/kw.json
@@ -1,5 +1,5 @@
 {
-  "about.blocks": "Moderated servers",
+  "about.blocks": "Leurennow koswys",
   "about.contact": "Contact:",
   "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Reason not available",
@@ -20,7 +20,7 @@
   "account.blocked": "Lettys",
   "account.browse_more_on_origin_server": "Peuri moy y'n profil derowel",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Messach didro dhe @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Hedhi ow gwarnya pan wra @{name} postya",
   "account.domain_blocked": "Gorfarth lettys",
   "account.edit_profile": "Golegi profil",
@@ -102,7 +102,7 @@
   "column.blocks": "Devnydhyoryon lettys",
   "column.bookmarks": "Folennosow",
   "column.community": "Amserlin leel",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Peuri profilys",
   "column.domain_blocks": "Gorfarthow lettys",
   "column.favourites": "Re drudh",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Lettya gorfarth dhien",
   "confirmations.domain_block.message": "Owgh hwi wir, wir sur a vynnes lettya'n {domain} dhien? Y'n brassa rann a gasow, boghes lettyansow medrys po tawheansow yw lowr ha gwell. Ny wrewgh hwi gweles dalgh a'n worfarth na yn py amserlin boblek pynag po yn agas gwarnyansow. Agas holyoryon an worfarth na a vydh diles.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Digelmi",
   "confirmations.logout.message": "Owgh hwi sur a vynnes digelmi?",
   "confirmations.mute.confirm": "Tawhe",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Ny wrussowgh lettya devnydhyoryon vyth hwath.",
   "empty_column.bookmarked_statuses": "Nyns eus dhywgh postow gans folennos hwath. Pan wrewgh gorra onan, ev a wra omdhiskwedhes omma.",
   "empty_column.community": "An amserlin leel yw gwag. Skrifewgh neppytn yn poblek dh'y lonchya!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Nyns eus gorfarthow lettys hwath.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "Nyns eus dhywgh postow drudh hwath. Pan wrewgh merkya onan vel drudh, ev a wra omdhiskwedhes omma.",
   "empty_column.favourites": "Ny wrug nagonan merkya'n post ma vel drudh hwath. Pan wra, hynn a wra omdhiskwedhes omma.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "Nyns eus dhywgh govynnow holya hwath. Pan wrewgh degemeres onan, ev a wra omdhiskwedhes omma.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Nyns eus travyth y'n bòlnos ma hwath.",
   "empty_column.home": "Agas amserlin dre yw gwag! Holyewgh moy a dus dh'y lenwel. {suggestions}",
   "empty_column.home.suggestions": "Gweles profyansow",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Ri kummyas",
   "follow_request.reject": "Denagha",
   "follow_requests.unlocked_explanation": "Kyn na vo agas akont alhwedhys, an meni {domain} a wrug tybi y fia da genowgh dasweles govynnow holya a'n akontys ma dre leuv.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Gwithys",
   "getting_started.heading": "Dhe dhalleth",
   "hashtag.column_header.tag_mode.all": "ha(g) {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Folennosow",
   "navigation_bar.community_timeline": "Amserlin leel",
   "navigation_bar.compose": "Komposya post nowydh",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Diskudha",
   "navigation_bar.domain_blocks": "Gorfarthow lettys",
   "navigation_bar.edit_profile": "Golegi profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Re drudh",
   "navigation_bar.filters": "Geryow tawhes",
   "navigation_bar.follow_requests": "Govynnow holya",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Holyansow ha holyoryon",
   "navigation_bar.lists": "Rolyow",
   "navigation_bar.logout": "Digelmi",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Hwilas",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "Furvas hwilas avonsys",
-  "search_popout.tips.full_text": "Tekst sempel a wra daskor postow a wrussowgh aga skrifa, merkya vel drudh, po bos menegys ynna, keffrys ha henwyn devnydhyoryon ha displetyans, ha bòlnosow a dhesedh.",
-  "search_popout.tips.hashtag": "bòlnos",
-  "search_popout.tips.status": "post",
-  "search_popout.tips.text": "Tekst sempel a wra daskor henwyn displegya ha devnydhyoryon, ha bòlnosow",
-  "search_popout.tips.user": "devnydhyer",
-  "search_results.accounts": "Tus",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Bòlnosow",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Ygeri ynterfas koswa rag @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Ygeri an post ma y'n ynterfas koswa",
@@ -551,7 +559,8 @@
   "status.copy": "Dasskrifa kolm dhe'n post",
   "status.delete": "Dilea",
   "status.detailed_status": "Gwel kesklapp a-vanyl",
-  "status.direct": "Messach didro dhe @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/la.json b/app/javascript/mastodon/locales/la.json
index 1a54cc90d..00de10f73 100644
--- a/app/javascript/mastodon/locales/la.json
+++ b/app/javascript/mastodon/locales/la.json
@@ -2,7 +2,7 @@
   "about.blocks": "Moderated servers",
   "about.contact": "Ratio:",
   "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
-  "about.domain_blocks.no_reason_available": "Reason not available",
+  "about.domain_blocks.no_reason_available": "ratio abdere est",
   "about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",
   "about.domain_blocks.silenced.explanation": "You will generally not see profiles and content from this server, unless you explicitly look it up or opt into it by following.",
   "about.domain_blocks.silenced.title": "Limited",
@@ -20,7 +20,7 @@
   "account.blocked": "Impeditum est",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Dominium impeditum",
   "account.edit_profile": "Recolere notionem",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Signa paginales",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "Dilecti",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "Confutare",
@@ -197,7 +199,7 @@
   "emoji_button.clear": "Clear",
   "emoji_button.custom": "Custom",
   "emoji_button.flags": "Flags",
-  "emoji_button.food": "Food & Drink",
+  "emoji_button.food": "cibus et potus",
   "emoji_button.label": "Insert emoji",
   "emoji_button.nature": "Nature",
   "emoji_button.not_found": "No matching emojis found",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
   "empty_column.favourites": "No one has favourited this post yet. When someone does, they will show up here.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,7 +274,8 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
-  "generic.saved": "Saved",
+  "footer.status": "Status",
+  "generic.saved": "servavit",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "and {additional}",
   "hashtag.column_header.tag_mode.any": "or {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new post",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lists",
   "navigation_bar.logout": "Logout",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Quaerere",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Oblitterare",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Recolere",
   "status.edited": "Recultum {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/locales/locale-data/README.md b/app/javascript/mastodon/locales/locale-data/README.md
index 83368fae7..83368fae7 100644
--- a/app/javascript/locales/locale-data/README.md
+++ b/app/javascript/mastodon/locales/locale-data/README.md
diff --git a/app/javascript/locales/locale-data/oc.js b/app/javascript/mastodon/locales/locale-data/oc.js
index d4adc42eb..d4adc42eb 100644
--- a/app/javascript/locales/locale-data/oc.js
+++ b/app/javascript/mastodon/locales/locale-data/oc.js
diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json
index 1d4923254..d1da039df 100644
--- a/app/javascript/mastodon/locales/lt.json
+++ b/app/javascript/mastodon/locales/lt.json
@@ -1,7 +1,7 @@
 {
   "about.blocks": "Moderatorių prižiūrimi serveriai",
   "about.contact": "Kontaktai:",
-  "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
+  "about.disclaimer": "Mastodon, tai nemokama, atviro kodo programa, kuriuos prekybinis ženklas priklauso Mastodon GmbH.",
   "about.domain_blocks.no_reason_available": "Priežastis nežinoma",
   "about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",
   "about.domain_blocks.silenced.explanation": "You will generally not see profiles and content from this server, unless you explicitly look it up or opt into it by following.",
@@ -20,7 +20,7 @@
   "account.blocked": "Užblokuota",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Domain hidden",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Hidden domains",
   "column.favourites": "Mėgstamiausi",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "Mute",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "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_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lists",
   "navigation_bar.logout": "Logout",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/lv.json b/app/javascript/mastodon/locales/lv.json
index a593ab501..4f1032ae2 100644
--- a/app/javascript/mastodon/locales/lv.json
+++ b/app/javascript/mastodon/locales/lv.json
@@ -12,7 +12,7 @@
   "about.powered_by": "Decentralizētu sociālo tīklu nodrošina {mastodon}",
   "about.rules": "Servera noteikumi",
   "account.account_note_header": "Piezīme",
-  "account.add_or_remove_from_list": "Pievienot vai noņemt no saraksta",
+  "account.add_or_remove_from_list": "Pievienot vai Noņemt no sarakstiem",
   "account.badges.bot": "Robots",
   "account.badges.group": "Grupa",
   "account.block": "Bloķēt @{name}",
@@ -20,7 +20,7 @@
   "account.blocked": "Bloķēts",
   "account.browse_more_on_origin_server": "Pārlūkot vairāk sākotnējā profilā",
   "account.cancel_follow_request": "Atsaukt sekošanas pieprasījumu",
-  "account.direct": "Privāta ziņa @{name}",
+  "account.direct": "Pieminēt @{name} privāti",
   "account.disable_notifications": "Pārtraukt man paziņot, kad @{name} publicē ierakstu",
   "account.domain_blocked": "Domēns ir bloķēts",
   "account.edit_profile": "Rediģēt profilu",
@@ -32,7 +32,7 @@
   "account.follow": "Sekot",
   "account.followers": "Sekotāji",
   "account.followers.empty": "Šim lietotājam vēl nav sekotāju.",
-  "account.followers_counter": "{count, plural, zero {{counter} sekotāju} one {{counter} sekotājs} other {{counter} sekotāji}}",
+  "account.followers_counter": "{count, plural, one {{counter} Sekotājs} other {{counter} Sekotāji}}",
   "account.following": "Seko",
   "account.following_counter": "{count, plural, one {{counter} sekojamais} other {{counter} sekojamie}}",
   "account.follows.empty": "Šis lietotājs pagaidām nevienam neseko.",
@@ -102,10 +102,10 @@
   "column.blocks": "Bloķētie lietotāji",
   "column.bookmarks": "Grāmatzīmes",
   "column.community": "Vietējā laika līnija",
-  "column.direct": "Privātie ziņojumi",
+  "column.direct": "Privāti pieminēti",
   "column.directory": "Pārlūkot profilus",
   "column.domain_blocks": "Bloķētie domēni",
-  "column.favourites": "Izlase",
+  "column.favourites": "Izlases",
   "column.follow_requests": "Sekošanas pieprasījumi",
   "column.home": "Sākums",
   "column.lists": "Saraksti",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Tev ir nesaglabātas izmaiņas multivides aprakstā vai priekšskatījumā. Vēlies tās atmest?",
   "confirmations.domain_block.confirm": "Bloķēt visu domēnu",
   "confirmations.domain_block.message": "Vai tu tiešām vēlies bloķēt visu domēnu {domain}? Parasti pietiek, ja nobloķē vai apklusini kādu. Tu neredzēsi saturu vai paziņojumus no šī domēna nevienā laika līnijā. Tavi sekotāji no šī domēna tiks noņemti.",
+  "confirmations.edit.confirm": "Rediģēt",
+  "confirmations.edit.message": "Rediģējot, tiks pārrakstīts ziņojums, kuru tu šobrīd raksti. Vai tiešām vēlies turpināt?",
   "confirmations.logout.confirm": "Iziet",
   "confirmations.logout.message": "Vai tiešām vēlies izrakstīties?",
   "confirmations.mute.confirm": "Apklusināt",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Pašreiz tu neesi nevienu bloķējis.",
   "empty_column.bookmarked_statuses": "Pašreiz tev nav neviena grāmatzīmēm pievienota ieraksta. Kad tādu pievienosi, tas parādīsies šeit.",
   "empty_column.community": "Vietējā laika līnija ir tukša. Uzraksti kaut ko publiski, lai viss notiktu!",
-  "empty_column.direct": "Pašreiz tev nav privātu ziņu. Tiklīdz tādu nosūtīsi vai saņemsi, tās parādīsies šeit.",
+  "empty_column.direct": "Jums vēl nav nevienas privātas pieminēšanas. Nosūtot vai saņemot to, tas tiks parādīts šeit.",
   "empty_column.domain_blocks": "Vēl nav neviena bloķēta domēna.",
   "empty_column.explore_statuses": "Pašlaik nekā aktuāla nav. Pārbaudi vēlāk!",
-  "empty_column.favourited_statuses": "Pašreiz tev nav neviena izceltā ieraksta. Kad kādu izcelsi, tas parādīsies šeit.",
-  "empty_column.favourites": "Neviens šo ziņojumu vel nav izcēlis. Kad kāds to izdarīs, tas parādīsies šeit.",
+  "empty_column.favourited_statuses": "Tev vēl nav nevienas iecienītākās ziņas. Kad pievienosi kādu izlasei, tas tiks parādīts šeit.",
+  "empty_column.favourites": "Šo ziņu neviens vēl nav pievienojis izlasei. Kad kāds to izdarīs, tas parādīsies šeit.",
   "empty_column.follow_recommendations": "Neizdevās ģenerēt tev pielāgotus ieteikumus. Vari mēģināt izmantot meklēšanu, lai meklētu cilvēkus, kurus tu varētu pazīt, vai izpētīt populārākos tēmturus.",
   "empty_column.follow_requests": "Šobrīd tev nav sekošanas pieprasījumu. Kad kāds pieteiksies tev sekot, pieprasījums parādīsies šeit.",
+  "empty_column.followed_tags": "Tu vēl neesi sekojis nevienam tēmturim. Kad to izdarīsi, tie tiks parādīti šeit.",
   "empty_column.hashtag": "Ar šo tēmturi nekas nav atrodams.",
   "empty_column.home": "Tava mājas laika līnija ir tukša! Lai to aizpildītu, pieseko vairāk cilvēkiem. {suggestions}",
   "empty_column.home.suggestions": "Apskatīt dažus ieteikumus",
@@ -257,12 +260,13 @@
   "filter_modal.select_filter.subtitle": "Izmanto esošu kategoriju vai izveido jaunu",
   "filter_modal.select_filter.title": "Filtrēt šo ziņu",
   "filter_modal.title.status": "Filtrēt ziņu",
-  "follow_recommendations.done": "Darīts",
+  "follow_recommendations.done": "Izpildīts",
   "follow_recommendations.heading": "Seko cilvēkiem, no kuriem vēlies redzēt ziņas! Šeit ir daži ieteikumi.",
   "follow_recommendations.lead": "Ziņas no cilvēkiem, kuriem seko, mājas plūsmā tiks parādītas hronoloģiskā secībā. Nebaidies kļūdīties, tu tikpat viegli vari pārtraukt sekot cilvēkiem jebkurā laikā!",
   "follow_request.authorize": "Autorizēt",
   "follow_request.reject": "Noraidīt",
   "follow_requests.unlocked_explanation": "Lai gan tavs konts nav bloķēts, {domain} darbinieki iedomājās, ka, iespējams, vēlēsies pārskatīt pieprasījumus no šiem kontiem.",
+  "followed_tags": "Sekojamie tēmturi",
   "footer.about": "Par",
   "footer.directory": "Profilu direktorija",
   "footer.get_app": "Iegūt lietotni",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Īsinājumtaustiņi",
   "footer.privacy_policy": "Privātuma politika",
   "footer.source_code": "Skatīt pirmkodu",
+  "footer.status": "Statuss",
   "generic.saved": "Saglabāts",
   "getting_started.heading": "Darba sākšana",
   "hashtag.column_header.tag_mode.all": "un {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Fokusēt kolonnu",
   "keyboard_shortcuts.compose": "Fokusēt veidojamā teksta lauku",
   "keyboard_shortcuts.description": "Apraksts",
-  "keyboard_shortcuts.direct": "lai atvērtu privāto ziņojumu kolonnu",
+  "keyboard_shortcuts.direct": "lai atvērtu privāto pieminējumu sleju",
   "keyboard_shortcuts.down": "Pārvietoties lejup sarakstā",
   "keyboard_shortcuts.enter": "Atvērt ziņu",
   "keyboard_shortcuts.favourite": "Pievienot izlasei",
@@ -321,7 +326,7 @@
   "keyboard_shortcuts.legend": "Parādīt šo leģendu",
   "keyboard_shortcuts.local": "Atvērt vietējo laika līniju",
   "keyboard_shortcuts.mention": "Pieminēt autoru",
-  "keyboard_shortcuts.muted": "Atvērt apklusināto lietotāju sarakstu",
+  "keyboard_shortcuts.muted": "Atvērt noklusināto lietotāju sarakstu",
   "keyboard_shortcuts.my_profile": "Atvērt savu profilu",
   "keyboard_shortcuts.notifications": "Atvērt paziņojumu kolonnu",
   "keyboard_shortcuts.open_media": "Atvērt multividi",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Grāmatzīmes",
   "navigation_bar.community_timeline": "Vietējā laika līnija",
   "navigation_bar.compose": "Veidot jaunu ziņu",
-  "navigation_bar.direct": "Privātie ziņojumi",
+  "navigation_bar.direct": "Privāti pieminēti",
   "navigation_bar.discover": "Atklāt",
   "navigation_bar.domain_blocks": "Bloķētie domēni",
   "navigation_bar.edit_profile": "Rediģēt profilu",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Izlases",
   "navigation_bar.filters": "Apklusinātie vārdi",
   "navigation_bar.follow_requests": "Sekošanas pieprasījumi",
+  "navigation_bar.followed_tags": "Sekojamie tēmturi",
   "navigation_bar.follows_and_followers": "Sekojamie un sekotāji",
   "navigation_bar.lists": "Saraksti",
   "navigation_bar.logout": "Iziet",
@@ -392,7 +398,7 @@
   "not_signed_in_indicator.not_signed_in": "Lai piekļūtu šim resursam, tev ir jāpierakstās.",
   "notification.admin.report": "{name} sūdzējās par {target}",
   "notification.admin.sign_up": "{name} pierakstījās",
-  "notification.favourite": "{name} patika tava ziņa",
+  "notification.favourite": "{name} pievienoja tavu ziņu izlasei",
   "notification.follow": "{name} uzsāka tev sekot",
   "notification.follow_request": "{name} nosūtīja tev sekošanas pieprasījumu",
   "notification.mention": "{name} pieminēja tevi",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spams",
   "report_notification.categories.violation": "Noteikumu pārkāpums",
   "report_notification.open": "Atvērt sūdzību",
+  "search.no_recent_searches": "Nav nesen veiktu meklējumu",
   "search.placeholder": "Meklēšana",
+  "search.quick_action.account_search": "Profili atbilst {x}",
+  "search.quick_action.go_to_account": "Doties uz profilu {x}",
+  "search.quick_action.go_to_hashtag": "Doties uz tēmturi {x}",
+  "search.quick_action.open_url": "Atvērt URL Mastodonā",
+  "search.quick_action.status_search": "Ziņas atbilst {x}",
   "search.search_or_paste": "Meklē vai iekopē URL",
-  "search_popout.search_format": "Paplašināts meklēšanas formāts",
-  "search_popout.tips.full_text": "Vienkāršs teksts atgriež ierakstus, kurus rakstīji, atzīmēji kā favorītus, pastiprināji vai pieminēji, kā arī atbilstošus lietotājvārdus, parādāmos vārdus un tēmturus.",
-  "search_popout.tips.hashtag": "tēmturis",
-  "search_popout.tips.status": "ieraksts",
-  "search_popout.tips.text": "Vienkāršs teksts atgriež atbilstošus parādāmos vārdus, lietotājvārdus un tēmturus",
-  "search_popout.tips.user": "lietotājs",
-  "search_results.accounts": "Cilvēki",
+  "search_popout.quick_actions": "Ātrās darbības",
+  "search_popout.recent": "Nesen meklētais",
+  "search_results.accounts": "Profili",
   "search_results.all": "Visi",
   "search_results.hashtags": "Tēmturi",
   "search_results.nothing_found": "Nevarēja atrast neko šiem meklēšanas vienumiem",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Servera statistika:",
   "sign_in_banner.create_account": "Izveidot kontu",
   "sign_in_banner.sign_in": "Pierakstīties",
-  "sign_in_banner.text": "Pieraksties, lai sekotu profiliem vai atsaucēm, pievienotu ziņas izlasei, kopīgotu ziņas un atbildētu uz tām vai mijiedarbotos no sava konta citā serverī.",
+  "sign_in_banner.text": "Pierakstieties, lai sekotu profiliem vai atsaucēm, pievienotu izlasei, kopīgotu ziņas un atbildētu uz tām. Varat arī mijiedarboties no sava konta citā serverī.",
   "status.admin_account": "Atvērt @{name} moderēšanas saskarni",
   "status.admin_domain": "Atvērt {domain} moderēšanas saskarni",
   "status.admin_status": "Atvērt šo ziņu moderācijas saskarnē",
@@ -551,7 +559,8 @@
   "status.copy": "Kopēt saiti uz ziņu",
   "status.delete": "Dzēst",
   "status.detailed_status": "Detalizēts sarunas skats",
-  "status.direct": "Privāta ziņa @{name}",
+  "status.direct": "Pieminēt @{name} privāti",
+  "status.direct_indicator": "Pieminēts privāti",
   "status.edit": "Rediģēt",
   "status.edited": "Rediģēts {date}",
   "status.edited_x_times": "Rediģēts {count, plural, one {{count} reizi} other {{count} reizes}}",
diff --git a/app/javascript/mastodon/locales/mk.json b/app/javascript/mastodon/locales/mk.json
index 62be86557..c3778501b 100644
--- a/app/javascript/mastodon/locales/mk.json
+++ b/app/javascript/mastodon/locales/mk.json
@@ -20,7 +20,7 @@
   "account.blocked": "Блокиран",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Директна порана @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Скриен домен",
   "account.edit_profile": "Измени профил",
@@ -102,7 +102,7 @@
   "column.blocks": "Блокирани корисници",
   "column.bookmarks": "Bookmarks",
   "column.community": "Локална временска зона",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Види профили",
   "column.domain_blocks": "Скриени домеини",
   "column.favourites": "Омилени",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Сокриј цел домеин",
   "confirmations.domain_block.message": "Дали скроз сте сигурни дека ќе блокирате сѐ од {domain}? Во повеќето случаеви неколку таргетирани блокирања или заќутувања се доволни и предложени. Нема да ја видите содржината од тој домеин во никој јавен времеплов или вашите нотификации. Вашите следбеници од тој домеин ќе бидат остранети.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Одјави се",
   "confirmations.logout.message": "Дали сте сигурни дека сакате да се одјавите?",
   "confirmations.mute.confirm": "Заќути",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Немате сеуште блокирано корисници.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "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.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Немате сокриени домеини уште.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "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_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Одобри",
   "follow_request.reject": "Одбиј",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Започни",
   "hashtag.column_header.tag_mode.all": "и {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Уреди профил",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Омилени",
   "navigation_bar.filters": "Замолќени зборови",
   "navigation_bar.follow_requests": "Следи покани",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Следења и следбеници",
   "navigation_bar.lists": "Листи",
   "navigation_bar.logout": "Одјави се",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Барај",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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.hashtag": "хештег",
-  "search_popout.tips.status": "состојба",
-  "search_popout.tips.text": "Прост текст враќа совпаднати имиња, корисници и хештагови",
-  "search_popout.tips.user": "user",
-  "search_results.accounts": "People",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ml.json b/app/javascript/mastodon/locales/ml.json
index ee33be4a4..cfea1b34e 100644
--- a/app/javascript/mastodon/locales/ml.json
+++ b/app/javascript/mastodon/locales/ml.json
@@ -20,7 +20,7 @@
   "account.blocked": "തടഞ്ഞു",
   "account.browse_more_on_origin_server": "യഥാർത്ഥ പ്രൊഫൈലിലേക്ക് പോവുക",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "@{name}-ന്(ക്ക്) നേരിട്ട് സന്ദേശം അയക്കുക",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "@{name} പോസ്റ്റുചെയ്യുന്നത് എന്നെ അറിയിക്കുന്നത് നിർത്തുക",
   "account.domain_blocked": "മേഖല തടഞ്ഞു",
   "account.edit_profile": "പ്രൊഫൈൽ തിരുത്തുക",
@@ -102,7 +102,7 @@
   "column.blocks": "തടയപ്പെട്ട ഉപയോക്താക്കൾ",
   "column.bookmarks": "ബുക്ക്മാർക്കുകൾ",
   "column.community": "പ്രാദേശികമായ സമയരേഖ",
-  "column.direct": "നേരിട്ടുള്ള സന്ദേശങ്ങൾ",
+  "column.direct": "Private mentions",
   "column.directory": "പ്രൊഫൈലുകൾ മറിച്ചുനോക്കുക",
   "column.domain_blocks": "മറയ്ക്കപ്പെട്ട മേഖലകൾ",
   "column.favourites": "പ്രിയപ്പെട്ടവ",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "മുഴുവൻ ഡൊമെയ്‌നും തടയുക",
   "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "പുറത്തുകടക്കുക",
   "confirmations.logout.message": "നിങ്ങൾക്ക് ലോഗ് ഔട്ട് ചെയ്യണമെന്ന് ഉറപ്പാണോ?",
   "confirmations.mute.confirm": "നിശ്ശബ്ദമാക്കുക",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "നിങ്ങൾ ഇതുവരെ ഒരു ഉപയോക്താക്കളെയും തടഞ്ഞിട്ടില്ല.",
   "empty_column.bookmarked_statuses": "നിങ്ങൾക് ഇതുവരെ അടയാളപ്പെടുത്തിയ ടൂട്ടുകൾ ഇല്ല. അടയാളപ്പെടുത്തിയാൽ അത് ഇവിടെ വരും.",
   "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.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "മറയ്ക്കപ്പെട്ടിരിക്കുന്ന മേഖലകൾ ഇതുവരെ ഇല്ല.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "നിങ്ങൾക്ക് ഇത് വരെ ഒരു പ്രിയപ്പെട്ട ടൂട്ടും ഇല്ല. നിങ്ങൾ അങ്ങനെ ഒന്ന് പ്രിയപ്പെടുന്ന പക്ഷം അതിവിടെ കാണപ്പെടുന്നതാണ്.",
   "empty_column.favourites": "ഇതുവരെ ആരും ഈ ടൂട്ട് പ്രിയപ്പെട്ടതായി അടയാളപ്പെടുത്തിയിട്ടില്ല. ആരെങ്കിലും അങ്ങനെ ചെയ്യുന്നപക്ഷം അതിവിടെ കാണപ്പെടുന്നതാണ്.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "ഈ ഹാഷ്‌ടാഗിൽ ഇതുവരെ ഒന്നുമില്ല.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "ചില നിർദ്ദേശങ്ങൾ കാണുക",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "ചുമതലപ്പെടുത്തുക",
   "follow_request.reject": "നിരസിക്കുക",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "സംരക്ഷിച്ചു",
   "getting_started.heading": "തുടക്കം കുറിക്കുക",
   "hashtag.column_header.tag_mode.all": "{additional} ഉം കൂടെ",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "ബുക്ക്മാർക്കുകൾ",
   "navigation_bar.community_timeline": "പ്രാദേശിക സമയരേഖ",
   "navigation_bar.compose": "പുതിയ ടൂട്ട് എഴുതുക",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "കണ്ടെത്തുക",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "പ്രൊഫൈൽ തിരുത്തുക",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "പ്രിയപ്പെട്ടവ",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "പിന്തുടരാനുള്ള അഭ്യർത്ഥനകൾ",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "ലിസ്റ്റുകൾ",
   "navigation_bar.logout": "ലോഗൗട്ട്",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "തിരയുക",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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": "ഹാഷ്ടാഗ്",
-  "search_popout.tips.status": "ടൂട്ട്",
-  "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
-  "search_popout.tips.user": "ഉപയോക്താവ്",
-  "search_results.accounts": "ആളുകൾ",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "ഹാഷ്ടാഗുകൾ",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "ടൂട്ടിലേക്ക് ലിങ്ക് പകർത്തുക",
   "status.delete": "മായ്ക്കുക",
   "status.detailed_status": "വിശദമായ സംഭാഷണ കാഴ്‌ച",
-  "status.direct": "@{name} ന് നേരിട്ട് മെസേജ് അയക്കുക",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/mr.json b/app/javascript/mastodon/locales/mr.json
index c0028e2d4..b778c704e 100644
--- a/app/javascript/mastodon/locales/mr.json
+++ b/app/javascript/mastodon/locales/mr.json
@@ -20,7 +20,7 @@
   "account.blocked": "ब्लॉक केले आहे",
   "account.browse_more_on_origin_server": "मूळ प्रोफाइलवर अधिक ब्राउझ करा",
   "account.cancel_follow_request": "फॉलो विनंती मागे घ्या",
-  "account.direct": "थेट संदेश @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "जेव्हा @{name} पोस्ट करतात तेव्हा मला सूचित करणे थांबवा",
   "account.domain_blocked": "Domain hidden",
   "account.edit_profile": "प्रोफाइल एडिट करा",
@@ -54,7 +54,7 @@
   "account.posts_with_replies": "Toots and replies",
   "account.report": "@{name} ची तक्रार करा",
   "account.requested": "Awaiting approval",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name} ने आपल्याला फॉलो करण्याची रिक्वेस्ट केली आहे",
   "account.share": "@{name} चे प्रोफाइल शेअर करा",
   "account.show_reblogs": "{name}चे सर्व बुस्ट्स दाखवा",
   "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}",
@@ -102,7 +102,7 @@
   "column.blocks": "ब्लॉक केलेले खातेधारक",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "गुप्त डोमेन्स",
   "column.favourites": "आवडते",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "संपूर्ण डोमेन लपवा",
   "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "तुमची खात्री आहे की तुम्ही लॉग आउट करू इच्छिता?",
   "confirmations.mute.confirm": "आवाज बंद करा",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no hidden domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "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_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "to focus a status in one of the columns",
   "keyboard_shortcuts.compose": "to focus the compose textarea",
   "keyboard_shortcuts.description": "वर्णन",
-  "keyboard_shortcuts.direct": "थेट संदेश स्तंभ उघडण्यासाठी",
+  "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",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lists",
   "navigation_bar.logout": "Logout",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ms.json b/app/javascript/mastodon/locales/ms.json
index 715c59f7e..456706ce6 100644
--- a/app/javascript/mastodon/locales/ms.json
+++ b/app/javascript/mastodon/locales/ms.json
@@ -20,7 +20,7 @@
   "account.blocked": "Disekat",
   "account.browse_more_on_origin_server": "Layari selebihnya di profil asal",
   "account.cancel_follow_request": "Menarik balik permintaan mengikut",
-  "account.direct": "Mesej terus @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Berhenti maklumkan saya apabila @{name} mengirim hantaran",
   "account.domain_blocked": "Domain disekat",
   "account.edit_profile": "Sunting profil",
@@ -102,7 +102,7 @@
   "column.blocks": "Pengguna yang disekat",
   "column.bookmarks": "Tanda buku",
   "column.community": "Garis masa tempatan",
-  "column.direct": "Mesej terus",
+  "column.direct": "Private mentions",
   "column.directory": "Layari profil",
   "column.domain_blocks": "Domain disekat",
   "column.favourites": "Kegemaran",
@@ -128,7 +128,7 @@
   "compose.language.search": "Cari bahasa...",
   "compose_form.direct_message_warning_learn_more": "Ketahui lebih lanjut",
   "compose_form.encryption_warning": "Hantaran pada Mastodon tidak disulitkan hujung ke hujung. Jangan berkongsi sebarang maklumat sensitif melalui Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "Hantaran ini tidak akan disenaraikan di bawah mana-mana tanda pagar kerana ia tidak tersenarai. Hanya hantaran awam sahaja boleh dicari menggunakan tanda pagar.",
   "compose_form.lock_disclaimer": "Akaun anda tidak {locked}. Sesiapa pun boleh mengikuti anda untuk melihat hantaran pengikut-sahaja anda.",
   "compose_form.lock_disclaimer.lock": "dikunci",
   "compose_form.placeholder": "Apakah yang sedang anda fikirkan?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Anda belum menyimpan perubahan pada penerangan atau pratonton media. Anda ingin membuangnya?",
   "confirmations.domain_block.confirm": "Sekat keseluruhan domain",
   "confirmations.domain_block.message": "Adakah anda betul-betul, sungguh-sungguh pasti anda ingin menyekat keseluruhan {domain}? Selalunya, beberapa sekatan atau pembisuan tersasar sudah memadai dan lebih diutamakan. Anda tidak akan nampak kandungan daripada domain tersebut di mana-mana garis masa awam mahupun pemberitahuan anda. Pengikut anda daripada domain tersebut juga akan dibuang.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log keluar",
   "confirmations.logout.message": "Adakah anda pasti anda ingin log keluar?",
   "confirmations.mute.confirm": "Bisukan",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Anda belum menyekat sesiapa.",
   "empty_column.bookmarked_statuses": "Anda belum ada hantaran yang ditanda buku. Apabila anda menanda buku sesuatu, ia akan muncul di sini.",
   "empty_column.community": "Garis masa tempatan kosong. Tulislah secara awam untuk memulakan sesuatu!",
-  "empty_column.direct": "Anda belum mempunyai mesej terus. Apabila anda menghantar atau menerima mesej, mesej-mesej akan dipaparkan di sini.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Belum ada domain yang disekat.",
   "empty_column.explore_statuses": "Tiada apa-apa yang sohor kini sekarang. Semaklah kemudian!",
   "empty_column.favourited_statuses": "Anda belum ada hantaran yang digemari. Apabila anda menggemari sesuatu, ia akan muncul di sini.",
   "empty_column.favourites": "Tiada sesiapa yang menggemari hantaran ini. Apabila ada yang menggemari, ia akan muncul di sini.",
   "empty_column.follow_recommendations": "Nampaknya tiada cadangan yang boleh dijana untuk anda. Anda boleh cuba gunakan gelintar untuk mencari orang yang anda mungkin kenal atau jelajahi tanda pagar sohor kini.",
   "empty_column.follow_requests": "Anda belum mempunyai permintaan ikutan. Ia akan terpapar di sini apabila ada nanti.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Belum ada apa-apa dengan tanda pagar ini.",
   "empty_column.home": "Garis masa laman utama anda kosong! Ikuti lebih ramai orang untuk mengisinya. {suggestions}",
   "empty_column.home.suggestions": "Lihat cadangan",
@@ -236,11 +239,11 @@
   "errors.unexpected_crash.copy_stacktrace": "Salin surih tindanan ke papan keratan",
   "errors.unexpected_crash.report_issue": "Laporkan masalah",
   "explore.search_results": "Hasil carian",
-  "explore.suggested_follows": "For you",
+  "explore.suggested_follows": "Untuk anda",
   "explore.title": "Terokai",
-  "explore.trending_links": "News",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "Hashtags",
+  "explore.trending_links": "Baru",
+  "explore.trending_statuses": "Hantar",
+  "explore.trending_tags": "-Hashtags",
   "filter_modal.added.context_mismatch_explanation": "Kumpulan penapis ini tidak terpakai pada konteks di mana anda mengakses hantaran ini. Jika anda ingin hantaran ini untuk ditapis dalam konteks ini juga, anda perlu menyunting penapis tersebut.",
   "filter_modal.added.context_mismatch_title": "Konteks tidak sepadan!",
   "filter_modal.added.expired_explanation": "Kumpulan filter ini telah tamat tempoh, anda perlu mengubah tarikh luput untuk melaksanakannya.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Benarkan",
   "follow_request.reject": "Tolak",
   "follow_requests.unlocked_explanation": "Walaupun akaun anda tidak dikunci, kakitangan {domain} merasakan anda mungkin ingin menyemak permintaan ikutan daripada akaun ini secara manual.",
+  "followed_tags": "Topik terpilih",
   "footer.about": "Perihal",
   "footer.directory": "Direktori profil",
   "footer.get_app": "Dapatkan app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Pintasan papan kekunci",
   "footer.privacy_policy": "Dasar privasi",
   "footer.source_code": "Lihat kod sumber",
+  "footer.status": "Status:",
   "generic.saved": "Disimpan",
   "getting_started.heading": "Mari bermula",
   "hashtag.column_header.tag_mode.all": "dan {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Tumpu pada lajur",
   "keyboard_shortcuts.compose": "to focus the compose textarea",
   "keyboard_shortcuts.description": "Keterangan",
-  "keyboard_shortcuts.direct": "untuk membuka lajur mesej terus",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "to move down in the list",
   "keyboard_shortcuts.enter": "Buka hantaran",
   "keyboard_shortcuts.favourite": "Hantaran kegemaran",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Tanda buku",
   "navigation_bar.community_timeline": "Garis masa tempatan",
   "navigation_bar.compose": "Karang hantaran baharu",
-  "navigation_bar.direct": "Mesej terus",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Teroka",
   "navigation_bar.domain_blocks": "Domain disekat",
   "navigation_bar.edit_profile": "Sunting profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Kegemaran",
   "navigation_bar.filters": "Perkataan yang dibisukan",
   "navigation_bar.follow_requests": "Permintaan ikutan",
+  "navigation_bar.followed_tags": "Ikuti hashtag",
   "navigation_bar.follows_and_followers": "Ikutan dan pengikut",
   "navigation_bar.lists": "Senarai",
   "navigation_bar.logout": "Log keluar",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Langgaran peraturan",
   "report_notification.open": "Buka laporan",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Cari",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Cari atau tampal URL",
-  "search_popout.search_format": "Format gelintar lanjutan",
-  "search_popout.tips.full_text": "Teks ringkas mengembalikan hantaran yang anda telah tulis, menggemari, menggalak, atau telah disebutkan, dan juga nama pengguna, nama paparan, dan tanda pagar yang dipadankan.",
-  "search_popout.tips.hashtag": "tanda pagar",
-  "search_popout.tips.status": "hantaran",
-  "search_popout.tips.text": "Teks ringkas mengembalikan nama paparan, nama pengguna dan tanda pagar yang sepadan",
-  "search_popout.tips.user": "pengguna",
-  "search_results.accounts": "Orang",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Semua",
   "search_results.hashtags": "Tanda pagar",
   "search_results.nothing_found": "Tidak dapat menemui apa-apa untuk istilah carian tersebut",
@@ -542,7 +550,7 @@
   "sign_in_banner.sign_in": "Daftar masuk",
   "sign_in_banner.text": "Daftar masuk untuk mengikut profil atau tanda pagar, menggemari, mengkongsi dan membalas kepada hantaran, atau berinteraksi daripada akaun anda pada pelayan lain.",
   "status.admin_account": "Buka antara muka penyederhanaan untuk @{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_domain": "antara muka penyederhanaan",
   "status.admin_status": "Buka hantaran ini dalam antara muka penyederhanaan",
   "status.block": "Sekat @{name}",
   "status.bookmark": "Tanda buku",
@@ -551,7 +559,8 @@
   "status.copy": "Salin pautan ke hantaran",
   "status.delete": "Padam",
   "status.detailed_status": "Paparan perbualan terperinci",
-  "status.direct": "Mesej terus @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Sunting",
   "status.edited": "Disunting {date}",
   "status.edited_x_times": "Disunting {count, plural, other {{count} kali}}",
diff --git a/app/javascript/mastodon/locales/my.json b/app/javascript/mastodon/locales/my.json
index 31e250047..197addd1b 100644
--- a/app/javascript/mastodon/locales/my.json
+++ b/app/javascript/mastodon/locales/my.json
@@ -1,305 +1,310 @@
 {
-  "about.blocks": "Moderated servers",
-  "about.contact": "Contact:",
-  "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
-  "about.domain_blocks.no_reason_available": "Reason not available",
-  "about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",
-  "about.domain_blocks.silenced.explanation": "You will generally not see profiles and content from this server, unless you explicitly look it up or opt into it by following.",
-  "about.domain_blocks.silenced.title": "Limited",
-  "about.domain_blocks.suspended.explanation": "No data from this server will be processed, stored or exchanged, making any interaction or communication with users from this server impossible.",
-  "about.domain_blocks.suspended.title": "Suspended",
-  "about.not_available": "This information has not been made available on this server.",
-  "about.powered_by": "Decentralized social media powered by {mastodon}",
-  "about.rules": "Server rules",
-  "account.account_note_header": "Note",
-  "account.add_or_remove_from_list": "Add or Remove from lists",
-  "account.badges.bot": "Bot",
-  "account.badges.group": "Group",
-  "account.block": "Block @{name}",
-  "account.block_domain": "Block domain {domain}",
-  "account.blocked": "Blocked",
-  "account.browse_more_on_origin_server": "Browse more on the original profile",
-  "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
-  "account.disable_notifications": "Stop notifying me when @{name} posts",
-  "account.domain_blocked": "Domain blocked",
+  "about.blocks": "ထိန်းချုပ်ထားသော ဆာဗာများ",
+  "about.contact": "ဆက်သွယ်ရန်:",
+  "about.disclaimer": "Mastodon သည် အခမဲ့ဖြစ်ပြီး open-source software နှင့် Mastodon gGmbH ၏ ကုန်အမှတ်တံဆိပ်တစ်ခုဖြစ်သည်။",
+  "about.domain_blocks.no_reason_available": "အကြောင်းပြချက်မရရှိပါ",
+  "about.domain_blocks.preamble": "Mastodon သည် ယေဘူယျအားဖြင့် သင့်အား အစုအဝေးရှိ အခြားဆာဗာများမှ အသုံးပြုသူများထံမှ အကြောင်းအရာများကို ကြည့်ရှုပြီး အပြန်အလှန် တုံ့ပြန်နိုင်စေပါသည်။ ဤအရာများသည် ဤအထူးဆာဗာတွင် ပြုလုပ်ထားသော ခြွင်းချက်ဖြစ်သည်။",
+  "about.domain_blocks.silenced.explanation": "ရှင်းရှင်းလင်းလင်း ရှာကြည့်ခြင်း သို့မဟုတ် လိုက်ကြည့်ခြင်းဖြင့် ၎င်းကို ရွေးချယ်ခြင်းမှလွဲ၍ ဤဆာဗာမှ ပရိုဖိုင်များနှင့် အကြောင်းအရာများကို ယေဘုယျအားဖြင့် သင်သည် မမြင်ရပါ။",
+  "about.domain_blocks.silenced.title": "ကန့်သတ်ထားသော",
+  "about.domain_blocks.suspended.explanation": "ဤဆာဗာမှ ဒေတာများကို စီမံဆောင်ရွက်ခြင်း၊ သိမ်းဆည်းခြင်း သို့မဟုတ် ဖလှယ်ခြင်း မပြုဘဲ၊ ဤဆာဗာမှ အသုံးပြုသူများနှင့် အပြန်အလှန် ဆက်သွယ်မှု သို့မဟုတ် ဆက်သွယ်မှုတို့ကို မဖြစ်နိုင်အောင် ပြုလုပ်ပေးမည်မဟုတ်ပါ။",
+  "about.domain_blocks.suspended.title": "ရပ်ဆိုင်းထားသည်။",
+  "about.not_available": "ဤအချက်အလက်ကို ဤဆာဗာတွင် မရရှိနိုင်ပါ။",
+  "about.powered_by": "{mastodon} မှ ဗဟိုချုပ်ကိုင်မှုလျှော့ချထားသော ဆိုရှယ်မီဒီယာ",
+  "about.rules": "ဆာဗာစည်းမျဉ်းများ\n",
+  "account.account_note_header": "မှတ်ချက်",
+  "account.add_or_remove_from_list": "စာရင်းများမှ ထည့်ပါ သို့မဟုတ် ဖယ်ရှားပါ။\n",
+  "account.badges.bot": "စက်ရုပ်",
+  "account.badges.group": "အုပ်စု",
+  "account.block": "@{name} ကိုဘလော့မည်",
+  "account.block_domain": " {domain} ဒိုမိန်းကိုပိတ်မည်",
+  "account.blocked": "ဘလော့ထားသည်",
+  "account.browse_more_on_origin_server": "မူရင်းပရိုဖိုင်တွင် ပိုမိုကြည့်ရှုပါ။",
+  "account.cancel_follow_request": "ဖောလိုးပယ်ဖျက်ခြင်း",
+  "account.direct": "@{name} သာသိရှိနိုင်အောင်မန်းရှင်းခေါ်မည်",
+  "account.disable_notifications": "@{name} ပို့စ်တင်သည့်အခါ ကျွန်ုပ်ကို အသိပေးခြင်းရပ်ပါ။",
+  "account.domain_blocked": "ဒိုမိန်း ပိတ်ပင်ထားခဲ့သည်\n",
   "account.edit_profile": "ကိုယ်ရေးမှတ်တမ်းပြင်ဆင်မည်",
-  "account.enable_notifications": "Notify me when @{name} posts",
-  "account.endorse": "Feature on profile",
-  "account.featured_tags.last_status_at": "Last post on {date}",
-  "account.featured_tags.last_status_never": "No posts",
-  "account.featured_tags.title": "{name}'s featured hashtags",
+  "account.enable_notifications": "@{name} ပို့စ်တင်သည့်အခါ ကျွန်ုပ်ကို အကြောင်းကြားပါ။",
+  "account.endorse": "အကောင့်ပရိုဖိုင်တွင်ဖော်ပြပါ",
+  "account.featured_tags.last_status_at": "{date} တွင် နောက်ဆုံးပို့စ်",
+  "account.featured_tags.last_status_never": "ပို့စ်တင်ထားခြင်းမရှိပါ",
+  "account.featured_tags.title": "ဖော်ပြထားသောဟက်ရှ်တက်ခ်များ",
   "account.follow": "စောင့်ကြည့်မည်",
-  "account.followers": "Followers",
-  "account.followers.empty": "No one follows this user yet.",
-  "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
+  "account.followers": "စောင့်ကြည့်သူများ",
+  "account.followers.empty": "ဤသူကို စောင့်ကြည့်သူ မရှိသေးပါ။",
+  "account.followers_counter": "{count, plural, one {{counter} ဖော်လိုဝါများ} other {{counter} ဖော်လိုဝါများ}}",
   "account.following": "စောင့်ကြည့်နေသည်",
-  "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
-  "account.follows.empty": "This user doesn't follow anyone yet.",
-  "account.follows_you": "Follows you",
-  "account.go_to_profile": "Go to profile",
-  "account.hide_reblogs": "Hide boosts from @{name}",
-  "account.joined_short": "Joined",
-  "account.languages": "Change subscribed languages",
-  "account.link_verified_on": "Ownership of this link was checked on {date}",
-  "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
+  "account.following_counter": "{count, plural, one {{counter} ဖော်လိုလုပ်နေသည်} other {{counter} ဖော်လိုလုပ်နေသည်}}",
+  "account.follows.empty": "ဤသူသည် မည်သူ့ကိုမျှ စောင့်ကြည့်ခြင်း မရှိသေးပါ။",
+  "account.follows_you": "သင့်ကို စောင့်ကြည့်နေသည်",
+  "account.go_to_profile": "ပရိုဖိုင်းသို့ သွားရန်",
+  "account.hide_reblogs": "@{name} ၏ မျှဝေမှုကို ဝှက်ထားရန်",
+  "account.joined_short": "ပူးပေါင်း",
+  "account.languages": "ဘာသာစကားပြောင်းမည်",
+  "account.link_verified_on": "ဤလင့်ခ်၏ ပိုင်ဆိုင်မှုကို {date} က စစ်ဆေးခဲ့သည်။",
+  "account.locked_info": "အကောင့်ကိုယ်ရေးကိုယ်တာကိုလော့ချထားသည်။အကောင့်ပိုင်ရှင်မှ ခွင့်ပြုချက်လိုအပ်သည်။",
   "account.media": "မီဒီယာ",
-  "account.mention": "Mention @{name}",
-  "account.moved_to": "{name} has indicated that their new account is now:",
-  "account.mute": "Mute @{name}",
-  "account.mute_notifications": "Mute notifications from @{name}",
-  "account.muted": "Muted",
-  "account.open_original_page": "Open original page",
+  "account.mention": "{name}ကိုမန်းရှင်းထားသည်",
+  "account.moved_to": "{name} ၏အကောင့်အသစ်မှာ",
+  "account.mute": "{name}ကိုပိတ်ထားရန်",
+  "account.mute_notifications": "{name}ထံမှသတိပေးချက်",
+  "account.muted": "ပိတ်ထားရန်",
+  "account.open_original_page": "မူလစာမျက်နှာကိုဖွင့်ပါ",
   "account.posts": "ပို့စ်များ",
-  "account.posts_with_replies": "Posts and replies",
-  "account.report": "Report @{name}",
-  "account.requested": "Awaiting approval. Click to cancel follow request",
-  "account.requested_follow": "{name} has requested to follow you",
-  "account.share": "Share @{name}'s profile",
-  "account.show_reblogs": "Show boosts from @{name}",
-  "account.statuses_counter": "{count, plural, one {{counter} Post} other {{counter} Posts}}",
-  "account.unblock": "Unblock @{name}",
-  "account.unblock_domain": "Unblock domain {domain}",
-  "account.unblock_short": "Unblock",
-  "account.unendorse": "Don't feature on profile",
-  "account.unfollow": "Unfollow",
-  "account.unmute": "Unmute @{name}",
-  "account.unmute_notifications": "Unmute notifications from @{name}",
-  "account.unmute_short": "Unmute",
+  "account.posts_with_replies": "ပို့စ်နှင့် ရီပလိုင်းများ",
+  "account.report": "တိုင်ကြားမည်{name}",
+  "account.requested": "ခွင့်ပြုချက်စောင့်နေသည်။ ဖော်လိုးပယ်ဖျက်ရန်နှိပ်ပါ",
+  "account.requested_follow": "{name} မှသင့်ကိုဖော်လိုပြုလုပ်လိုသည်",
+  "account.share": "{name}၏ပရိုဖိုင်ကိုမျှဝေပါ",
+  "account.show_reblogs": "@{name} မှ မျှ၀ေမှုများကို ပြပါ\n",
+  "account.statuses_counter": "{count, plural, one {{counter} ပိုစ့်များ} other {{counter} ပိုစ့်များ}}",
+  "account.unblock": "{name} ကို ဘလော့ဖြုတ်မည်",
+  "account.unblock_domain": " {domain} ဒိုမိန်းကိုပြန်ဖွင့်မည်",
+  "account.unblock_short": "ဘလော့ဖြုတ်ရန်",
+  "account.unendorse": "အကောင့်ပရိုဖိုင်တွင်မဖော်ပြပါ",
+  "account.unfollow": "စောင့်ကြည့်ခြင်းအား ပယ်ဖျက်မည်",
+  "account.unmute": "{name} ကို ပြန်ဖွင့်ရန်",
+  "account.unmute_notifications": "{name}ထံမှသတိပေးချက်ပြန်ဖွင့်မည်",
+  "account.unmute_short": "ပြန်ဖွင့်ရန်",
   "account_note.placeholder": "Click to add a note",
-  "admin.dashboard.daily_retention": "User retention rate by day after sign-up",
-  "admin.dashboard.monthly_retention": "User retention rate by month after sign-up",
-  "admin.dashboard.retention.average": "Average",
-  "admin.dashboard.retention.cohort": "Sign-up month",
-  "admin.dashboard.retention.cohort_size": "New users",
-  "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Rate limited",
-  "alert.unexpected.message": "An unexpected error occurred.",
-  "alert.unexpected.title": "Oops!",
-  "announcement.announcement": "Announcement",
-  "attachments_list.unprocessed": "(unprocessed)",
-  "audio.hide": "Hide audio",
-  "autosuggest_hashtag.per_week": "{count} per week",
-  "boost_modal.combo": "You can press {combo} to skip this next time",
-  "bundle_column_error.copy_stacktrace": "Copy error report",
-  "bundle_column_error.error.body": "The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.",
-  "bundle_column_error.error.title": "Oh, no!",
-  "bundle_column_error.network.body": "There was an error when trying to load this page. This could be due to a temporary problem with your internet connection or this server.",
-  "bundle_column_error.network.title": "Network error",
-  "bundle_column_error.retry": "Try again",
-  "bundle_column_error.return": "Go back home",
-  "bundle_column_error.routing.body": "The requested page could not be found. Are you sure the URL in the address bar is correct?",
-  "bundle_column_error.routing.title": "404",
-  "bundle_modal_error.close": "Close",
-  "bundle_modal_error.message": "Something went wrong while loading this component.",
-  "bundle_modal_error.retry": "Try again",
-  "closed_registrations.other_server_instructions": "Since Mastodon is decentralized, you can create an account on another server and still interact with this one.",
-  "closed_registrations_modal.description": "Creating an account on {domain} is currently not possible, but please keep in mind that you do not need an account specifically on {domain} to use Mastodon.",
-  "closed_registrations_modal.find_another_server": "Find another server",
-  "closed_registrations_modal.preamble": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
-  "closed_registrations_modal.title": "Signing up on Mastodon",
+  "admin.dashboard.daily_retention": "အကောင့်ဖွင့်ပြီးနောက် တစ်ရက်ပြီးတစ်ရက် အသုံးပြုသူ ထိန်းသိမ်းမှုနှုန်း",
+  "admin.dashboard.monthly_retention": "အကောင့်ဖွင့်ပြီးနောက် တစ်လအလိုက် အသုံးပြုသူ ထိန်းသိမ်းမှုနှုန်း",
+  "admin.dashboard.retention.average": "ပျမ်းမျှ",
+  "admin.dashboard.retention.cohort": "အကောင့်ပြုလုပ်မှုလ",
+  "admin.dashboard.retention.cohort_size": "အသုံးပြုသူအသစ်များ",
+  "alert.rate_limited.message": " {retry_time, time, medium}ပြီးနောက် ထပ်စမ်းကြည့်ပါ",
+  "alert.rate_limited.title": "နှုန်းထားကန့်သတ်ထားသည်။\n",
+  "alert.unexpected.message": "မမျှော်လင့်ထားသော အမှားတစ်ခု ဖြစ်ပွားခဲ့သည်။",
+  "alert.unexpected.title": "အယ်!",
+  "announcement.announcement": "ကြေငြာချက်",
+  "attachments_list.unprocessed": "(မလုပ်ဆောင်ရသေး)",
+  "audio.hide": "အသံပိတ်မည်",
+  "autosuggest_hashtag.per_week": "တစ်ပတ်လျှင် {count}\n",
+  "boost_modal.combo": "ဤအရာကို နောက်တစ်ကြိမ်ကျော်ရန် {combo} ကိုနှိပ်နိုင်သည်။",
+  "bundle_column_error.copy_stacktrace": "စာကူးရာတွင်ပြဿနာရှိသည်",
+  "bundle_column_error.error.body": "ဤစာမျက်နှာကို ဖော်ပြရာတွင် ပြဿနာရှိနေသည်",
+  "bundle_column_error.error.title": "မှားနေသည်",
+  "bundle_column_error.network.body": "ဒီစာမျက်နှာအား ဖွင့်လို့မရပါ။ အင်တာနက်ကွန်နက်ရှင် (သို့) ဆာဗာ ပြဿနာဖြစ်နေသည်။",
+  "bundle_column_error.network.title": "အင်တာနက်ကွန်ယက် ပြဿနာ",
+  "bundle_column_error.retry": "ထပ်ကြိုးစားပါ",
+  "bundle_column_error.return": "Homeကိုပြန်သွားမည်",
+  "bundle_column_error.routing.body": "ရှာနေသောအရာမှာမရှိပါ။ URL မှန်မမှန်ပြန်စစ်ပေးပါ",
+  "bundle_column_error.routing.title": "လေးသုံညလေး",
+  "bundle_modal_error.close": "ပိတ်ပါ",
+  "bundle_modal_error.message": "ဤဝက်ဘ်စာမျက်နှာအား ဖွင့်နေစဥ် အမှားတစ်ခု ဖြစ်ပေါ်ခဲ့သည်။",
+  "bundle_modal_error.retry": "ထပ်မံကြိုးစားပါ",
+  "closed_registrations.other_server_instructions": "Mastodon ကို ဗဟိုချုပ်ကိုင်မှု လျှော့ချထားသောကြောင့် သင်သည် အခြားဆာဗာတစ်ခုပေါ်တွင် အကောင့်တစ်ခု ဖန်တီးနိုင်ပြီး ဤတစ်ခုနှင့် အပြန်အလှန် တုံ့ပြန်ဆဲဖြစ်သည်။",
+  "closed_registrations_modal.description": "{domain} တွင် အကောင့်တစ်ခုဖန်တီးခြင်းသည် လောလောဆယ်မဖြစ်နိုင်ပါ၊ သို့သော် Mastodon ကိုအသုံးပြုရန်အတွက် သင်သည် {domain} တွင် အထူးအကောင့်တစ်ခုမလိုအပ်ကြောင်း ကျေးဇူးပြု၍ သတိရပါ။",
+  "closed_registrations_modal.find_another_server": "အခြားဆာဗာကိုရှာပါ။",
+  "closed_registrations_modal.preamble": "Mastodon ကို ဗဟိုချုပ်ကိုင်မှု လျှော့ချထားသောကြောင့် သင့်အကောင့်ကို မည်သည့်နေရာတွင်ပင် ဖန်တီးပါစေ၊ သင်သည် ဤဆာဗာပေါ်ရှိ မည်သူမဆိုနှင့် လိုက်လျောညီထွေ တုံ့ပြန်နိုင်မည်ဖြစ်သည်။ သင်ကိုယ်တိုင်ပင် လက်ခံဆောင်ရွက်ပေးနိုင်သည်။",
+  "closed_registrations_modal.title": "Mastodon တွင်အကောင့်ပြုလုပ်ပါ။\n",
   "column.about": "အကြောင်း",
-  "column.blocks": "Blocked users",
-  "column.bookmarks": "Bookmarks",
-  "column.community": "Local timeline",
-  "column.direct": "Direct messages",
-  "column.directory": "Browse profiles",
-  "column.domain_blocks": "Blocked domains",
-  "column.favourites": "Favourites",
-  "column.follow_requests": "Follow requests",
-  "column.home": "Home",
-  "column.lists": "Lists",
-  "column.mutes": "Muted users",
+  "column.blocks": "ဘလော့ထားသောအကောင့်များ",
+  "column.bookmarks": "မှတ်တမ်းများ",
+  "column.community": "ဒေသတွင်း အချိန်ဇယား",
+  "column.direct": "သီးသန့်ဖော်ပြခြင်း",
+  "column.directory": "ပရိုဖိုင်များကို ရှာဖွေမည်\n",
+  "column.domain_blocks": "  ဒိုမိန်းကိုပိတ်မည်",
+  "column.favourites": "အကြိုက်ဆုံးများ",
+  "column.follow_requests": "စောင့်ကြည့်ရန် တောင်းဆိုမှုများ",
+  "column.home": "ပင်မစာမျက်နှာ",
+  "column.lists": "စာရင်းများ",
+  "column.mutes": "မပေါ်အောင်ပိတ်ထားသောအသုံးပြုသူများ",
   "column.notifications": "အသိပေးချက်များ",
   "column.pins": "Pinned post",
-  "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.public": "အားလုံးဖတ်နိုင်သောအချိန်ဇယား",
+  "column_back_button.label": "နောက်သို့",
+  "column_header.hide_settings": "ဆက်တင်များကို ဖျောက်ပါ။",
+  "column_header.moveLeft_settings": "ကော်လံကို ဘယ်ဘက်သို့ ရွှေ့ပါ။",
+  "column_header.moveRight_settings": "ကော်လံကို ညာဘက်သို့ ရွှေ့ပါ။",
+  "column_header.pin": "ထိပ်တွင်တွဲထားမည်",
+  "column_header.show_settings": "ဆက်တင်များကို ပြပါ။",
+  "column_header.unpin": "မတွဲတော့ပါ",
   "column_subheading.settings": "ဆက်တင်များ",
-  "community.column_settings.local_only": "Local only",
+  "community.column_settings.local_only": "ပြည်တွင်း၌သာ",
   "community.column_settings.media_only": "Media only",
-  "community.column_settings.remote_only": "Remote only",
-  "compose.language.change": "Change language",
-  "compose.language.search": "Search languages...",
-  "compose_form.direct_message_warning_learn_more": "Learn more",
+  "community.column_settings.remote_only": "အဝေးမှသာ",
+  "compose.language.change": "ဘာသာစကား ပြောင်းမည်",
+  "compose.language.search": "ဘာသာစကားကိုရှာမည်",
+  "compose_form.direct_message_warning_learn_more": "ထပ်သိရှိလိုသည်",
   "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts 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.hashtag_warning": "ဤပို့စ်သည် အများသူငှာမဟုတ်သောကြောင့် မည်သည့် hashtag အောက်တွင် ဖော်ပြမည်မဟုတ်ပါ။ အများသူငှာ ပို့စ်များကိုသာ hashtag ဖြင့် ရှာဖွေနိုင်သည်။",
+  "compose_form.lock_disclaimer": "သင့်အကောင့်ကို {သော့ခတ်မထားပါ}။ သင့်နောက်လိုက်-သီးသန့်ပို့စ်များကို ကြည့်ရှုရန် မည်သူမဆို သင့်အား လိုက်ကြည့်နိုင်ပါသည်။",
+  "compose_form.lock_disclaimer.lock": "သော့ခတ်ထားမယ်",
   "compose_form.placeholder": "What is on your mind?",
-  "compose_form.poll.add_option": "Add a choice",
-  "compose_form.poll.duration": "Poll duration",
-  "compose_form.poll.option_placeholder": "Choice {number}",
-  "compose_form.poll.remove_option": "Remove this choice",
-  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
-  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
-  "compose_form.publish": "Publish",
-  "compose_form.publish_form": "Publish",
+  "compose_form.poll.add_option": "ရွေးချယ်မှုထပ်မံပေါင်းထည့်ပါ",
+  "compose_form.poll.duration": "စစ်တမ်းကြာချိန်",
+  "compose_form.poll.option_placeholder": "ရွေးချယ်မှု {number}\n",
+  "compose_form.poll.remove_option": "ဤရွေးချယ်မှုကို ဖယ်ထုတ်ပါ",
+  "compose_form.poll.switch_to_multiple": "စစ်တမ်းတွင်တစ်ခုထပ်ပိုသောဆန္ဒပြုချက်လက်ခံမည်",
+  "compose_form.poll.switch_to_single": "စစ်တမ်းတွင် တစ်ခုကိုသာရွေးချယ်ခွင့်ပြုမည်",
+  "compose_form.publish": "ပို့စ်တင်မည်",
+  "compose_form.publish_form": "ပို့စ်တင်မည်",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.save_changes": "Save changes",
+  "compose_form.save_changes": "ပြောင်းလဲမှုများကို သိမ်းဆည်းပါ",
   "compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}",
   "compose_form.sensitive.marked": "{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}",
   "compose_form.sensitive.unmarked": "{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}",
   "compose_form.spoiler.marked": "Text is hidden behind warning",
   "compose_form.spoiler.unmarked": "Text is not hidden",
-  "compose_form.spoiler_placeholder": "Write your warning here",
-  "confirmation_modal.cancel": "Cancel",
-  "confirmations.block.block_and_report": "Block & Report",
-  "confirmations.block.confirm": "Block",
-  "confirmations.block.message": "Are you sure you want to block {name}?",
-  "confirmations.cancel_follow_request.confirm": "Withdraw request",
-  "confirmations.cancel_follow_request.message": "Are you sure you want to withdraw your request to follow {name}?",
-  "confirmations.delete.confirm": "Delete",
+  "compose_form.spoiler_placeholder": "သတိပေးစာကိုဤနေရာတွင်ရေးပါ",
+  "confirmation_modal.cancel": "ပယ်ဖျက်မည်",
+  "confirmations.block.block_and_report": "ဘလော့ပြီး တိုင်ကြားမည်",
+  "confirmations.block.confirm": "ဘလော့မည်",
+  "confirmations.block.message": "အကောင့်မှ ထွက်ရန် သေချာပါသလား?",
+  "confirmations.cancel_follow_request.confirm": "ပန်ကြားချက်ကို ပယ်ဖျက်မည်",
+  "confirmations.cancel_follow_request.message": "{name} ကို စောင့်ကြည့်ခြင်းအားပယ်ဖျက်ရန် သေချာပါသလား။",
+  "confirmations.delete.confirm": "ဖျက်မည်",
   "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.discard_edit_media.confirm": "Discard",
-  "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
+  "confirmations.delete_list.confirm": "ဖျက်မည်",
+  "confirmations.delete_list.message": "ဖျက်ရန် သေချာပါသလား?",
+  "confirmations.discard_edit_media.confirm": "ဖယ်ထုတ်ပါ",
+  "confirmations.discard_edit_media.message": "သင်သည် မီဒီယာဖော်ပြချက် သို့မဟုတ် အစမ်းကြည့်ရှုခြင်းတွင် မသိမ်းဆည်းရသေးသော အပြောင်းအလဲများရှိသည်။ မည်သို့ပင်ဖြစ်စေ ဖျက်ပစ်မည်လား။",
   "confirmations.domain_block.confirm": "Hide entire domain",
-  "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable. You will not see content from that domain in any public timelines or your notifications. Your followers from that domain will be removed.",
-  "confirmations.logout.confirm": "Log out",
-  "confirmations.logout.message": "Are you sure you want to log out?",
-  "confirmations.mute.confirm": "Mute",
-  "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
-  "confirmations.mute.message": "Are you sure you want to mute {name}?",
-  "confirmations.redraft.confirm": "Delete & redraft",
+  "confirmations.domain_block.message": "{domain} တစ်ခုလုံးကို ဘလော့လုပ်ရန် တကယ် သေချာပါသလား? များသောအားဖြင့် အနည်းစုကို ပစ်မှတ်ထား ဘလော့လုပ်ခြင်းသည် လုံလောက်ပါသည်။ ထို ဒိုမိန်းမှ အကြောင်းအရာ တစ်ခုမှ မြင်ရမည်မဟုတ်သည့်အပြင် ထို ဒိုမိန်းတွင်ရှိသော သင်၏ စောင့်ကြည့်သူများပါ ဖယ်ရှားပစ်မည်ဖြစ်သည်။",
+  "confirmations.edit.confirm": "ပြင်ရန်",
+  "confirmations.edit.message": "ယခုပြင်ဆင်ခြင်းတွင် သင်လက်ရှိမက်ဆေ့ချ်ကို ဖျက်ပစ်ပြီး အသစ်ရေးပါမည်။ ရှေ့ဆက်လိုသည်မှာ သေချာပါသလား။",
+  "confirmations.logout.confirm": "အကောင့်မှထွက်မည်",
+  "confirmations.logout.message": "အကောင့်မှ ထွက်ရန် သေချာပါသလား?",
+  "confirmations.mute.confirm": "ပိတ်ထားရန်",
+  "confirmations.mute.explanation": "၎င်းသည် ၎င်းတို့ထံမှ ပို့စ်များနှင့် ၎င်းတို့ကို ဖော်ပြထားသော ပို့စ်များကို ဖျောက်ထားမည်ဖြစ်ပြီး၊ သို့သော် ၎င်းတို့သည် သင့်ပို့စ်များကို မြင်နိုင်ပြီး သင့်အား လိုက်ကြည့်နိုင်စေမည်ဖြစ်သည်။",
+  "confirmations.mute.message": "{name} ကို မမြင်လိုသည်မှာ သေချာပါသလား။ ",
+  "confirmations.redraft.confirm": "ဖျက်ပြီး ပြန်လည်ရေးမည်။",
   "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
   "confirmations.reply.confirm": "စာပြန်မည်",
-  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
-  "confirmations.unfollow.confirm": "Unfollow",
-  "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
-  "conversation.delete": "Delete conversation",
-  "conversation.mark_as_read": "Mark as read",
-  "conversation.open": "View conversation",
-  "conversation.with": "With {names}",
-  "copypaste.copied": "Copied",
-  "copypaste.copy": "Copy",
-  "directory.federated": "From known fediverse",
-  "directory.local": "From {domain} only",
-  "directory.new_arrivals": "New arrivals",
-  "directory.recently_active": "Recently active",
-  "disabled_account_banner.account_settings": "Account settings",
-  "disabled_account_banner.text": "Your account {disabledAccount} is currently disabled.",
-  "dismissable_banner.community_timeline": "These are the most recent public posts from people whose accounts are hosted by {domain}.",
-  "dismissable_banner.dismiss": "Dismiss",
-  "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
-  "dismissable_banner.explore_statuses": "These posts from this and other servers in the decentralized network are gaining traction on this server right now.",
-  "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
-  "dismissable_banner.public_timeline": "These are the most recent public posts from people on this and other servers of the decentralized network that this server knows about.",
+  "confirmations.reply.message": "စာပြန်လျှင်ယခင်စာများကိုအလိုအလျောက်ပျက်သွားစေမည်။ ဆက်လက်လုပ်ဆောင်မည်လား?",
+  "confirmations.unfollow.confirm": "စောင့်ကြည့်ခြင်းအား ပယ်ဖျက်မည်",
+  "confirmations.unfollow.message": "{name} ကို စောင်ကြည့်ခြင်းအား ပယ်ဖျက်မည်မှာသေချာပါသလား။",
+  "conversation.delete": "ဤစကားပြောဆိုမှုကို ဖျက်ပစ်မည်",
+  "conversation.mark_as_read": "ဖတ်ပြီးသားအဖြစ်မှတ်ထားပါ",
+  "conversation.open": "Conversation ကိုကြည့်မည်",
+  "conversation.with": "{အမည်များ} ဖြင့်",
+  "copypaste.copied": "ကူယူပြီးပါပြီ",
+  "copypaste.copy": "ကူးယူပါ",
+  "directory.federated": "သင် သိသော ဖက်ဒီမှ",
+  "directory.local": "{domain} မှသာလျှင်\n",
+  "directory.new_arrivals": "အသစ်ရောက်ရှိမှုများ",
+  "directory.recently_active": "မကြာသေးခင်က ဖွင့်ထားသော",
+  "disabled_account_banner.account_settings": "အကောင့်ဆက်တင်များ",
+  "disabled_account_banner.text": "{disabledAccount} သည်လတ်တလောပိတ်ခံထားရသည်",
+  "dismissable_banner.community_timeline": "အကောင့်များမှ လတ်တလောတင်ထားသည့်အများမြင်ပို့စ်များမှာ {domain} တွင် တင်ထားသောပို့စ်များဖြစ်သည်။",
+  "dismissable_banner.dismiss": "ပယ်ရန်",
+  "dismissable_banner.explore_links": "ဤသတင်းများကို ယခုအချိန်တွင် ဗဟိုချုပ်ကိုင်မှုလျှော့ချထားသော ကွန်ရက်၏ အခြားဆာဗာများမှ လူများက ပြောဆိုနေကြပါသည်။",
+  "dismissable_banner.explore_statuses": "ဤစာများနှင့် ဗဟိုချုပ်ကိုင်မှုလျှော့ချထားသော ကွန်ရက်ရှိ အခြားဆာဗာများမှ ဤပို့စ်များသည် ယခုဆာဗာပေါ်တွင် ဆွဲဆောင်မှု ရှိလာပါသည်။",
+  "dismissable_banner.explore_tags": "ဤ hashtag များသည် ယခုအချိန်တွင် ဗဟိုချုပ်ကိုင်မှုလျှော့ချထားသော ကွန်ရက်၏ အခြားဆာဗာများပေါ်ရှိ လူများကြားတွင် ဆွဲဆောင်မှုရှိလာပါသည်",
+  "dismissable_banner.public_timeline": "ဗဟိုချုပ်ကိုင်မှုလျှော့ချထားသော ဤဆာဗာနှင့် အခြားဆာဗာတို့တွင် တင်ထားသည့် လတ်တလော အများမြင်ပို့စ်များဖြစ်သည်။",
   "embed.instructions": "Embed this status on your website by copying the code below.",
-  "embed.preview": "Here is what it will look like:",
-  "emoji_button.activity": "Activity",
-  "emoji_button.clear": "Clear",
-  "emoji_button.custom": "Custom",
+  "embed.preview": "ဒါမျိုးမြင်ရမှာပါ။",
+  "emoji_button.activity": "လုပ်ဆောင်ချက်",
+  "emoji_button.clear": "ရှင်းလင်းမည်",
+  "emoji_button.custom": "စိတ်ကြိုက်",
   "emoji_button.flags": "Flags",
-  "emoji_button.food": "Food & Drink",
-  "emoji_button.label": "Insert emoji",
-  "emoji_button.nature": "Nature",
-  "emoji_button.not_found": "No matching emojis found",
+  "emoji_button.food": "အစားအသောက်",
+  "emoji_button.label": "အီမိုဂျီထည့်ပါ",
+  "emoji_button.nature": "သဘာဝ",
+  "emoji_button.not_found": "ကိုက်ညီသော အီမိုဂျီများ ရှာမတွေ့ပါ",
   "emoji_button.objects": "Objects",
-  "emoji_button.people": "People",
-  "emoji_button.recent": "Frequently used",
-  "emoji_button.search": "Search...",
-  "emoji_button.search_results": "Search results",
-  "emoji_button.symbols": "Symbols",
-  "emoji_button.travel": "Travel & Places",
-  "empty_column.account_suspended": "Account suspended",
+  "emoji_button.people": "လူများ",
+  "emoji_button.recent": "မကြာခဏ အသုံးပြုသော",
+  "emoji_button.search": "ရှာရန်...",
+  "emoji_button.search_results": "ရှာဖွေမှု ရလဒ်များ",
+  "emoji_button.symbols": "သင်္ကေတများ",
+  "emoji_button.travel": "ခရီးသွားခြင်း နှင့် နေရာများ",
+  "empty_column.account_suspended": "အကောင့်ရပ်ဆိုင်းထားသည်",
   "empty_column.account_timeline": "No posts found",
-  "empty_column.account_unavailable": "Profile unavailable",
-  "empty_column.blocks": "You haven't blocked any users yet.",
-  "empty_column.bookmarked_statuses": "You don't have any bookmarked posts yet. When you bookmark one, it will show up here.",
-  "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
-  "empty_column.domain_blocks": "There are no blocked domains yet.",
-  "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
-  "empty_column.favourited_statuses": "You don't have any favourite posts yet. When you favourite one, it will show up here.",
-  "empty_column.favourites": "No one has favourited this post yet. When someone does, they will show up here.",
-  "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
-  "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! Follow more people to fill it up. {suggestions}",
-  "empty_column.home.suggestions": "See some suggestions",
+  "empty_column.account_unavailable": "ပရိုဖိုင် မရနိုင်ပါ",
+  "empty_column.blocks": "ပိတ်ထားသောအကောင့်များမရှိသေးပါ",
+  "empty_column.bookmarked_statuses": "သင့်တွင် မှတ်သားထားသော ပို့စ်များ မရှိသေးပါ။ တစ်ခုကို အမှတ်အသားပြုလိုက်ပါက ၎င်းကို ဤနေရာတွင် ပြထားပါမည်။",
+  "empty_column.community": "ဒေသတွင်းစာမျက်နှာမှာ အလွတ်ဖြစ်နေသည်။ အများမြင်နိုင်ရန်အတွက် တစ်ခုခုရေးပါ။",
+  "empty_column.direct": "သင့်တွင် သီးသန့်ဖော်ပြချက်များ မရှိသေးပါ။ ပေးပို့ခြင်း သို့မဟုတ် လက်ခံသောအခါ၊ ၎င်းသည် ဤနေရာတွင် ပေါ်လာလိမ့်မည်။",
+  "empty_column.domain_blocks": "သင်ပိတ်ထားသော ဒိုမိန်းမရှိသေးပါ",
+  "empty_column.explore_statuses": "အခုလောလောဆယ်တွင် ရေပန်းစားနေသောပို့စ်များ မရှိသေးပါ။ နောက်မှ ပြန်စစ်ဆေးပါရန်။",
+  "empty_column.favourited_statuses": "သင့်တွင် အကြိုက်ဆုံးပို့စ်များ မရှိသေးပါ။ တစ်ခုကို သင်နှစ်သက်ထားပါက ၎င်းကို ဤနေရာတွင် ပြထားပါမည်။",
+  "empty_column.favourites": "ဤပို့စ်ကို အကြိုက်တွေ့သူ မရှိသေးပါ။ တစ်စုံတစ်ယောက်က ကြိုက်နှစ်သက်ပါက ဤနေရာတွင် ပြထားပါမည်။",
+  "empty_column.follow_recommendations": "သင့်အတွက် အကြံဉာဏ်များ မရရှိနိုင်ပါ။ သင်သိနိုင်သည့်လူများ သို့မဟုတ် ခေတ်စားနေသည့် hashtags များ ရှာဖွေရန်တို့အတွက် ရှာဖွေမှုကို အသုံးပြုနိုင်သည်။",
+  "empty_column.follow_requests": "သင့်တွင် စောင့်ကြည့်ရန် တောင်းဆိုမှုများ မရှိသေးပါ။ သင်လက်ခံရရှိပါက ၎င်းကို ဤနေရာတွင် ပြထားပါမည်။",
+  "empty_column.followed_tags": "သင်သည် မည်သည့် hashtag ကိုမျှ စောင့်မကြည့်ရသေးပါ။ စောင့်ကြည့်ပါက ဤနေရာတွင် ပြပေးပါမည်။",
+  "empty_column.hashtag": "ဤ hashtag တွင် မည်သည့်အရာမျှ မရှိသေးပါ။",
+  "empty_column.home": "သင့်ပင်မစာမျက်နှာမှာ အလွတ်ဖြစ်နေပါသည်။ ဖြည့်ရန်အတွက် လူများကို စောင့်ကြည့်ပါ {suggestions}",
+  "empty_column.home.suggestions": "ဆက်လက်ဖတ်ရှုမည်",
   "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. When other people interact with you, you will see it here.",
-  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up",
-  "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.",
-  "error.unexpected_crash.explanation_addons": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.",
-  "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
-  "error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
-  "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
-  "errors.unexpected_crash.report_issue": "Report issue",
-  "explore.search_results": "Search results",
-  "explore.suggested_follows": "For you",
-  "explore.title": "Explore",
-  "explore.trending_links": "News",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "Hashtags",
-  "filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.",
-  "filter_modal.added.context_mismatch_title": "Context mismatch!",
-  "filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.",
-  "filter_modal.added.expired_title": "Expired filter!",
-  "filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.",
-  "filter_modal.added.review_and_configure_title": "Filter settings",
-  "filter_modal.added.settings_link": "settings page",
-  "filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.",
-  "filter_modal.added.title": "Filter added!",
-  "filter_modal.select_filter.context_mismatch": "does not apply to this context",
-  "filter_modal.select_filter.expired": "expired",
-  "filter_modal.select_filter.prompt_new": "New category: {name}",
-  "filter_modal.select_filter.search": "Search or create",
-  "filter_modal.select_filter.subtitle": "Use an existing category or create a new one",
-  "filter_modal.select_filter.title": "Filter this post",
-  "filter_modal.title.status": "Filter a post",
-  "follow_recommendations.done": "Done",
-  "follow_recommendations.heading": "Follow people you'd like to see posts from! Here are some suggestions.",
-  "follow_recommendations.lead": "Posts from people you follow will show up in chronological order on your home feed. Don't be afraid to make mistakes, you can unfollow people just as easily any time!",
-  "follow_request.authorize": "Authorize",
-  "follow_request.reject": "Reject",
-  "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
-  "footer.about": "About",
-  "footer.directory": "Profiles directory",
-  "footer.get_app": "Get the app",
-  "footer.invite": "Invite people",
-  "footer.keyboard_shortcuts": "Keyboard shortcuts",
-  "footer.privacy_policy": "Privacy policy",
-  "footer.source_code": "View source code",
-  "generic.saved": "Saved",
-  "getting_started.heading": "Getting started",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.select.no_options_message": "No suggestions found",
-  "hashtag.column_settings.select.placeholder": "Enter hashtags…",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "empty_column.lists": "သင့်တွင် List မရှိသေးပါ။ List အသစ်ဖွင့်လျှင် ဤနေရာတွင်ကြည့်ရှုနိုင်မည်",
+  "empty_column.mutes": "ပိတ်ထားသောအကောင့်များမရှိသေးပါ",
+  "empty_column.notifications": "သတိပေးချက်မရှိသေးပါ။ သတိပေးချက်အသစ်ရှိလျှင် ဤနေရာတွင်ကြည့်ရှုနိုင်သည်",
+  "empty_column.public": "ဤနေရာတွင် မည်သည့်အရာမျှမရှိပါ။ တစ်ခုခုရေးပါ သို့မဟုတ် ဖြည့်စွက်ရန်အတွက် အခြားဆာဗာ အသုံးပြုသူများကို စောင့်ကြည့်ပါ။",
+  "error.unexpected_crash.explanation": " ကျွန်ုပ်တို့၏ကုဒ်တွင် ချွတ်ယွင်းချက်တစ်ခု သို့မဟုတ် ဘရောက်ဆာနှင့် အဆင်မပြေမှုတို့ကြောင့် ဤစာမျက်နှာကို မှန်ကန်စွာ မပြသနိုင်ပါ။",
+  "error.unexpected_crash.explanation_addons": "ဤစာမျက်နှာကို မှန်ကန်စွာ မပြသနိုင်ခဲ့ပါ။ ဤအမှားသည် Browser add-on ထည့်သွင်းထားမှုများ သို့မဟုတ် အလိုအလျောက် ဘာသာပြန်ကိရိယာများကြောင့် ဖြစ်နိုင်သည်။",
+  "error.unexpected_crash.next_steps": "စာမျက်နှာကို ပြန်လည်စတင်ကြည့်ပါ။ ၎င်းမှာ အကူအညီမဖြစ်ပါက သင်သည် အခြား Browser သို့မဟုတ် မူရင်းအက်ပ်မှတစ်ဆင့် Mastodon ကို ဆက်လက်အသုံးပြုနိုင်ပါမည်။",
+  "error.unexpected_crash.next_steps_addons": "၎င်းတို့ကို ပိတ်ပြီး စာမျက်နှာကို ပြန်လည်စတင်ကြည့်ပါ။ အဆင်မပြေပါက အခြားဘရောက်ဆာ သို့မဟုတ် မူရင်းအက်ပ်မှတစ်ဆင့် Mastodon ကို ဆက်ပြီးအသုံးပြုနိုင်ပါမည်။",
+  "errors.unexpected_crash.copy_stacktrace": "stacktrace ကို ကလစ်ဘုတ်သို့ ကူးယူပါ",
+  "errors.unexpected_crash.report_issue": "အဆင်မပြေမှုကို တိုင်ကြားရန်",
+  "explore.search_results": "ရှာဖွေမှုရလဒ်များ",
+  "explore.suggested_follows": "သင့်အတွက်",
+  "explore.title": "စူးစမ်းရန်",
+  "explore.trending_links": "သတင်းများ",
+  "explore.trending_statuses": "ပို့စ်တင်မယ်",
+  "explore.trending_tags": "ဟက်ရှ်တက်များ",
+  "filter_modal.added.context_mismatch_explanation": "ဤစစ်ထုတ်မှု အမျိုးအစားသည် ဤပို့စ်ကို သင်ဝင်ရောက်ခဲ့ခြင်းနှင့် မသက်ဆိုင်ပါ။ ပို့စ်ကို စစ်ထုတ်လိုပါက စစ်ထုတ်မှုကို ပြင်ဆင်ရမည်ဖြစ်သည်။",
+  "filter_modal.added.context_mismatch_title": "အကြောင်းအရာ မကိုက်ညီပါ။",
+  "filter_modal.added.expired_explanation": "ဤစစ်ထုတ်မှုအမျိုးအစားမှာ သက်တမ်းကုန်သွားပါပြီ။ အသုံးပြုလိုပါက သက်တမ်းကုန်ဆုံးရက်ကို ပြောင်းလဲနိုင်ပါသည်။",
+  "filter_modal.added.expired_title": "သက်တမ်းကုန်နေသော စစ်ထုတ်မှု။",
+  "filter_modal.added.review_and_configure": "ဤစစ်ထုတ်မှုအမျိုးအစားကို ပြန်လည်သုံးသပ်ပြီး ထပ်မံပြင်ဆင်သတ်မှတ်ရန်၊ {settings_link} သို့ သွားပါ။",
+  "filter_modal.added.review_and_configure_title": "စစ်ထုတ်မှု သတ်မှတ်ချက်များ",
+  "filter_modal.added.settings_link": "သတ်မှတ်ချက်များစာမျက်နှာ",
+  "filter_modal.added.short_explanation": "ဤပို့စ်ကို အောက်ပါ စစ်ထုတ်မှုအမျိုးအစားတွင် ပေါင်းထည့်ထားပါသည် - {title}။",
+  "filter_modal.added.title": "စစ်ထုတ်မှု ထည့်သွင်းပြီးပါပြီ။",
+  "filter_modal.select_filter.context_mismatch": "ဤအကြောင်းအရာနှင့် မသက်ဆိုင်ပါ",
+  "filter_modal.select_filter.expired": "သက်တမ်းကုန်သွားပါပြီ",
+  "filter_modal.select_filter.prompt_new": "အမျိုးအစားအသစ် - {name}",
+  "filter_modal.select_filter.search": "ရှာရန် သို့မဟုတ် ဖန်တီးရန်",
+  "filter_modal.select_filter.subtitle": "ရှိပြီးသားအမျိုးအစားကို သုံးပါ သို့မဟုတ် အသစ်တစ်ခု ဖန်တီးပါ",
+  "filter_modal.select_filter.title": "ဤပို့စ်ကို စစ်ထုတ်ပါ",
+  "filter_modal.title.status": "ပို့စ်တစ်ခု စစ်ထုတ်ပါ",
+  "follow_recommendations.done": "ပြီးပါပြီ",
+  "follow_recommendations.heading": "သင်မြင်လိုသော သူများထံမှ ပို့စ်များကို စောင့်ကြည့်ပါ။ ဤတွင် အကြံပြုချက်အချို့ရှိပါသည်။",
+  "follow_recommendations.lead": "သင်စောင့်ကြည့်ထားသူများ၏ ပို့စ်များမှာ သင့်ပင်မစာမျက်နှာတွင် အချိန်နှင့်တပြေးညီ ပေါ်လာပါမည်။ မကြောက်ပါနှင့်။ အချိန်မရွေး စောင့်ကြည့်ခြင်းအား ရပ်တန့်နိုင်ပါသည်။",
+  "follow_request.authorize": "လုပ်ပိုင်ခွင့်",
+  "follow_request.reject": "ဖယ်ရှားပါ",
+  "follow_requests.unlocked_explanation": "သင့်အကောင့်ကို လော့ခ်ချမထားသော်လည်း၊ {domain} ဝန်ထမ်းများသည် ဤအကောင့်များမှ လိုက်နာရန်တောင်းဆိုမှုများကို ကိုယ်တိုင်ပြန်လည်စစ်ဆေးလိုမည်ဟု ထင်မြင်ယူဆပါသည်။",
+  "followed_tags": "Hashtag ကို စောင့်ကြည့်ပါမည်",
+  "footer.about": "အကြောင်း",
+  "footer.directory": "ပရိုဖိုင်များလမ်းညွှန်",
+  "footer.get_app": "အက်ပ်ကို ရယူပါ",
+  "footer.invite": "လူများကို ဖိတ်ပါ",
+  "footer.keyboard_shortcuts": "ကီးဘုတ်အမြန်ခလုတ်များ",
+  "footer.privacy_policy": "ကိုယ်ရေးအချက်အလက်မူဝါဒ",
+  "footer.source_code": "မူရင်းကုဒ်အားကြည့်ရှုမည်",
+  "footer.status": "အခြေအနေ",
+  "generic.saved": "သိမ်းဆည်းထားပြီး",
+  "getting_started.heading": "စတင်မည်",
+  "hashtag.column_header.tag_mode.all": "နှင့် {additional}",
+  "hashtag.column_header.tag_mode.any": "သို့မဟုတ် {additional}",
+  "hashtag.column_header.tag_mode.none": "မပါဘဲ {additional}",
+  "hashtag.column_settings.select.no_options_message": "အကြံပြုချက်မတွေ့ပါ",
+  "hashtag.column_settings.select.placeholder": "hashtag သို့ ဝင်ရောက်နေခြင်း...",
+  "hashtag.column_settings.tag_mode.all": "ဤအရာအားလုံး",
+  "hashtag.column_settings.tag_mode.any": "ဤအရာထဲမှ တစ်ခု",
+  "hashtag.column_settings.tag_mode.none": "တစ်ခုမှ မဟုတ်ပါ။",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
-  "hashtag.follow": "Follow hashtag",
-  "hashtag.unfollow": "Unfollow hashtag",
-  "home.column_settings.basic": "Basic",
-  "home.column_settings.show_reblogs": "Show boosts",
-  "home.column_settings.show_replies": "Show replies",
-  "home.hide_announcements": "Hide announcements",
-  "home.show_announcements": "Show announcements",
-  "interaction_modal.description.favourite": "With an account on Mastodon, you can favourite this post to let the author know you appreciate it and save it for later.",
-  "interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.",
-  "interaction_modal.description.reblog": "With an account on Mastodon, you can boost this post to share it with your own followers.",
-  "interaction_modal.description.reply": "With an account on Mastodon, you can respond to this post.",
-  "interaction_modal.on_another_server": "On a different server",
-  "interaction_modal.on_this_server": "On this server",
-  "interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favourite Mastodon app or the web interface of your Mastodon server.",
-  "interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.",
-  "interaction_modal.title.favourite": "Favourite {name}'s post",
-  "interaction_modal.title.follow": "Follow {name}",
-  "interaction_modal.title.reblog": "Boost {name}'s post",
-  "interaction_modal.title.reply": "Reply to {name}'s post",
+  "hashtag.follow": "Hashtag ကို စောင့်ကြည့်မယ်",
+  "hashtag.unfollow": "Hashtag ကို မစောင့်ကြည့်ပါ",
+  "home.column_settings.basic": "အခြေခံ",
+  "home.column_settings.show_reblogs": "Boost များကို ပြပါ",
+  "home.column_settings.show_replies": "ပြန်စာများကို ပြပါ",
+  "home.hide_announcements": "ကြေညာချက်များကို ဖျောက်ပါ",
+  "home.show_announcements": "ကြေညာချက်များကို ပြပါ",
+  "interaction_modal.description.favourite": "Mastodon အကောင့်တစ်ခုဖြင့် ဤပို့စ်ကို နှစ်သက်ကြောင်းနှင့် စာရေးသူအား သင်နှစ်သက်ကြောင်း အသိပေးပြီးနောက် ၎င်းကို နောက်မှ သိမ်းဆည်းနိုင်သည်။",
+  "interaction_modal.description.follow": "Mastodon အကောင့်ဖြင့် သင်၏ ပင်မစာမျက်နှာတွင် ၎င်းတို့၏ ပို့စ်များကို ရရှိရန်အတွက် {name} ကို စောင့်ကြည့်နိုင်ပါသည်။",
+  "interaction_modal.description.reblog": "Mastodon အကောင့်တစ်ခုဖြင့် သင်၏စောင့်ကြည့်သူများကို မျှဝေရန်အတွက် ဤပို့စ်ကို Boost လုပ်ပါ။",
+  "interaction_modal.description.reply": "Mastodon အကောင့်တစ်ခုဖြင့် သင် ဤပို့စ်ကို တုံ့ပြန်နိုင်ပါသည်။",
+  "interaction_modal.on_another_server": "တခြားဆာဗာပေါ်တွင်",
+  "interaction_modal.on_this_server": "ဤဆာဗာတွင်",
+  "interaction_modal.other_server_instructions": "သင်အကြိုက်ဆုံး Mastodon အက်ပ် သို့မဟုတ် သင့် Mastodon ဆာဗာ၏ ဝဘ်ရှိ ရှာဖွေမှုနေရာတွင် ဤ URL ကို ကူးယူပြီး ထည့်ပါ။",
+  "interaction_modal.preamble": "Mastodon မှာ ဗဟိုချုပ်ကိုင်မှု မရှိခြင်းကြောင့် ဤတစ်ခုအတွက် သင့်တွင်အကောင့်မရှိပါက အခြား Mastodon ဆာဗာ သို့မဟုတ် အဆင်ပြေသောပလက်ဖောင်းတွင် ရှိသော သင့်လက်ရှိအကောင့်ဖြင့် အသုံးပြုနိုင်ပါသည်။",
+  "interaction_modal.title.favourite": "အကြိုက်ဆုံး {name} ၏ ပို့စ်",
+  "interaction_modal.title.follow": "{name} ကို စောင့်ကြည့်မယ်",
+  "interaction_modal.title.reblog": "{name} ၏ ပို့စ်ကို Boost လုပ်ပါ",
+  "interaction_modal.title.reply": "{name} ၏ ပို့စ်ကို စာပြန်မယ်",
   "intervals.full.days": "{number, plural, one {# day} other {# days}}",
   "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
   "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
@@ -308,8 +313,8 @@
   "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.description": "ဖော်ပြချက်",
+  "keyboard_shortcuts.direct": "သီးသန့်ဖော်ပြချက်များကော်လံကိုဖွင့်ရန်",
   "keyboard_shortcuts.down": "to move down in the list",
   "keyboard_shortcuts.enter": "to open status",
   "keyboard_shortcuts.favourite": "to favourite",
@@ -317,7 +322,7 @@
   "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.hotkey": "သော့ချက်",
   "keyboard_shortcuts.legend": "to display this legend",
   "keyboard_shortcuts.local": "to open local timeline",
   "keyboard_shortcuts.mention": "to mention author",
@@ -337,319 +342,323 @@
   "keyboard_shortcuts.toot": "to start a brand new post",
   "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
   "keyboard_shortcuts.up": "to move up in the list",
-  "lightbox.close": "Close",
-  "lightbox.compress": "Compress image view box",
-  "lightbox.expand": "Expand image view box",
-  "lightbox.next": "Next",
-  "lightbox.previous": "Previous",
-  "limited_account_hint.action": "Show profile anyway",
-  "limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
-  "lists.account.add": "Add to list",
-  "lists.account.remove": "Remove from list",
-  "lists.delete": "Delete list",
-  "lists.edit": "Edit list",
-  "lists.edit.submit": "Change title",
-  "lists.new.create": "Add list",
-  "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.followed": "Any followed user",
-  "lists.replies_policy.list": "Members of the list",
-  "lists.replies_policy.none": "No one",
-  "lists.replies_policy.title": "Show replies to:",
-  "lists.search": "Search among people you follow",
-  "lists.subheading": "Your lists",
+  "lightbox.close": "ပိတ်ပါ",
+  "lightbox.compress": "ရုပ်ပုံမြင်ကွင်းအကွက်ကို ချုံ့ပါ",
+  "lightbox.expand": "ပုံကိုဖွင့်ပါ",
+  "lightbox.next": "ရှေ့သို့",
+  "lightbox.previous": "ရှေ့သို့",
+  "limited_account_hint.action": "ဘာပဲဖြစ်ဖြစ် ပရိုဖိုင်ကို ပြပါ",
+  "limited_account_hint.title": "ဤပရိုဖိုင်ကို {domain} ၏ စိစစ်သူများမှ ဖျောက်ထားသည်။",
+  "lists.account.add": "စာရင်းထဲသို့ထည့်ပါ",
+  "lists.account.remove": "စာရင်းမှ ဖယ်ရှားလိုက်ပါ။",
+  "lists.delete": "စာရင်းကိုဖျက်ပါ",
+  "lists.edit": "စာရင်းကိုပြင်ဆင်ပါ",
+  "lists.edit.submit": "ခေါင်းစဥ် ပြောင်းလဲရန်",
+  "lists.new.create": "စာရင်းသွင်းပါ",
+  "lists.new.title_placeholder": "စာရင်းသစ်ခေါင်းစဥ်",
+  "lists.replies_policy.followed": "မည်သည့်စောင့်ကြည့်သူမဆို",
+  "lists.replies_policy.list": "စာရင်းထဲမှ အဖွဲ့ဝင်များ",
+  "lists.replies_policy.none": "တစ်ယောက်မှမရှိပါ",
+  "lists.replies_policy.title": "ပြန်စာများကို ပြရန် -",
+  "lists.search": "မိမိဖောလိုးထားသူများမှရှာဖွေမည်",
+  "lists.subheading": "သင့်၏စာရင်းများ",
   "load_pending": "{count, plural, one {# new item} other {# new items}}",
-  "loading_indicator.label": "Loading...",
+  "loading_indicator.label": "လုပ်ဆောင်နေသည်…",
   "media_gallery.toggle_visible": "{number, plural, one {Hide image} other {Hide images}}",
-  "missing_indicator.label": "Not found",
-  "missing_indicator.sublabel": "This resource could not be found",
-  "moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.",
-  "mute_modal.duration": "Duration",
-  "mute_modal.hide_notifications": "Hide notifications from this user?",
-  "mute_modal.indefinite": "Indefinite",
+  "missing_indicator.label": "မတွေ့ပါ",
+  "missing_indicator.sublabel": "ရှာဖွေနေသည်ကိုမတွေ့ပါ",
+  "moved_to_account_banner.text": "{movedToAccount} အကောင့်သို့ပြောင်းလဲထားသဖြင့် {disabledAccount} အကောင့်မှာပိတ်ထားသည်",
+  "mute_modal.duration": "ကြာချိန်",
+  "mute_modal.hide_notifications": "ဤအကောင့်မှသတိပေးချက်များကိုပိတ်မလား?",
+  "mute_modal.indefinite": "ရေတွက်လို့မရပါ",
   "navigation_bar.about": "အကြောင်း",
-  "navigation_bar.blocks": "Blocked users",
-  "navigation_bar.bookmarks": "Bookmarks",
-  "navigation_bar.community_timeline": "Local timeline",
-  "navigation_bar.compose": "Compose new post",
-  "navigation_bar.direct": "Direct messages",
-  "navigation_bar.discover": "Discover",
+  "navigation_bar.blocks": "ဘလော့ထားသောအကောင့်များ",
+  "navigation_bar.bookmarks": "မှတ်ထားသည်များ",
+  "navigation_bar.community_timeline": "ဒေသစံတော်ချိန်",
+  "navigation_bar.compose": "ပို့စ်အသစ်ရေးပါ",
+  "navigation_bar.direct": "သီးသန့်ဖော်ပြချက်များ",
+  "navigation_bar.discover": "ရှာဖွေပါ",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "ကိုယ်ရေးမှတ်တမ်းပြင်ဆင်မည်",
-  "navigation_bar.explore": "Explore",
-  "navigation_bar.favourites": "Favourites",
-  "navigation_bar.filters": "Muted words",
-  "navigation_bar.follow_requests": "Follow requests",
-  "navigation_bar.follows_and_followers": "Follows and followers",
-  "navigation_bar.lists": "Lists",
-  "navigation_bar.logout": "Logout",
-  "navigation_bar.mutes": "Muted users",
-  "navigation_bar.personal": "Personal",
-  "navigation_bar.pins": "Pinned posts",
-  "navigation_bar.preferences": "Preferences",
-  "navigation_bar.public_timeline": "Federated timeline",
-  "navigation_bar.search": "Search",
-  "navigation_bar.security": "Security",
-  "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.",
-  "notification.admin.report": "{name} reported {target}",
-  "notification.admin.sign_up": "{name} signed up",
+  "navigation_bar.explore": "စူးစမ်းရန်",
+  "navigation_bar.favourites": "အကြိုက်ဆုံးများ",
+  "navigation_bar.filters": "စကားလုံးများ ပိတ်ထားပါ",
+  "navigation_bar.follow_requests": "တောင်းဆိုချက်များကို စောင့်ကြည့်ပါ",
+  "navigation_bar.followed_tags": "Hashtag ကို စောင့်ကြည့်မယ်",
+  "navigation_bar.follows_and_followers": "စောင့်ကြည့်သူများနှင့် စောင့်ကြည့်စာရင်း",
+  "navigation_bar.lists": "စာရင်းများ",
+  "navigation_bar.logout": "ထွက်မယ်",
+  "navigation_bar.mutes": "အသုံးပြုသူများကို ပိတ်ထားပါ",
+  "navigation_bar.personal": "ကိုယ်ရေးကိုယ်တာ",
+  "navigation_bar.pins": "ပင်တွဲထားသောပို့စ်များ",
+  "navigation_bar.preferences": "စိတ်ကြိုက်သတ်မှတ်ချက်များ",
+  "navigation_bar.public_timeline": "ဖက်ဒီစာမျက်နှာ",
+  "navigation_bar.search": "ရှာရန်",
+  "navigation_bar.security": "လုံခြုံရေး",
+  "not_signed_in_indicator.not_signed_in": "ဤရင်းမြစ်သို့ ဝင်ရောက်ရန်အတွက် သင်သည် အကောင့်ဝင်ရန် လိုအပ်ပါသည်။",
+  "notification.admin.report": "{name} က {target} ကို တိုင်ကြားခဲ့သည်",
+  "notification.admin.sign_up": "{name} က အကောင့်ဖွင့်ထားသည်",
   "notification.favourite": "{name} favourited your status",
-  "notification.follow": "{name} followed you",
-  "notification.follow_request": "{name} has requested to follow you",
-  "notification.mention": "{name} mentioned you",
-  "notification.own_poll": "Your poll has ended",
-  "notification.poll": "A poll you have voted in has ended",
+  "notification.follow": "{name} က သင့်ကို စောင့်ကြည့်ခဲ့သည်",
+  "notification.follow_request": "{name} က သင့်ကို စောင့်ကြည့်ရန် တောင်းဆိုထားသည်",
+  "notification.mention": "{name} က သင့်ကို ဖော်ပြခဲ့သည်",
+  "notification.own_poll": "စစ်တမ်းကောက်မှု ပြီးဆုံးပါပြီ",
+  "notification.poll": "သင်ပါဝင်ခဲ့သော စစ်တမ်းပြီးပါပြီ",
   "notification.reblog": "{name} boosted your status",
-  "notification.status": "{name} just posted",
-  "notification.update": "{name} edited a post",
-  "notifications.clear": "Clear notifications",
-  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
-  "notifications.column_settings.admin.report": "New reports:",
-  "notifications.column_settings.admin.sign_up": "New sign-ups:",
-  "notifications.column_settings.alert": "Desktop notifications",
-  "notifications.column_settings.favourite": "Favourites:",
-  "notifications.column_settings.filter_bar.advanced": "Display all categories",
-  "notifications.column_settings.filter_bar.category": "Quick filter bar",
-  "notifications.column_settings.filter_bar.show_bar": "Show filter bar",
-  "notifications.column_settings.follow": "New followers:",
-  "notifications.column_settings.follow_request": "New follow requests:",
-  "notifications.column_settings.mention": "Mentions:",
-  "notifications.column_settings.poll": "Poll results:",
-  "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.reblog": "Boosts:",
-  "notifications.column_settings.show": "Show in column",
-  "notifications.column_settings.sound": "Play sound",
-  "notifications.column_settings.status": "New posts:",
-  "notifications.column_settings.unread_notifications.category": "Unread notifications",
-  "notifications.column_settings.unread_notifications.highlight": "Highlight unread notifications",
-  "notifications.column_settings.update": "Edits:",
-  "notifications.filter.all": "All",
-  "notifications.filter.boosts": "Boosts",
-  "notifications.filter.favourites": "Favourites",
-  "notifications.filter.follows": "Follows",
-  "notifications.filter.mentions": "Mentions",
-  "notifications.filter.polls": "Poll results",
-  "notifications.filter.statuses": "Updates from people you follow",
-  "notifications.grant_permission": "Grant permission.",
-  "notifications.group": "{count} notifications",
-  "notifications.mark_as_read": "Mark every notification as read",
-  "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
-  "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
-  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
-  "notifications_permission_banner.enable": "Enable desktop notifications",
-  "notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.",
-  "notifications_permission_banner.title": "Never miss a thing",
-  "picture_in_picture.restore": "Put it back",
-  "poll.closed": "Closed",
-  "poll.refresh": "Refresh",
-  "poll.total_people": "{count, plural, one {# person} other {# people}}",
+  "notification.status": "{name} က အခုလေးတင် ပို့စ်တင်လိုက်ပါပြီ",
+  "notification.update": "{name} က ပို့စ်တစ်ခုကို ပြင်ဆင်ခဲ့သည်",
+  "notifications.clear": "အသိပေးချက်များအား ရှင်းလင်းပါ",
+  "notifications.clear_confirmation": "သတိပေးချက်အားလုံးကို အပြီးတိုင်ဖယ်ရှားမည်",
+  "notifications.column_settings.admin.report": "တိုင်ကြားစာအသစ်များ -",
+  "notifications.column_settings.admin.sign_up": "အကောင့်အသစ်များ -",
+  "notifications.column_settings.alert": "Desktop သတိပေးချက်များ",
+  "notifications.column_settings.favourite": "ကြိုက်နှစ်သက်မှုများ",
+  "notifications.column_settings.filter_bar.advanced": "ခေါင်းစဥ်အားလုံးများကိုဖော်ပြပါ",
+  "notifications.column_settings.filter_bar.category": "အမြန်စစ်ထုတ်မှုဘား",
+  "notifications.column_settings.filter_bar.show_bar": "စစ်ထုတ်မှုဘားကို ပြပါ",
+  "notifications.column_settings.follow": "စောင့်ကြည့်သူအသစ်များ -",
+  "notifications.column_settings.follow_request": "စောင့်ကြည့်ရန် တောင်းဆိုမှုအသစ်များ -",
+  "notifications.column_settings.mention": "ဖော်ပြချက်များ -",
+  "notifications.column_settings.poll": "စစ်တမ်းရလဒ်",
+  "notifications.column_settings.push": "အသိပေးချက်များအား ရအောင်ပို့ခြင်း",
+  "notifications.column_settings.reblog": "Boosts -",
+  "notifications.column_settings.show": "ကော်လံတွင်ပြပါ",
+  "notifications.column_settings.sound": "အသံဖွင့်မည်",
+  "notifications.column_settings.status": "ပို့စ်အသစ်များ -",
+  "notifications.column_settings.unread_notifications.category": "မဖတ်ရသေးသောအသိပေးချက်များ -",
+  "notifications.column_settings.unread_notifications.highlight": "မဖတ်ရသေးသော အသိပေးချက်များကို ဖော်ပြပါ",
+  "notifications.column_settings.update": "ပြင်ဆင်ထားမှုများ -",
+  "notifications.filter.all": "အားလုံး",
+  "notifications.filter.boosts": "အားပေးမည်",
+  "notifications.filter.favourites": "ကြိုက်နှစ်သက်မှုများ",
+  "notifications.filter.follows": "စောင့်ကြည့်မယ်",
+  "notifications.filter.mentions": " မန်းရှင်းမည်",
+  "notifications.filter.polls": "စစ်တမ်းရလဒ်",
+  "notifications.filter.statuses": "သင်စောင့်ကြည့်သူများထံမှ အပ်ဒိတ်များ",
+  "notifications.grant_permission": "ခွင့်ပြုချက်ပေးမည်",
+  "notifications.group": "အသိပေးချက်များ {count} ခု",
+  "notifications.mark_as_read": "အသိပေးချက်တိုင်းကို ဖတ်ပြီးကြောင်း အမှတ်အသားပြုပါ",
+  "notifications.permission_denied": "ဘရောက်ဆာခွင့်ပြုချက်များ တောင်းဆိုမှုအား ငြင်းခဲ့သောကြောင့် ဒက်စ်တော့ အသိပေးချက်များကို မရရှိနိုင်ပါ",
+  "notifications.permission_denied_alert": "ဘရောက်ဆာခွင့်ပြုချက်ကို ငြင်းပယ်ခဲ့သောကြောင့် ဒက်စ်တော့ အသိပေးချက်များကို ဖွင့်၍မရပါ",
+  "notifications.permission_required": "လိုအပ်သောခွင့်ပြုချက်ကို မပေးထားသောကြောင့် ဒက်စ်တော့ အသိပေးချက်များကို မရရှိနိုင်ပါ။",
+  "notifications_permission_banner.enable": "ဒက်စ်တော့ အသိပေးချက်များကို ဖွင့်ပါ",
+  "notifications_permission_banner.how_to_control": "Mastodon မဖွင့်သည့်အခါ အကြောင်းကြားချက်များကို လက်ခံရယူရန်၊ ဒက်စ်တော့ အသိပေးချက်များကို ဖွင့်ပါ။ ၎င်းတို့ကို ဖွင့်ပြီးသည်နှင့် ၎င်းတို့ကို ဖွင့်ပြီးသည်နှင့် အထက် {icon} ခလုတ်မှ ဒက်စ်တော့ အသိပေးချက်များကို ထုတ်ပေးသည့် အပြန်အလှန်တုံ့ပြန်မှု အမျိုးအစားများကို သင် အတိအကျ ထိန်းချုပ်နိုင်သည်။",
+  "notifications_permission_banner.title": "လက်လွတ်မခံပါနှင့်",
+  "picture_in_picture.restore": "ပြန်ထားပါ",
+  "poll.closed": "ပိတ်သွားပြီ",
+  "poll.refresh": "ပြန်ဖွင့်မည်",
+  "poll.total_people": "{count, plural, one {# person} other {# people}}\n",
   "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
-  "poll.vote": "Vote",
-  "poll.voted": "You voted for this answer",
+  "poll.vote": "မဲပေးမည်",
+  "poll.voted": "သင်ဤအဖြေကိုမဲပေးခဲ့သည်",
   "poll.votes": "{votes, plural, one {# vote} other {# votes}}",
-  "poll_button.add_poll": "Add a poll",
-  "poll_button.remove_poll": "Remove poll",
+  "poll_button.add_poll": "စစ်တမ်းကောက်မည်",
+  "poll_button.remove_poll": "စစ်တမ်းပယ်ဖျက်မည်",
   "privacy.change": "Adjust status privacy",
-  "privacy.direct.long": "Visible for mentioned users only",
+  "privacy.direct.long": "မန်းရှင်းခေါ်သူသီးသန့်",
   "privacy.direct.short": "Direct",
-  "privacy.private.long": "Visible for followers only",
-  "privacy.private.short": "Followers-only",
-  "privacy.public.long": "Visible for all",
-  "privacy.public.short": "Public",
-  "privacy.unlisted.long": "Visible for all, but opted-out of discovery features",
-  "privacy.unlisted.short": "Unlisted",
-  "privacy_policy.last_updated": "Last updated {date}",
-  "privacy_policy.title": "Privacy Policy",
-  "refresh": "Refresh",
-  "regeneration_indicator.label": "Loading…",
-  "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+  "privacy.private.long": "ဖော်လိုးလုပ်သူသီးသန့်",
+  "privacy.private.short": "စောင့်ကြည့်သူများသာ",
+  "privacy.public.long": "အားလုံး မြင်နိုင်သည်",
+  "privacy.public.short": "အများကိုပြမည်",
+  "privacy.unlisted.long": "အားလုံးမြင်နိုင်သော်လည်း ရှာဖွေမှုများမှ ဖယ်ထုတ်ထားသည်",
+  "privacy.unlisted.short": "စာရင်းမသွင်းထားပါ",
+  "privacy_policy.last_updated": "နောက်ဆုံး ပြင်ဆင်ခဲ့သည့်ရက်စွဲ {date}",
+  "privacy_policy.title": "ကိုယ်ရေးအချက်အလက်မူဝါဒ",
+  "refresh": "ပြန်လည်စတင်ပါ",
+  "regeneration_indicator.label": "လုပ်ဆောင်နေသည်…",
+  "regeneration_indicator.sublabel": "သင့်ပင်မစာမျက်နှာကို ပြင်ဆင်နေပါသည်။",
   "relative_time.days": "{number}d",
   "relative_time.full.days": "{number, plural, one {# day} other {# days}} ago",
   "relative_time.full.hours": "{number, plural, one {# hour} other {# hours}} ago",
-  "relative_time.full.just_now": "just now",
+  "relative_time.full.just_now": "အခုလေးတင်",
   "relative_time.full.minutes": "{number, plural, one {# minute} other {# minutes}} ago",
   "relative_time.full.seconds": "{number, plural, one {# second} other {# seconds}} ago",
   "relative_time.hours": "{number}h",
-  "relative_time.just_now": "now",
+  "relative_time.just_now": "ယခု",
   "relative_time.minutes": "{number}m",
   "relative_time.seconds": "{number}s",
-  "relative_time.today": "today",
-  "reply_indicator.cancel": "Cancel",
-  "report.block": "Block",
-  "report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.",
-  "report.categories.other": "Other",
-  "report.categories.spam": "Spam",
-  "report.categories.violation": "Content violates one or more server rules",
-  "report.category.subtitle": "Choose the best match",
-  "report.category.title": "Tell us what's going on with this {type}",
+  "relative_time.today": "ယနေ့",
+  "reply_indicator.cancel": "ပယ်ဖျက်မည်",
+  "report.block": "ဘလော့မည်",
+  "report.block_explanation": "၎င်းတို့ရဲ့ ပို့စ်တွေကို မြင်ရမှာမဟုတ်ဘူး။ သင့်ပို့စ်များကို မမြင်နိုင်သလို သင့်ကို စောင့်ကြည့်၍ရမည်လည်းမဟုတ်ပါ။ ၎င်းတို့ကို ပိတ်ပင်ထားပါသည်။",
+  "report.categories.other": "အခြား",
+  "report.categories.spam": "ပြင်ပစာများ",
+  "report.categories.violation": "ဤစာတွင် သတ်မှတ်ထားသောစည်းကမ်းများကို ဖောက်ဖျက်သောအကြောင်းအရာပါဝင်နေသည်",
+  "report.category.subtitle": "အကိုက်ညီဆုံးကိုရွေးချယ်ပါ",
+  "report.category.title": "ဤ {type} တွင် ဘာဖြစ်နေသည်ကို ပြောပြပါ",
   "report.category.title_account": "ကိုယ်ရေးမှတ်တမ်း",
-  "report.category.title_status": "post",
-  "report.close": "Done",
-  "report.comment.title": "Is there anything else you think we should know?",
-  "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.mute": "Mute",
-  "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.",
-  "report.next": "Next",
+  "report.category.title_status": "ပို့စ်",
+  "report.close": "ပြီးပြီ",
+  "report.comment.title": "မိမိထင်မြင်ယူဆချက်များကိုဖော်ပြပေးပါ",
+  "report.forward": "{target} သို့တစ်ဆင့်ပို့ပေးမည်",
+  "report.forward_hint": "ဤအကောင့်မှာတစ်ခြားဆာဗာမှဖြစ်သည်။ အမည်မသိတိုင်းကြားချက်ဖွင့်လိုပါသလား?",
+  "report.mute": "ပိတ်ထားရန်",
+  "report.mute_explanation": "၎င်းတို့၏ပို့စ်များကို သင်မြင်ရမည်မဟုတ်ပါ။ ၎င်းတို့မှာ သင့်ကို စောင့်ကြည့်၍ ပို့စ်များကို မြင်နိုင်ဆဲဖြစ်ပြီး ၎င်းတို့ကို အသံပိတ်ထားကြောင်း သိမည်မဟုတ်ပါ။\n",
+  "report.next": "ရှေ့သို့",
   "report.placeholder": "Type or paste additional comments",
-  "report.reasons.dislike": "I don't like it",
-  "report.reasons.dislike_description": "It is not something you want to see",
-  "report.reasons.other": "It's something else",
-  "report.reasons.other_description": "The issue does not fit into other categories",
-  "report.reasons.spam": "It's spam",
-  "report.reasons.spam_description": "Malicious links, fake engagement, or repetitive replies",
-  "report.reasons.violation": "It violates server rules",
-  "report.reasons.violation_description": "You are aware that it breaks specific rules",
-  "report.rules.subtitle": "Select all that apply",
-  "report.rules.title": "Which rules are being violated?",
-  "report.statuses.subtitle": "Select all that apply",
-  "report.statuses.title": "Are there any posts that back up this report?",
+  "report.reasons.dislike": "မကြိုက်ပါ",
+  "report.reasons.dislike_description": "ပိုမိုမြင်လိုသည်ရှိပါသလား",
+  "report.reasons.other": "တစ်ခုခုဖြစ်နေသည်",
+  "report.reasons.other_description": "ဤအချက်မှာ အခြားအမျိုးအစားများနှင့် မကိုက်ညီပါ။",
+  "report.reasons.spam": "၎င်းမှာ Spam ဖြစ်သည်",
+  "report.reasons.spam_description": "အန္တရာယ်ရှိသော လင့်ခ်များ၊ အတုအယောင်များ သို့မဟုတ် ထပ်တလဲလဲ ပြန်ကြားမှုများ",
+  "report.reasons.violation": "၎င်းမှာ ဆာဗာစည်းမျဉ်းများကို ချိုးဖောက်ထားသည်",
+  "report.reasons.violation_description": "သတ်မှတ်ထားသော စည်းကမ်းများကို ချိုးဖောက်ထားကြောင်း သင် သိရှိထားသည်။",
+  "report.rules.subtitle": "သက်ဆိုင်သမျှကို ရွေးပါ",
+  "report.rules.title": "မည်သည့်စည်းကမ်းများကို ချိုးဖောက်ထားပါသလဲ။",
+  "report.statuses.subtitle": "သက်ဆိုင်သမျှကို ရွေးပါ",
+  "report.statuses.title": "ဤတိုင်ကြားစာကို အရန်ကူးထားသည့် ပို့စ်များ ရှိပါသလား။",
   "report.submit": "Submit report",
   "report.target": "Report {target}",
-  "report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:",
-  "report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:",
-  "report.thanks.title": "Don't want to see this?",
-  "report.thanks.title_actionable": "Thanks for reporting, we'll look into this.",
-  "report.unfollow": "Unfollow @{name}",
-  "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.",
+  "report.thanks.take_action": "ဤသည်မှာ Mastodon တွင် သင်မြင်ရသည့်အရာများ ထိန်းချုပ်ရန်အတွက် ရွေးချယ်ရမည့်နေရာဖြစ်သည် -",
+  "report.thanks.take_action_actionable": "၎င်းကို ကျွန်ုပ်တို့ သုံးသပ်နေချိန်တွင် သင်သည် @{name} ကို အရေးယူနိုင်သည် -",
+  "report.thanks.title": "ဒါကို မမြင်ချင်ဘူးလား။",
+  "report.thanks.title_actionable": "တိုင်ကြားခြင်းအတွက် ကျေးဇူးတင်ပါသည်၊ ဤအရာကို ကျွန်ုပ်တို့ စစ်ဆေးပါမည်။",
+  "report.unfollow": "@{name} ကို မစောင့်ကြည့်တော့ပါ",
+  "report.unfollow_explanation": "သင် ဤအကောင့်ကို စောင့်ကြည့်နေသည်။ သင့်ပင်မစာမျက်နှာတွင် ၎င်းတို့၏ပို့စ်များကို ထပ်ပြီးမတွေ့ချင်တော့ပါက စောင့်ကြည့်ခြင်းကို ရပ်တန့်နိုင်ပါသည်။",
   "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached",
-  "report_notification.categories.other": "Other",
+  "report_notification.categories.other": "အခြား",
   "report_notification.categories.spam": "Spam",
-  "report_notification.categories.violation": "Rule violation",
-  "report_notification.open": "Open report",
-  "search.placeholder": "Search",
-  "search.search_or_paste": "Search or paste URL",
-  "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.all": "All",
+  "report_notification.categories.violation": "စည်းကမ်းဖောက်ဖျက်ခြင်း",
+  "report_notification.open": "အစီရင်ခံစာကိုဖွင့်ပါ",
+  "search.no_recent_searches": "လတ်တလောရှာဖွေမှုများမရှိပါ။",
+  "search.placeholder": "ရှာဖွေရန်",
+  "search.quick_action.account_search": "{x} နှင့် ကိုက်ညီသော ပရိုဖိုင်များ",
+  "search.quick_action.go_to_account": "ပရိုဖိုင် {x} သို့သွားမည်",
+  "search.quick_action.go_to_hashtag": "hashtag {x} သို့သွားမည်",
+  "search.quick_action.open_url": "Mastodon တွင် URL ကိုဖွင့်မည်",
+  "search.quick_action.status_search": "{x} နှင့် ကိုက်ညီသော ပို့စ်များ",
+  "search.search_or_paste": "URL ရိုက်ထည့်ပါ သို့မဟုတ် ရှာဖွေပါ",
+  "search_popout.quick_actions": "အမြန်လုပ်ဆောင်မှုများ",
+  "search_popout.recent": "လတ်တလော ရှာဖွေမှုများ",
+  "search_results.accounts": "စာမျက်နှာ",
+  "search_results.all": "အားလုံး",
   "search_results.hashtags": "ဟက်ရှ်တက်များ",
-  "search_results.nothing_found": "Could not find anything for these search terms",
-  "search_results.statuses": "Posts",
-  "search_results.statuses_fts_disabled": "Searching posts by their content is not enabled on this Mastodon server.",
-  "search_results.title": "Search for {q}",
+  "search_results.nothing_found": "ရှာဖွေလိုသောအရာမရှိပါ",
+  "search_results.statuses": "ပို့စ်တင်မယ်",
+  "search_results.statuses_fts_disabled": "ဤ Mastodon ဆာဗာတွင် ၎င်းတို့၏ အကြောင်းအရာအလိုက် ပို့စ်များရှာဖွေခြင်းကို ဖွင့်မထားပါ။",
+  "search_results.title": "{q} ကို ရှာပါ",
   "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
-  "server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
-  "server_banner.active_users": "active users",
-  "server_banner.administered_by": "Administered by:",
-  "server_banner.introduction": "{domain} is part of the decentralized social network powered by {mastodon}.",
-  "server_banner.learn_more": "Learn more",
-  "server_banner.server_stats": "Server stats:",
-  "sign_in_banner.create_account": "Create account",
-  "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
-  "status.admin_account": "Open moderation interface for @{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "server_banner.about_active_users": "ပြီးခဲ့သည့် ရက်ပေါင်း ၃၀ အတွင်း ဤဆာဗာကို အသုံးပြုသူများ (လအလိုက် လက်ရှိအသုံးပြုသူများ)",
+  "server_banner.active_users": "လက်ရှိအသုံးပြုသူများ",
+  "server_banner.administered_by": "မှ စီမံခန့်ခွဲသည် -",
+  "server_banner.introduction": "{domain} သည် {mastodon} မှ ပံ့ပိုးပေးထားသော ဗဟိုချုပ်ကိုင်မှုမရှိသည့် လူမှုကွန်ရက်တစ်ခုဖြစ်သည်။",
+  "server_banner.learn_more": "ပိုမိုသိရှိရန်",
+  "server_banner.server_stats": "ဆာဗာအား လက်ရှိအသုံးပြုသူများ -",
+  "sign_in_banner.create_account": "အကောင့်ဖန်တီးမည်",
+  "sign_in_banner.sign_in": "အကောင့်ဝင်မည်",
+  "sign_in_banner.text": "ပရိုဖိုင်များ သို့မဟုတ် hashtags များ၊ အကြိုက်ဆုံး၊ မျှဝေပြီး ပို့စ်များနှင့် ပို့စ် ပြန်ကြားစာများ ကြည့်ရှုရန်အတွက် အကောင့်ဝင်ရောက်ပါ။ အခြားဆာဗာတစ်ခုပေါ်ရှိ သင့်အကောင့်မှလည်း အပြန်အလှန် ဖလှယ်နိုင်ပါသည်။",
+  "status.admin_account": "@{name} အတွက် စိစစ်ခြင်းကြားခံနယ်ကို ဖွင့်ပါ",
+  "status.admin_domain": "{domain} အတွက် စိစစ်ခြင်းကြားခံနယ်ကို ဖွင့်ပါ",
   "status.admin_status": "Open this status in the moderation interface",
-  "status.block": "Block @{name}",
-  "status.bookmark": "Bookmark",
+  "status.block": "@{name} ကိုဘလော့မည်",
+  "status.bookmark": "မှတ်ထားသည်များ",
   "status.cancel_reblog_private": "Unboost",
-  "status.cannot_reblog": "This post cannot be boosted",
+  "status.cannot_reblog": "ဤပို့စ်ကို Boost ၍ မရပါ",
   "status.copy": "Copy link to status",
-  "status.delete": "Delete",
-  "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
-  "status.edit": "Edit",
-  "status.edited": "Edited {date}",
-  "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
+  "status.delete": "ဖျက်ရန်",
+  "status.detailed_status": "အသေးစိတ်စကားပြောဆိုမှုမြင်ကွင်း",
+  "status.direct": "@{name} ကို သီးသန့်ဖော်ပြမည်\n",
+  "status.direct_indicator": "သီးသန့်ဖော်ပြခြင်း။",
+  "status.edit": "ပြင်ဆင်ရန်",
+  "status.edited": "{date} ကို ပြင်ဆင်ပြီးပါပြီ",
+  "status.edited_x_times": "{count, plural, one {{count} time} other {{count} times}} ပြင်ဆင်ခဲ့သည်",
   "status.embed": "Embed",
-  "status.favourite": "Favourite",
-  "status.filter": "Filter this post",
-  "status.filtered": "Filtered",
-  "status.hide": "Hide post",
-  "status.history.created": "{name} created {date}",
-  "status.history.edited": "{name} edited {date}",
-  "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 post",
-  "status.read_more": "Read more",
+  "status.favourite": "ကြိုက်နှစ်သက်မှုများ",
+  "status.filter": "ဤပို့စ်ကို စစ်ထုတ်ပါ",
+  "status.filtered": "စစ်ထုတ်ထားသည်",
+  "status.hide": "ပို့စ်ကိုပိတ်ထားမည်",
+  "status.history.created": "{name} က {date} က ဖန်တီးခဲ့သည်",
+  "status.history.edited": "{name} က {date} က ပြင်ဆင်ခဲ့သည်",
+  "status.load_more": "ပို၍ဆောင်ရွက်ပါ",
+  "status.media_hidden": "မီဒီယာကို ဖျောက်ထားပါ",
+  "status.mention": "@{name} ကို ဖော်ပြထားသည်",
+  "status.more": "နောက်ထပ်",
+  "status.mute": "@{name} ကို ပိတ်ထားရန်",
+  "status.mute_conversation": "စကားဝိုင်းကို ပိတ်ထားရန်",
+  "status.open": "ပို့စ်ကိုချဲ့ထွင်မည်",
+  "status.pin": "ပရိုဖိုင်တွင် ပင်ထားပါ",
+  "status.pinned": "ပင်တွဲထားသောပို့စ်",
+  "status.read_more": "ပိုမိုဖတ်ရှုရန်",
   "status.reblog": "Boost",
-  "status.reblog_private": "Boost with original visibility",
-  "status.reblogged_by": "{name} boosted",
-  "status.reblogs.empty": "No one has boosted this post yet. When someone does, they will show up here.",
-  "status.redraft": "Delete & re-draft",
-  "status.remove_bookmark": "Remove bookmark",
-  "status.replied_to": "Replied to {name}",
-  "status.reply": "Reply",
-  "status.replyAll": "Reply to thread",
-  "status.report": "Report @{name}",
-  "status.sensitive_warning": "Sensitive content",
-  "status.share": "Share",
-  "status.show_filter_reason": "Show anyway",
-  "status.show_less": "Show less",
-  "status.show_less_all": "Show less for all",
-  "status.show_more": "Show more",
-  "status.show_more_all": "Show more for all",
-  "status.show_original": "Show original",
-  "status.translate": "Translate",
-  "status.translated_from_with": "Translated from {lang} using {provider}",
-  "status.uncached_media_warning": "Not available",
-  "status.unmute_conversation": "Unmute conversation",
-  "status.unpin": "Unpin from profile",
-  "subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
-  "subscribed_languages.save": "Save changes",
-  "subscribed_languages.target": "Change subscribed languages for {target}",
-  "suggestions.dismiss": "Dismiss suggestion",
-  "suggestions.header": "You might be interested in…",
-  "tabs_bar.federated_timeline": "Federated",
-  "tabs_bar.home": "Home",
-  "tabs_bar.local_timeline": "Local",
-  "tabs_bar.notifications": "Notifications",
+  "status.reblog_private": "မူရင်းပုံစံဖြင့် Boost လုပ်ပါ",
+  "status.reblogged_by": "{name} က Boost လုပ်ထားသည်",
+  "status.reblogs.empty": "ဤပို့စ်ကို မည်သူမှ Boost လုပ်ထားခြင်းမရှိသေးပါ။ တစ်ယောက်ယောက်မှ Boost လုပ်ပါက ဤနေရာတွင်ပေါ်လာပါမည်။",
+  "status.redraft": "ဖျက်ပြီး ပြန်ရေးပါ",
+  "status.remove_bookmark": "မှတ်ထားသည်များကို ဖယ်ရှားပါ",
+  "status.replied_to": "{name} ကို စာပြန်ခဲ့သည်",
+  "status.reply": "စာပြန်ရန်",
+  "status.replyAll": "thread သို့ စာပြန်ပါ",
+  "status.report": "@{name} ကို တိုင်ကြားရန်",
+  "status.sensitive_warning": "သတိထားရသော အကြောင်းအရာ",
+  "status.share": "မျှဝေ",
+  "status.show_filter_reason": "မည်သို့ပင်ဖြစ်စေ ပြပါ",
+  "status.show_less": "အနည်းငယ်သာ ပြပါ",
+  "status.show_less_all": "အားလုံးအတွက် အနည်းငယ်သာ ပြပါ",
+  "status.show_more": "ပိုမိုပြရန်",
+  "status.show_more_all": "အားလုံးအတွက် ပိုပြပါ",
+  "status.show_original": "မူရင်းပြပါ",
+  "status.translate": "ဘာသာပြန်ပါ",
+  "status.translated_from_with": "{provider} ကို အသုံးပြု၍ {lang} မှ ဘာသာပြန်ထားသည်",
+  "status.uncached_media_warning": "မရနိုင်ပါ",
+  "status.unmute_conversation": "စကားဝိုင်းကို ပိတ်ထားရန်",
+  "status.unpin": "ပရိုဖိုင်မှ ပင်ဖြုတ်ပါ။",
+  "subscribed_languages.lead": "ပြောင်းလဲပြီးနောက် ရွေးချယ်ထားသော ဘာသာစကားများ၏ ပို့စ်များကိုသာ သင့် ပင်မစာမျက်နှာနှင့် စာရင်းစာမျက်နှာတွင်သာ ပေါ်လာမည်ဖြစ်ပါသည်။ ဘာသာစကားအားလုံး၏ ပို့စ်များအား ကြည့်ရှုလိုပါက အပြောင်းအလဲမပြုလုပ်ပါနှင့်။",
+  "subscribed_languages.save": "ပြောင်းလဲမှုများကို သိမ်းဆည်းပါ",
+  "subscribed_languages.target": "{target} အတွက် စာရင်းသွင်းထားသော ဘာသာစကားများကို ပြောင်းပါ",
+  "suggestions.dismiss": "အကြံပြုချက်ကို ဖယ်လိုက်ပါ",
+  "suggestions.header": "စိတ်ဝင်စားနိုင်သည်...",
+  "tabs_bar.federated_timeline": "ဖက်ဒီ",
+  "tabs_bar.home": "ပင်မစာမျက်နှာ",
+  "tabs_bar.local_timeline": "ပြည်တွင်း",
+  "tabs_bar.notifications": "အသိပေးချက်များ",
   "time_remaining.days": "{number, plural, one {# day} other {# days}} left",
   "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left",
   "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
-  "time_remaining.moments": "Moments remaining",
+  "time_remaining.moments": "အခိုက်အတန့်များ ကျန်ရှိနေသေးသည်",
   "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
-  "timeline_hint.remote_resource_not_displayed": "{resource} from other servers are not displayed.",
-  "timeline_hint.resources.followers": "Followers",
-  "timeline_hint.resources.follows": "Follows",
-  "timeline_hint.resources.statuses": "Older posts",
+  "timeline_hint.remote_resource_not_displayed": "အခြားဆာဗာများမှ {resource} ကို ပြသမည်မဟုတ်ပါ။",
+  "timeline_hint.resources.followers": "စောင့်ကြည့်သူများ",
+  "timeline_hint.resources.follows": "စောင့်ကြည့်မယ်",
+  "timeline_hint.resources.statuses": "ပို့စ်အဟောင်းများ",
   "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {{days} days}}",
-  "trends.trending_now": "Trending now",
-  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "trends.trending_now": "လက်ရှိခေတ်စားနေသော ပို့စ်များ",
+  "ui.beforeunload": "Mastodon မှ ထွက်ခွာပါက သင့်မူကြမ်း ဆုံးရှုံးသွားပါမည်။",
   "units.short.billion": "{count}B",
   "units.short.million": "{count}M",
   "units.short.thousand": "{count}K",
-  "upload_area.title": "Drag & drop to upload",
-  "upload_button.label": "Add images, a video or an audio file",
-  "upload_error.limit": "File upload limit exceeded.",
-  "upload_error.poll": "File upload not allowed with polls.",
-  "upload_form.audio_description": "Describe for people with hearing loss",
-  "upload_form.description": "Describe for the visually impaired",
-  "upload_form.description_missing": "No description added",
-  "upload_form.edit": "Edit",
-  "upload_form.thumbnail": "Change thumbnail",
-  "upload_form.undo": "Delete",
-  "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
-  "upload_modal.analyzing_picture": "Analyzing picture…",
-  "upload_modal.apply": "Apply",
-  "upload_modal.applying": "Applying…",
-  "upload_modal.choose_image": "Choose image",
-  "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
-  "upload_modal.detect_text": "Detect text from picture",
-  "upload_modal.edit_media": "Edit media",
-  "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.",
-  "upload_modal.preparing_ocr": "Preparing OCR…",
-  "upload_modal.preview_label": "Preview ({ratio})",
-  "upload_progress.label": "Uploading…",
-  "upload_progress.processing": "Processing…",
-  "video.close": "Close video",
-  "video.download": "Download file",
-  "video.exit_fullscreen": "Exit full screen",
-  "video.expand": "Expand video",
-  "video.fullscreen": "Full screen",
-  "video.hide": "Hide video",
-  "video.mute": "Mute sound",
-  "video.pause": "Pause",
-  "video.play": "Play",
-  "video.unmute": "Unmute sound"
+  "upload_area.title": "ပို့စ်တင်ရန် ဆွဲချလိုက်ပါ",
+  "upload_button.label": "ပုံများ၊ ဗီဒီယို သို့မဟုတ် အသံဖိုင်တစ်ခု ထည့်ပါ",
+  "upload_error.limit": "ဖိုင်အများဆုံးတင်နိုင်မည့်ကန့်သတ်ချက်ကို ကျော်သွားပါပြီ။",
+  "upload_error.poll": "စစ်တမ်းနှင့်အတူဖိုင်များတင်ခွင့်မပြုပါ",
+  "upload_form.audio_description": "အကြားအာရုံချို့ယွင်းသော ခက်ခဲသောသူများအတွက် ဖော်ပြထားသည်",
+  "upload_form.description": "အမြင်အာရုံချို့ယွင်းသော ခက်ခဲသောသူများအတွက် ဖော်ပြထားသည်",
+  "upload_form.description_missing": "ဖော်ပြချက် မထည့်ပါ",
+  "upload_form.edit": "ပြင်ရန်",
+  "upload_form.thumbnail": "ပုံသေးကို ပြောင်းပါ",
+  "upload_form.undo": "ဖျက်ရန်",
+  "upload_form.video_description": "အမြင်အာရုံနှင့်အကြားအာရုံ ချို့ယွင်းသော ခက်ခဲသောသူများအတွက် ဖော်ပြထားသည်",
+  "upload_modal.analyzing_picture": "ပုံအား ပိုင်းခြားစိတ်ဖြာနေသည်...",
+  "upload_modal.apply": "သုံးပါ",
+  "upload_modal.applying": "အသုံးချနေသည်...",
+  "upload_modal.choose_image": "ပုံရွေးပါ",
+  "upload_modal.description_placeholder": "သီဟိုဠ်မှ ဉာဏ်ကြီးရှင်သည် အာယုဝဍ္ဎနဆေးညွှန်းစာကို ဇလွန်ဈေးဘေး ဗာဒံပင်ထက် အဓိဋ္ဌာန်လျက် ဂဃနဏဖတ်ခဲ့သည်",
+  "upload_modal.detect_text": "ပုံမှစာသားကို ရှာဖွေပါ",
+  "upload_modal.edit_media": "မီဒီယာကို ပြင်ဆင်ရန်",
+  "upload_modal.hint": "ပုံသေးအားလုံးတွင် အမြဲတမ်းကြည့်ရှုနိုင်သည့် focal point ကို ရွေးချယ်ရန် Preview ပေါ်ရှိ စက်ဝိုင်းကို နှိပ်ပါ သို့မဟုတ် ဖိဆွဲပါ။",
+  "upload_modal.preparing_ocr": "OCR ပြင်ဆင်နေသည်…",
+  "upload_modal.preview_label": "({ratio}) အစမ်းကြည့်ရှုရန်",
+  "upload_progress.label": "တင်နေသည်...",
+  "upload_progress.processing": "လုပ်ဆောင်နေသည်…",
+  "video.close": "ဗီဒီယိုကို ပိတ်ပါ",
+  "video.download": "ဖိုင်ကို ဒေါင်းလုဒ်လုပ်ပါ",
+  "video.exit_fullscreen": "မျက်နှာပြင်အပြည့်မှ ထွက်ပါ",
+  "video.expand": "ဗီဒီယိုကို ချဲ့ပါ",
+  "video.fullscreen": "မျက်နှာပြင်အပြည့်",
+  "video.hide": "ဗီဒီယိုကို ဖျောက်ပါ",
+  "video.mute": "အသံပိတ်ထားပါ",
+  "video.pause": "ခဏရပ်ပါ",
+  "video.play": "ဖွင့်ပါ",
+  "video.unmute": "အသံပြန်ဖွင့်ပါ"
 }
diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json
index 479573e84..7be1e0c5c 100644
--- a/app/javascript/mastodon/locales/nl.json
+++ b/app/javascript/mastodon/locales/nl.json
@@ -20,7 +20,7 @@
   "account.blocked": "Geblokkeerd",
   "account.browse_more_on_origin_server": "Zie meer op het originele profiel",
   "account.cancel_follow_request": "Volgverzoek annuleren",
-  "account.direct": "@{name} een direct bericht sturen",
+  "account.direct": "@{name} een privébericht sturen",
   "account.disable_notifications": "Geen melding meer geven wanneer @{name} een bericht plaatst",
   "account.domain_blocked": "Domein geblokkeerd",
   "account.edit_profile": "Profiel bewerken",
@@ -55,7 +55,7 @@
   "account.report": "@{name} rapporteren",
   "account.requested": "Wachten op goedkeuring. Klik om het volgverzoek te annuleren",
   "account.requested_follow": "{name} wil je graag volgen",
-  "account.share": "Profiel van @{name} delen",
+  "account.share": "Account van @{name} delen",
   "account.show_reblogs": "Boosts van @{name} tonen",
   "account.statuses_counter": "{count, plural, one {{counter} bericht} other {{counter} berichten}}",
   "account.unblock": "@{name} deblokkeren",
@@ -102,7 +102,7 @@
   "column.blocks": "Geblokkeerde gebruikers",
   "column.bookmarks": "Bladwijzers",
   "column.community": "Lokale tijdlijn",
-  "column.direct": "Directe berichten",
+  "column.direct": "Privéberichten",
   "column.directory": "Gebruikersgids",
   "column.domain_blocks": "Geblokkeerde domeinen",
   "column.favourites": "Favorieten",
@@ -128,7 +128,7 @@
   "compose.language.search": "Talen zoeken...",
   "compose_form.direct_message_warning_learn_more": "Meer leren",
   "compose_form.encryption_warning": "Berichten op Mastodon worden, net zoals op andere social media, niet end-to-end versleuteld. Deel daarom geen gevoelige informatie via Mastodon.",
-  "compose_form.hashtag_warning": "Dit bericht valt niet onder een hashtag te bekijken, omdat deze niet op openbaar is. Alleen openbare berichten kunnen via hashtags gevonden worden.",
+  "compose_form.hashtag_warning": "Dit bericht valt niet onder een hashtag te bekijken, omdat deze niet openbaar is. Alleen openbare berichten kunnen via hashtags gevonden worden.",
   "compose_form.lock_disclaimer": "Jouw account is niet {locked}. Iedereen kan jou volgen en kan de berichten zien die je alleen aan jouw volgers hebt gericht.",
   "compose_form.lock_disclaimer.lock": "besloten",
   "compose_form.placeholder": "Wat wil je kwijt?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Je hebt niet-opgeslagen wijzigingen in de mediabeschrijving of voorvertonning, wil je deze toch weggooien?",
   "confirmations.domain_block.confirm": "Blokkeer alles van deze server",
   "confirmations.domain_block.message": "Weet je het echt heel erg zeker dat je alles van {domain} wilt blokkeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en beter. Je zult geen berichten van deze server op openbare tijdlijnen zien of in jouw meldingen. Jouw volgers van deze server worden verwijderd.",
+  "confirmations.edit.confirm": "Bewerken",
+  "confirmations.edit.message": "Door nu te reageren overschrijf je het bericht dat je op dit moment aan het schrijven bent. Weet je zeker dat je verder wil gaan?",
   "confirmations.logout.confirm": "Uitloggen",
   "confirmations.logout.message": "Weet je zeker dat je wilt uitloggen?",
   "confirmations.mute.confirm": "Negeren",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Jij hebt nog geen enkele gebruiker geblokkeerd.",
   "empty_column.bookmarked_statuses": "Jij hebt nog geen berichten aan je bladwijzers toegevoegd. Wanneer je er een aan jouw bladwijzers toevoegt, valt deze hier te zien.",
   "empty_column.community": "De lokale tijdlijn is nog leeg. Plaats een openbaar bericht om de spits af te bijten!",
-  "empty_column.direct": "Je hebt nog geen directe berichten. Wanneer je er een verzend of ontvangt, komt deze hier te staan.",
+  "empty_column.direct": "Je hebt nog geen privéberichten. Wanneer je er een verstuurt of ontvangt, zullen deze hier verschijnen.",
   "empty_column.domain_blocks": "Er zijn nog geen geblokkeerde domeinen.",
   "empty_column.explore_statuses": "Momenteel zijn er geen trends. Kom later terug!",
   "empty_column.favourited_statuses": "Jij hebt nog geen favoriete berichten. Wanneer je een bericht als favoriet markeert, valt deze hier te zien.",
   "empty_column.favourites": "Niemand heeft dit bericht nog als favoriet gemarkeerd. Wanneer iemand dit doet, valt dat hier te zien.",
   "empty_column.follow_recommendations": "Het lijkt er op dat er geen aanbevelingen voor jou aangemaakt kunnen worden. Je kunt proberen te zoeken naar mensen die je wellicht kent, zoeken op hashtags, de lokale en globale tijdlijnen bekijken of de gebruikersgids doorbladeren.",
   "empty_column.follow_requests": "Jij hebt nog enkel volgverzoek ontvangen. Wanneer je er eentje ontvangt, valt dat hier te zien.",
+  "empty_column.followed_tags": "Je hebt nog geen hashtags gevolgd. Wanneer je dit doet, zullen ze hier verschijnen.",
   "empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.",
   "empty_column.home": "Deze tijdlijn is leeg! Volg meer mensen om het te vullen. {suggestions}",
   "empty_column.home.suggestions": "Enkele aanbevelingen bekijken",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Goedkeuren",
   "follow_request.reject": "Afwijzen",
   "follow_requests.unlocked_explanation": "Ook al is jouw account niet besloten, de medewerkers van {domain} denken dat jij misschien de volgende volgverzoeken handmatig wil controleren.",
+  "followed_tags": "Gevolgde hashtags",
   "footer.about": "Over",
   "footer.directory": "Gebruikersgids",
   "footer.get_app": "App downloaden",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Sneltoetsen",
   "footer.privacy_policy": "Privacybeleid",
   "footer.source_code": "Broncode bekijken",
+  "footer.status": "Status",
   "generic.saved": "Opgeslagen",
   "getting_started.heading": "Aan de slag",
   "hashtag.column_header.tag_mode.all": "en {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Op één van de kolommen focussen",
   "keyboard_shortcuts.compose": "Tekstveld om een bericht te schrijven focussen",
   "keyboard_shortcuts.description": "Omschrijving",
-  "keyboard_shortcuts.direct": "Directe berichten tonen",
+  "keyboard_shortcuts.direct": "om de kolom met privéberichten te openen",
   "keyboard_shortcuts.down": "Naar beneden in de lijst bewegen",
   "keyboard_shortcuts.enter": "Volledig bericht tonen",
   "keyboard_shortcuts.favourite": "Als favoriet markeren",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bladwijzers",
   "navigation_bar.community_timeline": "Lokale tijdlijn",
   "navigation_bar.compose": "Nieuw bericht schrijven",
-  "navigation_bar.direct": "Directe berichten",
+  "navigation_bar.direct": "Privéberichten",
   "navigation_bar.discover": "Ontdekken",
   "navigation_bar.domain_blocks": "Geblokkeerde domeinen",
   "navigation_bar.edit_profile": "Profiel bewerken",
@@ -379,7 +384,8 @@
   "navigation_bar.favourites": "Favorieten",
   "navigation_bar.filters": "Filters",
   "navigation_bar.follow_requests": "Volgverzoeken",
-  "navigation_bar.follows_and_followers": "Volgers en gevolgden",
+  "navigation_bar.followed_tags": "Gevolgde hashtags",
+  "navigation_bar.follows_and_followers": "Volgers en gevolgde accounts",
   "navigation_bar.lists": "Lijsten",
   "navigation_bar.logout": "Uitloggen",
   "navigation_bar.mutes": "Genegeerde gebruikers",
@@ -450,7 +456,7 @@
   "poll_button.remove_poll": "Poll verwijderen",
   "privacy.change": "Zichtbaarheid van bericht aanpassen",
   "privacy.direct.long": "Alleen aan vermelde gebruikers tonen",
-  "privacy.direct.short": "Direct bericht",
+  "privacy.direct.short": "Privébericht",
   "privacy.private.long": "Alleen aan volgers tonen",
   "privacy.private.short": "Alleen volgers",
   "privacy.public.long": "Voor iedereen zichtbaar",
@@ -481,7 +487,7 @@
   "report.categories.violation": "De inhoud overtreedt een of meerdere serverregels",
   "report.category.subtitle": "Kies wat het beste overeenkomt",
   "report.category.title": "Vertel ons wat er met dit {type} aan de hand is",
-  "report.category.title_account": "profiel",
+  "report.category.title_account": "account",
   "report.category.title_status": "bericht",
   "report.close": "Klaar",
   "report.comment.title": "Zijn er nog andere dingen waarvan je denkt dat wij dat moeten weten?",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Overtreden regel(s)",
   "report_notification.open": "Rapportage openen",
+  "search.no_recent_searches": "Geen recente zoekopdrachten",
   "search.placeholder": "Zoeken",
+  "search.quick_action.account_search": "Accounts die overeenkomen met {x}",
+  "search.quick_action.go_to_account": "Ga naar account {x}",
+  "search.quick_action.go_to_hashtag": "Ga naar hashtag {x}",
+  "search.quick_action.open_url": "URL in Mastodon openen",
+  "search.quick_action.status_search": "Berichten die overeenkomen met {x}",
   "search.search_or_paste": "Zoek of voer een URL in",
-  "search_popout.search_format": "Geavanceerd zoeken",
-  "search_popout.tips.full_text": "Gebruik gewone tekst om te zoeken in jouw berichten, gebooste berichten, favorieten en in berichten waarin je bent vermeldt, en tevens naar gebruikersnamen, weergavenamen en hashtags.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "bericht",
-  "search_popout.tips.text": "Gebruik gewone tekst om te zoeken op weergavenamen, gebruikersnamen en hashtags",
-  "search_popout.tips.user": "gebruiker",
-  "search_results.accounts": "Gebruikers",
+  "search_popout.quick_actions": "Snelle acties",
+  "search_popout.recent": "Recente zoekopdrachten",
+  "search_results.accounts": "Accounts",
   "search_results.all": "Alles",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Deze zoektermen leveren geen resultaat op",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Serverstats:",
   "sign_in_banner.create_account": "Registreren",
   "sign_in_banner.sign_in": "Inloggen",
-  "sign_in_banner.text": "Wanneer je een account op deze server hebt, kun je inloggen om mensen of hashtags te volgen, op berichten te reageren of om deze te delen. Wanneer je een account op een andere server hebt, kun je daar inloggen en daar interactie met mensen op deze server hebben.",
+  "sign_in_banner.text": "Wanneer je een account op deze server hebt, kun je inloggen om mensen of hashtags te volgen, op berichten te reageren of om deze te delen. Wanneer je een account op een andere server hebt, kun je daar inloggen en daar ook interactie met mensen op deze server hebben.",
   "status.admin_account": "Moderatie-omgeving van @{name} openen",
   "status.admin_domain": "Moderatie-omgeving van {domain} openen",
   "status.admin_status": "Dit bericht in de moderatie-omgeving tonen",
@@ -551,11 +559,12 @@
   "status.copy": "Link naar bericht kopiëren",
   "status.delete": "Verwijderen",
   "status.detailed_status": "Uitgebreide gespreksweergave",
-  "status.direct": "@{name} een direct bericht sturen",
+  "status.direct": "@{name} een privébericht sturen",
+  "status.direct_indicator": "Privébericht",
   "status.edit": "Bewerken",
   "status.edited": "Bewerkt op {date}",
   "status.edited_x_times": "{count, plural, one {{count} keer} other {{count} keer}} bewerkt",
-  "status.embed": "Insluiten",
+  "status.embed": "Embedden",
   "status.favourite": "Favoriet",
   "status.filter": "Dit bericht filteren",
   "status.filtered": "Gefilterd",
diff --git a/app/javascript/mastodon/locales/nn.json b/app/javascript/mastodon/locales/nn.json
index e158f67b0..40b59ac4d 100644
--- a/app/javascript/mastodon/locales/nn.json
+++ b/app/javascript/mastodon/locales/nn.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blokkert",
   "account.browse_more_on_origin_server": "Sjå gjennom meir på den opphavlege profilen",
   "account.cancel_follow_request": "Trekk attende fylgeførespurnad",
-  "account.direct": "Send melding til @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Slutt å varsle meg når @{name} skriv innlegg",
   "account.domain_blocked": "Domenet er sperra",
   "account.edit_profile": "Rediger profil",
@@ -34,7 +34,7 @@
   "account.followers.empty": "Ingen fylgjer denne brukaren enno.",
   "account.followers_counter": "{count, plural, one {{counter} fylgjar} other {{counter} fylgjarar}}",
   "account.following": "Fylgjer",
-  "account.following_counter": "{count, plural, one {Fylgjar {counter}} other {Fylgjar {counter}}}",
+  "account.following_counter": "{count, plural, one {Fylgjer {counter}} other {Fylgjer {counter}}}",
   "account.follows.empty": "Denne brukaren fylgjer ikkje nokon enno.",
   "account.follows_you": "Fylgjer deg",
   "account.go_to_profile": "Gå til profil",
@@ -47,7 +47,7 @@
   "account.mention": "Nemn @{name}",
   "account.moved_to": "{name} seier at deira nye konto no er:",
   "account.mute": "Målbind @{name}",
-  "account.mute_notifications": "Målbind varsel frå @{name}",
+  "account.mute_notifications": "Demp varsel frå @{name}",
   "account.muted": "Målbunden",
   "account.open_original_page": "Opne originalsida",
   "account.posts": "Tut",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokkerte brukarar",
   "column.bookmarks": "Bokmerke",
   "column.community": "Lokal tidsline",
-  "column.direct": "Direktemeldingar",
+  "column.direct": "Private mentions",
   "column.directory": "Sjå gjennom profilar",
   "column.domain_blocks": "Skjulte domene",
   "column.favourites": "Favorittar",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Du har ulagra endringar i mediaskildringa eller førehandsvisinga. Vil du forkaste dei likevel?",
   "confirmations.domain_block.confirm": "Skjul alt frå domenet",
   "confirmations.domain_block.message": "Er du heilt, heilt sikker på at du vil skjula heile {domain}? I dei fleste tilfelle er det godt nok og føretrekt med nokre få målretta blokkeringar eller målbindingar. Du kjem ikkje til å sjå innhald frå domenet i fødererte tidsliner eller i varsla dine. Fylgjarane dine frå domenet vert fjerna.",
+  "confirmations.edit.confirm": "Rediger",
+  "confirmations.edit.message": "Å redigera no vil overskriva den meldinga du er i ferd med å skriva. Er du sikker på at du vil halda fram?",
   "confirmations.logout.confirm": "Logg ut",
   "confirmations.logout.message": "Er du sikker på at du vil logga ut?",
   "confirmations.mute.confirm": "Målbind",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Du har ikkje blokkert nokon enno.",
   "empty_column.bookmarked_statuses": "Du har ikkje lagra noko bokmerke enno. Når du set bokmerke på eit innlegg, dukkar det opp her.",
   "empty_column.community": "Den lokale tidslina er tom. Skriv noko offentleg å få ballen til å rulle!",
-  "empty_column.direct": "Du har ingen direktemeldingar enno. Når du sender eller får ei, vil ho dukka opp her.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Det er ingen skjulte domene til no.",
   "empty_column.explore_statuses": "Ingenting er i støytet nett no. Prøv igjen seinare!",
   "empty_column.favourited_statuses": "Du har ingen favoritt-tut ennå. Når du merkjer eit som favoritt, så dukkar det opp her.",
   "empty_column.favourites": "Ingen har merkt dette tutet som favoritt enno. Når nokon gjer det, så dukkar det opp her.",
   "empty_column.follow_recommendations": "Det ser ikkje ut til at noko forslag kunne genererast til deg. Prøv søkjefunksjonen for å finna folk du kjenner, eller utforsk populære emneknaggar.",
   "empty_column.follow_requests": "Du har ingen følgjeførespurnadar ennå. Når du får ein, så vil den dukke opp her.",
+  "empty_column.followed_tags": "Du fylgjer ingen emneknaggar enno. Når du gjer det, vil dei syna her.",
   "empty_column.hashtag": "Det er ingenting i denne emneknaggen enno.",
   "empty_column.home": "Heime-tidslina di er tom! Følg fleire folk for å fylle ho med innhald. {suggestions}",
   "empty_column.home.suggestions": "Sjå nokre forslag",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autoriser",
   "follow_request.reject": "Avvis",
   "follow_requests.unlocked_explanation": "Sjølv om kontoen din ikkje er låst tenkte dei som driv {domain} at du kanskje ville gå gjennom førespurnadar frå desse kontoane manuelt.",
+  "followed_tags": "Fylgde emneknaggar",
   "footer.about": "Om",
   "footer.directory": "Profilmappe",
   "footer.get_app": "Få appen",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Snøggtastar",
   "footer.privacy_policy": "Personvernsreglar",
   "footer.source_code": "Vis kjeldekode",
+  "footer.status": "Status",
   "generic.saved": "Gøymt",
   "getting_started.heading": "Kom i gang",
   "hashtag.column_header.tag_mode.all": "og {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Fokuskolonne",
   "keyboard_shortcuts.compose": "for å fokusera tekstfeltet for skriving",
   "keyboard_shortcuts.description": "Skildring",
-  "keyboard_shortcuts.direct": "for å opna direktemeldingskolonna",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Flytt nedover i lista",
   "keyboard_shortcuts.enter": "Opne innlegg",
   "keyboard_shortcuts.favourite": "Merk som favoritt",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bokmerke",
   "navigation_bar.community_timeline": "Lokal tidsline",
   "navigation_bar.compose": "Lag nytt tut",
-  "navigation_bar.direct": "Direktemeldingar",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Oppdag",
   "navigation_bar.domain_blocks": "Skjulte domene",
   "navigation_bar.edit_profile": "Rediger profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favorittar",
   "navigation_bar.filters": "Målbundne ord",
   "navigation_bar.follow_requests": "Fylgjeførespurnader",
+  "navigation_bar.followed_tags": "Fylgde emneknaggar",
   "navigation_bar.follows_and_followers": "Fylgje og fylgjarar",
   "navigation_bar.lists": "Lister",
   "navigation_bar.logout": "Logg ut",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Søppelpost",
   "report_notification.categories.violation": "Regelbrot",
   "report_notification.open": "Opne rapport",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Søk",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Søk eller lim inn URL",
-  "search_popout.search_format": "Avansert søkeformat",
-  "search_popout.tips.full_text": "Enkel tekst returnerer statusar du har skrive, likt, framheva eller vorte nemnd i, i tillegg til samsvarande brukarnamn, visningsnamn og emneknaggar.",
-  "search_popout.tips.hashtag": "emneknagg",
-  "search_popout.tips.status": "innlegg",
-  "search_popout.tips.text": "Enkel tekst returnerer samsvarande visningsnamn, brukarnamn og emneknaggar",
-  "search_popout.tips.user": "brukar",
-  "search_results.accounts": "Folk",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Alt",
   "search_results.hashtags": "Emneknaggar",
   "search_results.nothing_found": "Kunne ikkje finne noko for desse søkeorda",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Tenarstatistikk:",
   "sign_in_banner.create_account": "Opprett konto",
   "sign_in_banner.sign_in": "Logg inn",
-  "sign_in_banner.text": "Logg inn for å fylgje profiler eller emneknaggar, markere, framheve og svare på innlegg – eller samhandle med aktivitet på denne tenaren frå kontoen din på ein annan tenar.",
+  "sign_in_banner.text": "Logg inn for å fylgja profilar eller emneknaggar, og for å lika, dela og svara på innlegg. Du kan òg samhandla med aktivitet på denne tenaren frå kontoar på andre tenarar.",
   "status.admin_account": "Opne moderasjonsgrensesnitt for @{name}",
   "status.admin_domain": "Opna moderatorgrensesnittet for {domain}",
   "status.admin_status": "Opne denne statusen i moderasjonsgrensesnittet",
@@ -551,7 +559,8 @@
   "status.copy": "Kopier lenke til status",
   "status.delete": "Slett",
   "status.detailed_status": "Detaljert samtalevisning",
-  "status.direct": "Send melding til @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Rediger",
   "status.edited": "Redigert {date}",
   "status.edited_x_times": "Redigert {count, plural, one {{count} gong} other {{count} gonger}}",
diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json
index 53de4ce00..a03c51b0a 100644
--- a/app/javascript/mastodon/locales/no.json
+++ b/app/javascript/mastodon/locales/no.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blokkert",
   "account.browse_more_on_origin_server": "Bla mer på den opprinnelige profilen",
   "account.cancel_follow_request": "Trekk tilbake følge-forespørselen",
-  "account.direct": "Send direktemelding til @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Slutt å varsle meg når @{name} legger ut innlegg",
   "account.domain_blocked": "Domene blokkert",
   "account.edit_profile": "Rediger profil",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokkerte brukere",
   "column.bookmarks": "Bokmerker",
   "column.community": "Lokal tidslinje",
-  "column.direct": "Direktemeldinger",
+  "column.direct": "Private mentions",
   "column.directory": "Bla gjennom profiler",
   "column.domain_blocks": "Skjulte domener",
   "column.favourites": "Favoritter",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Du har ulagrede endringer i mediebeskrivelsen eller i forhåndsvisning, forkast dem likevel?",
   "confirmations.domain_block.confirm": "Skjul alt fra domenet",
   "confirmations.domain_block.message": "Er du sikker på at du vil skjule hele domenet {domain}? I de fleste tilfeller er det bedre med målrettet blokkering eller demping.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Logg ut",
   "confirmations.logout.message": "Er du sikker på at du vil logge ut?",
   "confirmations.mute.confirm": "Demp",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Du har ikke blokkert noen brukere enda.",
   "empty_column.bookmarked_statuses": "Du har ikke noen bokmerkede innlegg enda. Når du har noen bokmerkede innlegg, vil de dukke opp her.",
   "empty_column.community": "Den lokale tidslinjen er tom. Skriv noe offentlig for å få snøballen til å rulle!",
-  "empty_column.direct": "Du har ingen direktemeldinger enda. Etter du har sendt eller mottatt en, så vil den dukke opp her.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Det er ingen skjulte domener enda.",
   "empty_column.explore_statuses": "Ingenting er populært akkurat nå. Prøv igjen senere!",
   "empty_column.favourited_statuses": "Du har ikke noen favorittinnlegg enda. Når du favorittmarkerer et inlegg, vil det dukke opp her.",
   "empty_column.favourites": "Ingen har favorittmarkert dette innlegget ennå. Når noen gjør det, vil de dukke opp her.",
   "empty_column.follow_recommendations": "Ser ut som at det ikke finnes noen forslag for deg. Du kan prøve å bruke søk for å se etter folk du kan vite eller utforske trendende hashtags.",
   "empty_column.follow_requests": "Du har ingen følgeforespørsler enda. Når du mottar en, vil den dukke opp her.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Det er ingenting i denne emneknaggen ennå.",
   "empty_column.home": "Hjem-tidslinjen din er tom! Følg flere folk for å fylle den. {suggestions}",
   "empty_column.home.suggestions": "Se noen forslag",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autoriser",
   "follow_request.reject": "Avvis",
   "follow_requests.unlocked_explanation": "Selv om kontoen din ikke er låst, tror {domain} ansatte at du kanskje vil gjennomgå forespørsler fra disse kontoene manuelt.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "Om",
   "footer.directory": "Profilkatalog",
   "footer.get_app": "Last ned appen",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Hurtigtaster",
   "footer.privacy_policy": "Personvernregler",
   "footer.source_code": "Vis kildekode",
+  "footer.status": "Status",
   "generic.saved": "Lagret",
   "getting_started.heading": "Kom i gang",
   "hashtag.column_header.tag_mode.all": "og {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Gå til en kolonne",
   "keyboard_shortcuts.compose": "Gå til komponeringsfeltet",
   "keyboard_shortcuts.description": "Beskrivelse",
-  "keyboard_shortcuts.direct": "for å åpne kolonne med direktemeldinger",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Flytt nedover i listen",
   "keyboard_shortcuts.enter": "Åpne innlegg",
   "keyboard_shortcuts.favourite": "Marker innlegg som favoritt",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bokmerker",
   "navigation_bar.community_timeline": "Lokal tidslinje",
   "navigation_bar.compose": "Skriv et nytt innlegg",
-  "navigation_bar.direct": "Direktemeldinger",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Oppdag",
   "navigation_bar.domain_blocks": "Skjulte domener",
   "navigation_bar.edit_profile": "Rediger profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoritter",
   "navigation_bar.filters": "Stilnede ord",
   "navigation_bar.follow_requests": "Følgeforespørsler",
+  "navigation_bar.followed_tags": "Fulgte emneknagger",
   "navigation_bar.follows_and_followers": "Følginger og følgere",
   "navigation_bar.lists": "Lister",
   "navigation_bar.logout": "Logg ut",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Søppelpost",
   "report_notification.categories.violation": "Regelbrudd",
   "report_notification.open": "Åpne rapport",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Søk",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Søk eller lim inn URL",
-  "search_popout.search_format": "Avansert søkeformat",
-  "search_popout.tips.full_text": "Enkel tekst gir resultater for innlegg du har skrevet, favorittmarkert, fremhevet, eller har blitt nevnt i, i tillegg til samsvarende brukernavn, visningsnavn og emneknagger.",
-  "search_popout.tips.hashtag": "emneknagg",
-  "search_popout.tips.status": "status",
-  "search_popout.tips.text": "Enkel tekst returnerer matchende visningsnavn, brukernavn og emneknagger",
-  "search_popout.tips.user": "bruker",
-  "search_results.accounts": "Folk",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Alle",
   "search_results.hashtags": "Emneknagger",
   "search_results.nothing_found": "Fant ikke noe for disse søkeordene",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Serverstatistikk:",
   "sign_in_banner.create_account": "Opprett konto",
   "sign_in_banner.sign_in": "Logg inn",
-  "sign_in_banner.text": "Logg inn for å følge profiler eller hashtags, like, dele og svare på innlegg eller interagere fra din konto på en annen server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Åpne moderatorgrensesnittet for @{name}",
   "status.admin_domain": "Åpne moderatorgrensesnittet for {domain}",
   "status.admin_status": "Åpne denne statusen i moderatorgrensesnittet",
@@ -551,7 +559,8 @@
   "status.copy": "Kopier lenken til innlegget",
   "status.delete": "Slett",
   "status.detailed_status": "Detaljert samtalevisning",
-  "status.direct": "Send direktemelding til @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Rediger",
   "status.edited": "Redigert {date}",
   "status.edited_x_times": "Redigert {count, plural,one {{count} gang} other {{count} ganger}}",
diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json
index 4865843dd..c333c2ece 100644
--- a/app/javascript/mastodon/locales/oc.json
+++ b/app/javascript/mastodon/locales/oc.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocat",
   "account.browse_more_on_origin_server": "Navigar sul perfil original",
   "account.cancel_follow_request": "Retirar la demanda d’abonament",
-  "account.direct": "Escriure un MP a @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Quitar de m’avisar quand @{name} publica quicòm",
   "account.domain_blocked": "Domeni amagat",
   "account.edit_profile": "Modificar lo perfil",
@@ -102,7 +102,7 @@
   "column.blocks": "Personas blocadas",
   "column.bookmarks": "Marcadors",
   "column.community": "Flux public local",
-  "column.direct": "Messatges dirèctes",
+  "column.direct": "Private mentions",
   "column.directory": "Percórrer los perfils",
   "column.domain_blocks": "Domenis resconduts",
   "column.favourites": "Favorits",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Amagar tot lo domeni",
   "confirmations.domain_block.message": "Volètz vertadièrament blocar complètament {domain} ? De còps cal pas que blocar o rescondre unas personas solament.\nVeiretz pas cap de contengut d’aquel domeni dins cap de flux public o dins vòstras notificacions. Vòstres seguidors d’aquel domeni seràn levats.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Desconnexion",
   "confirmations.logout.message": "Volètz vertadièrament vos desconnectar ?",
   "confirmations.mute.confirm": "Rescondre",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Avètz pas blocat degun pel moment.",
   "empty_column.bookmarked_statuses": "Avètz pas cap de tuts marcats pel moment. Quand ne marquetz un, serà mostrat aquí.",
   "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.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "I a pas encara cap de domeni amagat.",
   "empty_column.explore_statuses": "I a pas res en tendéncia pel moment. Tornatz mai tard !",
   "empty_column.favourited_statuses": "Avètz pas encara cap de tut favorit. Quand n’auretz un, apareisserà aquí.",
   "empty_column.favourites": "Degun a pas encara mes en favorit aqueste tut. Quand qualqu’un o farà, apareisserà aquí.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "Avètz pas encara de demanda d’abonament. Quand n’auretz una apareisserà aquí.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "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.suggestions": "Veire mai de suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Acceptar",
   "follow_request.reject": "Regetar",
   "follow_requests.unlocked_explanation": "Encara que vòstre compte siasque pas verrolhat, la còla de {domain} pensèt que volriatz benlèu repassar las demandas d’abonament d’aquestes comptes.",
+  "followed_tags": "Etiquetas seguidas",
   "footer.about": "A prepaus",
   "footer.directory": "Annuari de perfils",
   "footer.get_app": "Obténer l’aplicacion",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Acorchis clavièr",
   "footer.privacy_policy": "Politica de confidencialitat",
   "footer.source_code": "Veire lo còdi font",
+  "footer.status": "Estat",
   "generic.saved": "Enregistrat",
   "getting_started.heading": "Per començar",
   "hashtag.column_header.tag_mode.all": "e {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "centrar un estatut a una colomna",
   "keyboard_shortcuts.compose": "anar al camp tèxte",
   "keyboard_shortcuts.description": "descripcion",
-  "keyboard_shortcuts.direct": "dobrir la colomna de messatges dirèctes",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "far davalar dins la lista",
   "keyboard_shortcuts.enter": "dobrir los estatuts",
   "keyboard_shortcuts.favourite": "apondre als favorits",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Marcadors",
   "navigation_bar.community_timeline": "Flux public local",
   "navigation_bar.compose": "Escriure un nòu tut",
-  "navigation_bar.direct": "Messatges dirèctes",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Trobar",
   "navigation_bar.domain_blocks": "Domenis resconduts",
   "navigation_bar.edit_profile": "Modificar lo perfil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favorits",
   "navigation_bar.filters": "Mots ignorats",
   "navigation_bar.follow_requests": "Demandas d’abonament",
+  "navigation_bar.followed_tags": "Etiquetas seguidas",
   "navigation_bar.follows_and_followers": "Abonament e seguidors",
   "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Desconnexion",
@@ -499,10 +505,10 @@
   "report.reasons.spam_description": "Ligams enganaires, fals engatjament o responsas repetitivas",
   "report.reasons.violation": "Viòla las règlas del servidor",
   "report.reasons.violation_description": "You are aware that it breaks specific rules",
-  "report.rules.subtitle": "Select all that apply",
-  "report.rules.title": "Which rules are being violated?",
+  "report.rules.subtitle": "Causissètz çò que s’aplica",
+  "report.rules.title": "Qualas règlas foguèron enfrachas ?",
   "report.statuses.subtitle": "Seleccionatz çò que s’aplica",
-  "report.statuses.title": "Are there any posts that back up this report?",
+  "report.statuses.title": "I a de publicacions per apiejar aqueste senhalament ?",
   "report.submit": "Mandar",
   "report.target": "Senhalar {target}",
   "report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Messatge indesirable",
   "report_notification.categories.violation": "Violacion de las règlas",
   "report_notification.open": "Dobrir lo senhalament",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Recercar",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Recercar o picar una URL",
-  "search_popout.search_format": "Format recèrca avançada",
-  "search_popout.tips.full_text": "Un tèxte simple que tòrna los estatuts qu’avètz escriches, mes en favorits, partejats, o ont sètz mencionat, e tanben los noms d’utilizaires, escais-noms e etiquetas que correspondonas.",
-  "search_popout.tips.hashtag": "etiqueta",
-  "search_popout.tips.status": "estatut",
-  "search_popout.tips.text": "Lo tèxte brut tòrna escais, noms d’utilizaire e etiquetas correspondents",
-  "search_popout.tips.user": "utilizaire",
-  "search_results.accounts": "Gents",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Tot",
   "search_results.hashtags": "Etiquetas",
   "search_results.nothing_found": "Cap de resultat per aquestes tèrmes de recèrca",
@@ -540,9 +548,9 @@
   "server_banner.server_stats": "Estatisticas del servidor :",
   "sign_in_banner.create_account": "Crear un compte",
   "sign_in_banner.sign_in": "Se connectar",
-  "sign_in_banner.text": "Connectatz-vos per sègre perfils o etiquetas, apondre als favorits, partejar e respondre als messatges o interagir de vòstre compte estant d’un autre servidor.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Dobrir l’interfàcia de moderacion per @{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_domain": "Dobrir l’interfàcia de moderacion per {domain}",
   "status.admin_status": "Dobrir aqueste estatut dins l’interfàcia de moderacion",
   "status.block": "Blocar @{name}",
   "status.bookmark": "Marcador",
@@ -551,7 +559,8 @@
   "status.copy": "Copiar lo ligam de l’estatut",
   "status.delete": "Escafar",
   "status.detailed_status": "Vista detalhada de la convèrsa",
-  "status.direct": "Messatge per @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modificar",
   "status.edited": "Modificat {date}",
   "status.edited_x_times": "Modificat {count, plural, un {{count} còp} other {{count} còps}}",
@@ -559,7 +568,7 @@
   "status.favourite": "Apondre als favorits",
   "status.filter": "Filtrar aquesta publicacion",
   "status.filtered": "Filtrat",
-  "status.hide": "Hide post",
+  "status.hide": "Amagar la publicacion",
   "status.history.created": "{name} o creèt lo {date}",
   "status.history.edited": "{name} o modifiquèt lo {date}",
   "status.load_more": "Cargar mai",
diff --git a/app/javascript/mastodon/locales/pa.json b/app/javascript/mastodon/locales/pa.json
index e646ef83c..d330c136c 100644
--- a/app/javascript/mastodon/locales/pa.json
+++ b/app/javascript/mastodon/locales/pa.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocked",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Domain blocked",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "Favourites",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "Mute",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite 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_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lists",
   "navigation_bar.logout": "Logout",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json
index eff34bcdd..a77da8885 100644
--- a/app/javascript/mastodon/locales/pl.json
+++ b/app/javascript/mastodon/locales/pl.json
@@ -17,10 +17,10 @@
   "account.badges.group": "Grupa",
   "account.block": "Blokuj @{name}",
   "account.block_domain": "Blokuj wszystko z {domain}",
-  "account.blocked": "Zablokowany(-a)",
+  "account.blocked": "Zablokowano",
   "account.browse_more_on_origin_server": "Zobacz więcej na oryginalnym profilu",
   "account.cancel_follow_request": "Wycofaj żądanie obserwowania",
-  "account.direct": "Wyślij wiadomość bezpośrednią do @{name}",
+  "account.direct": "Prywatna wzmianka @{name}",
   "account.disable_notifications": "Przestań powiadamiać mnie o wpisach @{name}",
   "account.domain_blocked": "Ukryto domenę",
   "account.edit_profile": "Edytuj profil",
@@ -39,7 +39,7 @@
   "account.follows_you": "Obserwuje Cię",
   "account.go_to_profile": "Przejdź do profilu",
   "account.hide_reblogs": "Ukryj podbicia od @{name}",
-  "account.joined_short": "Dołączył(a)",
+  "account.joined_short": "Dołączony",
   "account.languages": "Zmień subskrybowane języki",
   "account.link_verified_on": "Własność tego odnośnika została potwierdzona {date}",
   "account.locked_info": "To konto jest prywatne. Właściciel ręcznie wybiera kto może go obserwować.",
@@ -102,7 +102,7 @@
   "column.blocks": "Zablokowani użytkownicy",
   "column.bookmarks": "Zakładki",
   "column.community": "Lokalna oś czasu",
-  "column.direct": "Wiadomości bezpośrednie",
+  "column.direct": "Prywatne wzmianki",
   "column.directory": "Przeglądaj profile",
   "column.domain_blocks": "Ukryte domeny",
   "column.favourites": "Ulubione",
@@ -120,10 +120,6 @@
   "column_header.pin": "Przypnij",
   "column_header.show_settings": "Pokaż ustawienia",
   "column_header.unpin": "Cofnij przypięcie",
-  "column.heading": "Różne",
-  "column.subheading": "Różne opcje",
-  "column_subheading.lists": "Listy",
-  "column_subheading.navigation": "Nawigacja",
   "column_subheading.settings": "Ustawienia",
   "community.column_settings.local_only": "Tylko Lokalne",
   "community.column_settings.media_only": "Tylko multimedia",
@@ -166,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Masz niezapisane zmiany w opisie lub podglądzie, odrzucić je mimo to?",
   "confirmations.domain_block.confirm": "Ukryj wszystko z domeny",
   "confirmations.domain_block.message": "Czy na pewno chcesz zablokować całą domenę {domain}? Zwykle lepszym rozwiązaniem jest blokada lub wyciszenie kilku użytkowników.",
+  "confirmations.edit.confirm": "Edytuj",
+  "confirmations.edit.message": "Edytowanie wpisu nadpisze wiadomość, którą obecnie piszesz. Czy na pewno chcesz to zrobić?",
   "confirmations.logout.confirm": "Wyloguj",
   "confirmations.logout.message": "Czy na pewno chcesz się wylogować?",
   "confirmations.mute.confirm": "Wycisz",
@@ -218,13 +216,14 @@
   "empty_column.blocks": "Nie zablokowałeś(-aś) jeszcze żadnego użytkownika.",
   "empty_column.bookmarked_statuses": "Nie dodałeś(-aś) żadnego wpisu do zakładek. Kiedy to zrobisz, pojawi się on tutaj.",
   "empty_column.community": "Lokalna oś czasu jest pusta. Napisz coś publicznie, aby zagaić!",
-  "empty_column.direct": "Nie masz żadnych wiadomości bezpośrednich. Kiedy dostaniesz lub wyślesz jakąś, pojawi się ona tutaj.",
+  "empty_column.direct": "Nie masz jeszcze żadnych prywatnych wzmianek. Kiedy je wyślesz lub otrzymasz, pojawią się tutaj.",
   "empty_column.domain_blocks": "Brak ukrytych domen.",
   "empty_column.explore_statuses": "Nic nie jest w tej chwili popularne. Sprawdź później!",
   "empty_column.favourited_statuses": "Nie dodałeś(-aś) żadnego wpisu do ulubionych. Kiedy to zrobisz, pojawi się on tutaj.",
   "empty_column.favourites": "Nikt nie dodał tego wpisu do ulubionych. Gdy ktoś to zrobi, pojawi się tutaj.",
   "empty_column.follow_recommendations": "Wygląda na to, że nie można wygenerować dla Ciebie żadnych sugestii. Możesz spróbować wyszukać osoby, które znasz, lub przeglądać popularne hasztagi.",
   "empty_column.follow_requests": "Nie masz żadnych próśb o możliwość obserwacji. Kiedy ktoś utworzy ją, pojawi się tutaj.",
+  "empty_column.followed_tags": "Nie obserwujesz jeszcze żadnych hashtagów. Kiedy to zrobisz, pojawią się one tutaj.",
   "empty_column.hashtag": "Nie ma wpisów oznaczonych tym hasztagiem. Możesz napisać pierwszy(-a).",
   "empty_column.home": "Nie obserwujesz nikogo. Odwiedź globalną oś czasu lub użyj wyszukiwarki, aby znaleźć interesujące Cię profile.",
   "empty_column.home.suggestions": "Zobacz kilka sugestii",
@@ -267,6 +266,7 @@
   "follow_request.authorize": "Autoryzuj",
   "follow_request.reject": "Odrzuć",
   "follow_requests.unlocked_explanation": "Mimo że Twoje konto nie jest zablokowane, zespół {domain} uznał że możesz chcieć ręcznie przejrzeć prośby o możliwość obserwacji.",
+  "followed_tags": "Obserwowane hasztagi",
   "footer.about": "O serwerze",
   "footer.directory": "Katalog profilów",
   "footer.get_app": "Pobierz aplikację",
@@ -274,6 +274,7 @@
   "footer.keyboard_shortcuts": "Skróty klawiszowe",
   "footer.privacy_policy": "Polityka prywatności",
   "footer.source_code": "Zobacz kod źródłowy",
+  "footer.status": "Status",
   "generic.saved": "Zapisano",
   "getting_started.heading": "Rozpocznij",
   "hashtag.column_header.tag_mode.all": "i {additional}",
@@ -313,7 +314,7 @@
   "keyboard_shortcuts.column": "aby przejść do wpisu z jednej z kolumn",
   "keyboard_shortcuts.compose": "aby przejść do pola tworzenia wpisu",
   "keyboard_shortcuts.description": "Opis",
-  "keyboard_shortcuts.direct": "aby otworzyć kolumnę wiadomości bezpośrednich",
+  "keyboard_shortcuts.direct": "aby otworzyć kolumnę z wzmiankami prywatnymi",
   "keyboard_shortcuts.down": "aby przejść na dół listy",
   "keyboard_shortcuts.enter": "aby otworzyć wpis",
   "keyboard_shortcuts.favourite": "aby dodać do ulubionych",
@@ -375,7 +376,7 @@
   "navigation_bar.bookmarks": "Zakładki",
   "navigation_bar.community_timeline": "Lokalna oś czasu",
   "navigation_bar.compose": "Utwórz nowy wpis",
-  "navigation_bar.direct": "Wiadomości bezpośrednie",
+  "navigation_bar.direct": "Prywatne wzmianki",
   "navigation_bar.discover": "Odkrywaj",
   "navigation_bar.domain_blocks": "Ukryte domeny",
   "navigation_bar.edit_profile": "Edytuj profil",
@@ -383,10 +384,10 @@
   "navigation_bar.favourites": "Ulubione",
   "navigation_bar.filters": "Wyciszone słowa",
   "navigation_bar.follow_requests": "Prośby o obserwację",
+  "navigation_bar.followed_tags": "Obserwowane hasztagi",
   "navigation_bar.follows_and_followers": "Obserwowani i obserwujący",
   "navigation_bar.lists": "Listy",
   "navigation_bar.logout": "Wyloguj",
-  "navigation_bar.misc": "Różne",
   "navigation_bar.mutes": "Wyciszeni użytkownicy",
   "navigation_bar.personal": "Osobiste",
   "navigation_bar.pins": "Przypięte wpisy",
@@ -397,14 +398,14 @@
   "not_signed_in_indicator.not_signed_in": "Musisz się zalogować, aby otrzymać dostęp do tego zasobu.",
   "notification.admin.report": "{name} zgłosił {target}",
   "notification.admin.sign_up": "Użytkownik {name} zarejestrował się",
-  "notification.favourite": "{name} dodał(a) Twój wpis do ulubionych",
-  "notification.follow": "{name} zaczął(-ęła) Cię obserwować",
-  "notification.follow_request": "{name} poprosił(a) o możliwość obserwacji Cię",
-  "notification.mention": "{name} wspomniał(a) o tobie",
+  "notification.favourite": "{name} dodaje Twój wpis do ulubionych",
+  "notification.follow": "{name} obserwuje Cię",
+  "notification.follow_request": "{name} prosi o możliwość obserwowania Ciebie",
+  "notification.mention": "Wspomniało o Tobie przez {name}",
   "notification.own_poll": "Twoje głosowanie zakończyło się",
   "notification.poll": "Głosowanie w którym brałeś(-aś) udział zakończyło się",
-  "notification.reblog": "{name} podbił(a) Twój wpis",
-  "notification.status": "{name} właśnie utworzył(a) wpis",
+  "notification.reblog": "Twój post został podbity przez {name}",
+  "notification.status": "Właśnie umieszczono wpis przez {name}",
   "notification.update": "{name} edytował post",
   "notifications.clear": "Wyczyść powiadomienia",
   "notifications.clear_confirmation": "Czy na pewno chcesz bezpowrotnie usunąć wszystkie powiadomienia?",
@@ -521,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Naruszenie zasad",
   "report_notification.open": "Otwórz raport",
+  "search.no_recent_searches": "Brak ostatnich wyszukiwań",
   "search.placeholder": "Szukaj",
+  "search.quick_action.account_search": "Profile pasujące do {x}",
+  "search.quick_action.go_to_account": "Przejdź do profilu {x}",
+  "search.quick_action.go_to_hashtag": "Przejdź do hasztaga {x}",
+  "search.quick_action.open_url": "Otwórz adres URL w Mastodonie",
+  "search.quick_action.status_search": "Wpisy pasujące do {x}",
   "search.search_or_paste": "Wyszukaj lub wklej adres",
-  "search_popout.search_format": "Zaawansowane wyszukiwanie",
-  "search_popout.tips.full_text": "Pozwala na wyszukiwanie wpisów które napisałeś(-aś), dodałeś(-aś) do ulubionych lub podbiłeś(-aś), w których o Tobie wspomniano, oraz pasujące nazwy użytkowników, pełne nazwy i hashtagi.",
-  "search_popout.tips.hashtag": "hasztag",
-  "search_popout.tips.status": "wpis",
-  "search_popout.tips.text": "Proste wyszukiwanie pasujących pseudonimów, nazw użytkowników i hasztagów",
-  "search_popout.tips.user": "użytkownik",
-  "search_results.accounts": "Ludzie",
+  "search_popout.quick_actions": "Szybkie akcje",
+  "search_popout.recent": "Ostatnie wyszukiwania",
+  "search_results.accounts": "Profile",
   "search_results.all": "Wszystkie",
   "search_results.hashtags": "Hasztagi",
   "search_results.nothing_found": "Nie znaleziono innych wyników dla tego wyszukania",
@@ -545,7 +548,7 @@
   "server_banner.server_stats": "Statystyki serwera:",
   "sign_in_banner.create_account": "Załóż konto",
   "sign_in_banner.sign_in": "Zaloguj się",
-  "sign_in_banner.text": "Zaloguj się, aby obserwować profile lub hasztagi, jak również dodawaj wpisy do ulubionych, udostępniaj je dalej i odpowiadaj na nie lub wchodź w interakcje z kontem na innym serwerze.",
+  "sign_in_banner.text": "Zaloguj się, aby obserwować profile lub hashtagi, polubić, udostępnić oraz odpowiedzieć na posty. Możesz również wejść w interakcję z konta na innym serwerze.",
   "status.admin_account": "Otwórz interfejs moderacyjny dla @{name}",
   "status.admin_domain": "Otwórz interfejs moderacyjny dla {domain}",
   "status.admin_status": "Otwórz ten wpis w interfejsie moderacyjnym",
@@ -556,7 +559,8 @@
   "status.copy": "Skopiuj odnośnik do wpisu",
   "status.delete": "Usuń",
   "status.detailed_status": "Szczegółowy widok konwersacji",
-  "status.direct": "Wyślij wiadomość bezpośrednią do @{name}",
+  "status.direct": "Prywatna wzmianka @{name}",
+  "status.direct_indicator": "Prywatna wzmianka",
   "status.edit": "Edytuj",
   "status.edited": "Edytowano {date}",
   "status.edited_x_times": "Edytowano {count, plural, one {{count} raz} other {{count} razy}}",
@@ -565,8 +569,8 @@
   "status.filter": "Filtruj ten wpis",
   "status.filtered": "Filtrowany(-a)",
   "status.hide": "Ukryj post",
-  "status.history.created": "{name} utworzył(a) {date}",
-  "status.history.edited": "{name} edytował(a) {date}",
+  "status.history.created": "{name} utworzone {date}",
+  "status.history.edited": "{name} edytowane {date}",
   "status.load_more": "Załaduj więcej",
   "status.media_hidden": "Multimedia ukryte",
   "status.mention": "Wspomnij o @{name}",
@@ -579,7 +583,7 @@
   "status.read_more": "Czytaj dalej",
   "status.reblog": "Podbij",
   "status.reblog_private": "Podbij dla odbiorców oryginalnego wpisu",
-  "status.reblogged_by": "{name} podbił(a)",
+  "status.reblogged_by": "Podbite przez {name}",
   "status.reblogs.empty": "Nikt nie podbił jeszcze tego wpisu. Gdy ktoś to zrobi, pojawi się tutaj.",
   "status.redraft": "Usuń i przeredaguj",
   "status.remove_bookmark": "Usuń zakładkę",
diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json
index c1ab913d9..cdab4c812 100644
--- a/app/javascript/mastodon/locales/pt-BR.json
+++ b/app/javascript/mastodon/locales/pt-BR.json
@@ -20,7 +20,7 @@
   "account.blocked": "Bloqueado",
   "account.browse_more_on_origin_server": "Veja mais no perfil original",
   "account.cancel_follow_request": "Cancelar solicitação para seguir",
-  "account.direct": "Enviar toot direto para @{name}",
+  "account.direct": "Mencione em privado @{name}",
   "account.disable_notifications": "Cancelar notificações de @{name}",
   "account.domain_blocked": "Domínio bloqueado",
   "account.edit_profile": "Editar perfil",
@@ -37,7 +37,7 @@
   "account.following_counter": "{count, plural, one {segue {counter}} other {segue {counter}}}",
   "account.follows.empty": "Nada aqui.",
   "account.follows_you": "te segue",
-  "account.go_to_profile": "Ir para o perfil",
+  "account.go_to_profile": "Ir ao perfil",
   "account.hide_reblogs": "Ocultar boosts de @{name}",
   "account.joined_short": "Entrou",
   "account.languages": "Mudar idiomas inscritos",
@@ -65,7 +65,7 @@
   "account.unfollow": "Deixar de seguir",
   "account.unmute": "Dessilenciar @{name}",
   "account.unmute_notifications": "Mostrar notificações de @{name}",
-  "account.unmute_short": "Dessilenciar",
+  "account.unmute_short": "Desativar silêncio",
   "account_note.placeholder": "Nota pessoal sobre este perfil aqui",
   "admin.dashboard.daily_retention": "Taxa de retenção de usuários por dia, após a inscrição",
   "admin.dashboard.monthly_retention": "Taxa de retenção de usuários por mês, após a inscrição",
@@ -82,13 +82,13 @@
   "autosuggest_hashtag.per_week": "{count} por semana",
   "boost_modal.combo": "Pressione {combo} para pular isso na próxima vez",
   "bundle_column_error.copy_stacktrace": "Copiar relatório do erro",
-  "bundle_column_error.error.body": "A página solicitada não pode ser renderizada. Pode ser devido a um erro em nosso código ou um problema de compatibilidade do navegador.",
+  "bundle_column_error.error.body": "A página solicitada não pôde ser renderizada. Pode ser devido a um erro no nosso código, ou um problema de compatibilidade do seu navegador.",
   "bundle_column_error.error.title": "Ah, não!",
   "bundle_column_error.network.body": "Ocorreu um erro ao tentar carregar esta página. Isso pode ser devido a um problema temporário com sua conexão de internet ou deste servidor.",
   "bundle_column_error.network.title": "Erro de rede",
   "bundle_column_error.retry": "Tente novamente",
   "bundle_column_error.return": "Voltar à página inicial",
-  "bundle_column_error.routing.body": "A página solicitada não foi encontrada. Tem certeza de que a URL na barra de endereços está correta?",
+  "bundle_column_error.routing.body": "A página solicitada não foi encontrada. Tem certeza de que o URL na barra de endereços está correta?",
   "bundle_column_error.routing.title": "404",
   "bundle_modal_error.close": "Fechar",
   "bundle_modal_error.message": "Erro ao carregar este componente.",
@@ -102,7 +102,7 @@
   "column.blocks": "Usuários bloqueados",
   "column.bookmarks": "Salvos",
   "column.community": "Linha local",
-  "column.direct": "Mensagens diretas",
+  "column.direct": "Menções privadas",
   "column.directory": "Explorar perfis",
   "column.domain_blocks": "Domínios bloqueados",
   "column.favourites": "Favoritos",
@@ -128,7 +128,7 @@
   "compose.language.search": "Pesquisar idiomas...",
   "compose_form.direct_message_warning_learn_more": "Saiba mais",
   "compose_form.encryption_warning": "As publicações no Mastodon não são criptografadas de ponta-a-ponta. Não compartilhe nenhuma informação sensível no Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "Esta publicação não será exibida sob nenhuma hashtag, já que não é pública. Apenas postagens públicas podem ser pesquisadas por meio de hashtags.",
   "compose_form.lock_disclaimer": "Seu perfil não está {locked}. Qualquer um pode te seguir e ver os toots privados.",
   "compose_form.lock_disclaimer.lock": "trancado",
   "compose_form.placeholder": "No que você está pensando?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Há mudanças não salvas na descrição ou pré-visualização da mídia. Descartar assim mesmo?",
   "confirmations.domain_block.confirm": "Bloquear instância",
   "confirmations.domain_block.message": "Você tem certeza de que deseja bloquear tudo de {domain}? Você não verá mais o conteúdo desta instância em nenhuma linha do tempo pública ou nas suas notificações. Seus seguidores desta instância serão removidos.",
+  "confirmations.edit.confirm": "Editar",
+  "confirmations.edit.message": "Editar agora irá substituir a mensagem que está sendo criando. Tem certeza de que deseja continuar?",
   "confirmations.logout.confirm": "Sair",
   "confirmations.logout.message": "Você tem certeza de que deseja sair?",
   "confirmations.mute.confirm": "Silenciar",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Nada aqui.",
   "empty_column.bookmarked_statuses": "Nada aqui. Quando você salvar um toot, ele aparecerá aqui.",
   "empty_column.community": "A linha local está vazia. Publique algo para começar!",
-  "empty_column.direct": "Você ainda não tem mensagens diretas. Quando você enviar ou receber uma, será exibida aqui.",
+  "empty_column.direct": "Você ainda não tem mensagens privadas. Quando você enviar ou receber uma, será exibida aqui.",
   "empty_column.domain_blocks": "Nada aqui.",
   "empty_column.explore_statuses": "Nada está em alta no momento. Volte mais tarde!",
   "empty_column.favourited_statuses": "Nada aqui. Quando você favoritar um toot, ele aparecerá aqui.",
   "empty_column.favourites": "Nada aqui. Quando alguém favoritar, o autor aparecerá aqui.",
   "empty_column.follow_recommendations": "Parece que não há sugestões para você. Tente usar a pesquisa para encontrar pessoas que você possa conhecer ou explorar hashtags.",
   "empty_column.follow_requests": "Nada aqui. Quando você tiver seguidores pendentes, eles aparecerão aqui.",
+  "empty_column.followed_tags": "Você ainda não seguiu nenhuma hashtag. Quando seguir uma, elas serão exibidas aqui.",
   "empty_column.hashtag": "Nada aqui.",
   "empty_column.home": "Sua página inicial está vazia! Siga mais pessoas para começar: {suggestions}",
   "empty_column.home.suggestions": "Veja algumas sugestões",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Aprovar",
   "follow_request.reject": "Recusar",
   "follow_requests.unlocked_explanation": "Apesar de seu perfil não ser trancado, {domain} exige que você revise a solicitação para te seguir destes perfis manualmente.",
+  "followed_tags": "Hashtags seguidas",
   "footer.about": "Sobre",
   "footer.directory": "Diretório de perfis",
   "footer.get_app": "Baixe o app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Atalhos de teclado",
   "footer.privacy_policy": "Política de privacidade",
   "footer.source_code": "Exibir código-fonte",
+  "footer.status": "Status",
   "generic.saved": "Salvo",
   "getting_started.heading": "Primeiros passos",
   "hashtag.column_header.tag_mode.all": "e {additional}",
@@ -294,7 +299,7 @@
   "interaction_modal.description.reply": "Com uma conta no Mastodon, você pode responder a esta publicação.",
   "interaction_modal.on_another_server": "Em um servidor diferente",
   "interaction_modal.on_this_server": "Neste servidor",
-  "interaction_modal.other_server_instructions": "Copie e cole esta URL no campo de pesquisa do seu aplicativo Mastodon favorito ou a interface web do seu servidor Mastodon.",
+  "interaction_modal.other_server_instructions": "Copie e cole este URL no campo de pesquisa de seu aplicativo Mastodon favorito ou na interface web de seu servidor Mastodon.",
   "interaction_modal.preamble": "Como o Mastodon é descentralizado, você pode usar sua conta existente em outro servidor Mastodon ou plataforma compatível se você não tiver uma conta neste servidor.",
   "interaction_modal.title.favourite": "Favoritar publicação de {name}",
   "interaction_modal.title.follow": "Seguir {name}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "focar na coluna",
   "keyboard_shortcuts.compose": "focar no compositor",
   "keyboard_shortcuts.description": "Descrição",
-  "keyboard_shortcuts.direct": "para abrir a coluna de mensagens diretas",
+  "keyboard_shortcuts.direct": "para abrir a coluna de menções privadas",
   "keyboard_shortcuts.down": "mover para baixo",
   "keyboard_shortcuts.enter": "abrir toot",
   "keyboard_shortcuts.favourite": "favoritar toot",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Salvos",
   "navigation_bar.community_timeline": "Linha do tempo local",
   "navigation_bar.compose": "Compor novo toot",
-  "navigation_bar.direct": "Mensagens diretas",
+  "navigation_bar.direct": "Menções privadas",
   "navigation_bar.discover": "Descobrir",
   "navigation_bar.domain_blocks": "Domínios bloqueados",
   "navigation_bar.edit_profile": "Editar perfil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoritos",
   "navigation_bar.filters": "Palavras filtradas",
   "navigation_bar.follow_requests": "Seguidores pendentes",
+  "navigation_bar.followed_tags": "Hashtags seguidas",
   "navigation_bar.follows_and_followers": "Segue e seguidores",
   "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Sair",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Violação de regra",
   "report_notification.open": "Abrir denúncia",
+  "search.no_recent_searches": "Nenhuma busca recente",
   "search.placeholder": "Pesquisar",
+  "search.quick_action.account_search": "Perfis correspondentes a {x}",
+  "search.quick_action.go_to_account": "Ir para a página do perfil {x}",
+  "search.quick_action.go_to_hashtag": "Ir para a hashtag {x}",
+  "search.quick_action.open_url": "Abrir link no Mastodon",
+  "search.quick_action.status_search": "Publicações correspondentes a {x}",
   "search.search_or_paste": "Buscar ou colar URL",
-  "search_popout.search_format": "Formato de pesquisa avançada",
-  "search_popout.tips.full_text": "Texto simples retorna toots que você escreveu, favoritou, deu boost, ou em que foi mencionado, assim como nomes de usuário e de exibição, e hashtags correspondentes.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "toot",
-  "search_popout.tips.text": "Texto simples retorna nomes de exibição e de usuário, e hashtags correspondentes",
-  "search_popout.tips.user": "usuário",
-  "search_results.accounts": "Pessoas",
+  "search_popout.quick_actions": "Ações rápidas",
+  "search_popout.recent": "Buscas Recentes",
+  "search_results.accounts": "Perfis",
   "search_results.all": "Tudo",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Não foi possível encontrar nada para estes termos de busca",
@@ -540,9 +548,9 @@
   "server_banner.server_stats": "Estatísticas do servidor:",
   "sign_in_banner.create_account": "Criar conta",
   "sign_in_banner.sign_in": "Entrar",
-  "sign_in_banner.text": "Entre para seguir perfis ou hashtags, favoritar, compartilhar e responder publicações, interagir a partir da sua conta em um servidor diferente.",
+  "sign_in_banner.text": "Entre para seguir perfis ou hashtags, favoritar, compartilhar e responder publicações. Você também pode interagir a partir da sua conta em um servidor diferente.",
   "status.admin_account": "Abrir interface de moderação para @{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_domain": "Abrir interface de moderação para {domain}",
   "status.admin_status": "Abrir este toot na interface de moderação",
   "status.block": "Bloquear @{name}",
   "status.bookmark": "Salvar",
@@ -551,7 +559,8 @@
   "status.copy": "Copiar link",
   "status.delete": "Excluir",
   "status.detailed_status": "Visão detalhada da conversa",
-  "status.direct": "Enviar toot direto para @{name}",
+  "status.direct": "Mencione em privado @{name}",
+  "status.direct_indicator": "Menção privada",
   "status.edit": "Editar",
   "status.edited": "Editado em {date}",
   "status.edited_x_times": "Editado {count, plural, one {{count} hora} other {{count} vezes}}",
@@ -559,7 +568,7 @@
   "status.favourite": "Favoritar",
   "status.filter": "Filtrar esta publicação",
   "status.filtered": "Filtrado",
-  "status.hide": "Hide post",
+  "status.hide": "Ocultar publicação",
   "status.history.created": "{name} criou {date}",
   "status.history.edited": "{name} editou {date}",
   "status.load_more": "Ver mais",
diff --git a/app/javascript/mastodon/locales/pt-PT.json b/app/javascript/mastodon/locales/pt-PT.json
index 6a7560262..e93ff73cf 100644
--- a/app/javascript/mastodon/locales/pt-PT.json
+++ b/app/javascript/mastodon/locales/pt-PT.json
@@ -20,7 +20,7 @@
   "account.blocked": "Bloqueado(a)",
   "account.browse_more_on_origin_server": "Encontrar mais no perfil original",
   "account.cancel_follow_request": "Retirar pedido para seguir",
-  "account.direct": "Enviar mensagem direta para @{name}",
+  "account.direct": "Mencionar @{name} em privado",
   "account.disable_notifications": "Parar de me notificar das publicações de @{name}",
   "account.domain_blocked": "Domínio bloqueado",
   "account.edit_profile": "Editar perfil",
@@ -102,7 +102,7 @@
   "column.blocks": "Utilizadores Bloqueados",
   "column.bookmarks": "Marcadores",
   "column.community": "Cronologia local",
-  "column.direct": "Mensagens diretas",
+  "column.direct": "Menções privadas",
   "column.directory": "Explorar perfis",
   "column.domain_blocks": "Domínios bloqueados",
   "column.favourites": "Preferidos",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Tem alterações por guardar na descrição ou pré-visualização do conteúdo. Descartar mesmo assim?",
   "confirmations.domain_block.confirm": "Esconder tudo deste domínio",
   "confirmations.domain_block.message": "De certeza que queres bloquear completamente o domínio {domain}? Na maioria dos casos, silenciar ou bloquear alguns utilizadores é suficiente e é o recomendado. Não irás ver conteúdo daquele domínio em cronologia alguma nem nas tuas notificações. Os teus seguidores daquele domínio serão removidos.",
+  "confirmations.edit.confirm": "Editar",
+  "confirmations.edit.message": "Editar agora irá sobrescrever a mensagem que está a compor. Tem a certeza de que deseja continuar?",
   "confirmations.logout.confirm": "Terminar sessão",
   "confirmations.logout.message": "Tem a certeza de que quer terminar a sessão?",
   "confirmations.mute.confirm": "Silenciar",
@@ -212,15 +214,16 @@
   "empty_column.account_timeline": "Sem publicações por aqui!",
   "empty_column.account_unavailable": "Perfil indisponível",
   "empty_column.blocks": "Ainda não bloqueaste qualquer utilizador.",
-  "empty_column.bookmarked_statuses": "Ainda não tem nenhuma publicação nos seus marcadores. Quando tiver, serão exibidas aqui.",
+  "empty_column.bookmarked_statuses": "Ainda não adicionou nenhuma publicação aos itens salvos. Quando adicionar, eles serão exibidos aqui.",
   "empty_column.community": "A cronologia local está vazia. Escreve algo público para começar!",
-  "empty_column.direct": "Ainda não tem qualquer mensagem direta. Quando enviar ou receber alguma, ela irá aparecer aqui.",
+  "empty_column.direct": "Ainda não tem qualquer menção privada. Quando enviar ou receber uma, ela irá aparecer aqui.",
   "empty_column.domain_blocks": "Ainda não há qualquer domínio escondido.",
   "empty_column.explore_statuses": "Nada está em alta no momento. Volte mais tarde!",
   "empty_column.favourited_statuses": "Ainda não tens quaisquer publicações nos marcadores. Quando tiveres, aparecerão aqui.",
   "empty_column.favourites": "Ainda ninguém tem esta publicação nos seus marcadores. Quando alguém o tiver, ele irá aparecer aqui.",
   "empty_column.follow_recommendations": "Parece que não foi possível gerar nenhuma sugestão para si. Pode tentar utilizar a pesquisa para procurar pessoas que conheça ou explorar as #etiquetas em destaque.",
   "empty_column.follow_requests": "Ainda não tens nenhum pedido de seguidor. Quando receberes algum, ele irá aparecer aqui.",
+  "empty_column.followed_tags": "Ainda não segue nenhuma hashtag. Quando o fizer, ela aparecerá aqui.",
   "empty_column.hashtag": "Não foram encontradas publicações com essa #etiqueta.",
   "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.",
   "empty_column.home.suggestions": "Ver algumas sugestões",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autorizar",
   "follow_request.reject": "Rejeitar",
   "follow_requests.unlocked_explanation": "Apesar de a sua não ser privada, a administração de {domain} pensa que poderá querer rever manualmente os pedidos de seguimento dessas contas.",
+  "followed_tags": "Hashtags seguidas",
   "footer.about": "Sobre",
   "footer.directory": "Diretório de perfis",
   "footer.get_app": "Obtém a aplicação",
@@ -270,7 +274,8 @@
   "footer.keyboard_shortcuts": "Atalhos do teclado",
   "footer.privacy_policy": "Política de privacidade",
   "footer.source_code": "Ver código-fonte",
-  "generic.saved": "Salvo",
+  "footer.status": "Estado",
+  "generic.saved": "Guardado",
   "getting_started.heading": "Primeiros passos",
   "hashtag.column_header.tag_mode.all": "e {additional}",
   "hashtag.column_header.tag_mode.any": "ou {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "para focar uma publicação numa das colunas",
   "keyboard_shortcuts.compose": "para focar na área de publicação",
   "keyboard_shortcuts.description": "Descrição",
-  "keyboard_shortcuts.direct": "para abrir a coluna das mensagens diretas",
+  "keyboard_shortcuts.direct": "para abrir a coluna de menções privadas",
   "keyboard_shortcuts.down": "para mover para baixo na lista",
   "keyboard_shortcuts.enter": "para expandir uma publicação",
   "keyboard_shortcuts.favourite": "Juntar aos marcadores",
@@ -368,10 +373,10 @@
   "mute_modal.indefinite": "Indefinidamente",
   "navigation_bar.about": "Sobre",
   "navigation_bar.blocks": "Utilizadores bloqueados",
-  "navigation_bar.bookmarks": "Itens salvos",
+  "navigation_bar.bookmarks": "Marcadores",
   "navigation_bar.community_timeline": "Cronologia local",
   "navigation_bar.compose": "Escrever novo toot",
-  "navigation_bar.direct": "Mensagens diretas",
+  "navigation_bar.direct": "Menções privadas",
   "navigation_bar.discover": "Descobrir",
   "navigation_bar.domain_blocks": "Domínios escondidos",
   "navigation_bar.edit_profile": "Editar perfil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoritos",
   "navigation_bar.filters": "Palavras silenciadas",
   "navigation_bar.follow_requests": "Seguidores pendentes",
+  "navigation_bar.followed_tags": "Hashtags seguidas",
   "navigation_bar.follows_and_followers": "Seguindo e seguidores",
   "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Sair",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Violação de regra",
   "report_notification.open": "Abrir denúncia",
+  "search.no_recent_searches": "Nenhuma pesquisa recente",
   "search.placeholder": "Pesquisar",
+  "search.quick_action.account_search": "Perfis com correspondência a {x}",
+  "search.quick_action.go_to_account": "Ir para o perfil {x}",
+  "search.quick_action.go_to_hashtag": "Ir para a hashtag {x}",
+  "search.quick_action.open_url": "Abrir ligação no Mastodon",
+  "search.quick_action.status_search": "Publicações com correspondência a {x}",
   "search.search_or_paste": "Pesquisar ou introduzir URL",
-  "search_popout.search_format": "Formato avançado de pesquisa",
-  "search_popout.tips.full_text": "Texto simples devolve publicações que escreveu, marcou, reforçou, ou em que foi mencionado, tal como nomes de utilizador, alcunhas e #etiquetas.",
-  "search_popout.tips.hashtag": "etiqueta",
-  "search_popout.tips.status": "publicação",
-  "search_popout.tips.text": "O texto simples retorna a correspondência de nomes, utilizadores e #etiquetas",
-  "search_popout.tips.user": "utilizador",
-  "search_results.accounts": "Pessoas",
+  "search_popout.quick_actions": "Ações rápidas",
+  "search_popout.recent": "Pesquisas recentes",
+  "search_results.accounts": "Perfis",
   "search_results.all": "Tudo",
   "search_results.hashtags": "Etiquetas",
   "search_results.nothing_found": "Não foi possível encontrar resultados para as expressões pesquisadas",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Estatísticas do servidor:",
   "sign_in_banner.create_account": "Criar conta",
   "sign_in_banner.sign_in": "Iniciar sessão",
-  "sign_in_banner.text": "Inicie sessão para seguir perfis ou #etiquetas, ou marcadores, para partilhar ou responder às publicações, ou interagir através da sua conta noutro servidor.",
+  "sign_in_banner.text": "Inicie sessão para seguir perfis ou hashtags, adicionar aos favoritos, partilhar ou responder a publicações. Pode ainda interagir através da sua conta noutro servidor.",
   "status.admin_account": "Abrir a interface de moderação para @{name}",
   "status.admin_domain": "Abrir interface de moderação para {domain}",
   "status.admin_status": "Abrir esta publicação na interface de moderação",
@@ -551,7 +559,8 @@
   "status.copy": "Copiar ligação para a publicação",
   "status.delete": "Eliminar",
   "status.detailed_status": "Vista pormenorizada da conversa",
-  "status.direct": "Mensagem direta @{name}",
+  "status.direct": "Mencionar @{name} em privado",
+  "status.direct_indicator": "Menção privada",
   "status.edit": "Editar",
   "status.edited": "Editado em {date}",
   "status.edited_x_times": "Editado {count, plural,one {{count} vez} other {{count} vezes}}",
diff --git a/app/javascript/mastodon/locales/ro.json b/app/javascript/mastodon/locales/ro.json
index 6e5674bd1..aa6710685 100644
--- a/app/javascript/mastodon/locales/ro.json
+++ b/app/javascript/mastodon/locales/ro.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocat",
   "account.browse_more_on_origin_server": "Vezi mai multe pe profilul original",
   "account.cancel_follow_request": "Retrage cererea de urmărire",
-  "account.direct": "Trimite-i un mesaj direct lui @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Nu îmi mai trimite notificări când postează @{name}",
   "account.domain_blocked": "Domeniu blocat",
   "account.edit_profile": "Modifică profilul",
@@ -54,7 +54,7 @@
   "account.posts_with_replies": "Postări și răspunsuri",
   "account.report": "Raportează pe @{name}",
   "account.requested": "Se așteaptă aprobarea. Apasă pentru a anula cererea de urmărire",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name} A cerut să vă urmărească",
   "account.share": "Distribuie profilul lui @{name}",
   "account.show_reblogs": "Arată impulsurile de la @{name}",
   "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}",
@@ -102,7 +102,7 @@
   "column.blocks": "Utilizatori blocați",
   "column.bookmarks": "Marcaje",
   "column.community": "Cronologie locală",
-  "column.direct": "Mesaje directe",
+  "column.direct": "Private mentions",
   "column.directory": "Explorează profiluri",
   "column.domain_blocks": "Domenii blocate",
   "column.favourites": "Favorite",
@@ -128,7 +128,7 @@
   "compose.language.search": "Căutare limbi…",
   "compose_form.direct_message_warning_learn_more": "Află mai multe",
   "compose_form.encryption_warning": "Postările pe Mastodon nu sunt criptate în ambele părți. Nu împărtășiți nici o informație sensibilă pe Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "Această postare nu va fi listată sub niciun hashtag, deoarece nu este publică. Doar postările publice pot fi căutate de hashtag.",
   "compose_form.lock_disclaimer": "Contul tău nu este {locked}. Oricine se poate abona la tine pentru a îți vedea postările numai pentru abonați.",
   "compose_form.lock_disclaimer.lock": "privat",
   "compose_form.placeholder": "La ce te gândești?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Ai modificări nesalvate în descrierea sau previzualizarea media, renunți oricum?",
   "confirmations.domain_block.confirm": "Blochează întregul domeniu",
   "confirmations.domain_block.message": "Ești absolut sigur că vrei să blochezi tot domeniul {domain}? În cele mai multe cazuri, raportarea sau blocarea anumitor lucruri este suficientă și de preferat. Nu vei mai vedea niciun conținut din acest domeniu în vreun flux public sau în vreo notificare. Abonații tăi din acest domeniu vor fi eliminați.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Deconectare",
   "confirmations.logout.message": "Ești sigur că vrei să te deconectezi?",
   "confirmations.mute.confirm": "Ignoră",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Momentan nu ai blocat niciun utilizator.",
   "empty_column.bookmarked_statuses": "Momentan nu ai nicio postare marcată. Când vei marca una, va apărea aici.",
   "empty_column.community": "Nu există nimic în cronologia locală. Postează ceva public pentru a sparge gheața!",
-  "empty_column.direct": "Momentan nu ai niciun mesaj direct. Când trimiți sau primești un mesaj, va apărea aici.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Momentan nu există domenii blocate.",
   "empty_column.explore_statuses": "Nimic nu figurează în tendințe în acest moment. Verifică din nou mai târziu!",
   "empty_column.favourited_statuses": "Momentan nu ai nicio postare favorită. Când vei adăuga una, va apărea aici.",
   "empty_column.favourites": "Momentan nimeni nu a adăugat această postare la favorite. Când cineva o va face, va apărea aici.",
   "empty_column.follow_recommendations": "Se pare că nu am putut genera nicio sugestie pentru tine. Poți încerca funcția de căutare pentru a căuta persoane pe care le cunoști, sau poți explora tendințele.",
   "empty_column.follow_requests": "Momentan nu ai nicio cerere de abonare. Când vei primi una, va apărea aici.",
+  "empty_column.followed_tags": "Încă nu urmăriți niciun harstag -uri. Când o vei face, vor apărea aici.",
   "empty_column.hashtag": "Acest hashtag încă nu a fost folosit.",
   "empty_column.home": "Nu există nimic în cronologia ta! Abonează-te la mai multe persoane pentru a o umple. {suggestions}",
   "empty_column.home.suggestions": "Vezi sugestiile",
@@ -236,11 +239,11 @@
   "errors.unexpected_crash.copy_stacktrace": "Copiere stacktrace în clipboard",
   "errors.unexpected_crash.report_issue": "Raportează o problemă",
   "explore.search_results": "Rezultatele căutării",
-  "explore.suggested_follows": "For you",
+  "explore.suggested_follows": "Pentru tine",
   "explore.title": "Explorează",
-  "explore.trending_links": "News",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "Hashtags",
+  "explore.trending_links": "Noutăți",
+  "explore.trending_statuses": "Postări",
+  "explore.trending_tags": "Hastaguri",
   "filter_modal.added.context_mismatch_explanation": "Această categorie de filtre nu se aplică în contextul în care ați accesat acestă postare. Dacă doriți ca postarea să fie filtrată și în acest context, va trebui să editați filtrul.",
   "filter_modal.added.context_mismatch_title": "Nepotrivire contextuală!",
   "filter_modal.added.expired_explanation": "Această categorie de filtre a expirat, va trebui să modifici data de expirare pentru ca aceasta să se aplice.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Acceptă",
   "follow_request.reject": "Respinge",
   "follow_requests.unlocked_explanation": "Chiar dacă contul tău nu este blocat, personalul {domain} a considerat că ai putea prefera să consulți manual cererile de abonare de la aceste conturi.",
+  "followed_tags": "Hastaguri urmărite",
   "footer.about": "Despre",
   "footer.directory": "Catalogul de profiluri",
   "footer.get_app": "Obține aplicația",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Comenzi rapide de la tastatură",
   "footer.privacy_policy": "Politica de confidenţialitate",
   "footer.source_code": "Vizualizează codul sursă",
+  "footer.status": "Status",
   "generic.saved": "Salvat",
   "getting_started.heading": "Primii pași",
   "hashtag.column_header.tag_mode.all": "și {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Focalizează pe coloană",
   "keyboard_shortcuts.compose": "Focalizează pe zona de text",
   "keyboard_shortcuts.description": "Descriere",
-  "keyboard_shortcuts.direct": "pentru a deschide coloana cu mesaje directe",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Coboară în listă",
   "keyboard_shortcuts.enter": "Deschide postarea",
   "keyboard_shortcuts.favourite": "Adaugă postarea la favorite",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Marcaje",
   "navigation_bar.community_timeline": "Cronologie locală",
   "navigation_bar.compose": "Compune o nouă postare",
-  "navigation_bar.direct": "Mesaje directe",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Descoperă",
   "navigation_bar.domain_blocks": "Domenii blocate",
   "navigation_bar.edit_profile": "Modifică profilul",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favorite",
   "navigation_bar.filters": "Cuvinte ignorate",
   "navigation_bar.follow_requests": "Cereri de abonare",
+  "navigation_bar.followed_tags": "Hashtag-uri urmărite",
   "navigation_bar.follows_and_followers": "Abonamente și abonați",
   "navigation_bar.lists": "Liste",
   "navigation_bar.logout": "Deconectare",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Mesaje nedorite",
   "report_notification.categories.violation": "Încălcare a regulilor",
   "report_notification.open": "Deschide raportarea",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Caută",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Caută sau lipește un URL",
-  "search_popout.search_format": "Formate pentru căutare avansată",
-  "search_popout.tips.full_text": "Textele simple returnează postări pe care le-ai scris, favorizat, impulsionat, sau în care sunt menționate, deasemenea și utilizatorii sau hashtag-urile care se potrivesc.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "stare",
-  "search_popout.tips.text": "Textele simple returnează nume, nume de utilizatori și hashtag-urile care se potrivesc",
-  "search_popout.tips.user": "utilizator",
-  "search_results.accounts": "Persoane",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Toate",
   "search_results.hashtags": "Hashtag-uri",
   "search_results.nothing_found": "Nu am putut găsi nimic care să corespundă termenilor de căutare",
@@ -551,7 +559,8 @@
   "status.copy": "Copiează link-ul postării",
   "status.delete": "Șterge",
   "status.detailed_status": "Conversația detaliată",
-  "status.direct": "Mesaj direct către @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Modifică",
   "status.edited": "Modificat în data de {date}",
   "status.edited_x_times": "Modificată {count, plural, one {o dată} few {de {count} ori} other {de {count} de ori}}",
diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json
index 890b62bf4..c86a33bdd 100644
--- a/app/javascript/mastodon/locales/ru.json
+++ b/app/javascript/mastodon/locales/ru.json
@@ -20,7 +20,7 @@
   "account.blocked": "Заблокировано",
   "account.browse_more_on_origin_server": "Посмотреть в оригинальном профиле",
   "account.cancel_follow_request": "Отозвать запрос на подписку",
-  "account.direct": "Написать @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Не уведомлять о постах от @{name}",
   "account.domain_blocked": "Домен заблокирован",
   "account.edit_profile": "Редактировать профиль",
@@ -102,7 +102,7 @@
   "column.blocks": "Заблокированные пользователи",
   "column.bookmarks": "Закладки",
   "column.community": "Локальная лента",
-  "column.direct": "Личные сообщения",
+  "column.direct": "Private mentions",
   "column.directory": "Просмотр профилей",
   "column.domain_blocks": "Заблокированные домены",
   "column.favourites": "Избранное",
@@ -162,13 +162,15 @@
   "confirmations.discard_edit_media.message": "У вас есть несохранённые изменения описания мультимедиа или предпросмотра, отменить их?",
   "confirmations.domain_block.confirm": "Да, заблокировать узел",
   "confirmations.domain_block.message": "Вы точно уверены, что хотите заблокировать {domain} полностью? В большинстве случаев нескольких блокировок и игнорирований вполне достаточно. Вы перестанете видеть публичную ленту и уведомления оттуда. Ваши подписчики из этого домена будут удалены.",
+  "confirmations.edit.confirm": "Редактировать",
+  "confirmations.edit.message": "В данный момент, редактирование перезапишет составляемое вами сообщение. Вы уверены, что хотите продолжить?",
   "confirmations.logout.confirm": "Выйти",
   "confirmations.logout.message": "Вы уверены, что хотите выйти?",
   "confirmations.mute.confirm": "Игнорировать",
   "confirmations.mute.explanation": "Это действие скроет посты данного пользователя и те, в которых он упоминается, но при этом он по-прежнему сможет подписаться и смотреть ваши посты.",
   "confirmations.mute.message": "Вы уверены, что хотите добавить {name} в список игнорируемых?",
   "confirmations.redraft.confirm": "Удалить и исправить",
-  "confirmations.redraft.message": "Вы уверены, что хотите отредактировать этот пост? Старый пост будет удалён, а вместе с ним пропадут отметки «В избранное», продвижения и ответы.",
+  "confirmations.redraft.message": "Вы уверены, что хотите удалить и переписать этот пост? Отметки «избранного», продвижения и ответы к оригинальному посту будут удалены.",
   "confirmations.reply.confirm": "Ответить",
   "confirmations.reply.message": "При ответе, текст набираемого поста будет очищен. Продолжить?",
   "confirmations.unfollow.confirm": "Отписаться",
@@ -200,10 +202,10 @@
   "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.recent": "Часто используемые",
   "emoji_button.search": "Найти...",
   "emoji_button.search_results": "Результаты поиска",
   "emoji_button.symbols": "Символы",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Вы ещё никого не заблокировали.",
   "empty_column.bookmarked_statuses": "У вас пока нет постов в закладках. Как добавите один, он отобразится здесь.",
   "empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!",
-  "empty_column.direct": "У вас пока нет личных сообщений. Как только вы отправите или получите одно, оно появится здесь.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Скрытых доменов пока нет.",
   "empty_column.explore_statuses": "Нет актуального. Проверьте позже!",
   "empty_column.favourited_statuses": "Вы не добавили ни один пост в «Избранное». Как только вы это сделаете, он появится здесь.",
   "empty_column.favourites": "Никто ещё не добавил этот пост в «Избранное». Как только кто-то это сделает, это отобразится здесь.",
   "empty_column.follow_recommendations": "Похоже, у нас нет предложений для вас. Вы можете попробовать поискать людей, которых уже знаете, или изучить актуальные хэштеги.",
   "empty_column.follow_requests": "Вам ещё не приходили запросы на подписку. Все новые запросы будут показаны здесь.",
+  "empty_column.followed_tags": "Вы еще не подписались ни на один хэштег. Когда вы это сделаете, они появятся здесь.",
   "empty_column.hashtag": "С этим хэштегом пока ещё ничего не постили.",
   "empty_column.home": "Ваша лента совсем пуста! Подпишитесь на других, чтобы заполнить её. {suggestions}",
   "empty_column.home.suggestions": "Посмотреть некоторые предложения",
@@ -228,7 +231,7 @@
   "empty_column.lists": "У вас ещё нет списков. Созданные вами списки будут показаны здесь.",
   "empty_column.mutes": "Вы ещё никого не добавляли в список игнорируемых.",
   "empty_column.notifications": "У вас пока нет уведомлений. Взаимодействуйте с другими, чтобы завести разговор.",
-  "empty_column.public": "Здесь совсем пусто. Опубликуйте что-нибудь или подпишитесь на пользователей с других сообществ, чтобы заполнить ленту",
+  "empty_column.public": "Здесь ничего нет! Опубликуйте что-нибудь или подпишитесь на пользователей с других узлов, чтобы заполнить ленту",
   "error.unexpected_crash.explanation": "Из-за несовместимого браузера или ошибки в нашем коде, эта страница не может быть корректно отображена.",
   "error.unexpected_crash.explanation_addons": "Эта страница не может быть корректно отображена. Скорее всего, эта ошибка вызвана расширением браузера или инструментом автоматического перевода.",
   "error.unexpected_crash.next_steps": "Попробуйте обновить страницу. Если проблема не исчезает, используйте Mastodon из-под другого браузера или приложения.",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Авторизовать",
   "follow_request.reject": "Отказать",
   "follow_requests.unlocked_explanation": "Хотя ваша учетная запись не закрыта, команда {domain} подумала, что вы захотите просмотреть запросы от этих учетных записей вручную.",
+  "followed_tags": "Отслеживаемые хэштеги",
   "footer.about": "О проекте",
   "footer.directory": "Каталог профилей",
   "footer.get_app": "Скачать приложение",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Сочетания клавиш",
   "footer.privacy_policy": "Политика конфиденциальности",
   "footer.source_code": "Исходный код",
+  "footer.status": "Статус",
   "generic.saved": "Сохранено",
   "getting_started.heading": "Начать",
   "hashtag.column_header.tag_mode.all": "и {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "фокус на одном из столбцов",
   "keyboard_shortcuts.compose": "фокус на поле ввода",
   "keyboard_shortcuts.description": "Описание",
-  "keyboard_shortcuts.direct": "чтобы открыть колонку с личными сообщениями",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "вниз по списку",
   "keyboard_shortcuts.enter": "открыть пост",
   "keyboard_shortcuts.favourite": "в избранное",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Закладки",
   "navigation_bar.community_timeline": "Локальная лента",
   "navigation_bar.compose": "Создать новый пост",
-  "navigation_bar.direct": "Личные сообщения",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Изучайте",
   "navigation_bar.domain_blocks": "Скрытые домены",
   "navigation_bar.edit_profile": "Изменить профиль",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Избранное",
   "navigation_bar.filters": "Игнорируемые слова",
   "navigation_bar.follow_requests": "Запросы на подписку",
+  "navigation_bar.followed_tags": "Отслеживаемые хэштеги",
   "navigation_bar.follows_and_followers": "Подписки и подписчики",
   "navigation_bar.lists": "Списки",
   "navigation_bar.logout": "Выйти",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Спам",
   "report_notification.categories.violation": "Нарушение правил",
   "report_notification.open": "Открыть жалобу",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Поиск",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Поиск (или вставьте URL)",
-  "search_popout.search_format": "Продвинутый формат поиска",
-  "search_popout.tips.full_text": "Поиск по простому тексту отобразит посты, которые вы написали, добавили в избранное, продвинули или в которых были упомянуты, а также подходящие имена пользователей и хэштеги.",
-  "search_popout.tips.hashtag": "хэштег",
-  "search_popout.tips.status": "пост",
-  "search_popout.tips.text": "Простой ввод текста покажет совпадающие имена пользователей, отображаемые имена и хэштеги",
-  "search_popout.tips.user": "пользователь",
-  "search_results.accounts": "Люди",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Все",
   "search_results.hashtags": "Хэштеги",
   "search_results.nothing_found": "Ничего не найдено по этому запросу",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Статистика сервера:",
   "sign_in_banner.create_account": "Создать учётную запись",
   "sign_in_banner.sign_in": "Войти",
-  "sign_in_banner.text": "Войдите, чтобы следить за профилями, хэштегами или избранным, делиться сообщениями и отвечать на них или взаимодействовать с вашей учётной записью на другом сервере.",
+  "sign_in_banner.text": "Войдите, чтобы отслеживать профили, хэштеги или избранное, делиться сообщениями и отвечать на них. Вы также можете взаимодействовать с вашей учётной записью на другом сервере.",
   "status.admin_account": "Открыть интерфейс модератора для @{name}",
   "status.admin_domain": "Открыть интерфейс модерации {domain}",
   "status.admin_status": "Открыть этот пост в интерфейсе модератора",
@@ -551,7 +559,8 @@
   "status.copy": "Скопировать ссылку на пост",
   "status.delete": "Удалить",
   "status.detailed_status": "Подробный просмотр обсуждения",
-  "status.direct": "Написать @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Изменить",
   "status.edited": "Последнее изменение: {date}",
   "status.edited_x_times": "{count, plural, one {{count} изменение} many {{count} изменений} other {{count} изменения}}",
diff --git a/app/javascript/mastodon/locales/sa.json b/app/javascript/mastodon/locales/sa.json
index c4e51431b..9b16be4f8 100644
--- a/app/javascript/mastodon/locales/sa.json
+++ b/app/javascript/mastodon/locales/sa.json
@@ -20,14 +20,14 @@
   "account.blocked": "अवरुद्धम्",
   "account.browse_more_on_origin_server": "अधिकं मूलव्यक्तिगतविवरणे दृश्यताम्",
   "account.cancel_follow_request": "अनुसरणयाचनामपनय",
-  "account.direct": "प्रत्यक्षसन्देशः @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "यदा @{name} स्थापयति तदा माम्मा ज्ञापय",
   "account.domain_blocked": "प्रदेशो निषिद्धः",
   "account.edit_profile": "सम्पाद्यताम्",
   "account.enable_notifications": "यदा @{name} स्थापयति तदा मां ज्ञापय",
   "account.endorse": "व्यक्तिगतविवरणे वैशिष्ट्यम्",
   "account.featured_tags.last_status_at": "{date} दिने गतस्थापनम्",
-  "account.featured_tags.last_status_never": "न स्थापनम्",
+  "account.featured_tags.last_status_never": "न पत्रम्",
   "account.featured_tags.title": "{name} इत्यस्य विशेषहैस्टैगः",
   "account.follow": "अनुस्रियताम्",
   "account.followers": "अनुसर्तारः",
@@ -50,14 +50,14 @@
   "account.mute_notifications": "@{name} सूचनाः निष्क्रियन्ताम्",
   "account.muted": "निःशब्दम्",
   "account.open_original_page": "मूलपृष्ठमुट्घाटय",
-  "account.posts": "दौत्यानि",
-  "account.posts_with_replies": "दौत्यानि प्रत्युत्तराणि च",
+  "account.posts": "पत्राणि",
+  "account.posts_with_replies": "पत्राणि प्रत्युत्तराणि च",
   "account.report": "आविद्यताम् @{name}",
   "account.requested": "स्वीकृतिः प्रतीक्ष्यते । नश्यतामित्यस्मिन्नुद्यतां निराकर्तुम् ।",
-  "account.requested_follow": "{name} has requested to follow you",
+  "account.requested_follow": "{name} त्वामनुसर्तुमयाचीत्",
   "account.share": "@{name} मित्रस्य विवरणं विभाज्यताम्",
   "account.show_reblogs": "@{name} मित्रस्य प्रकाशनानि दृश्यन्ताम्",
-  "account.statuses_counter": "{count, plural, one {{counter} दौत्यम्} two {{counter} दौत्ये} other {{counter} दौत्यानि}}",
+  "account.statuses_counter": "{count, plural, one {{counter} पत्रम्}  two{{counter} पत्रे} other {{counter} पत्राणि}}",
   "account.unblock": "निषेधता नश्यताम् @{name}",
   "account.unblock_domain": "प्रदेशनिषेधता नश्यताम् {domain}",
   "account.unblock_short": "अनवरुन्धि",
@@ -93,16 +93,16 @@
   "bundle_modal_error.close": "पिधीयताम्",
   "bundle_modal_error.message": "आरोपणे कश्चन दोषो जातः",
   "bundle_modal_error.retry": "पुनः यतताम्",
-  "closed_registrations.other_server_instructions": "Since Mastodon is decentralized, you can create an account on another server and still interact with this one.",
-  "closed_registrations_modal.description": "Creating an account on {domain} is currently not possible, but please keep in mind that you do not need an account specifically on {domain} to use Mastodon.",
-  "closed_registrations_modal.find_another_server": "Find another server",
-  "closed_registrations_modal.preamble": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
-  "closed_registrations_modal.title": "Signing up on Mastodon",
-  "column.about": "About",
+  "closed_registrations.other_server_instructions": "यतोहि मस्टोडोनक्रेन्द्रीयकृतमस्ति, अन्यास्मिन्सर्वरि एकौण्टं स्रष्टुं शक्नोषि एवञ्च एतेन संयोक्तुं शक्नोषि।",
+  "closed_registrations_modal.description": "{domain} मध्ये एकौण्टं करणमधुना न सम्भवति, किन्तु कृपया अवधीयतां यन्मास्टोडोनमुपयोक्तुं {domain} मध्ये एकौण्टं करणं नावश्यकम्।",
+  "closed_registrations_modal.find_another_server": "अन्य सर्वरमन्विच्छ",
+  "closed_registrations_modal.preamble": "मास्टोडोनस्ति अक्रेन्द्रीयकृतमतः कुहापि एकौण्टं स्रष्टुमर्हसि, अस्मिन्सर्वरि सदस्यैश्च संयोक्तुं शक्नोषि। स्वस्य सर्वरं स्रष्टुमपि शक्नोषि।",
+  "closed_registrations_modal.title": "मास्टोडोनि पञ्जीकरणम्",
+  "column.about": "विषये",
   "column.blocks": "निषिद्धभोक्तारः",
   "column.bookmarks": "पुटचिह्नानि",
   "column.community": "स्थानीयसमयतालिका",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "व्यक्तित्वानि दृश्यन्ताम्",
   "column.domain_blocks": "निषिद्धप्रदेशाः",
   "column.favourites": "प्रियाः",
@@ -111,7 +111,7 @@
   "column.lists": "सूचयः",
   "column.mutes": "निःशब्दाः भोक्तारः",
   "column.notifications": "सूचनाः",
-  "column.pins": "कीलितदौत्यानि",
+  "column.pins": "कीलितपत्राणि",
   "column.public": "सङ्घीयसमयतालिका",
   "column_back_button.label": "पूर्वम्",
   "column_header.hide_settings": "विन्यासाः छाद्यन्ताम्",
@@ -124,12 +124,12 @@
   "community.column_settings.local_only": "केवलं स्थानीयम्",
   "community.column_settings.media_only": "सामग्री केवलम्",
   "community.column_settings.remote_only": "दर्गमः केवलम्",
-  "compose.language.change": "Change language",
-  "compose.language.search": "Search languages...",
+  "compose.language.change": "भाषां परिवर्तय",
+  "compose.language.search": "भाषाः अन्विच्छ",
   "compose_form.direct_message_warning_learn_more": "अधिकं ज्ञायताम्",
-  "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
-  "compose_form.lock_disclaimer": "तव लेखा न प्रवेष्टुमशक्या {locked} । कोऽप्यनुसर्ता ते केवलमनुसर्तृृणां कृते स्थितानि दौत्यानि द्रष्टुं शक्नोति ।",
+  "compose_form.encryption_warning": "मस्टोडोनि पत्राणि न अन्ततोऽन्तं सङ्कूटितानि। मास्टोडिनि संवेदनशीलसूचनां मा प्रेषय।",
+  "compose_form.hashtag_warning": "पत्रमिदमसार्वजनिकान्न कस्मिन्नपि प्रचलितवस्तुषु सूचितमिदं प्रेषणम्। केवलं सार्वजनिकप्रेषराणि प्रचलितवस्तुचिह्नेन अन्वेषयितुं शक्यते।",
+  "compose_form.lock_disclaimer": "तव लेखा न प्रवेष्टुमशक्या {locked} । कोऽप्यनुसर्ता ते केवलमनुसर्तृृणां कृते स्थितानि पत्राणि द्रष्टुं शक्नोति ।",
   "compose_form.lock_disclaimer.lock": "अवरुद्धः",
   "compose_form.placeholder": "मनसि ते किमस्ति?",
   "compose_form.poll.add_option": "मतमपरं युज्यताम्",
@@ -138,10 +138,10 @@
   "compose_form.poll.remove_option": "मतमेतन्नश्यताम्",
   "compose_form.poll.switch_to_multiple": "मतदानं परिवर्तयित्वा बहुवैकल्पिकमतदानं क्रियताम्",
   "compose_form.poll.switch_to_single": "मतदानं परिवर्तयित्वा निर्विकल्पमतदानं क्रियताम्",
-  "compose_form.publish": "Publish",
-  "compose_form.publish_form": "Publish",
+  "compose_form.publish": "प्रकाशीकुरु",
+  "compose_form.publish_form": "प्रकाशीकुरु",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.save_changes": "Save changes",
+  "compose_form.save_changes": "परिवर्तनानि रक्ष",
   "compose_form.sensitive.hide": "संवेदनशीलसामग्रीत्यङ्यताम्",
   "compose_form.sensitive.marked": "संवेदनशीलसामग्रीत्यङ्कितम्",
   "compose_form.sensitive.unmarked": "संवेदनशीलसामग्रीति नाङ्कितम्",
@@ -152,49 +152,51 @@
   "confirmations.block.block_and_report": "अवरुध्य आविद्यताम्",
   "confirmations.block.confirm": "निषेधः",
   "confirmations.block.message": "निश्चयेनाऽवरोधो विधेयः {name}?",
-  "confirmations.cancel_follow_request.confirm": "Withdraw request",
-  "confirmations.cancel_follow_request.message": "Are you sure you want to withdraw your request to follow {name}?",
-  "confirmations.delete.confirm": "नश्यताम्",
-  "confirmations.delete.message": "निश्चयेन दौत्यमिदं नश्यताम्?",
-  "confirmations.delete_list.confirm": "नश्यताम्",
-  "confirmations.delete_list.message": "सूचिरियं निश्चयेन स्थायित्वेन च नश्यताम् वा?",
-  "confirmations.discard_edit_media.confirm": "Discard",
-  "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
+  "confirmations.cancel_follow_request.confirm": "अनुरोधनमपनय",
+  "confirmations.cancel_follow_request.message": "{name} अनुसरणस्यानुरोधमपनेतुं दृढीकृतं वा?",
+  "confirmations.delete.confirm": "मार्जय",
+  "confirmations.delete.message": "निश्चयेन पत्रमिदं मार्जितुमिच्छसि?",
+  "confirmations.delete_list.confirm": "मार्जय",
+  "confirmations.delete_list.message": "सूचिरियं निश्चयेन स्थायित्वेन च मार्जितुमिच्छसि वा?",
+  "confirmations.discard_edit_media.confirm": "अपास्य",
+  "confirmations.discard_edit_media.message": "माध्यमवर्णनां प्रदर्शनञ्च अरक्षितानि परिवर्तनानि सन्ति, तानि अपासितुमिच्छसि वा?",
   "confirmations.domain_block.confirm": "निषिद्धः प्रदेशः क्रियताम्",
   "confirmations.domain_block.message": "नूनं निश्चयेनैव विनष्टुमिच्छति पूर्णप्रदेशमेव {domain} ? अधिकांशसन्दर्भेऽस्थायित्वेन निषेधता निःशब्दत्वञ्च पर्याप्तं चयनीयञ्च । न तस्मात् प्रदेशात्सर्वे विषया द्रष्टुमशक्याः किस्यांश्चिदपि सर्वजनिकसमयतालिकायां वा स्वीयसूचनापटले । सर्वेऽनुसर्तारस्ते प्रदेशात् ये सन्ति ते नश्यन्ते ।",
+  "confirmations.edit.confirm": "सम्पादय",
+  "confirmations.edit.message": "सम्पादनमिदानीं लिख्यते तर्हि पूर्वलिखितसन्देशं विनश्य पुनः लिख्यते। निश्चयेनैवं कर्तव्यम्?",
   "confirmations.logout.confirm": "बहिर्गम्यताम्",
   "confirmations.logout.message": "निश्चयेनैव बहिर्गमनं वाञ्छितम्?",
   "confirmations.mute.confirm": "निःशब्दम्",
-  "confirmations.mute.explanation": "एतेन तेषां प्रकटनानि तथा च यत्र ते उल्लिखिताः तानि छाद्यन्ते, किन्त्वेवं सत्यपि ते त्वामनुसर्तुं ततश्च प्रकटनानि द्रष्टुं शक्नुवन्ति ।",
+  "confirmations.mute.explanation": "एतेन तेषां पत्राणि तथा च यत्र ते उल्लिखिताः तानि छाद्यन्ते, किन्त्वेवं सत्यपि ते त्वामनुसर्तुं ततश्च पत्राणि द्रष्टुं शक्नुवन्ति ।",
   "confirmations.mute.message": "किं निश्चयेन निःशब्दं भवेत् {name} मित्रमेतत् ?",
-  "confirmations.redraft.confirm": "विनश्य पुनः लिख्यताम्",
-  "confirmations.redraft.message": "किं वा निश्चयेन नष्टुमिच्छसि दौत्यमेतत्तथा च पुनः लेखितुं? प्रकाशनानि प्रीतयश्च विनष्टा भविष्यन्ति, प्रत्युत्तराण्यपि नश्यन्ते ।",
+  "confirmations.redraft.confirm": "मार्जय पुनश्च लिख्यताम्",
+  "confirmations.redraft.message": "किं वा निश्चयेन नष्टुमिच्छसि पत्रमेतत्तथा च पुनः लेखितुं? प्रकाशनानि प्रीतयश्च विनष्टा भविष्यन्ति, प्रत्युत्तराण्यपि नश्यन्ते ।",
   "confirmations.reply.confirm": "उत्तरम्",
   "confirmations.reply.message": "प्रत्युत्तरमिदानीं लिख्यते तर्हि पूर्वलिखितसन्देशं विनश्य पुनः लिख्यते । निश्चयेनैवं कर्तव्यम् ?",
   "confirmations.unfollow.confirm": "अनुसरणं नश्यताम्",
   "confirmations.unfollow.message": "निश्चयेनैवाऽनुसरणं नश्यतां {name} मित्रस्य?",
-  "conversation.delete": "वार्तालापो नश्यताम्",
+  "conversation.delete": "वार्तालापं मार्जय",
   "conversation.mark_as_read": "पठितमित्यङ्क्यताम्",
   "conversation.open": "वार्तालापो दृश्यताम्",
   "conversation.with": "{names} जनैः साकम्",
-  "copypaste.copied": "Copied",
-  "copypaste.copy": "Copy",
+  "copypaste.copied": "प्रतिलिपिंकृतम्",
+  "copypaste.copy": "प्रतिलिपिः",
   "directory.federated": "सुपरिचितं Fediverse इति स्थानात्",
   "directory.local": "{domain} प्रदेशात्केवलम्",
   "directory.new_arrivals": "नवामगमाः",
   "directory.recently_active": "नातिपूर्वं सक्रियः",
-  "disabled_account_banner.account_settings": "Account settings",
-  "disabled_account_banner.text": "Your account {disabledAccount} is currently disabled.",
-  "dismissable_banner.community_timeline": "These are the most recent public posts from people whose accounts are hosted by {domain}.",
-  "dismissable_banner.dismiss": "Dismiss",
-  "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
-  "dismissable_banner.explore_statuses": "These posts from this and other servers in the decentralized network are gaining traction on this server right now.",
-  "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
-  "dismissable_banner.public_timeline": "These are the most recent public posts from people on this and other servers of the decentralized network that this server knows about.",
-  "embed.instructions": "दौत्यमेतत् स्वीयजालस्थाने स्थापयितुमधो लिखितो विध्यादेशो युज्यताम्",
+  "disabled_account_banner.account_settings": "एकौण्टः सेटिङ्ग्स्",
+  "disabled_account_banner.text": "तव एकौण्ट् {disabledAccount} अधुना निष्कृतमस्ति।",
+  "dismissable_banner.community_timeline": "तानि तेषां जनानां नूतनतमानि सार्वजनिकानि पत्राणि सन्ति येषामेकौण्टः {domain} द्वारा होस्त् भवन्ति।",
+  "dismissable_banner.dismiss": "अपास्य",
+  "dismissable_banner.explore_links": "एतासां वार्तानां विषये अधुना अकेन्द्रीकृतजालस्य अस्मिनन्येषु च सर्वर्षु जनैश्चर्चा क्रियते।",
+  "dismissable_banner.explore_statuses": "अकेन्द्रीकृतजालस्य अस्मदन्येभ्यश्च सर्वर्भ्यः एतानि पत्राणि इदानीमस्मिन्सर्वरि कर्षणं प्राप्नुवन्ति।",
+  "dismissable_banner.explore_tags": "अकेन्द्रीकृतजालस्य अस्मदन्येभ्यश्च सर्वर्भ्यः एतानि प्रचलितवस्तूनि इदानीमस्मिन्सर्वरि कर्षणं प्राप्नुवन्ति।",
+  "dismissable_banner.public_timeline": "एतानि अकेन्द्रीकृतजालस्य अस्मिनन्येषु च सर्वर्षु जनेभ्योऽद्यतनतमानि सार्वजनिकपत्राणि सन्ति येषां विषये सर्वरयं वेत्ति।",
+  "embed.instructions": "पत्रमेतत्स्वीयजालस्थाने स्थापयितुमधो लिखितो विध्यादेशो युज्यताम्",
   "embed.preview": "अत्रैवं दृश्यते तत्:",
   "emoji_button.activity": "आचरणम्",
-  "emoji_button.clear": "Clear",
+  "emoji_button.clear": "मार्जय",
   "emoji_button.custom": "स्वीयानुकूलम्",
   "emoji_button.flags": "ध्वजाः",
   "emoji_button.food": "भोजनं पेयञ्च",
@@ -208,116 +210,119 @@
   "emoji_button.search_results": "अन्वेषणपरिणामाः",
   "emoji_button.symbols": "चिह्नानि",
   "emoji_button.travel": "यात्रा च स्थानानि",
-  "empty_column.account_suspended": "Account suspended",
-  "empty_column.account_timeline": "न दौत्यान्यत्र",
+  "empty_column.account_suspended": "एकौण्ट् निलम्बितः",
+  "empty_column.account_timeline": "न पत्रमिह!",
   "empty_column.account_unavailable": "व्यक्तित्वं न प्राप्यते",
   "empty_column.blocks": "नैकोऽप्युपभोक्ता निषिद्धो वर्तते",
-  "empty_column.bookmarked_statuses": "नैकमपि पुटचिह्नयुक्तदौत्यानि सन्ति । यदा भविष्यति तदत्र दृश्यते ।",
+  "empty_column.bookmarked_statuses": "नैकमपि पुटचिह्नयुक्तपत्राणि सन्ति । यदा भविष्यति तदत्र दृश्यते ।",
   "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.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "न निषिद्धप्रदेशाः सन्ति ।",
-  "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
-  "empty_column.favourited_statuses": "न प्रियदौत्यानि सन्ति । यदा प्रीतिरित्यङ्क्यतेऽत्र दृश्यते ।",
-  "empty_column.favourites": "नैतद्दौत्यं प्रियमस्ति कस्मै अपि । यदा कस्मै प्रियं भवति तदाऽत्र दृश्यते ।",
-  "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
+  "empty_column.explore_statuses": "अधुना किमपि न प्रचलति। परे पुनः पश्य!",
+  "empty_column.favourited_statuses": "न तव अधुना पर्यन्तं प्रियपत्राणि सन्ति। यदा प्रीतिरित्यङ्क्यतेऽत्र दृश्यते।",
+  "empty_column.favourites": "नैतत्पत्रं प्रियमस्ति कस्मै अपि। यदा कस्मै प्रियं भवति तदाऽत्र दृश्यते।",
+  "empty_column.follow_recommendations": "तुभ्यं कोऽपि प्रबोधो न जनयितुं न शक्यतेऽति प्रतीयते। अन्वेष्यप्रणालिं प्रयोक्तुं जनानवलोकयितुं शक्नोषि उत प्रचलितवस्तूनि अन्वेषितुं शक्नोषि।",
   "empty_column.follow_requests": "नाऽनुसरणानुरोधस्ते वर्तते । यदैको प्राप्यतेऽत्र दृश्यते ।",
+  "empty_column.followed_tags": "कान्यपि प्रचलितवस्तूनीदानीमपि नान्वसार्षीः। यदा अनुसरसि तदा तानि इह दृश्यन्ते।",
   "empty_column.hashtag": "नाऽस्मिन् प्रचलितवस्तुचिह्ने किमपि ।",
   "empty_column.home": "गृहसमयतालिका रिक्ताऽस्ति । गम्यतां {public} वाऽन्वेषणैः प्रारभ्यतां मेलनं क्रियताञ्च ।",
-  "empty_column.home.suggestions": "See some suggestions",
-  "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": "You don't have any notifications yet. When other people interact with you, you will see it here.",
-  "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other servers to fill it up",
-  "error.unexpected_crash.explanation": "Due to a bug in our code or a browser compatibility issue, this page could not be displayed correctly.",
-  "error.unexpected_crash.explanation_addons": "This page could not be displayed correctly. This error is likely caused by a browser add-on or automatic translation tools.",
-  "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
-  "error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
-  "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
-  "errors.unexpected_crash.report_issue": "Report issue",
-  "explore.search_results": "Search results",
-  "explore.suggested_follows": "For you",
-  "explore.title": "Explore",
-  "explore.trending_links": "News",
-  "explore.trending_statuses": "Posts",
-  "explore.trending_tags": "Hashtags",
-  "filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.",
-  "filter_modal.added.context_mismatch_title": "Context mismatch!",
-  "filter_modal.added.expired_explanation": "This filter category has expired, you will need to change the expiration date for it to apply.",
-  "filter_modal.added.expired_title": "Expired filter!",
-  "filter_modal.added.review_and_configure": "To review and further configure this filter category, go to the {settings_link}.",
-  "filter_modal.added.review_and_configure_title": "Filter settings",
-  "filter_modal.added.settings_link": "settings page",
-  "filter_modal.added.short_explanation": "This post has been added to the following filter category: {title}.",
-  "filter_modal.added.title": "Filter added!",
-  "filter_modal.select_filter.context_mismatch": "does not apply to this context",
-  "filter_modal.select_filter.expired": "expired",
-  "filter_modal.select_filter.prompt_new": "New category: {name}",
-  "filter_modal.select_filter.search": "Search or create",
-  "filter_modal.select_filter.subtitle": "Use an existing category or create a new one",
-  "filter_modal.select_filter.title": "Filter this post",
-  "filter_modal.title.status": "Filter a post",
-  "follow_recommendations.done": "Done",
-  "follow_recommendations.heading": "Follow people you'd like to see posts from! Here are some suggestions.",
-  "follow_recommendations.lead": "Posts from people you follow will show up in chronological order on your home feed. Don't be afraid to make mistakes, you can unfollow people just as easily any time!",
-  "follow_request.authorize": "Authorize",
-  "follow_request.reject": "Reject",
-  "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
-  "footer.about": "About",
-  "footer.directory": "Profiles directory",
-  "footer.get_app": "Get the app",
-  "footer.invite": "Invite people",
-  "footer.keyboard_shortcuts": "Keyboard shortcuts",
-  "footer.privacy_policy": "Privacy policy",
-  "footer.source_code": "View source code",
-  "generic.saved": "Saved",
-  "getting_started.heading": "Getting started",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.select.no_options_message": "No suggestions found",
-  "hashtag.column_settings.select.placeholder": "Enter hashtags…",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "empty_column.home.suggestions": "काञ्चित्प्रबोधान्पश्य",
+  "empty_column.list": "न किमपि वर्तते सूच्यामस्याम् । यदा सूच्याः सदस्या नवपत्राणि प्रकटीकुर्वन्ति तदाऽत्राऽऽयान्ति ।",
+  "empty_column.lists": "तव पार्श्वे न कापि सूचिर्वर्तते। यदैकां सृजसि तदा अत्र दृश्यते।",
+  "empty_column.mutes": "त्वया अद्यापि नैकोऽप्युपभोक्ता मूकीकृतो वर्तते।",
+  "empty_column.notifications": "तव पार्श्वे न अधुना पर्यन्तं किमपि विज्ञापनं वर्तते। यदा अन्या जना त्वया संयोजयन्ति तदा इह पश्यसि।",
+  "empty_column.public": "न इह किमपि वर्तते! किञ्चिल्लिख सार्वजनिकरूपेण, उत स्वयमन्यसर्वर्तः उपभोक्तॄननुसर एतत्पूरयितुम्",
+  "error.unexpected_crash.explanation": "अस्माकं विद्यादेशे दोषादुत ब्रौसर्सङ्गतिसमस्यायाः पृष्ठमिदं सम्यग्रूपेण न दृश्यते।",
+  "error.unexpected_crash.explanation_addons": "पृष्ठमिदं सम्यग्रूपेण न दृश्यते। दोषोऽयं सम्भवतो ब्रौसरेडनुत स्वचलितानुवादोपकरणानां कारणात्।",
+  "error.unexpected_crash.next_steps": "पृष्ठं पुनः रेफ्रेशं कर्तुं यतस्व। यदि तत्परेऽपि कार्यं नाकार्षीत्तर्ह्यप्यन्यब्रौसरा उत नेटिवेपा मास्टोडोनुपयोक्तुं शक्नोषि।",
+  "error.unexpected_crash.next_steps_addons": "तानि निष्क्रियं कृत्वा पृष्ठं रिफ्रेशं कर्तुं यतस्व। यदि तत्परेऽपि कार्यं नाकार्षीत्तर्ह्यप्यन्यब्रौसरा उत नेटिवेपा मास्टोडोनुपयोक्तुं शक्नोषि।",
+  "errors.unexpected_crash.copy_stacktrace": "स्तेक्त्रेसमनुलिपिं कुरु क्लिप्फलकं",
+  "errors.unexpected_crash.report_issue": "दोषमावेदय",
+  "explore.search_results": "परिणामानविच्छ",
+  "explore.suggested_follows": "तुभ्यम्",
+  "explore.title": "अन्विच्छ",
+  "explore.trending_links": "वार्ताः",
+  "explore.trending_statuses": "पत्राणि",
+  "explore.trending_tags": "प्रचलितवस्तूनि",
+  "filter_modal.added.context_mismatch_explanation": "यस्मिन्सन्दर्भे त्वया पत्रमिदं प्राप्तं तस्मिनयं फिल्तर्वर्गो न प्रवर्तते। यदि इच्छसि पत्रं फिल्तर्कर्तुमस्मिन्सन्दर्भेऽपि, त्वया फिल्तर्सम्पादयितव्यम्।",
+  "filter_modal.added.context_mismatch_title": "सन्दर्भोऽसमः!",
+  "filter_modal.added.expired_explanation": "फिल्तर्वर्गोऽयं समाप्तः, तस्य संयोजनाय त्वया समाप्तिदिवसः परिवर्तितव्यः।",
+  "filter_modal.added.expired_title": "गतप्राणः फिल्तार्!",
+  "filter_modal.added.review_and_configure": "फिल्तर्वर्गमिममवलोकनाय अधिकविन्यासाय च, {settings_link} प्रति याहि।",
+  "filter_modal.added.review_and_configure_title": "विन्यासान्फिल्तरं कुरु",
+  "filter_modal.added.settings_link": "विन्यासपृष्ठम्",
+  "filter_modal.added.short_explanation": "पत्रमिदं निम्नलिखितः फिल्तर्वर्गेन योजितः: {title}।",
+  "filter_modal.added.title": "फिल्तर्योजितम्!",
+  "filter_modal.select_filter.context_mismatch": "अस्मिन्सन्दर्भे न प्रवर्तते",
+  "filter_modal.select_filter.expired": "समाप्तम्",
+  "filter_modal.select_filter.prompt_new": "नूतनवर्गः: {name}",
+  "filter_modal.select_filter.search": "अन्विच्छ उत सृज",
+  "filter_modal.select_filter.subtitle": "वर्तमानवर्गं प्रयोजय उत नूतनं सृज",
+  "filter_modal.select_filter.title": "पत्रमिदं फिल्तरं कुरु",
+  "filter_modal.title.status": "पत्रं फिल्तरं कुरु",
+  "follow_recommendations.done": "कृतम्",
+  "follow_recommendations.heading": "जनमनुसर यस्मात्पत्राणि द्रष्टुमिच्छसि! सन्ति इह काश्चित्सूचनाः।",
+  "follow_recommendations.lead": "जनेभ्यः पत्राणि याननुसरसि कालिकक्रमेण दर्शिष्यन्ते तव गृहनिरासे। दोषान्मा बिभीहि, कदापि सरलरूपेण ते जनाननुसरणं वारयितुं शक्नोषि!",
+  "follow_request.authorize": "प्रमाणीकुरु",
+  "follow_request.reject": "प्रत्याख्याहि",
+  "follow_requests.unlocked_explanation": "यद्यपि ते एकौण्ट् तालयन्त्रेण न बन्धितं, {domain} अधिकारिगणोऽचिचिन्तद्यदेतेभ्य एकौण्ट्भ्योऽनुसरणानुरोधानां समीक्षां स्वहस्तेन चिकीर्षसीति।",
+  "followed_tags": "अनुसरितानि प्रचलितवस्तूनि",
+  "footer.about": "विषये",
+  "footer.directory": "मुखपार्श्वविभागः",
+  "footer.get_app": "एप् लभस्व",
+  "footer.invite": "जनं निमन्त्रय",
+  "footer.keyboard_shortcuts": "कीफलकहर्स्वमार्गाः",
+  "footer.privacy_policy": "गोपनीयतानीतिः",
+  "footer.source_code": "स्रोतविद्यादेशं दर्शय",
+  "footer.status": "स्थितिः",
+  "generic.saved": "रक्षितम्",
+  "getting_started.heading": "आरम्भकरणम्",
+  "hashtag.column_header.tag_mode.all": "{additional} च",
+  "hashtag.column_header.tag_mode.any": "वा {additional}",
+  "hashtag.column_header.tag_mode.none": "{additional} विहाय",
+  "hashtag.column_settings.select.no_options_message": "न का अपि सूचनाः प्राप्ताः",
+  "hashtag.column_settings.select.placeholder": "प्रचलितवस्तूनि प्रविश",
+  "hashtag.column_settings.tag_mode.all": "एतानि सर्वाणि",
+  "hashtag.column_settings.tag_mode.any": "किञ्चिदेतेषु",
+  "hashtag.column_settings.tag_mode.none": "न किमप्येतेषु",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
-  "hashtag.follow": "Follow hashtag",
-  "hashtag.unfollow": "Unfollow hashtag",
-  "home.column_settings.basic": "Basic",
-  "home.column_settings.show_reblogs": "Show boosts",
-  "home.column_settings.show_replies": "Show replies",
-  "home.hide_announcements": "Hide announcements",
-  "home.show_announcements": "Show announcements",
-  "interaction_modal.description.favourite": "With an account on Mastodon, you can favourite this post to let the author know you appreciate it and save it for later.",
-  "interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.",
-  "interaction_modal.description.reblog": "With an account on Mastodon, you can boost this post to share it with your own followers.",
-  "interaction_modal.description.reply": "With an account on Mastodon, you can respond to this post.",
-  "interaction_modal.on_another_server": "On a different server",
-  "interaction_modal.on_this_server": "On this server",
-  "interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favourite Mastodon app or the web interface of your Mastodon server.",
-  "interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.",
-  "interaction_modal.title.favourite": "Favourite {name}'s post",
-  "interaction_modal.title.follow": "Follow {name}",
-  "interaction_modal.title.reblog": "Boost {name}'s post",
-  "interaction_modal.title.reply": "Reply to {name}'s post",
-  "intervals.full.days": "{number, plural, one {# day} other {# days}}",
-  "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
-  "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
+  "hashtag.follow": "प्रचलितवस्तु अनुसर",
+  "hashtag.unfollow": "प्रचलितवस्तु अनुसरणं वारय",
+  "home.column_settings.basic": "मूलभूतम्",
+  "home.column_settings.show_reblogs": "बुस्त् दर्शय",
+  "home.column_settings.show_replies": "उत्तराणि दर्शय",
+  "home.hide_announcements": "विज्ञापनानि प्रच्छादय",
+  "home.show_announcements": "विज्ञापनानि दर्शय",
+  "interaction_modal.description.favourite": "मास्टोडोनि एकौण्टा, पत्रमिदं प्रियं कर्तुं शक्नोषि तस्य लेखकं प्रशंसां करोषीति ज्ञापयितुमिदं पश्चाद्रक्षितुञ्च।",
+  "interaction_modal.description.follow": "मास्टोडोनि एकौण्टा {name} नाम्ना उपभोक्तारमनुसर्तुं शक्नोषि तस्य पत्राणि लब्धुं ते गृहनिरासे।",
+  "interaction_modal.description.reblog": "मास्टोडिनि एकौण्टा पत्रमिदं बुस्तिति कर्तुं शक्नोषि ते स्वानुसारिणो भागं कर्तुम्।",
+  "interaction_modal.description.reply": "मास्टोडोनि एकौण्टा पत्रमिदं प्रतिवादयितुं शक्नोषि।",
+  "interaction_modal.on_another_server": "अन्यस्मिन्सर्वरि",
+  "interaction_modal.on_this_server": "अस्मिन्सर्वरि",
+  "interaction_modal.other_server_instructions": "एतत् URL प्रतिलिपिं कृत्वा स्वस्य प्रियस्य मास्टोडोन ऐपोऽन्वेषणक्षेत्रे उत स्वस्य मास्तोडोन्सर्वरो जालमध्यस्थे चिनु।",
+  "interaction_modal.preamble": "यतो मास्टोडोन्विकेन्द्रीयकृतोऽस्ति, अन्येन मास्टोडोन्सर्वरा उत सुसङ्गतेन आश्रयेण ते वर्तमानौकौण्टं प्रयोक्तुं शक्नोषि यदि अस्मिन्कोऽपि ते एकौण्ट् नास्ति।",
+  "interaction_modal.title.favourite": "प्रियस्य {name} नाम्ना उपभोक्तुः पत्रम्।",
+  "interaction_modal.title.follow": "{name} अनुसर",
+  "interaction_modal.title.reblog": "{name} नाम्ना उपभोक्तुः पत्रं बुस्त्कुरु",
+  "interaction_modal.title.reply": "{name} नाम्ना उपभोक्तुःपत्रं प्रतिवादय",
+  "intervals.full.days": "{number, plural, one {# दिनम्} other {# दिनानि}}",
+  "intervals.full.hours": "{number, plural, one {# होरा} other {# होराः}}",
+  "intervals.full.minutes": "{number, plural, one {# क्षणम्} other {# क्षणानि}}",
   "keyboard_shortcuts.back": "to navigate back",
   "keyboard_shortcuts.blocked": "to open blocked users list",
-  "keyboard_shortcuts.boost": "to boost",
+  "keyboard_shortcuts.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.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.enter": "पत्रं उद्घाटय",
+  "keyboard_shortcuts.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.hotkey": "होत्की",
   "keyboard_shortcuts.legend": "to display this legend",
   "keyboard_shortcuts.local": "to open local timeline",
   "keyboard_shortcuts.mention": "to mention author",
@@ -325,258 +330,262 @@
   "keyboard_shortcuts.my_profile": "to open your profile",
   "keyboard_shortcuts.notifications": "to open notifications column",
   "keyboard_shortcuts.open_media": "to open media",
-  "keyboard_shortcuts.pinned": "to open pinned toots list",
+  "keyboard_shortcuts.pinned": "कीलितपत्राणां सूचिमुद्घाटय",
   "keyboard_shortcuts.profile": "to open author's profile",
-  "keyboard_shortcuts.reply": "to reply",
+  "keyboard_shortcuts.reply": "पत्राय प्रतिवादय",
   "keyboard_shortcuts.requests": "to open follow requests list",
   "keyboard_shortcuts.search": "to focus search",
   "keyboard_shortcuts.spoilers": "to show/hide CW field",
   "keyboard_shortcuts.start": "to open \"get started\" column",
   "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
   "keyboard_shortcuts.toggle_sensitivity": "to show/hide media",
-  "keyboard_shortcuts.toot": "to start a brand new toot",
+  "keyboard_shortcuts.toot": "नूतनपत्रमारभस्व",
   "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
   "keyboard_shortcuts.up": "to move up in the list",
-  "lightbox.close": "Close",
-  "lightbox.compress": "Compress image view box",
-  "lightbox.expand": "Expand image view box",
-  "lightbox.next": "Next",
-  "lightbox.previous": "Previous",
-  "limited_account_hint.action": "Show profile anyway",
-  "limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
-  "lists.account.add": "Add to list",
-  "lists.account.remove": "Remove from list",
-  "lists.delete": "Delete list",
-  "lists.edit": "Edit list",
-  "lists.edit.submit": "Change title",
-  "lists.new.create": "Add list",
-  "lists.new.title_placeholder": "New list title",
-  "lists.replies_policy.followed": "Any followed user",
-  "lists.replies_policy.list": "Members of the list",
-  "lists.replies_policy.none": "No one",
-  "lists.replies_policy.title": "Show replies to:",
-  "lists.search": "Search among people you follow",
-  "lists.subheading": "Your lists",
-  "load_pending": "{count, plural, one {# new item} other {# new items}}",
-  "loading_indicator.label": "Loading...",
-  "media_gallery.toggle_visible": "{number, plural, one {Hide image} other {Hide images}}",
-  "missing_indicator.label": "Not found",
-  "missing_indicator.sublabel": "This resource could not be found",
-  "moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.",
-  "mute_modal.duration": "Duration",
-  "mute_modal.hide_notifications": "Hide notifications from this user?",
-  "mute_modal.indefinite": "Indefinite",
-  "navigation_bar.about": "About",
-  "navigation_bar.blocks": "Blocked users",
-  "navigation_bar.bookmarks": "Bookmarks",
-  "navigation_bar.community_timeline": "Local timeline",
-  "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
-  "navigation_bar.discover": "Discover",
+  "lightbox.close": "पिधीयताम्",
+  "lightbox.compress": "सङ्कुच चित्रप्रदर्शनपेटकम्",
+  "lightbox.expand": "चित्रप्रदर्शनपेटकं विस्तारय",
+  "lightbox.next": "परः",
+  "lightbox.previous": "पूर्वः",
+  "limited_account_hint.action": "प्रोफैलं दर्शय कथञ्चित्",
+  "limited_account_hint.title": "{domain} इत्यस्य प्रशासकैरयं प्रोफैल्प्रच्छन्नः।",
+  "lists.account.add": "सूचेर्मध्ये योजय",
+  "lists.account.remove": "सूचेर्मार्जय",
+  "lists.delete": "सूचिं मार्जय",
+  "lists.edit": "सूचिं सम्पादय",
+  "lists.edit.submit": "उपाधिं परिवर्तय",
+  "lists.new.create": "सूचिं योजय",
+  "lists.new.title_placeholder": "नूतनसूच्युपाधिः",
+  "lists.replies_policy.followed": "कोऽप्यनुसारितोपभोक्ता",
+  "lists.replies_policy.list": "सूचेस्सदस्याः",
+  "lists.replies_policy.none": "न कोऽपि",
+  "lists.replies_policy.title": "एतमुत्तराणि दर्शय :",
+  "lists.search": "त्वया अनुसारितजनेषु अन्विष्य",
+  "lists.subheading": "तव सूचयः",
+  "load_pending": "{count, plural, one {# नूतनवस्तु} other {# नूतनवस्तूनि}}",
+  "loading_indicator.label": "आरोपयति...",
+  "media_gallery.toggle_visible": "{number, plural, one {चित्रं प्रच्छादय} other {चित्राणि प्रच्छादय}}",
+  "missing_indicator.label": "नोपलब्धम्",
+  "missing_indicator.sublabel": "उपायोऽयं नोपलब्धुं शक्यते",
+  "moved_to_account_banner.text": "तव एकौण्ट् {disabledAccount} अधुना निष्कृतो यतोहि {movedToAccount} अस्मिन्त्वमसार्षीः।",
+  "mute_modal.duration": "परिमाणम्",
+  "mute_modal.hide_notifications": "अस्मादुपभोक्तुर्विज्ञापनानि प्रच्छादयितुमिच्छसि वा?",
+  "mute_modal.indefinite": "अ॑परिमितम्",
+  "navigation_bar.about": "विषये",
+  "navigation_bar.blocks": "निषिद्धभोक्तारः",
+  "navigation_bar.bookmarks": "पुटचिह्नानि",
+  "navigation_bar.community_timeline": "स्थानीयसमयतालिका",
+  "navigation_bar.compose": "नूतनपत्रं रचय",
+  "navigation_bar.direct": "Private mentions",
+  "navigation_bar.discover": "आविष्कुरु",
   "navigation_bar.domain_blocks": "Hidden domains",
-  "navigation_bar.edit_profile": "Edit profile",
-  "navigation_bar.explore": "Explore",
-  "navigation_bar.favourites": "Favourites",
-  "navigation_bar.filters": "Muted words",
-  "navigation_bar.follow_requests": "Follow requests",
-  "navigation_bar.follows_and_followers": "Follows and followers",
-  "navigation_bar.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.search": "Search",
-  "navigation_bar.security": "Security",
-  "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.",
-  "notification.admin.report": "{name} reported {target}",
-  "notification.admin.sign_up": "{name} signed up",
-  "notification.favourite": "{name} favourited your status",
-  "notification.follow": "{name} followed you",
-  "notification.follow_request": "{name} has requested to follow you",
-  "notification.mention": "{name} mentioned you",
-  "notification.own_poll": "Your poll has ended",
-  "notification.poll": "A poll you have voted in has ended",
-  "notification.reblog": "{name} boosted your status",
-  "notification.status": "{name} just posted",
-  "notification.update": "{name} edited a post",
-  "notifications.clear": "Clear notifications",
-  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
-  "notifications.column_settings.admin.report": "New reports:",
-  "notifications.column_settings.admin.sign_up": "New sign-ups:",
-  "notifications.column_settings.alert": "Desktop notifications",
-  "notifications.column_settings.favourite": "Favourites:",
-  "notifications.column_settings.filter_bar.advanced": "Display all categories",
-  "notifications.column_settings.filter_bar.category": "Quick filter bar",
-  "notifications.column_settings.filter_bar.show_bar": "Show filter bar",
-  "notifications.column_settings.follow": "New followers:",
-  "notifications.column_settings.follow_request": "New follow requests:",
-  "notifications.column_settings.mention": "Mentions:",
-  "notifications.column_settings.poll": "Poll results:",
-  "notifications.column_settings.push": "Push notifications",
-  "notifications.column_settings.reblog": "Boosts:",
-  "notifications.column_settings.show": "Show in column",
-  "notifications.column_settings.sound": "Play sound",
-  "notifications.column_settings.status": "New toots:",
-  "notifications.column_settings.unread_notifications.category": "Unread notifications",
-  "notifications.column_settings.unread_notifications.highlight": "Highlight unread notifications",
-  "notifications.column_settings.update": "Edits:",
-  "notifications.filter.all": "All",
-  "notifications.filter.boosts": "Boosts",
-  "notifications.filter.favourites": "Favourites",
-  "notifications.filter.follows": "Follows",
-  "notifications.filter.mentions": "Mentions",
-  "notifications.filter.polls": "Poll results",
-  "notifications.filter.statuses": "Updates from people you follow",
-  "notifications.grant_permission": "Grant permission.",
-  "notifications.group": "{count} notifications",
-  "notifications.mark_as_read": "Mark every notification as read",
-  "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
-  "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
-  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
-  "notifications_permission_banner.enable": "Enable desktop notifications",
-  "notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.",
-  "notifications_permission_banner.title": "Never miss a thing",
-  "picture_in_picture.restore": "Put it back",
-  "poll.closed": "Closed",
-  "poll.refresh": "Refresh",
-  "poll.total_people": "{count, plural, one {# person} other {# people}}",
-  "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
-  "poll.vote": "Vote",
-  "poll.voted": "You voted for this answer",
-  "poll.votes": "{votes, plural, one {# vote} other {# votes}}",
-  "poll_button.add_poll": "Add a poll",
-  "poll_button.remove_poll": "Remove poll",
-  "privacy.change": "Adjust status privacy",
-  "privacy.direct.long": "Visible for mentioned users only",
+  "navigation_bar.edit_profile": "प्रोफैलं सम्पाद्यताम्",
+  "navigation_bar.explore": "अन्विच्छ",
+  "navigation_bar.favourites": "प्रियाः",
+  "navigation_bar.filters": "मूकीकृतानि पदानि",
+  "navigation_bar.follow_requests": "अनुसरणानुरोधाः",
+  "navigation_bar.followed_tags": "अनुसरितानि प्रचलितवस्तूनि",
+  "navigation_bar.follows_and_followers": "याननुसरति अनुसारिणश्च",
+  "navigation_bar.lists": "सूचयः",
+  "navigation_bar.logout": "निष्क्रमणं कुरु",
+  "navigation_bar.mutes": "निःशब्दा भोक्तारः",
+  "navigation_bar.personal": "व्यक्तिगतम्",
+  "navigation_bar.pins": "कीलितपत्राणि",
+  "navigation_bar.preferences": "अधिकरुचयः",
+  "navigation_bar.public_timeline": "सङ्घीयसमयतालिका",
+  "navigation_bar.search": "अन्विच्छ",
+  "navigation_bar.security": "सुरक्षा",
+  "not_signed_in_indicator.not_signed_in": "उपायमिमं लब्धुं सम्प्रवेश आवश्यकः।",
+  "notification.admin.report": "{name} {target} प्रतिवेदयञ्चकार",
+  "notification.admin.sign_up": "{name} संविवेश",
+  "notification.favourite": "{name} तव पत्रं प्रियमकार्षीत्",
+  "notification.follow": "{name} त्वामनुससार",
+  "notification.follow_request": "{name} त्वामनुसर्तुमयाचीत्",
+  "notification.mention": "{name} त्वामुल्लिलेख",
+  "notification.own_poll": "तव निर्वाचनं समाप्तम्",
+  "notification.poll": "यस्मिन्निर्वाचने मतमदास्तन्निर्वाचनं समाप्तम्",
+  "notification.reblog": "{name} तव पत्रं बुस्तिमिति अकार्षीत्",
+  "notification.status": "{name} अधुना अस्थापयिष्ट",
+  "notification.update": "{name} पत्रमेकं समपादयिष्ट",
+  "notifications.clear": "विज्ञापनानि मार्जय",
+  "notifications.clear_confirmation": "तव सर्वाणि विज्ञापनानि सर्वकालाय मार्जयितुमिच्छसि वा?",
+  "notifications.column_settings.admin.report": "नूतनावेदनानि",
+  "notifications.column_settings.admin.sign_up": "नूतनपञ्जीकरणम्:",
+  "notifications.column_settings.alert": "देस्क्टप्विज्ञापनानि",
+  "notifications.column_settings.favourite": "प्रियाः",
+  "notifications.column_settings.filter_bar.advanced": "सर्वाणि वर्गाणि प्रदर्शय",
+  "notifications.column_settings.filter_bar.category": "द्रुतशोधकशलाका",
+  "notifications.column_settings.filter_bar.show_bar": "शोधकशालकां दर्शय",
+  "notifications.column_settings.follow": "नूतनानुसारिणः:",
+  "notifications.column_settings.follow_request": "नूतनानुसरणानुरोधाः:",
+  "notifications.column_settings.mention": "उल्लिखितानि :",
+  "notifications.column_settings.poll": "मतदानस्य परिणामः :",
+  "notifications.column_settings.push": "आघातविज्ञापनानि",
+  "notifications.column_settings.reblog": "बुस्तः :",
+  "notifications.column_settings.show": "स्तम्भे दर्शय",
+  "notifications.column_settings.sound": "ध्वनिं वादय",
+  "notifications.column_settings.status": "नूतनपत्राणि:",
+  "notifications.column_settings.unread_notifications.category": "अपठितविज्ञापनानि",
+  "notifications.column_settings.unread_notifications.highlight": "अपठितविज्ञापनानि उद्वर्णय",
+  "notifications.column_settings.update": "सम्पादनानि :",
+  "notifications.filter.all": "सर्वम्",
+  "notifications.filter.boosts": "बुस्तः",
+  "notifications.filter.favourites": "प्रियाः",
+  "notifications.filter.follows": "अनुसरति",
+  "notifications.filter.mentions": "उल्लिखितानि",
+  "notifications.filter.polls": "मतदानस्य परिणामः",
+  "notifications.filter.statuses": "त्वयानुसरितजनेभ्यः परिवर्तनानि",
+  "notifications.grant_permission": "अनुमतिं देहि।",
+  "notifications.group": "{count} विज्ञापनानि",
+  "notifications.mark_as_read": "सर्वाणि विज्ञापनानि पठितरूपेण चिह्नीकुरु",
+  "notifications.permission_denied": "पूर्वस्यास्वीकृतब्रौसरनुमत्यनुरोधस्य कारणाद्देस्क्तप्विज्ञापनानि न उपलब्धानि",
+  "notifications.permission_denied_alert": "देस्क्तप्विज्ञापनानि सकर्तुं न शक्यते यतो ब्रौसरनुमतिं पूर्वेऽस्वीकृतम्",
+  "notifications.permission_required": "देस्क्तप्विज्ञापनानि नोपलब्धानि यतोहि आवश्यकानुमतिं न स्वीकृतम्।",
+  "notifications_permission_banner.enable": "देस्क्टप्विज्ञापनानि सशक्तं कुरु",
+  "notifications_permission_banner.how_to_control": "यदा माटोडोन्नोद्घाटितस्तदा विज्ञापनानि प्राप्तुं देस्क्तप्विज्ञापनानि सशक्तं कुरु। यदा तानि सशक्तानि तदा {icon} गण्डस्य माध्यमेन केऽपि प्रकारास्संवादा देस्क्तप्विज्ञापनानि जनयन्तीति नियामकं कर्तुं शक्नोषि।",
+  "notifications_permission_banner.title": "मा कदापि वस्तु त्यज",
+  "picture_in_picture.restore": "तत्प्रतिस्थापय",
+  "poll.closed": "बद्धम्",
+  "poll.refresh": "नवीकुरु",
+  "poll.total_people": "{count, plural, one {# जनः} other {# जनाः}}",
+  "poll.total_votes": "{count, plural, one {# मतम्} other {# मतानि}}",
+  "poll.vote": "मतम्",
+  "poll.voted": "एतदुत्तरं मतमदाः",
+  "poll.votes": "{votes, plural, one {# मतम्} other {# मतानि}}",
+  "poll_button.add_poll": "निर्वाचनं योजय",
+  "poll_button.remove_poll": "निर्वाचनं मार्जय",
+  "privacy.change": "पत्रस्य गोपनीयतां परिवर्तय",
+  "privacy.direct.long": "केवलमुल्लिखितोभोक्तृभ्यो दृश्यते",
   "privacy.direct.short": "Direct",
-  "privacy.private.long": "Visible for followers only",
+  "privacy.private.long": "केवलं येऽनुसरन्ति त्वां तेभ्यो दृश्यते",
   "privacy.private.short": "Followers-only",
-  "privacy.public.long": "Visible for all",
-  "privacy.public.short": "Public",
-  "privacy.unlisted.long": "Visible for all, but opted-out of discovery features",
-  "privacy.unlisted.short": "Unlisted",
-  "privacy_policy.last_updated": "Last updated {date}",
-  "privacy_policy.title": "Privacy Policy",
-  "refresh": "Refresh",
-  "regeneration_indicator.label": "Loading…",
-  "regeneration_indicator.sublabel": "Your home feed is being prepared!",
-  "relative_time.days": "{number}d",
-  "relative_time.full.days": "{number, plural, one {# day} other {# days}} ago",
-  "relative_time.full.hours": "{number, plural, one {# hour} other {# hours}} ago",
-  "relative_time.full.just_now": "just now",
-  "relative_time.full.minutes": "{number, plural, one {# minute} other {# minutes}} ago",
-  "relative_time.full.seconds": "{number, plural, one {# second} other {# seconds}} ago",
-  "relative_time.hours": "{number}h",
-  "relative_time.just_now": "now",
-  "relative_time.minutes": "{number}m",
-  "relative_time.seconds": "{number}s",
-  "relative_time.today": "today",
-  "reply_indicator.cancel": "Cancel",
-  "report.block": "Block",
-  "report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.",
-  "report.categories.other": "Other",
-  "report.categories.spam": "Spam",
-  "report.categories.violation": "Content violates one or more server rules",
-  "report.category.subtitle": "Choose the best match",
-  "report.category.title": "Tell us what's going on with this {type}",
-  "report.category.title_account": "profile",
-  "report.category.title_status": "post",
-  "report.close": "Done",
-  "report.comment.title": "Is there anything else you think we should know?",
-  "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.mute": "Mute",
-  "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.",
-  "report.next": "Next",
+  "privacy.public.long": "सर्वेभ्यो दृश्यते",
+  "privacy.public.short": "सार्वजनिकम्",
+  "privacy.unlisted.long": "सर्वेभ्यो दृश्यते किन्तु आविष्कारविशेषताभ्योऽन्तरभूतं नास्ति",
+  "privacy.unlisted.short": "असूचीकृतम्",
+  "privacy_policy.last_updated": "अन्तिमवारं परिवर्तितम् {date}",
+  "privacy_policy.title": "गोपनीयतानीतिः",
+  "refresh": "नवीकुरु",
+  "regeneration_indicator.label": "आरोपयति…",
+  "regeneration_indicator.sublabel": "तव गृहनिरासः सज्जीकृतोऽस्ति!",
+  "relative_time.days": "{number}दि",
+  "relative_time.full.days": "{number, plural, one {# दिनं} other {# दिनानि}} पूर्वम्",
+  "relative_time.full.hours": "{number, plural, one {# होरा} other {# होराः}} पूर्वम्",
+  "relative_time.full.just_now": "अधुनैव",
+  "relative_time.full.minutes": "{number, plural, one {# क्षणं} other {# क्षणानि}} पूर्वम्",
+  "relative_time.full.seconds": "{number, plural, one {# पलं} other {# पलानि}} पूर्वम्",
+  "relative_time.hours": "{number}हो",
+  "relative_time.just_now": "अधुना",
+  "relative_time.minutes": "{number}क्ष",
+  "relative_time.seconds": "{number}प",
+  "relative_time.today": "अद्य",
+  "reply_indicator.cancel": "नश्यताम्",
+  "report.block": "निषेधः",
+  "report.block_explanation": "तेषां पत्राणि न द्रक्ष्यसि। ते तव पत्राणि द्रष्टुमुत त्वामनुसर्तुं न शक्नुवन्ति। ते अवरुद्धाः इति ते वक्तुं शक्नुवन्ति।",
+  "report.categories.other": "अन्य",
+  "report.categories.spam": "फल्गुसन्देशाः",
+  "report.categories.violation": "पूरकः एकं वा एकाधिकान्सर्वरो नियमानुल्लङ्घयति",
+  "report.category.subtitle": "उत्तमं मेलनं चिनु",
+  "report.category.title": "अनेन {type} इत्यनेन किम्भवतीति ब्रूहि नः",
+  "report.category.title_account": "प्रोफैल्",
+  "report.category.title_status": "पत्रम्",
+  "report.close": "कृतम्",
+  "report.comment.title": "अन्य किमपि नो ज्ञापयितुमिच्छसि वा?",
+  "report.forward": "{target} प्रत्यग्रे प्रेषय",
+  "report.forward_hint": "एकौण्टयमन्यसर्वर्तः। अनामिकप्रतिलिमावेदनस्य तत्रापि प्रेषयितुमिच्छसि वा?",
+  "report.mute": "मूकीकुरु",
+  "report.mute_explanation": "तेषां पत्राणि न द्रक्ष्यसि। तेऽधुनापि त्वामनुसृत्य तव पत्राणि द्रष्टुं शक्नुवन्ति किन्तु ते त्वया मूकीकृता इति ते न वेदिष्यन्ति।",
+  "report.next": "परम्",
   "report.placeholder": "Type or paste additional comments",
-  "report.reasons.dislike": "I don't like it",
-  "report.reasons.dislike_description": "It is not something you want to see",
-  "report.reasons.other": "It's something else",
-  "report.reasons.other_description": "The issue does not fit into other categories",
-  "report.reasons.spam": "It's spam",
-  "report.reasons.spam_description": "Malicious links, fake engagement, or repetitive replies",
-  "report.reasons.violation": "It violates server rules",
-  "report.reasons.violation_description": "You are aware that it breaks specific rules",
-  "report.rules.subtitle": "Select all that apply",
-  "report.rules.title": "Which rules are being violated?",
-  "report.statuses.subtitle": "Select all that apply",
-  "report.statuses.title": "Are there any posts that back up this report?",
+  "report.reasons.dislike": "एतन्न मे रोचते",
+  "report.reasons.dislike_description": "एतन्न किञ्चित्त्वं द्रष्टुमिच्छसि",
+  "report.reasons.other": "एतत्किञ्चिदन्य एव",
+  "report.reasons.other_description": "समस्या अन्यवर्गेषु नोपज्यते",
+  "report.reasons.spam": "फल्गुसन्देशोऽयम्",
+  "report.reasons.spam_description": "हिंसापरायणास्सन्धयः, असत्याभ्युपगमो वा पुनरावर्तनीयानि उत्तराणि",
+  "report.reasons.violation": "एतत्सर्वरो नियमानुलङ्घयति",
+  "report.reasons.violation_description": "एतद्विशेषनियमामुलङ्घयतीति वेत्सि",
+  "report.rules.subtitle": "यानि प्रयोजयन्ति तानि चिनु",
+  "report.rules.title": "केषां नियमानामुल्लङ्घनं क्रियते?",
+  "report.statuses.subtitle": "यानि प्रयोजयन्ति तानि चिनु",
+  "report.statuses.title": "कानि पत्राणि सन्ति वा यानि आवेदनमिदं समर्थयन्ति?",
   "report.submit": "Submit report",
   "report.target": "Report {target}",
-  "report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:",
-  "report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:",
-  "report.thanks.title": "Don't want to see this?",
-  "report.thanks.title_actionable": "Thanks for reporting, we'll look into this.",
-  "report.unfollow": "Unfollow @{name}",
-  "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.",
-  "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached",
-  "report_notification.categories.other": "Other",
-  "report_notification.categories.spam": "Spam",
-  "report_notification.categories.violation": "Rule violation",
-  "report_notification.open": "Open report",
-  "search.placeholder": "Search",
-  "search.search_or_paste": "Search or paste URL",
-  "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.all": "All",
-  "search_results.hashtags": "Hashtags",
-  "search_results.nothing_found": "Could not find anything for these search terms",
-  "search_results.statuses": "Toots",
-  "search_results.statuses_fts_disabled": "Searching toots by their content is not enabled on this Mastodon server.",
-  "search_results.title": "Search for {q}",
-  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
-  "server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
-  "server_banner.active_users": "active users",
-  "server_banner.administered_by": "Administered by:",
-  "server_banner.introduction": "{domain} is part of the decentralized social network powered by {mastodon}.",
-  "server_banner.learn_more": "Learn more",
-  "server_banner.server_stats": "Server stats:",
-  "sign_in_banner.create_account": "Create account",
-  "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
-  "status.admin_account": "Open moderation interface for @{name}",
-  "status.admin_domain": "Open moderation interface for {domain}",
-  "status.admin_status": "Open this status in the moderation interface",
-  "status.block": "Block @{name}",
-  "status.bookmark": "Bookmark",
-  "status.cancel_reblog_private": "Unboost",
-  "status.cannot_reblog": "This post cannot be boosted",
-  "status.copy": "Copy link to status",
-  "status.delete": "Delete",
-  "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
-  "status.edit": "Edit",
-  "status.edited": "Edited {date}",
-  "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
-  "status.embed": "Embed",
-  "status.favourite": "Favourite",
-  "status.filter": "Filter this post",
-  "status.filtered": "Filtered",
-  "status.hide": "Hide post",
-  "status.history.created": "{name} created {date}",
-  "status.history.edited": "{name} edited {date}",
-  "status.load_more": "Load more",
-  "status.media_hidden": "Media hidden",
-  "status.mention": "Mention @{name}",
-  "status.more": "More",
-  "status.mute": "Mute @{name}",
-  "status.mute_conversation": "Mute conversation",
-  "status.open": "Expand this status",
-  "status.pin": "Pin on profile",
-  "status.pinned": "Pinned toot",
-  "status.read_more": "Read more",
-  "status.reblog": "Boost",
+  "report.thanks.take_action": "इमे इह तव विकल्पास्सन्ति मास्टोडोनि यत्पश्यसि तद्यन्त्रयितुम् :",
+  "report.thanks.take_action_actionable": "एतस्य अस्माभिरवलोकनस्य समये @{name} इत्यस्योपरि अभियोगयितुं शक्नोषि :",
+  "report.thanks.title": "इदं द्रष्टुं नेच्छसि वा?",
+  "report.thanks.title_actionable": "अवेदनाय धन्यवादः, वयं एतदवलोकयिष्यामि।",
+  "report.unfollow": "@{name} विसर",
+  "report.unfollow_explanation": "समयोऽयमनुसरसि। तव गृहनिरासे तस्य पत्राणि न द्रष्टुं तं विसर।",
+  "report_notification.attached_statuses": "{count, plural, one {{count} पत्रं} other {{count} पत्राणि}} संयुज्यते/संयुज्यन्ते",
+  "report_notification.categories.other": "अन्य",
+  "report_notification.categories.spam": "फल्गुसन्देशाः",
+  "report_notification.categories.violation": "नियमोल्लङ्घनम्",
+  "report_notification.open": "आवेदनमुद्धाटय",
+  "search.no_recent_searches": "No recent searches",
+  "search.placeholder": "अन्विच्छ",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
+  "search.search_or_paste": "URL अन्विच्छ वा लेपनं कुरु",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
+  "search_results.all": "सर्वम्",
+  "search_results.hashtags": "प्रचलितवस्तूनि",
+  "search_results.nothing_found": "एतेभ्योऽन्वेषणपदेभ्यः किमपि न प्राप्तम्",
+  "search_results.statuses": "पत्राणि",
+  "search_results.statuses_fts_disabled": "तेषां पूरकेन पत्रान्वेषणमस्मिन्मास्टोडोन्सर्वरि सक्रीयं नास्ति।",
+  "search_results.title": "{q} कृते अन्विष्य",
+  "search_results.total": "{count, number} {count, plural, one {परिणामः} other {परिणामाः}}",
+  "server_banner.about_active_users": "विगतेषु ३० दिनेषु सर्वरमिममुपयुज्यमाणा जनाः (मासिकसक्रियोपभोक्तारः)",
+  "server_banner.active_users": "सक्रियोपभोक्तारः",
+  "server_banner.administered_by": "इत्यनेन अधिकृतः : ",
+  "server_banner.introduction": "{domain} {mastodon} इत्यनेन सामर्थितो विकेन्द्रीयसामाजिकजालकर्मणोंऽशोऽस्ति।",
+  "server_banner.learn_more": "अधिकं ज्ञायताम्",
+  "server_banner.server_stats": "सर्वरः स्थितिविषयकानि :",
+  "sign_in_banner.create_account": "समयं संसृज",
+  "sign_in_banner.sign_in": "सम्प्रवेशं कुरु",
+  "sign_in_banner.text": "प्रोफैल्युत प्रचलितवस्तूनि अनुसर्तुं, प्रियं, भागः, पत्राणि प्रतिवादयितुञ्च सम्प्रवेशः कर्तव्यः। अन्यसर्वर्यपि तव समयात्संवादयितुं शक्नोषि।",
+  "status.admin_account": "@{name} कृते अनतिक्रममध्यस्थमुद्धाटय",
+  "status.admin_domain": "{domain} कृते अनतिक्रममध्यस्थमुद्धाटय",
+  "status.admin_status": "पत्रमिदमुद्घाटय अनतिक्रममध्यस्थे",
+  "status.block": "अवरुध्यताम् @{name}",
+  "status.bookmark": "पुटचिह्नीकुरु",
+  "status.cancel_reblog_private": "विबुस्तं कुरु",
+  "status.cannot_reblog": "पत्रमिदं बुस्तं कर्तुं न शक्यते",
+  "status.copy": "सञ्चयं पत्रे प्रतिलिपिं कुरु",
+  "status.delete": "मार्जय",
+  "status.detailed_status": "विस्तृतसंभाषणदृश्यम्",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
+  "status.edit": "सम्पादय",
+  "status.edited": "सम्पादितं {date}",
+  "status.edited_x_times": "Edited {count, plural, one {{count} वारम्} other {{count} वारम्}}",
+  "status.embed": "निहितम्",
+  "status.favourite": "प्रियम्",
+  "status.filter": "पत्रमिदं फिल्तरं कुरु",
+  "status.filtered": "फिल्तर्कृतम्",
+  "status.hide": "प्रेषरणं प्रच्छादय",
+  "status.history.created": "{name} असृजत् {date}",
+  "status.history.edited": "{name} समपादयत् {date}",
+  "status.load_more": "अधिकं स्थापय",
+  "status.media_hidden": "प्रसारमाध्यमानि प्रच्छादितानि",
+  "status.mention": "उल्लिख्यताम् @{name}",
+  "status.more": "अधिकम्",
+  "status.mute": "निःशब्दम् @{name}",
+  "status.mute_conversation": "वार्तालापं मूकीकुरु",
+  "status.open": "पत्रमिदं विस्तारय",
+  "status.pin": "प्रोफैलि कीलीकुरु",
+  "status.pinned": "कीलितपत्रम्",
+  "status.read_more": "अधिकं पठ्यताम्",
+  "status.reblog": "बुस्त्",
   "status.reblog_private": "Boost with original visibility",
   "status.reblogged_by": "{name} boosted",
-  "status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
-  "status.redraft": "Delete & re-draft",
+  "status.reblogs.empty": "न केनापि पत्रमिदं बुस्त्कृतम्। यदा कोऽपि करोति, तानि इह दृश्यन्ते।",
+  "status.redraft": "मार्जय पुनश्च लिख्यताम्",
   "status.remove_bookmark": "Remove bookmark",
   "status.replied_to": "Replied to {name}",
   "status.reply": "Reply",
@@ -612,7 +621,7 @@
   "timeline_hint.remote_resource_not_displayed": "{resource} from other servers are not displayed.",
   "timeline_hint.resources.followers": "Followers",
   "timeline_hint.resources.follows": "Follows",
-  "timeline_hint.resources.statuses": "Older toots",
+  "timeline_hint.resources.statuses": "पूरातनपत्राणि",
   "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {{days} days}}",
   "trends.trending_now": "Trending now",
   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
@@ -628,7 +637,7 @@
   "upload_form.description_missing": "No description added",
   "upload_form.edit": "Edit",
   "upload_form.thumbnail": "Change thumbnail",
-  "upload_form.undo": "Delete",
+  "upload_form.undo": "मार्जय",
   "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analyzing picture…",
   "upload_modal.apply": "Apply",
diff --git a/app/javascript/mastodon/locales/sc.json b/app/javascript/mastodon/locales/sc.json
index f56f2d5bd..905d45738 100644
--- a/app/javascript/mastodon/locales/sc.json
+++ b/app/javascript/mastodon/locales/sc.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocadu",
   "account.browse_more_on_origin_server": "Esplora de prus in su profilu originale",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Messàgiu deretu a @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Non mi notìfiches prus cando @{name} pùblichet messàgios",
   "account.domain_blocked": "Domìniu blocadu",
   "account.edit_profile": "Modìfica profilu",
@@ -102,7 +102,7 @@
   "column.blocks": "Persones blocadas",
   "column.bookmarks": "Sinnalibros",
   "column.community": "Lìnia de tempus locale",
-  "column.direct": "Messàgios diretos",
+  "column.direct": "Private mentions",
   "column.directory": "Nàviga in is profilos",
   "column.domain_blocks": "Domìnios blocados",
   "column.favourites": "Preferidos",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Bloca totu su domìniu",
   "confirmations.domain_block.message": "Boles de seguru, ma a beru a beru, blocare {domain}? In sa parte manna de is casos, pagos blocos o silentziamentos de persones sunt sufitzientes e preferìbiles. No as a bìdere cuntenutos dae custu domìniu in peruna lìnia de tempus pùblica o in is notìficas tuas. Sa gente chi ti sighit dae cussu domìniu at a èssere bogada.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Essi·nche",
   "confirmations.logout.message": "Seguru chi boles essire?",
   "confirmations.mute.confirm": "A sa muda",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "No as blocadu ancora nemos.",
   "empty_column.bookmarked_statuses": "Non tenes ancora peruna publicatzione in is marcadores. Cando nd'as a agiùnghere una, at a èssere ammustrada inoghe.",
   "empty_column.community": "Sa lìnia de tempus locale est bòida. Iscrie inoghe pro cumintzare sa festa!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Non tenes ancora perunu domìniu blocadu.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "Non tenes ancora peruna publicatzione in is preferidos. Cando nd'as a agiùnghere una, at a èssere ammustrada inoghe.",
   "empty_column.favourites": "Nemos at marcadu ancora custa publicatzione comente preferida. Cando calicunu dd'at a fàghere, at a èssere ammustrada inoghe.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "Non tenes ancora peruna rechesta de sighidura. Cando nd'as a retzire una, at a èssere ammustrada inoghe.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "Ancora nudda in custa eticheta.",
   "empty_column.home": "Sa lìnia de tempus printzipale tua est bòida. Visita {public} o imprea sa chirca pro cumintzare e agatare àteras persones.",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autoriza",
   "follow_request.reject": "Refuda",
   "follow_requests.unlocked_explanation": "Fintzas si su contu tuo no est blocadu, su personale de {domain} at pensadu chi forsis bolias revisionare a manu is rechestas de custos contos.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "Informatziones",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Polìtica de riservadesa",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Sarvadu",
   "getting_started.heading": "Comente cumintzare",
   "hashtag.column_header.tag_mode.all": "e {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Sinnalibros",
   "navigation_bar.community_timeline": "Lìnia de tempus locale",
   "navigation_bar.compose": "Cumpone una publicatzione noa",
-  "navigation_bar.direct": "Messàgios diretos",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Iscoberi",
   "navigation_bar.domain_blocks": "Domìnios blocados",
   "navigation_bar.edit_profile": "Modìfica profilu",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Preferidos",
   "navigation_bar.filters": "Faeddos a sa muda",
   "navigation_bar.follow_requests": "Rechestas de sighidura",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Gente chi sighis e sighiduras",
   "navigation_bar.lists": "Listas",
   "navigation_bar.logout": "Essi",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Chirca",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "Formadu de chirca avantzada",
-  "search_popout.tips.full_text": "Testu sèmplitze pro agatare publicatziones chi as iscritu, marcadu comente a preferidas, cumpartzidu o chi t'ant mentovadu, e fintzas nòmines, nòmines de utente e etichetas.",
-  "search_popout.tips.hashtag": "eticheta",
-  "search_popout.tips.status": "publicatzione",
-  "search_popout.tips.text": "Testu sèmplitze pro agatare nòmines visualizados, nòmines de utente e etichetas",
-  "search_popout.tips.user": "utente",
-  "search_results.accounts": "Gente",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Etichetas",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Aberi s'interfache de moderatzione pro @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Aberi custa publicatzione in s'interfache de moderatzione",
@@ -551,7 +559,8 @@
   "status.copy": "Còpia su ligòngiu a sa publicatzione tua",
   "status.delete": "Cantzella",
   "status.detailed_status": "Visualizatzione de detàlliu de arresonada",
-  "status.direct": "Messàgiu deretu a @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/sco.json b/app/javascript/mastodon/locales/sco.json
index 5e13c89a5..8794806b2 100644
--- a/app/javascript/mastodon/locales/sco.json
+++ b/app/javascript/mastodon/locales/sco.json
@@ -20,7 +20,7 @@
   "account.blocked": "Dingied",
   "account.browse_more_on_origin_server": "Luik mair oan the oreeginal profile",
   "account.cancel_follow_request": "Resile follae requeest",
-  "account.direct": "Direck message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stap notifyin me whan @{name} posts",
   "account.domain_blocked": "Domain dingied",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Dingied uisers",
   "column.bookmarks": "Buikmairks",
   "column.community": "Local timeline",
-  "column.direct": "Direck messages",
+  "column.direct": "Private mentions",
   "column.directory": "Broose profiles",
   "column.domain_blocks": "Dingied domains",
   "column.favourites": "Best anes",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Ye'v chynges tae the media description or preview thit ye'v no saved, fling them awa onie weys?",
   "confirmations.domain_block.confirm": "Dingie the hail domain",
   "confirmations.domain_block.message": "Ye a hunner percent shair thit ye'r wantin tae dingie the hail {domain}? In maist cases a haunfae tairgtit dingies an wheeshts are eneuch an preferit. Ye wullnae see content fae that domain in onie public timelines or in yer notes. Yer follaers fae that domain wull be taen awa.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log oot",
   "confirmations.logout.message": "Ye shair thit ye'r wantin tae log oot?",
   "confirmations.mute.confirm": "Wheesht",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Ye huvnae dingied onie uisers yit.",
   "empty_column.bookmarked_statuses": "Ye dinnae hae onie buikmairkt posts yit. Efter ye buikmairk ane, it'll shaw up here.",
   "empty_column.community": "The loval timeline is toum. Screive socht public fir tae get gaun!",
-  "empty_column.direct": "Ye dinnae hae onie direck messages yit. Ance ye sen or get ane, it'll shaw up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There nae dingied domains yit.",
   "empty_column.explore_statuses": "Naethin is trendin the noo. Check back efter!",
   "empty_column.favourited_statuses": "Ye dinnae hae onie favourite posts yit. Whan ye favourite ane, it'll shaw up here.",
   "empty_column.favourites": "Naebody haes favouritit this post yit. Whan somebody dis, they'll shaw up here.",
   "empty_column.follow_recommendations": "It luiks lik nae suggestions cuid be generatit fir ye. Ye kin try uisin seirch fir tae luik fir fowk ye wad mibbie ken, or splore uisin trendin hashtags.",
   "empty_column.follow_requests": "Ye dinnae hae onie follaer requests yit. Whan ye get ane, it'll shaw up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There naethin in this hashtag yit.",
   "empty_column.home": "Yer hame timeline is toum! Follae mair fowk fir tae full it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Gie permission",
   "follow_request.reject": "Dingie",
   "follow_requests.unlocked_explanation": "Tho yer accoont isnae snibbed, the {domain} staff thocht ye'd mibbie want tae review follae requests fae thir accoonts bi haun.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "Aboot",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboord shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View the soorce code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Gettin stertit",
   "hashtag.column_header.tag_mode.all": "an {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Focus column",
   "keyboard_shortcuts.compose": "Focus compose text area",
   "keyboard_shortcuts.description": "Description",
-  "keyboard_shortcuts.direct": "tae open direct messages column",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Muive doon in the list",
   "keyboard_shortcuts.enter": "Open post",
   "keyboard_shortcuts.favourite": "Favourite post",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Buikmairks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Scrieve new post",
-  "navigation_bar.direct": "Direck messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Fin",
   "navigation_bar.domain_blocks": "Dingied domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Best anes",
   "navigation_bar.filters": "Wheesht wirds",
   "navigation_bar.follow_requests": "Follae requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follaes an follaers",
   "navigation_bar.lists": "Leets",
   "navigation_bar.logout": "Logoot",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Mince",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Seirch",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Seirch or paste URL",
-  "search_popout.search_format": "Advanced seirch format",
-  "search_popout.tips.full_text": "Simple text gie ye posts thit ye'v wrate, favouritit, heezed, or hae been menshied in, as weil as matchinnuisernemms, displey nemms, an hashtags.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "post",
-  "search_popout.tips.text": "Simple text gies ye matchin dipley nemms, uisernemms an hashtags",
-  "search_popout.tips.user": "uiser",
-  "search_results.accounts": "Fowk",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Aw",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Cuidnae fin ocht fir thir seirch terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Mak accoont",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in fir tae follae profiles o hashtags, favourite, shaire an reply tae posts, or interack fae yer accoont on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface fir @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this post in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link tae post",
   "status.delete": "Delete",
   "status.detailed_status": "Detailt conversation view",
-  "status.direct": "Direck message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Editit {date}",
   "status.edited_x_times": "Editit {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/si.json b/app/javascript/mastodon/locales/si.json
index 631fe3f31..82366c29c 100644
--- a/app/javascript/mastodon/locales/si.json
+++ b/app/javascript/mastodon/locales/si.json
@@ -20,7 +20,7 @@
   "account.blocked": "අවහිර කර ඇත",
   "account.browse_more_on_origin_server": "මුල් පැතිකඩෙහි තවත් පිරික්සන්න",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "@{name} සෘජු පණිවිඩය",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "@{name} පළ කරන විට මට දැනුම් නොදෙන්න",
   "account.domain_blocked": "වසම අවහිර කර ඇත",
   "account.edit_profile": "පැතිකඩ සංස්කරණය",
@@ -102,7 +102,7 @@
   "column.blocks": "අවහිර කළ අය",
   "column.bookmarks": "පොත් යොමු",
   "column.community": "දේශීය කාලරේඛාව",
-  "column.direct": "සෘජු පණිවිඩ",
+  "column.direct": "Private mentions",
   "column.directory": "පැතිකඩ පිරික්සන්න",
   "column.domain_blocks": "අවහිර කළ වසම්",
   "column.favourites": "ප්‍රියතමයන්",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "ඔබට මාධ්‍ය විස්තරයට හෝ පෙරදසුනට නොසුරකින ලද වෙනස්කම් තිබේ, කෙසේ වෙතත් ඒවා ඉවත දමන්නද?",
   "confirmations.domain_block.confirm": "සම්පූර්ණ වසම අවහිර කරන්න",
   "confirmations.domain_block.message": "ඔබට සම්පූර්ණ {domain}අවහිර කිරීමට අවශ්‍ය බව ඔබට සැබවින්ම විශ්වාසද? බොහෝ අවස්ථාවලදී ඉලක්කගත බ්ලොක් හෝ නිශ්ශබ්ද කිරීම් කිහිපයක් ප්රමාණවත් වන අතර වඩාත් යෝග්ය වේ. ඔබ කිසිදු පොදු කාලරාමුවක හෝ ඔබගේ දැනුම්දීම් වල එම වසමේ අන්තර්ගතය නොදකිනු ඇත. එම වසමෙන් ඔබගේ අනුගාමිකයින් ඉවත් කරනු ලැබේ.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "නික්මෙන්න",
   "confirmations.logout.message": "ඔබට නික්මෙන්න අවශ්‍ය බව විශ්වාසද?",
   "confirmations.mute.confirm": "නිශ්ශබ්ද",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "කිසිදු පරිශීලකයෙකු අවහිර කර නැත.",
   "empty_column.bookmarked_statuses": "ඔබට තවමත් පිටු සලකුණු කළ මෙවලම් කිසිවක් නොමැත. ඔබ එකක් පිටු සලකුණු කළ විට, එය මෙහි පෙන්වනු ඇත.",
   "empty_column.community": "දේශීය කාලරේඛාව හිස් ය. පන්දුව පෙරළීමට ප්‍රසිද්ධියේ යමක් ලියන්න!",
-  "empty_column.direct": "ඔබට තවමත් සෘජු පණිවිඩ කිසිවක් නොමැත. ඔබ එකක් යවන විට හෝ ලැබුණු විට, එය මෙහි පෙන්වනු ඇත.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "අවහිර කරන ලද වසම් නැත.",
   "empty_column.explore_statuses": "දැන් කිසිවක් නැඹුරු නොවේ. පසුව නැවත පරීක්ෂා කරන්න!",
   "empty_column.favourited_statuses": "ඔබට තවමත් ප්‍රියතම දත් කිසිවක් නැත. ඔබ කැමති එකක් වූ විට, එය මෙහි පෙන්වනු ඇත.",
   "empty_column.favourites": "කිසිවෙකු තවමත් මෙම මෙවලමට ප්‍රිය කර නැත. යමෙකු එසේ කළ විට, ඔවුන් මෙහි පෙන්වනු ඇත.",
   "empty_column.follow_recommendations": "ඔබ වෙනුවෙන් යෝජනා ජනනය කළ නොහැකි බව පෙනේ. ඔබ දන්නා හඳුනන පුද්ගලයින් සෙවීමට හෝ ප්‍රවණතා හැෂ් ටැග් ගවේෂණය කිරීමට ඔබට සෙවීම භාවිත කිරීමට උත්සාහ කළ හැක.",
   "empty_column.follow_requests": "ඔබට තවමත් අනුගමනය කිරීමේ ඉල්ලීම් කිසිවක් නොමැත. ඔබට එකක් ලැබුණු විට, එය මෙහි පෙන්වනු ඇත.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "මෙම හැෂ් ටැග් එකේ තවම කිසිවක් නොමැත.",
   "empty_column.home": "ඔබගේ නිවසේ කාලරේඛාව හිස්ය! එය පිරවීම සඳහා තවත් පුද්ගලයින් අනුගමනය කරන්න. {suggestions}",
   "empty_column.home.suggestions": "යෝජනා කිහිපයක් බලන්න",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "අවසරලත්",
   "follow_request.reject": "ප්‍රතික්‍ෂේප",
   "follow_requests.unlocked_explanation": "ඔබගේ ගිණුම අගුලු දමා නොතිබුණද, {domain} කාර්ය මණ්ඩලය සිතුවේ ඔබට මෙම ගිණුම් වලින් ලැබෙන ඉල්ලීම් හස්තීයව සමාලෝචනය කිරීමට අවශ්‍ය විය හැකි බවයි.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "සුරැකිණි",
   "getting_started.heading": "පටන් ගන්න",
   "hashtag.column_header.tag_mode.all": "සහ {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "එක් තීරුවක තත්ත්වය නාභිගත කිරීමට",
   "keyboard_shortcuts.compose": "රචනා පාඨ ප්‍රදේශය නාභිගත කිරීමට",
   "keyboard_shortcuts.description": "සවිස්තරය",
-  "keyboard_shortcuts.direct": "සෘජු පණිවිඩ තීරුව විවෘත කිරීමට",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "ලැයිස්තුවේ පහළට ගමන් කිරීමට",
   "keyboard_shortcuts.enter": "ලිපිය අරින්න",
   "keyboard_shortcuts.favourite": "කැමති කිරීමට",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "පොත්යොමු",
   "navigation_bar.community_timeline": "දේශීය කාලරේඛාව",
   "navigation_bar.compose": "නව ටූට් සාදන්න",
-  "navigation_bar.direct": "සෘජු පණිවිඩ",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "සොයා ගන්න",
   "navigation_bar.domain_blocks": "අවහිර කළ වසම්",
   "navigation_bar.edit_profile": "පැතිකඩ සංස්කරණය",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "ප්‍රියතමයන්",
   "navigation_bar.filters": "නිහඬ කළ වචන",
   "navigation_bar.follow_requests": "අනුගමන ඉල්ලීම්",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "අනුගමනය හා අනුගාමිකයින්",
   "navigation_bar.lists": "ලේඛන",
   "navigation_bar.logout": "නික්මෙන්න",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "ආයාචිත",
   "report_notification.categories.violation": "නීතිය කඩ කිරීම",
   "report_notification.open": "විවෘත වාර්තාව",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "සොයන්න",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "උසස් සෙවුම් ආකෘතිය",
-  "search_popout.tips.full_text": "සරල පෙළ ඔබ ලියා ඇති, ප්‍රිය කළ, වැඩි කළ හෝ සඳහන් කර ඇති තත්ත්වයන් මෙන්ම ගැළපෙන පරිශීලක නාම, සංදර්ශක නම් සහ හැෂ් ටැග් ලබා දෙයි.",
-  "search_popout.tips.hashtag": "හෑෂ් ටැගය",
-  "search_popout.tips.status": "ලිපිය",
-  "search_popout.tips.text": "සරල පෙළ ගැළපෙන සංදර්ශක නම්, පරිශීලක නාම සහ හැෂ් ටැග් ලබා දෙයි",
-  "search_popout.tips.user": "පරිශීලක",
-  "search_results.accounts": "මිනිසුන්",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "සියල්ල",
   "search_results.hashtags": "හැෂ් ටැග්",
   "search_results.nothing_found": "මෙම සෙවුම් පද සඳහා කිසිවක් සොයාගත නොහැකි විය",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "@{name}සඳහා මධ්‍යස්ථ අතුරුමුහුණත විවෘත කරන්න",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "මධ්‍යස්ථ අතුරුමුහුණතෙහි මෙම තත්ත්වය විවෘත කරන්න",
@@ -551,7 +559,8 @@
   "status.copy": "තත්වයට සබැඳිය පිටපත් කරන්න",
   "status.delete": "මකන්න",
   "status.detailed_status": "විස්තරාත්මක සංවාද දැක්ම",
-  "status.direct": "@{name} සෘජු පණිවිඩයක්",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "සංස්කරණය",
   "status.edited": "සංශෝධිතයි {date}",
   "status.edited_x_times": "සංශෝධිතයි {count, plural, one {වාර {count}} other {වාර {count}}}",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index efbccdc38..d8c39fabf 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -20,8 +20,8 @@
   "account.blocked": "Blokovaný/á",
   "account.browse_more_on_origin_server": "Prehľadávaj viac na pôvodnom profile",
   "account.cancel_follow_request": "Stiahni žiadosť o nasledovanie",
-  "account.direct": "Priama správa pre @{name}",
-  "account.disable_notifications": "Prestaň oznamovať, keď má príspevky @{name}",
+  "account.direct": "Privately mention @{name}",
+  "account.disable_notifications": "Prestaň ma oboznamovať, keď má @{name} príspevky",
   "account.domain_blocked": "Doména ukrytá",
   "account.edit_profile": "Uprav profil",
   "account.enable_notifications": "Oboznamuj ma, keď má @{name} príspevky",
@@ -50,7 +50,7 @@
   "account.mute_notifications": "Stĺm oboznámenia od @{name}",
   "account.muted": "Nevšímaný/á",
   "account.open_original_page": "Otvor pôvodnú stránku",
-  "account.posts": "Príspevky/ov",
+  "account.posts": "Príspevky",
   "account.posts_with_replies": "Príspevky a odpovede",
   "account.report": "Nahlás @{name}",
   "account.requested": "Čaká na schválenie. Klikni pre zrušenie žiadosti",
@@ -62,7 +62,7 @@
   "account.unblock_domain": "Prestaň skrývať {domain}",
   "account.unblock_short": "Odblokuj",
   "account.unendorse": "Nezobrazuj na profile",
-  "account.unfollow": "Prestaň následovať",
+  "account.unfollow": "Prestaň nasledovať",
   "account.unmute": "Prestaň ignorovať @{name}",
   "account.unmute_notifications": "Zruš nevšímanie si oznámení od @{name}",
   "account.unmute_short": "Zruš nevšímanie",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokovaní užívatelia",
   "column.bookmarks": "Záložky",
   "column.community": "Miestna časová os",
-  "column.direct": "Priame správy",
+  "column.direct": "Private mentions",
   "column.directory": "Prehľadávaj profily",
   "column.domain_blocks": "Skryté domény",
   "column.favourites": "Obľúbené",
@@ -162,10 +162,12 @@
   "confirmations.discard_edit_media.message": "Máte neuložené zmeny v popise alebo náhľade média, zahodiť ich aj tak?",
   "confirmations.domain_block.confirm": "Skry celú doménu",
   "confirmations.domain_block.message": "Si si naozaj istý/á, že chceš blokovať celú doménu {domain}? Vo väčšine prípadov stačí blokovať alebo ignorovať pár konkrétnych užívateľov, čo sa doporučuje. Neuvidíš obsah z tejto domény v žiadnej verejnej časovej osi, ani v oznámeniach. Tvoji následovníci pochádzajúci z tejto domény budú odstránení.",
+  "confirmations.edit.confirm": "Uprav",
+  "confirmations.edit.message": "Úpravou teraz prepíšeš správu, ktorú práve zostavuješ. Si si istý/á, že chceš pokračovať?",
   "confirmations.logout.confirm": "Odhlás sa",
   "confirmations.logout.message": "Si si istý/á, že sa chceš odhlásiť?",
   "confirmations.mute.confirm": "Nevšímaj si",
-  "confirmations.mute.explanation": "Toto nastavenie pred tebou skryje ich príspevky, alebo príspevky od iných v ktorých sú spomenutí, ale umožní im vidieť tvoje príspevky, aj ťa následovať.",
+  "confirmations.mute.explanation": "Toto nastavenie skryje ich príspevky, alebo príspevky od iných v ktorých sú spomenutí, ale umožní im vidieť tvoje príspevky, aj ťa nasledovať.",
   "confirmations.mute.message": "Naozaj si chceš nevšímať {name}?",
   "confirmations.redraft.confirm": "Vyčisti a prepíš",
   "confirmations.redraft.message": "Si si istý/á, že chceš premazať a prepísať tento príspevok? Jeho nadobudnuté vyzdvihnutia a obľúbenia, ale i odpovede na pôvodný príspevok budú odlúčené.",
@@ -205,7 +207,7 @@
   "emoji_button.people": "Ľudia",
   "emoji_button.recent": "Často používané",
   "emoji_button.search": "Hľadaj...",
-  "emoji_button.search_results": "Nájdené",
+  "emoji_button.search_results": "Výsledky hľadania",
   "emoji_button.symbols": "Symboly",
   "emoji_button.travel": "Cestovanie a miesta",
   "empty_column.account_suspended": "Účet bol vylúčený",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Ešte si nikoho nezablokoval/a.",
   "empty_column.bookmarked_statuses": "Ešte nemáš žiadné záložky. Keď si pridáš príspevok k záložkám, zobrazí sa tu.",
   "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 priame správy. Keď nejakú pošleš, alebo dostaneš, ukáže sa tu.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Žiadne domény ešte niesú skryté.",
   "empty_column.explore_statuses": "Momentálne nie je nič trendové. Pozrite sa neskôr!",
   "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.favourites": "Ešte si tento príspevok nikto neobľúbil. Keď si ho niekto obľúbi, bude zobrazený tu.",
   "empty_column.follow_recommendations": "Zdá sa že pre Vás nemohli byť vygenerované žiadne návrhy. Môžete skúsiť použiť vyhľadávanie aby ste našli ľudi ktorých poznáte, alebo preskúmať trendujúce heštegy.",
   "empty_column.follow_requests": "Ešte nemáš žiadne požiadavky o následovanie. Keď nejaké dostaneš, budú tu zobrazené.",
+  "empty_column.followed_tags": "Ešte nenasleduješ žiadne haštagy. Keď tak urobíš, zobrazia sa tu.",
   "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.suggestions": "Prezri nejaké návrhy",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Povoľ prístup",
   "follow_request.reject": "Odmietni",
   "follow_requests.unlocked_explanation": "Síce Váš učet nie je uzamknutý, ale {domain} tím si myslel že môžete chcieť skontrolovať žiadosti o sledovanie z týchto účtov manuálne.",
+  "followed_tags": "Nasledované haštagy",
   "footer.about": "O",
   "footer.directory": "Adresár profilov",
   "footer.get_app": "Stiahnuť aplikáciu",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Klávesové skratky",
   "footer.privacy_policy": "Zásady súkromia",
   "footer.source_code": "Zobraziť zdrojový kód",
+  "footer.status": "Stav",
   "generic.saved": "Uložené",
   "getting_started.heading": "Začni tu",
   "hashtag.column_header.tag_mode.all": "a {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "zameraj sa na príspevok v jednom zo stĺpcov",
   "keyboard_shortcuts.compose": "zameraj sa na písaciu plochu",
   "keyboard_shortcuts.description": "Popis",
-  "keyboard_shortcuts.direct": "pre otvorenie panelu priamých správ",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "posunúť sa dole v zozname",
   "keyboard_shortcuts.enter": "Otvor príspevok",
   "keyboard_shortcuts.favourite": "pridaj do obľúbených",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Záložky",
   "navigation_bar.community_timeline": "Miestna časová os",
   "navigation_bar.compose": "Napíš nový príspevok",
-  "navigation_bar.direct": "Priame správy",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Objavuj",
   "navigation_bar.domain_blocks": "Skryté domény",
   "navigation_bar.edit_profile": "Uprav profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Obľúbené",
   "navigation_bar.filters": "Filtrované slová",
   "navigation_bar.follow_requests": "Žiadosti o sledovanie",
+  "navigation_bar.followed_tags": "Nasledované haštagy",
   "navigation_bar.follows_and_followers": "Sledovania a následovatelia",
   "navigation_bar.lists": "Zoznamy",
   "navigation_bar.logout": "Odhlás sa",
@@ -393,8 +399,8 @@
   "notification.admin.report": "{name} nahlásil/a {target}",
   "notification.admin.sign_up": "{name} sa zaregistroval/a",
   "notification.favourite": "{name} si obľúbil/a tvoj príspevok",
-  "notification.follow": "{name} ťa začal/a následovať",
-  "notification.follow_request": "{name} žiada ťa následovať",
+  "notification.follow": "{name} ťa začal/a nasledovať",
+  "notification.follow_request": "{name} ťa žiada nasledovať",
   "notification.mention": "{name} ťa spomenul/a",
   "notification.own_poll": "Tvoja anketa sa skončila",
   "notification.poll": "Anketa v ktorej si hlasoval/a sa skončila",
@@ -445,7 +451,7 @@
   "poll.total_votes": "{count, plural, one {# hlas} few {# hlasov} many {# hlasov} other {# hlasov}}",
   "poll.vote": "Hlasuj",
   "poll.voted": "Hlasoval/a si za túto voľbu",
-  "poll.votes": "{votes, plural, one {# vote} other {# votes}}",
+  "poll.votes": "{votes, plural, one {# hlas} few {# hlasov} many {# hlasov} other {# hlasy}}",
   "poll_button.add_poll": "Pridaj anketu",
   "poll_button.remove_poll": "Odstráň anketu",
   "privacy.change": "Uprav súkromie príspevku",
@@ -463,11 +469,11 @@
   "regeneration_indicator.label": "Načítava sa…",
   "regeneration_indicator.sublabel": "Tvoja domovská nástenka sa pripravuje!",
   "relative_time.days": "{number}dní",
-  "relative_time.full.days": "{number, plural, one {# day} other {# days}} ago",
-  "relative_time.full.hours": "{number, plural, one {# hour} other {# hours}} ago",
+  "relative_time.full.days": "Ostáva {number, plural, one {# deň} few {# dní} many {# dní} other {# dni}}",
+  "relative_time.full.hours": "Pred {number, plural, one {# hodinou} few {# hodinami} many {# hodinami} other {# hodinami}}",
   "relative_time.full.just_now": "práve teraz",
-  "relative_time.full.minutes": "{number, plural, one {# minute} other {# minutes}} ago",
-  "relative_time.full.seconds": "{number, plural, one {# second} other {# seconds}} ago",
+  "relative_time.full.minutes": "Pred {number, plural, one {# minútou} few {# minútami} many {# minútami} other {# minútami}}",
+  "relative_time.full.seconds": "Pred {number, plural, one {# sekundou} few {# sekundami} many {# sekundami} other {# sekundami}}",
   "relative_time.hours": "{number}hod",
   "relative_time.just_now": "teraz",
   "relative_time.minutes": "{number}min",
@@ -502,13 +508,13 @@
   "report.rules.subtitle": "Vyberte všetky, ktoré sa vzťahujú",
   "report.rules.title": "Ktoré pravidlá sa porušujú?",
   "report.statuses.subtitle": "Vyberte všetky, ktoré sa vzťahujú",
-  "report.statuses.title": "Are there any posts that back up this report?",
+  "report.statuses.title": "Sú k dispozícii príspevky podporujúce toto hlásenie?",
   "report.submit": "Odošli",
   "report.target": "Nahlás {target}",
-  "report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:",
-  "report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:",
+  "report.thanks.take_action": "Tu sú tvoje možnosti kontrolovať, čo vidíš na Mastodone:",
+  "report.thanks.take_action_actionable": "Kým to vyhodnotíme, môžeš podniknúť kroky voči @{name}:",
   "report.thanks.title": "Nechceš to vidieť?",
-  "report.thanks.title_actionable": "Thanks for reporting, we'll look into this.",
+  "report.thanks.title_actionable": "Vďaka za nahlásenie, pozrieme sa na to.",
   "report.unfollow": "Nesleduj @{name}",
   "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.",
   "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached",
@@ -516,22 +522,24 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Porušenie pravidla",
   "report_notification.open": "Otvor hlásenie",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Hľadaj",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Hľadaj, alebo vlož URL adresu",
-  "search_popout.search_format": "Pokročilé vyhľadávanie",
-  "search_popout.tips.full_text": "Vráti jednoduchý textový výpis príspevkov ktoré si napísal/a, ktoré si obľúbil/a, povýšil/a, alebo aj tých, v ktorých si bol/a spomenutý/á, a potom všetky zadaniu odpovedajúce prezývky, mená a haštagy.",
-  "search_popout.tips.hashtag": "haštag",
-  "search_popout.tips.status": "príspevok",
-  "search_popout.tips.text": "Vráti jednoduchý textový výpis zhodujúcich sa mien, prezývok a haštagov",
-  "search_popout.tips.user": "užívateľ",
-  "search_results.accounts": "Ľudia",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Všetky",
   "search_results.hashtags": "Haštagy",
-  "search_results.nothing_found": "Could not find anything for these search terms",
+  "search_results.nothing_found": "Pre tieto výrazy nemožno nič nájsť",
   "search_results.statuses": "Príspevky",
   "search_results.statuses_fts_disabled": "Vyhľadávanie v obsahu príspevkov nieje na tomto Mastodon serveri povolené.",
   "search_results.title": "Hľadaj {q}",
-  "search_results.total": "{count, number} {count, plural, one {výsledok} many {výsledkov} other {výsledky}}",
+  "search_results.total": "{count, number} {count, plural, one {výsledok} many {výsledky} other {výsledkov}}",
   "server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
   "server_banner.active_users": "aktívni užívatelia",
   "server_banner.administered_by": "Správcom je:",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Serverové štatistiky:",
   "sign_in_banner.create_account": "Vytvor účet",
   "sign_in_banner.sign_in": "Prihlás sa",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Otvor moderovacie rozhranie užívateľa @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Otvor tento príspevok v moderovacom rozhraní",
@@ -551,10 +559,11 @@
   "status.copy": "Skopíruj odkaz na príspevok",
   "status.delete": "Zmazať",
   "status.detailed_status": "Podrobný náhľad celej konverzácie",
-  "status.direct": "Priama správa pre @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Uprav",
   "status.edited": "Upravené {date}",
-  "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
+  "status.edited_x_times": "Upravený {count, plural, one {{count} krát} other {{count} krát}}",
   "status.embed": "Vložiť",
   "status.favourite": "Páči sa mi",
   "status.filter": "Filtrovanie tohto príspevku",
@@ -591,7 +600,7 @@
   "status.show_more_all": "Všetkým ukáž viac",
   "status.show_original": "Ukáž pôvodný",
   "status.translate": "Preložiť",
-  "status.translated_from_with": "Translated from {lang} using {provider}",
+  "status.translated_from_with": "Preložené z {lang} pomocou {provider}",
   "status.uncached_media_warning": "Nedostupný/é",
   "status.unmute_conversation": "Prestaň si nevšímať konverzáciu",
   "status.unpin": "Odopni z profilu",
diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json
index 0c9125acd..3c6da433b 100644
--- a/app/javascript/mastodon/locales/sl.json
+++ b/app/javascript/mastodon/locales/sl.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blokirano",
   "account.browse_more_on_origin_server": "Brskaj več po izvirnem profilu",
   "account.cancel_follow_request": "Umakni zahtevo za sledenje",
-  "account.direct": "Neposredno sporočilo @{name}",
+  "account.direct": "Zasebno omeni @{name}",
   "account.disable_notifications": "Ne obveščaj me več, ko ima @{name} novo objavo",
   "account.domain_blocked": "Blokirana domena",
   "account.edit_profile": "Uredi profil",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokirani uporabniki",
   "column.bookmarks": "Zaznamki",
   "column.community": "Krajevna časovnica",
-  "column.direct": "Neposredna sporočila",
+  "column.direct": "Zasebne omembe",
   "column.directory": "Prebrskaj profile",
   "column.domain_blocks": "Blokirane domene",
   "column.favourites": "Priljubljene",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Imate ne shranjene spremembe za medijski opis ali predogled; jih želite kljub temu opustiti?",
   "confirmations.domain_block.confirm": "Blokiraj celotno domeno",
   "confirmations.domain_block.message": "Ali ste res, res prepričani, da želite blokirati celotno {domain}? V večini primerov je nekaj ciljnih blokiranj ali utišanj dovolj in boljše. Vsebino iz te domene ne boste videli v javnih časovnicah ali obvestilih. Vaši sledilci iz te domene bodo odstranjeni.",
+  "confirmations.edit.confirm": "Uredi",
+  "confirmations.edit.message": "Urejanje bo prepisalo sporočilo, ki ga trenutno sestavljate. Ali ste prepričani, da želite nadaljevati?",
   "confirmations.logout.confirm": "Odjava",
   "confirmations.logout.message": "Ali ste prepričani, da se želite odjaviti?",
   "confirmations.mute.confirm": "Utišanje",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Niste še blokirali nobenega uporabnika.",
   "empty_column.bookmarked_statuses": "Zaenkrat še nimate zaznamovanih objav. Ko objavo zaznamujete, se pojavi tukaj.",
   "empty_column.community": "Krajevna časovnica je prazna. Napišite nekaj javnega, da se bo snežna kepa zakotalila!",
-  "empty_column.direct": "Nimate še nobenih neposrednih sporočil. Ko ga boste poslali ali prejeli, se bo prikazal tukaj.",
+  "empty_column.direct": "Nimate še nobenih zasebnih omemb. Ko jih boste poslali ali prejeli, se bodo prikazale tukaj.",
   "empty_column.domain_blocks": "Zaenkrat ni blokiranih domen.",
   "empty_column.explore_statuses": "Trenutno ni nič v trendu. Preverite znova kasneje!",
   "empty_column.favourited_statuses": "Nimate priljubljenih objav. Ko boste vzljubili kakšno, bo prikazana tukaj.",
   "empty_column.favourites": "Nihče še ni vzljubil te objave. Ko jo bo nekdo, se bo pojavila tukaj.",
   "empty_column.follow_recommendations": "Kaže, da za vas ni mogoče pripraviti nobenih predlogov. Poskusite uporabiti iskanje, da poiščete osebe, ki jih poznate, ali raziščete ključnike, ki so v trendu.",
   "empty_column.follow_requests": "Nimate prošenj za sledenje. Ko boste prejeli kakšno, se bo prikazala tukaj.",
+  "empty_column.followed_tags": "Zaenkrat ne sledite še nobenemu ključniku. Ko boste, se bodo pojavili tukaj.",
   "empty_column.hashtag": "V tem ključniku še ni nič.",
   "empty_column.home": "Vaša domača časovnica je prazna! Sledite več osebam, da jo zapolnite. {suggestions}",
   "empty_column.home.suggestions": "Oglejte si nekaj predlogov",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Overi",
   "follow_request.reject": "Zavrni",
   "follow_requests.unlocked_explanation": "Čeprav vaš račun ni zaklenjen, zaposleni pri {domain} menijo, da bi morda želeli pregledati zahteve za sledenje teh računov ročno.",
+  "followed_tags": "Sledeni ključniki",
   "footer.about": "O Mastodonu",
   "footer.directory": "Imenik profilov",
   "footer.get_app": "Prenesite aplikacijo",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Tipkovne bližnjice",
   "footer.privacy_policy": "Pravilnik o zasebnosti",
   "footer.source_code": "Pokaži izvorno kodo",
+  "footer.status": "Stanje",
   "generic.saved": "Shranjeno",
   "getting_started.heading": "Kako začeti",
   "hashtag.column_header.tag_mode.all": "in {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Pozornost na stolpec",
   "keyboard_shortcuts.compose": "Pozornost na območje za sestavljanje besedila",
   "keyboard_shortcuts.description": "Opis",
-  "keyboard_shortcuts.direct": "odpri stolpec za neposredna sporočila",
+  "keyboard_shortcuts.direct": "za odpiranje stolpca zasebnih omemb",
   "keyboard_shortcuts.down": "Premakni navzdol po seznamu",
   "keyboard_shortcuts.enter": "Odpri objavo",
   "keyboard_shortcuts.favourite": "Vzljubi objavo",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Zaznamki",
   "navigation_bar.community_timeline": "Krajevna časovnica",
   "navigation_bar.compose": "Sestavi novo objavo",
-  "navigation_bar.direct": "Neposredna sporočila",
+  "navigation_bar.direct": "Zasebne omembe",
   "navigation_bar.discover": "Odkrijte",
   "navigation_bar.domain_blocks": "Blokirane domene",
   "navigation_bar.edit_profile": "Uredi profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Priljubljeni",
   "navigation_bar.filters": "Utišane besede",
   "navigation_bar.follow_requests": "Prošnje za sledenje",
+  "navigation_bar.followed_tags": "Sledeni ključniki",
   "navigation_bar.follows_and_followers": "Sledenja in sledilci",
   "navigation_bar.lists": "Seznami",
   "navigation_bar.logout": "Odjava",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Neželeno",
   "report_notification.categories.violation": "Kršitev pravila",
   "report_notification.open": "Odpri prijavo",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Iskanje",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Iščite ali prilepite URL",
-  "search_popout.search_format": "Napredna oblika iskanja",
-  "search_popout.tips.full_text": "Enostavno besedilo vrne objave, ki ste jih napisali, vzljubili, izpostavili ali ste bili v njih omenjeni, kot tudi ujemajoča se uporabniška imena, prikazna imena in ključnike.",
-  "search_popout.tips.hashtag": "ključnik",
-  "search_popout.tips.status": "objava",
-  "search_popout.tips.text": "Enostavno besedilo vrne ujemajoča se prikazna imena, uporabniška imena in ključnike",
-  "search_popout.tips.user": "uporabnik",
-  "search_results.accounts": "Ljudje",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profili",
   "search_results.all": "Vse",
   "search_results.hashtags": "Ključniki",
   "search_results.nothing_found": "Za ta iskalni niz ni zadetkov",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Statistika strežnika:",
   "sign_in_banner.create_account": "Ustvari račun",
   "sign_in_banner.sign_in": "Prijava",
-  "sign_in_banner.text": "Prijavite se, da sledite profilom ali ključnikom, dodajate med priljubljene, delite z drugimi ter odgovarjate na objave, pa tudi ostajate v interakciji iz svojega računa na drugem strežniku.",
+  "sign_in_banner.text": "Prijavite se, da sledite profilom ali ključnikom, dodajate med priljubljene, delite z drugimi ter odgovarjate na objave. V interakciji ste lahko tudi iz svojega računa na drugem strežniku.",
   "status.admin_account": "Odpri vmesnik za moderiranje za @{name}",
   "status.admin_domain": "Odpri vmesnik za moderiranje za {domain}",
   "status.admin_status": "Odpri to objavo v vmesniku za moderiranje",
@@ -551,7 +559,8 @@
   "status.copy": "Kopiraj povezavo do objave",
   "status.delete": "Izbriši",
   "status.detailed_status": "Podroben pogled pogovora",
-  "status.direct": "Neposredno sporočilo @{name}",
+  "status.direct": "Zasebno omeni @{name}",
+  "status.direct_indicator": "Zasebna omemba",
   "status.edit": "Uredi",
   "status.edited": "Urejeno {date}",
   "status.edited_x_times": "Urejeno {count, plural, one {#-krat} two {#-krat} few {#-krat} other {#-krat}}",
diff --git a/app/javascript/mastodon/locales/sq.json b/app/javascript/mastodon/locales/sq.json
index 10176e3bc..93a00e51e 100644
--- a/app/javascript/mastodon/locales/sq.json
+++ b/app/javascript/mastodon/locales/sq.json
@@ -8,7 +8,7 @@
   "about.domain_blocks.silenced.title": "E kufizuar",
   "about.domain_blocks.suspended.explanation": "S’do të përpunohen, depozitohen apo shkëmbehen të dhëna të këtij shërbyesi, duke e bërë të pamundur çfarëdo ndërveprimi apo komunikimi me përdorues nga ky shërbyes.",
   "about.domain_blocks.suspended.title": "E pezulluar",
-  "about.not_available": "Ky informacion nuk jepet në këtë shërbyes.",
+  "about.not_available": "Ky informacion, në këtë shërbyes, nuk jepet.",
   "about.powered_by": "Media shoqërore e decentralizuar, bazuar në {mastodon}",
   "about.rules": "Rregulla shërbyesi",
   "account.account_note_header": "Shënim",
@@ -20,7 +20,7 @@
   "account.blocked": "E bllokuar",
   "account.browse_more_on_origin_server": "Shfletoni më tepër rreth profilit origjinal",
   "account.cancel_follow_request": "Tërhiq mbrapsht kërkesë për ndjekje",
-  "account.direct": "Mesazh i drejtpërdrejtë për @{name}",
+  "account.direct": "Përmendje private për @{name}",
   "account.disable_notifications": "Resht së njoftuari mua, kur poston @{name}",
   "account.domain_blocked": "Përkatësia u bllokua",
   "account.edit_profile": "Përpunoni profilin",
@@ -46,7 +46,7 @@
   "account.media": "Media",
   "account.mention": "Përmendni @{name}",
   "account.moved_to": "{name} ka treguar se llogari e vet e re tani është:",
-  "account.mute": "Heshtoni @{name}",
+  "account.mute": "Heshtoje @{name}",
   "account.mute_notifications": "Heshtoji njoftimet prej @{name}",
   "account.muted": "Heshtuar",
   "account.open_original_page": "Hap faqen origjinale",
@@ -102,7 +102,7 @@
   "column.blocks": "Përdorues të bllokuar",
   "column.bookmarks": "Faqerojtës",
   "column.community": "Rrjedhë kohore vendore",
-  "column.direct": "Mesazhe të drejtpërdrejtë",
+  "column.direct": "Përmendje private",
   "column.directory": "Shfletoni profile",
   "column.domain_blocks": "Përkatësi të bllokuara",
   "column.favourites": "Të parapëlqyer",
@@ -112,7 +112,7 @@
   "column.mutes": "Përdorues të heshtuar",
   "column.notifications": "Njoftime",
   "column.pins": "Mesazhe të fiksuar",
-  "column.public": "Rrjedhë kohore e federuar",
+  "column.public": "Rrjedhë kohore e të federuarave",
   "column_back_button.label": "Mbrapsht",
   "column_header.hide_settings": "Fshihi rregullimet",
   "column_header.moveLeft_settings": "Shpjere shtyllën majtas",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Keni ndryshime të paruajtura te përshkrimi ose paraparja e medias, të hidhen tej, sido qoftë?",
   "confirmations.domain_block.confirm": "Bllokoje krejt përkatësinë",
   "confirmations.domain_block.message": "Jeni i sigurt, shumë i sigurt se doni të bllokohet krejt {domain}? Në shumicën e rasteve, ndoca bllokime ose heshtime me synim të caktuar janë të mjaftueshme dhe të parapëlqyera. S’keni për të parë lëndë nga kjo përkatësi në ndonjë rrjedhë kohore publike, apo te njoftimet tuaja. Ndjekësit tuaj prej asaj përkatësie do të hiqen.",
+  "confirmations.edit.confirm": "Përpunojeni",
+  "confirmations.edit.message": "Përpunimi tani do të sjellë mbishkrim të mesazhit që po hartoni aktualisht. Jeni i sigurt se doni të vazhdohet?",
   "confirmations.logout.confirm": "Dilni",
   "confirmations.logout.message": "Jeni i sigurt se doni të dilet?",
   "confirmations.mute.confirm": "Heshtoje",
@@ -185,12 +187,12 @@
   "directory.recently_active": "Aktivë së fundi",
   "disabled_account_banner.account_settings": "Rregullime llogarie",
   "disabled_account_banner.text": "Llogaria juaj {disabledAccount} është aktualisht e çaktivizuar.",
-  "dismissable_banner.community_timeline": "Këto janë postime më të freskëta publike nga persona llogaritë e të cilëve strehohen nga {domain}.",
+  "dismissable_banner.community_timeline": "Këto janë postimet më të freskëta publike nga persona llogaritë e të cilëve strehohen nga {domain}.",
   "dismissable_banner.dismiss": "Hidhe tej",
   "dismissable_banner.explore_links": "Këto histori të reja po tirren nga persona në këtë shërbyes dhe të tjerë të tillë të rrjetit të decentralizuar mu tani.",
   "dismissable_banner.explore_statuses": "Këto postime nga ky shërbyes dhe të tjerë në rrjetin e decentralizuar po tërheqin vëmendjen tani.",
   "dismissable_banner.explore_tags": "Këta hashtag-ë po tërheqin vëmendjen mes personave në këtë shërbyes dhe të tjerë të tillë të rrjetit të decentralizuar mu tani.",
-  "dismissable_banner.public_timeline": "Këto janë postimet publike më të freskëta nga persona në këtë shërbyes dhe të tjerë të rrejtit të decentralizuar për të cilët ky shërbyes ka dijeni.",
+  "dismissable_banner.public_timeline": "Këto janë postimet publike më të freskëta nga persona në këtë shërbyes dhe të tjerë të rrjetit të decentralizuar për të cilët ky shërbyes ka dijeni.",
   "embed.instructions": "Trupëzojeni këtë gjendje në sajtin tuaj duke kopjuar kodin më poshtë.",
   "embed.preview": "Ja si do të duket:",
   "emoji_button.activity": "Veprimtari",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "S’keni bllokuar ende ndonjë përdorues.",
   "empty_column.bookmarked_statuses": "S’keni faqeruajtur ende ndonjë mesazh. Kur faqeruani një të tillë, ai do të shfaqet këtu.",
   "empty_column.community": "Rrjedha kohore vendore është e zbrazët. Shkruani diçka publikisht që t’i hyhet valles!",
-  "empty_column.direct": "S’keni ende ndonjë mesazh të drejtpërdrejtë. Kur dërgoni ose merrni një të tillë, ai do të shfaqet këtu.",
+  "empty_column.direct": "S’keni ende ndonjë përmendje private. Kur dërgoni ose merrni një të tillë, do të shfaqet këtu.",
   "empty_column.domain_blocks": "Ende s’ka përkatësi të fshehura.",
   "empty_column.explore_statuses": "Asgjë në modë tani. Kontrolloni më vonë!",
   "empty_column.favourited_statuses": "S’keni ende ndonjë mesazh të parapëlqyer. Kur parapëlqeni një të tillë, ai do të shfaqet këtu.",
   "empty_column.favourites": "Askush s’e ka parapëlqyer ende këtë mesazh. Kur e bën dikush, ai do të shfaqet këtu.",
   "empty_column.follow_recommendations": "Duket se s’u prodhuan dot sugjerime për ju. Mund të provoni të kërkoni për persona që mund të njihni, ose të eksploroni hashtag-ë që janë në modë.",
   "empty_column.follow_requests": "Ende s’keni ndonjë kërkesë ndjekjeje. Kur të merrni një të tillë, do të shfaqet këtu.",
+  "empty_column.followed_tags": "S’keni ndjekur ende nodnjë hashtag. Kur të ndiqni të tillë, do të shfaqen këtu.",
   "empty_column.hashtag": "Ende s’ka gjë nën këtë hashtag.",
   "empty_column.home": "Rrjedha juaj kohore është e zbrazët! Vizitoni {public} ose përdorni kërkimin që t’ia filloni dhe të takoni përdorues të tjerë.",
   "empty_column.home.suggestions": "Shihni disa sugjerime",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Autorizoje",
   "follow_request.reject": "Hidhe tej",
   "follow_requests.unlocked_explanation": "Edhe pse llogaria juaj s’është e kyçur, ekipi i {domain} mendoi se mund të donit të shqyrtonit dorazi kërkesa ndjekjeje prej këtyre llogarive.",
+  "followed_tags": "Hashtag-ë të ndjekur",
   "footer.about": "Mbi",
   "footer.directory": "Drejtori profilesh",
   "footer.get_app": "Merreni aplikacionin",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Shkurtore tastiere",
   "footer.privacy_policy": "Rregulla privatësie",
   "footer.source_code": "Shihni kodin burim",
+  "footer.status": "Gjendje",
   "generic.saved": "U ruajt",
   "getting_started.heading": "Si t’ia fillohet",
   "hashtag.column_header.tag_mode.all": "dhe {additional}",
@@ -295,7 +300,7 @@
   "interaction_modal.on_another_server": "Në një tjetër shërbyes",
   "interaction_modal.on_this_server": "Në këtë shërbyes",
   "interaction_modal.other_server_instructions": "Kopjojeni dhe ngjiteni këtë URL te fusha e kërkimeve të aplikacionit tuaj të parapëlqyer Mastodon, ose të ndërfaqes web të shërbyesit tuaj Mastodon.",
-  "interaction_modal.preamble": "Ngaqë Mastodon-i është i decentralizuar, mund të përdorni llogarinë tuaj ekzistuese të sterhuar nga një tjetër shërbyes Mastodon, ose platformë e përputhshme, nëse s’keni një llogari në këtë shërbyes.",
+  "interaction_modal.preamble": "Ngaqë Mastodon-i është i decentralizuar, mund të përdorni llogarinë tuaj ekzistuese të strehuar nga një tjetër shërbyes Mastodon, ose platformë e përputhshme, nëse s’keni një llogari në këtë shërbyes.",
   "interaction_modal.title.favourite": "Parapëlqejeni postimin e {name}",
   "interaction_modal.title.follow": "Ndiq {name}",
   "interaction_modal.title.reblog": "Përforconi postimin e {name}",
@@ -309,12 +314,12 @@
   "keyboard_shortcuts.column": "Fokusi mbi një shtyllë",
   "keyboard_shortcuts.compose": "Fokusi te fusha e hartimit të mesazheve",
   "keyboard_shortcuts.description": "Përshkrim",
-  "keyboard_shortcuts.direct": "për hapje shtylle mesazhesh të drejtpërdrejtë",
+  "keyboard_shortcuts.direct": "që të hapni shtyllën e përmendjeve private",
   "keyboard_shortcuts.down": "Për zbritje poshtë nëpër listë",
   "keyboard_shortcuts.enter": "Për hapje postimi",
   "keyboard_shortcuts.favourite": "Për t’i vënë shenjë si të parapëlqyer një postimi",
   "keyboard_shortcuts.favourites": "Për hapje liste të parapëlqyerish",
-  "keyboard_shortcuts.federated": "Për hapje rrjedhe kohore të të federuarve",
+  "keyboard_shortcuts.federated": "Për hapje rrjedhe kohore të të federuarave",
   "keyboard_shortcuts.heading": "Shkurtore tastiere",
   "keyboard_shortcuts.home": "Për hapje rrjedhe kohore vetjake",
   "keyboard_shortcuts.hotkey": "Tast përkatës",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Faqerojtës",
   "navigation_bar.community_timeline": "Rrjedhë kohore vendore",
   "navigation_bar.compose": "Hartoni mesazh të ri",
-  "navigation_bar.direct": "Mesazhe të drejtpërdrejtë",
+  "navigation_bar.direct": "Përmendje private",
   "navigation_bar.discover": "Zbuloni",
   "navigation_bar.domain_blocks": "Përkatësi të bllokuara",
   "navigation_bar.edit_profile": "Përpunoni profilin",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Të parapëlqyer",
   "navigation_bar.filters": "Fjalë të heshtuara",
   "navigation_bar.follow_requests": "Kërkesa për ndjekje",
+  "navigation_bar.followed_tags": "Hashtag-ë të ndjekur",
   "navigation_bar.follows_and_followers": "Ndjekje dhe ndjekës",
   "navigation_bar.lists": "Lista",
   "navigation_bar.logout": "Dalje",
@@ -386,7 +392,7 @@
   "navigation_bar.personal": "Personale",
   "navigation_bar.pins": "Mesazhe të fiksuar",
   "navigation_bar.preferences": "Parapëlqime",
-  "navigation_bar.public_timeline": "Rrjedhë kohore të federuarish",
+  "navigation_bar.public_timeline": "Rrjedhë kohore të federuarash",
   "navigation_bar.search": "Kërkoni",
   "navigation_bar.security": "Siguri",
   "not_signed_in_indicator.not_signed_in": "Që të përdorni këtë burim, lypset të bëni hyrjen.",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "I padëshiruar",
   "report_notification.categories.violation": "Cenim rregullash",
   "report_notification.open": "Hape raportimin",
+  "search.no_recent_searches": "Pa kërkime së fundi",
   "search.placeholder": "Kërkoni",
+  "search.quick_action.account_search": "Profile me përputhje me {x}",
+  "search.quick_action.go_to_account": "Kalo te profili {x}",
+  "search.quick_action.go_to_hashtag": "Kalo te hashtag-u {x}",
+  "search.quick_action.open_url": "Hape URL-në në Mastodon",
+  "search.quick_action.status_search": "Postime me përputhje me {x}",
   "search.search_or_paste": "Kërkoni, ose hidhni një URL",
-  "search_popout.search_format": "Format kërkimi të mëtejshëm",
-  "search_popout.tips.full_text": "Kërkimi për tekst të thjeshtë përgjigjet me mesazhe që keni shkruar, parapëlqyer, përforcuar, ose ku jeni përmendur, si dhe emra përdoruesish, emra ekrani dhe hashtag-ë që kanë përputhje me termin e kërkimit.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "mesazh",
-  "search_popout.tips.text": "Kërkim për tekst të thjeshtë përgjigjet me emra, emra përdoruesish dhe hashtag-ë që kanë përputhje me termin e kërkimit",
-  "search_popout.tips.user": "përdorues",
-  "search_results.accounts": "Persona",
+  "search_popout.quick_actions": "Veprime të shpejta",
+  "search_popout.recent": "Kërkime së fundi",
+  "search_results.accounts": "Profile",
   "search_results.all": "Krejt",
   "search_results.hashtags": "Hashtag-ë",
   "search_results.nothing_found": "S’u gjet gjë për këto terma kërkimi",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Statistika shërbyesi:",
   "sign_in_banner.create_account": "Krijoni llogari",
   "sign_in_banner.sign_in": "Hyni",
-  "sign_in_banner.text": "Që të ndiqni profile ose hashtag-ë, të pëlqeni, të ndani me të tjerë dhe të përgjigjeni në postime, apo të ndërveproni me llogarinë tuaj nga një shërbyes tjetër, bëni hyrjen.",
+  "sign_in_banner.text": "Që të ndiqni profile ose hashtagë, t’u vini shenjë si të parapëlqyer, të ndani me të tjerë dhe t’i ripostoni në postime, bëni hyrjen në llogari. Mundeni edhe të ndërveproni që nga llogaria juaj në një shërbyes tjetër.",
   "status.admin_account": "Hap ndërfaqe moderimi për @{name}",
   "status.admin_domain": "Hap ndërfaqe moderimi për {domain}",
   "status.admin_status": "Hape këtë mesazh te ndërfaqja e moderimit",
@@ -551,7 +559,8 @@
   "status.copy": "Kopjoje lidhjen për te mesazhi",
   "status.delete": "Fshije",
   "status.detailed_status": "Pamje e hollësishme bisede",
-  "status.direct": "Mesazh i drejtpërdrejtë për @{name}",
+  "status.direct": "Përmendje private për @{name}",
+  "status.direct_indicator": "Përmendje private",
   "status.edit": "Përpunojeni",
   "status.edited": "Përpunuar më {date}",
   "status.edited_x_times": "Përpunuar {count, plural, one {{count} herë} other {{count} herë}}",
@@ -600,7 +609,7 @@
   "subscribed_languages.target": "Ndryshoni gjuhë pajtimesh për {target}",
   "suggestions.dismiss": "Mos e merr parasysh sugjerimin",
   "suggestions.header": "Mund t’ju interesonte…",
-  "tabs_bar.federated_timeline": "E federuar",
+  "tabs_bar.federated_timeline": "Të federuar",
   "tabs_bar.home": "Kreu",
   "tabs_bar.local_timeline": "Vendore",
   "tabs_bar.notifications": "Njoftime",
diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json
index 0feb79691..f35d7025c 100644
--- a/app/javascript/mastodon/locales/sr-Latn.json
+++ b/app/javascript/mastodon/locales/sr-Latn.json
@@ -4,12 +4,12 @@
   "about.disclaimer": "Mastodon je besplatan softver otvorenog koda i zaštićeni znak kompanije Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Razlog nije naveden",
   "about.domain_blocks.preamble": "Mastodon vam generalno omogućava da vidite sadržaj i komunicirate sa korisnicima sa bilo kog drugog servera u fediverzumu. Ovo su izuzeci koji su napravljeni na ovom serveru.",
-  "about.domain_blocks.silenced.explanation": "U načelu nećete videti profile i sadržaj sa ovog servera, osim ako ga eksplicitno ne potražite ili se uključite tako što ćete ga pratiti.",
+  "about.domain_blocks.silenced.explanation": "Nećete videti profile i sadržaj sa ovog servera osim ako ih eksplicitno ne potražite ili ne zapratite neki profil sa servera.",
   "about.domain_blocks.silenced.title": "Ograničen",
-  "about.domain_blocks.suspended.explanation": "Podaci sa ovog servera neće se obrađivati, čuvati ili razmenjivati, što onemogućava bilo kakvu interakciju ili komunikaciju sa korisnicima sa ovog servera.",
+  "about.domain_blocks.suspended.explanation": "Podaci sa ovog servera neće se obrađivati, čuvati niti razmenjivati, što će onemogućiti bilo kakvu interakciju ili komunikaciju sa korisnicima sa ovog servera.",
   "about.domain_blocks.suspended.title": "Suspendovan",
   "about.not_available": "Ove informacije nisu dostupne na ovom serveru.",
-  "about.powered_by": "Decentralizovana društvena medija koju pokreće {mastodon}",
+  "about.powered_by": "Decentralizovana društvena mreža koju pokreće {mastodon}",
   "about.rules": "Pravila servera",
   "account.account_note_header": "Napomena",
   "account.add_or_remove_from_list": "Dodaj ili ukloni sa lista",
@@ -20,7 +20,7 @@
   "account.blocked": "Blokiran",
   "account.browse_more_on_origin_server": "Pregledajte još na originalnom profilu",
   "account.cancel_follow_request": "Povuci zahtev za praćenje",
-  "account.direct": "Direktna poruka @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Zaustavi obaveštavanje za objave korisnika @{name}",
   "account.domain_blocked": "Domen je blokiran",
   "account.edit_profile": "Uredi profil",
@@ -38,11 +38,11 @@
   "account.follows.empty": "Ovaj korisnik još uvek nikog ne prati.",
   "account.follows_you": "Prati vas",
   "account.go_to_profile": "Idi na profil",
-  "account.hide_reblogs": "Sakrij podržavanja od @{name}",
-  "account.joined_short": "Pridružio se",
+  "account.hide_reblogs": "Sakrij podržavanja @{name}",
+  "account.joined_short": "Datum pridruživanja",
   "account.languages": "Promeni pretplaćene jezike",
   "account.link_verified_on": "Vlasništvo nad ovom vezom je provereno {date}",
-  "account.locked_info": "Status privatnosti ovog naloga je podešen na zaključano. Vlasnik ručno pregleda ko ga može pratiti.",
+  "account.locked_info": "Status privatnosti ovog naloga je podešen na „zaključano”. Vlasnik ručno pregleda ko ga može pratiti.",
   "account.media": "Multimedija",
   "account.mention": "Pomeni korisnika @{name}",
   "account.moved_to": "Korisnik {name} je naznačio da je njegov novi nalog sada:",
@@ -91,7 +91,7 @@
   "bundle_column_error.routing.body": "Nije moguće pronaći traženu stranicu. Da li ste sigurni da je URL u adresnom polju ispravan?",
   "bundle_column_error.routing.title": "404",
   "bundle_modal_error.close": "Zatvori",
-  "bundle_modal_error.message": "Nešto nije bilo u redu pri učitavanju ove komponente.",
+  "bundle_modal_error.message": "Nešto je pošlo naopako tokom učitavanja ove komponente.",
   "bundle_modal_error.retry": "Pokušajte ponovo",
   "closed_registrations.other_server_instructions": "Pošto je Mastodon decentralizovan, možete napraviti nalog na drugom serveru ali i dalje komunicirati sa ovim.",
   "closed_registrations_modal.description": "Kreiranje naloga na {domain} trenutno nije moguće, ali imajte u vidu da vam ne treba nalog zasebno na {domain} da biste koristili Mastodon.",
@@ -102,7 +102,7 @@
   "column.blocks": "Blokirani korisnici",
   "column.bookmarks": "Obeleživači",
   "column.community": "Lokalna vremenska linija",
-  "column.direct": "Direktne poruke",
+  "column.direct": "Private mentions",
   "column.directory": "Pregledaj profile",
   "column.domain_blocks": "Blokirani domeni",
   "column.favourites": "Omiljeno",
@@ -162,8 +162,10 @@
   "confirmations.discard_edit_media.message": "Imate nesačuvane promene u opisu ili pregledu medija, da li ipak hoćete da ih odbacite?",
   "confirmations.domain_block.confirm": "Blokiraj ceo domen",
   "confirmations.domain_block.message": "Da li ste zaista sigurni da želite da blokirate ceo domen {domain}? U većini slučajeva, dovoljno je i poželjno nekoliko ciljanih blokiranja ili ignorisanja. Nećete videti sadržaj sa tog domena ni u jednoj javnoj vremenskoj liniji ili u vašim obaveštenjima. Vaši pratioci sa tog domena će biti uklonjeni.",
-  "confirmations.logout.confirm": "Odjavi se",
-  "confirmations.logout.message": "Da li se sigurni da želite da se odjavite?",
+  "confirmations.edit.confirm": "Uredi",
+  "confirmations.edit.message": "Uređivanjem će se obrisati poruka koju trenutno sastavljate. Da li ste sigurni da želite da nastavite?",
+  "confirmations.logout.confirm": "Odjava",
+  "confirmations.logout.message": "Da li ste sigurni da želite da se odjavite?",
   "confirmations.mute.confirm": "Ignoriši",
   "confirmations.mute.explanation": "Ovo će sakriti objave od korisnika i objave koje ga pominju, ali će mu i dalje biti dozvoljeno da vidi vaše objave i da vas prati.",
   "confirmations.mute.message": "Da li stvarno želite da ignorišete korisnika {name}?",
@@ -187,13 +189,13 @@
   "disabled_account_banner.text": "Vaš nalog {disabledAccount} je trenutno onemogućen.",
   "dismissable_banner.community_timeline": "Ovo su najnovije javne objave ljudi čije naloge hostuje {domain}.",
   "dismissable_banner.dismiss": "Odbaci",
-  "dismissable_banner.explore_links": "O ovim vestima upravo sada razgovaraju ljudi na ovom i drugim serverima decentralizovane mreže.",
+  "dismissable_banner.explore_links": "O ovim vestima trenutno razgovaraju ljudi na ovom i drugim serverima decentralizovane mreže.",
   "dismissable_banner.explore_statuses": "Ove objave sa ovog i drugih servera u decentralizovanoj mreži postaju sve popularnije na ovom serveru.",
   "dismissable_banner.explore_tags": "Ove heš oznake postaju sve popularnije među ljudima na ovom i drugim serverima decentralizovane mreže.",
   "dismissable_banner.public_timeline": "Ovo su najnovije javne objave ljudi na ovom i drugim serverima decentralizovane mreže za koje ovaj server zna.",
   "embed.instructions": "Ugradite ovu objavu na svoj veb sajt kopiranjem koda ispod.",
   "embed.preview": "Evo kako će to izgledati:",
-  "emoji_button.activity": "Aktivnost",
+  "emoji_button.activity": "Aktivnosti",
   "emoji_button.clear": "Obriši",
   "emoji_button.custom": "Prilagođeno",
   "emoji_button.flags": "Zastavice",
@@ -214,29 +216,30 @@
   "empty_column.blocks": "Još uvek niste blokirali nijednog korisnika.",
   "empty_column.bookmarked_statuses": "Još uvek nemate objava dodanih u obeleživače. Kada dodate neku, pojaviće se ovde.",
   "empty_column.community": "Lokalna vremenska linija je prazna. Napišite nešto javno da započnete!",
-  "empty_column.direct": "Još uvek nemate nijednu direktnu poruku. Kada pošaljete ili primite neku, pojaviće se ovde.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "Još uvek nema blokiranih domena.",
   "empty_column.explore_statuses": "Trenutno ništa nije u trendu. Proverite ponovo kasnije!",
   "empty_column.favourited_statuses": "Još uvek nemate objava označenih kao omiljene. Kada označite neku, pojaviće se ovde.",
   "empty_column.favourites": "Niko još nije označio ovu objavu kao omiljenu. Kada neko to uradi, pojaviće se ovde.",
   "empty_column.follow_recommendations": "Izgleda da ne mogu da se generišu bilo kakvi predlozi za vas. Možete pokušati da koristite pretragu kako biste potražili ljude koje možda poznajete ili istražili popularne heš oznake.",
-  "empty_column.follow_requests": "Još uvek nemate zahteva za praćenje. Kada primite zahtev, pojaviće se ovde.",
+  "empty_column.follow_requests": "Još uvek nemate nijedan zahtev za praćenje. Kada primite zahtev, on će se pojaviti ovde.",
+  "empty_column.followed_tags": "Još uvek niste zapratili nijednu heš oznaku. Kada to uradite, one će se pojaviti ovde.",
   "empty_column.hashtag": "Još uvek nema ničega u ovoj heš oznaci.",
   "empty_column.home": "Vaša početna vremenska linija je prazna! Pratite više ljudi da biste je popunili. {suggestions}",
   "empty_column.home.suggestions": "Pogledajte neke predloge",
   "empty_column.list": "U ovoj listi još nema ničega. Kada članovi ove liste objave nešto novo, pojaviće se ovde.",
-  "empty_column.lists": "Još uvek nemate nijednu listu. Kada napravite jednu, pojaviće se ovde.",
+  "empty_column.lists": "Još uvek nemate nijednu listu. Kada napravite jednu, ona će se pojaviti ovde.",
   "empty_column.mutes": "Još uvek ne ignorišete nijednog korisnika.",
   "empty_column.notifications": "Još uvek nemate nikakva obaveštenja. Kada drugi ljudi budu u interakciji sa vama, videćete to ovde.",
   "empty_column.public": "Ovde nema ničega! Napišite nešto javno ili ručno pratite korisnike sa drugih servera da biste ovo popunili",
   "error.unexpected_crash.explanation": "Zbog greške u našem kodu ili problema sa kompatibilnošću pregledača, ova stranica se nije mogla pravilno prikazati.",
   "error.unexpected_crash.explanation_addons": "Ova stranica se nije mogla pravilno prikazati. Ovu grešku verovatno uzrokuju dodaci pregledača ili alati za automatsko prevođenje.",
   "error.unexpected_crash.next_steps": "Pokušajte da osvežite stranicu. Ako to ne pomogne, možda ćete i dalje moći da koristite Mastodon putem drugog pregledača ili matične aplikacije.",
-  "error.unexpected_crash.next_steps_addons": "Pokušajte da ih onemogućite i osvežite stranicu. Ako to ne pomogne, možda ćete i dalje moći da koristite Mastodon preko drugog pregledača ili matične aplikacije.",
+  "error.unexpected_crash.next_steps_addons": "Pokušajte da ih onemogućite i osvežite stranicu. Ako to ne pomogne, možete da nastavite da koristite Mastodon korišćenjem drugog pregledača ili matične aplikacije.",
   "errors.unexpected_crash.copy_stacktrace": "Kopiraj „stacktrace” u klipbord",
   "errors.unexpected_crash.report_issue": "Prijavi problem",
   "explore.search_results": "Rezultati pretrage",
-  "explore.suggested_follows": "Za tebe",
+  "explore.suggested_follows": "Za Vas",
   "explore.title": "Istraži",
   "explore.trending_links": "Vesti",
   "explore.trending_statuses": "Objave",
@@ -248,7 +251,7 @@
   "filter_modal.added.review_and_configure": "Za pregled i dalju konfiguraciju ove kategorije filtera, idite na {settings_link}.",
   "filter_modal.added.review_and_configure_title": "Podešavanja filtera",
   "filter_modal.added.settings_link": "stranica podešavanja",
-  "filter_modal.added.short_explanation": "Ova objava je dodata u sledeću kategoriju filtera: {title}.",
+  "filter_modal.added.short_explanation": "Ova objava je dodata u sledeću filter kategoriju: {title}.",
   "filter_modal.added.title": "Filter je dodat!",
   "filter_modal.select_filter.context_mismatch": "ne odnosi se na ovaj kontekst",
   "filter_modal.select_filter.expired": "isteklo",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Odobri",
   "follow_request.reject": "Odbij",
   "follow_requests.unlocked_explanation": "Iako vaš nalog nije zaključan, osoblje {domain} smatra da biste možda želeli da ručno pregledate zahteve za praćenje sa ovih naloga.",
+  "followed_tags": "Praćene heš oznake",
   "footer.about": "Osnovni podaci",
   "footer.directory": "Direktorijum profila",
   "footer.get_app": "Preuzmite aplikaciju",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Tasterske prečice",
   "footer.privacy_policy": "Politika privatnosti",
   "footer.source_code": "Prikaži izvorni kod",
+  "footer.status": "Status",
   "generic.saved": "Sačuvano",
   "getting_started.heading": "Prvi koraci",
   "hashtag.column_header.tag_mode.all": "i {additional}",
@@ -277,13 +282,13 @@
   "hashtag.column_header.tag_mode.none": "bez {additional}",
   "hashtag.column_settings.select.no_options_message": "Nisu pronađeni predlozi",
   "hashtag.column_settings.select.placeholder": "Unesite heš oznake…",
-  "hashtag.column_settings.tag_mode.all": "Sve ove",
+  "hashtag.column_settings.tag_mode.all": "Sve",
   "hashtag.column_settings.tag_mode.any": "Bilo koje od ovih",
   "hashtag.column_settings.tag_mode.none": "Nijedan od ovih",
   "hashtag.column_settings.tag_toggle": "Uključi dodatne oznake za ovu kolonu",
-  "hashtag.follow": "Prati heš oznaku",
+  "hashtag.follow": "Zaprati heš oznaku",
   "hashtag.unfollow": "Otprati heš oznaku",
-  "home.column_settings.basic": "Osnovno",
+  "home.column_settings.basic": "Osnovna",
   "home.column_settings.show_reblogs": "Prikaži podržavanja",
   "home.column_settings.show_replies": "Prikaži odgovore",
   "home.hide_announcements": "Sakrij najave",
@@ -297,7 +302,7 @@
   "interaction_modal.other_server_instructions": "Kopirajte i nalepite ovu URL adresu u polje pretrage svoje omiljene Mastodon aplikacije ili veb okruženje svog Mastodon servera.",
   "interaction_modal.preamble": "Pošto je Mastodon decentralizovan, možete koristiti svoj postojeći nalog koji hostuje drugi Mastodon server ili kompatibilna platforma ako nemate nalog na ovom.",
   "interaction_modal.title.favourite": "Označi objavu korisnika {name} kao omiljenu",
-  "interaction_modal.title.follow": "Prati {name}",
+  "interaction_modal.title.follow": "Zaprati {name}",
   "interaction_modal.title.reblog": "Podrži objavu korisnika {name}",
   "interaction_modal.title.reply": "Odgovori na objavu korisnika {name}",
   "intervals.full.days": "{number, plural, one {# dan} few {# dana} other {# dana}}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Fokusiraj kolonu",
   "keyboard_shortcuts.compose": "Fokusiraj polje za sastavljanje objave",
   "keyboard_shortcuts.description": "Opis",
-  "keyboard_shortcuts.direct": "za otvaranje kolone direktnih poruka",
+  "keyboard_shortcuts.direct": "to open direct messages column",
   "keyboard_shortcuts.down": "Premesti nadole u listi",
   "keyboard_shortcuts.enter": "Otvori objavu",
   "keyboard_shortcuts.favourite": "Označi objavu kao omiljenu",
@@ -351,10 +356,10 @@
   "lists.edit.submit": "Promeni naslov",
   "lists.new.create": "Dodaj listu",
   "lists.new.title_placeholder": "Naslov nove liste",
-  "lists.replies_policy.followed": "Svaki praćeni korisnik",
-  "lists.replies_policy.list": "Članovi liste",
-  "lists.replies_policy.none": "Niko",
-  "lists.replies_policy.title": "Prikaži odgovore na:",
+  "lists.replies_policy.followed": "Svakom praćenom korisniku",
+  "lists.replies_policy.list": "Članovima liste",
+  "lists.replies_policy.none": "Nikome",
+  "lists.replies_policy.title": "Prikaži odgovore:",
   "lists.search": "Pretraži među ljudima koje pratite",
   "lists.subheading": "Vaše liste",
   "load_pending": "{count, plural, one {# nova stavka} few {# nove stavke} other {# novih stavki}}",
@@ -365,13 +370,13 @@
   "moved_to_account_banner.text": "Vaš nalog {disabledAccount} je trenutno onemogućen jer ste prešli na {movedToAccount}.",
   "mute_modal.duration": "Trajanje",
   "mute_modal.hide_notifications": "Sakriti obaveštenja od ovog korisnika?",
-  "mute_modal.indefinite": "Neodređen",
+  "mute_modal.indefinite": "Neodređeno",
   "navigation_bar.about": "Osnovni podaci",
   "navigation_bar.blocks": "Blokirani korisnici",
   "navigation_bar.bookmarks": "Obeleživači",
   "navigation_bar.community_timeline": "Lokalna vremenska linija",
   "navigation_bar.compose": "Sastavi novu objavu",
-  "navigation_bar.direct": "Direktne poruke",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Otkrij",
   "navigation_bar.domain_blocks": "Blokirani domeni",
   "navigation_bar.edit_profile": "Uredi profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Omiljeno",
   "navigation_bar.filters": "Ignorisane reči",
   "navigation_bar.follow_requests": "Zahtevi za praćenje",
+  "navigation_bar.followed_tags": "Praćene heš oznake",
   "navigation_bar.follows_and_followers": "Praćenja i pratioci",
   "navigation_bar.lists": "Liste",
   "navigation_bar.logout": "Odjava",
@@ -389,9 +395,9 @@
   "navigation_bar.public_timeline": "Združena vremenska linija",
   "navigation_bar.search": "Pretraga",
   "navigation_bar.security": "Bezbednost",
-  "not_signed_in_indicator.not_signed_in": "Morate da se prijavite da pristupite ovom resursu.",
-  "notification.admin.report": "{name} je prijavio {target}",
-  "notification.admin.sign_up": "{name} se registrovao",
+  "not_signed_in_indicator.not_signed_in": "Morate da se prijavite da biste pristupili ovom resursu.",
+  "notification.admin.report": "{name} je prijavio/-la {target}",
+  "notification.admin.sign_up": "{name} se registrovao/-la",
   "notification.favourite": "{name} je označio vašu objavu kao omiljenu",
   "notification.follow": "{name} vas je zapratio",
   "notification.follow_request": "{name} je zatražio da vas prati",
@@ -408,7 +414,7 @@
   "notifications.column_settings.alert": "Obaveštenja na radnoj površini",
   "notifications.column_settings.favourite": "Omiljeni:",
   "notifications.column_settings.filter_bar.advanced": "Prikaži sve kategorije",
-  "notifications.column_settings.filter_bar.category": "Traka za brzi filter",
+  "notifications.column_settings.filter_bar.category": "Traka za brzo filtriranje",
   "notifications.column_settings.filter_bar.show_bar": "Prikaži traku sa filterima",
   "notifications.column_settings.follow": "Novi pratioci:",
   "notifications.column_settings.follow_request": "Novi zahtevi za praćenje:",
@@ -432,13 +438,13 @@
   "notifications.grant_permission": "Odobri dozvolu.",
   "notifications.group": "{count} obaveštenja",
   "notifications.mark_as_read": "Označi svako obaveštenje kao pročitano",
-  "notifications.permission_denied": "Obaveštenja na radnoj površini nisu dostupna zbog prethodno odbijenog zahteva za dozvolom pregledača",
+  "notifications.permission_denied": "Obaveštenja na radnoj površini nisu dostupna zbog prethodno odbijenog zahteva za dozvolu pregledača",
   "notifications.permission_denied_alert": "Obaveštenja na radnoj površini ne mogu biti omogućena, jer je dozvola pregledača ranije bila odbijena",
   "notifications.permission_required": "Obaveštenja na radnoj površini nisu dostupna jer potrebna dozvola nije dodeljena.",
   "notifications_permission_banner.enable": "Omogućiti obaveštenja na radnoj površini",
   "notifications_permission_banner.how_to_control": "Da biste primali obaveštenja kada Mastodon nije otvoren, omogućite obaveštenja na radnoj površini. Kada su obaveštenja na radnoj površini omogućena vrste interakcija koje ona generišu mogu se podešavati pomoću dugmeta {icon}.",
   "notifications_permission_banner.title": "Nikada ništa ne propustite",
-  "picture_in_picture.restore": "Vrati to nazad",
+  "picture_in_picture.restore": "Vrati nazad",
   "poll.closed": "Zatvoreno",
   "poll.refresh": "Osveži",
   "poll.total_people": "{count, plural, one {# osoba} few {# osobe} other {# osoba}}",
@@ -449,13 +455,13 @@
   "poll_button.add_poll": "Dodaj anketu",
   "poll_button.remove_poll": "Ukloni anketu",
   "privacy.change": "Promeni privatnost objave",
-  "privacy.direct.long": "Vidljivo samo za pomenute korisnike",
+  "privacy.direct.long": "Vidljivo samo pomenutim korisnicima",
   "privacy.direct.short": "Samo pomenute osobe",
-  "privacy.private.long": "Vidljivo samo za pratioce",
-  "privacy.private.short": "Samo pratioci",
+  "privacy.private.long": "Vidljivo samo pratiocima",
+  "privacy.private.short": "Samo pratiocima",
   "privacy.public.long": "Vidljivo za sve",
   "privacy.public.short": "Javno",
-  "privacy.unlisted.long": "Vidljivo za sve, ali isključeno iz funkcije otkrivanja",
+  "privacy.unlisted.long": "Vidljivo svima, ali isključeno iz funkcija otkrivanja",
   "privacy.unlisted.short": "Neizlistano",
   "privacy_policy.last_updated": "Poslednje ažuriranje {date}",
   "privacy_policy.title": "Politika privatnosti",
@@ -475,34 +481,34 @@
   "relative_time.today": "danas",
   "reply_indicator.cancel": "Otkaži",
   "report.block": "Blokiraj",
-  "report.block_explanation": "Nećete videti objave korisnika. Ni on neće videti vaše objave niti će moći da vas prati. Takođe može da zna da je blokiran.",
+  "report.block_explanation": "Nećete videti objave korisnika. Ni on neće videti Vaše objave niti će moći da Vas prati. Takođe će moći da sazna da je blokiran.",
   "report.categories.other": "Ostalo",
   "report.categories.spam": "Spam",
   "report.categories.violation": "Sadržaj krši jedno ili više pravila servera",
   "report.category.subtitle": "Odaberite najpribližnije",
-  "report.category.title": "Recite nam šta je problem sa ovim {type}",
-  "report.category.title_account": "profilom",
+  "report.category.title": "Recite nam šta je problem sa {type}",
+  "report.category.title_account": "ovim profilom",
   "report.category.title_status": "objavom",
   "report.close": "Gotovo",
-  "report.comment.title": "Da li ima nešto dodatno što treba da znamo?",
+  "report.comment.title": "Da li ima još nešto što biste hteli da nam date do znanja?",
   "report.forward": "Proslediti {target}",
   "report.forward_hint": "Nalog je sa drugog servera. Poslati anonimnu kopiju prijave i tamo?",
   "report.mute": "Ignoriši",
-  "report.mute_explanation": "Nećete videti objave korisnika. On i i dalje može da vas prati i vidi vaše objave i neće znati da je ignorisan.",
+  "report.mute_explanation": "Nećete videti objave korisnika. On će i dalje moći da Vas prati i vidi Vaše objave i neće znati da je ignorisan.",
   "report.next": "Sledeće",
   "report.placeholder": "Dodatni komentari",
   "report.reasons.dislike": "Ne sviđa mi se",
-  "report.reasons.dislike_description": "Ovo nije nešto što želiš da vidite",
+  "report.reasons.dislike_description": "Ovo nije nešto što želite da vidite",
   "report.reasons.other": "Nešto drugo",
   "report.reasons.other_description": "Problem se ne uklapa u druge kategorije",
   "report.reasons.spam": "Ovo je spam",
   "report.reasons.spam_description": "Zlonamerne veze, lažno angažovanje ili odgovori koji se ponavljaju",
   "report.reasons.violation": "Krši pravila servera",
-  "report.reasons.violation_description": "Svesni ste da krši neka određena pravila",
-  "report.rules.subtitle": "Izaberi sve što važi",
+  "report.reasons.violation_description": "Svesni ste da krši određena pravila",
+  "report.rules.subtitle": "Izaberite sva pravila koja su prekršena",
   "report.rules.title": "Koja pravila su prekršena?",
-  "report.statuses.subtitle": "Izaberi sve što važi",
-  "report.statuses.title": "Da li postoje bilo kakve objave koje podržavaju ovu prijavu?",
+  "report.statuses.subtitle": "Izaberite sve što važi",
+  "report.statuses.title": "Ima li bilo kakvih objava koje potkrepljuju ovu prijavu?",
   "report.submit": "Pošalji",
   "report.target": "Prijavljujem {target}",
   "report.thanks.take_action": "Ovo su Vaše opcije da kontrolišete šta vidite na Mastodon-u:",
@@ -516,31 +522,33 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Kršenje pravila",
   "report_notification.open": "Otvori prijavu",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Pretraga",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Pretražite ili unesite adresu",
-  "search_popout.search_format": "Napredni format pretrage",
-  "search_popout.tips.full_text": "Jednostavan tekst vraća objave koje ste napisali, dodali u omiljene, podržali ili u kojima ste bili pomenuti, kao i podudaranje korisničkih imena, prikazana imena i heš oznake.",
-  "search_popout.tips.hashtag": "heš oznaka",
-  "search_popout.tips.status": "objava",
-  "search_popout.tips.text": "Jednostavan tekst vraća podudaranje imena za prikaz, korisnička imena i heš oznake",
-  "search_popout.tips.user": "korisnik",
-  "search_results.accounts": "Ljudi",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Sve",
   "search_results.hashtags": "Heš oznake",
   "search_results.nothing_found": "Nije moguće pronaći ništa za ove termine za pretragu",
   "search_results.statuses": "Objave",
   "search_results.statuses_fts_disabled": "Pretraga objava po sadržaju nije omogućena na ovom Mastodon serveru.",
-  "search_results.title": "Pretraži {q}",
+  "search_results.title": "Traži {q}",
   "search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}",
   "server_banner.about_active_users": "Ljudi koji su koristili ovaj server u prethodnih 30 dana (mesečno aktivnih korisnika)",
   "server_banner.active_users": "aktivnih korisnika",
-  "server_banner.administered_by": "Administrirano od strane:",
+  "server_banner.administered_by": "Administrira:",
   "server_banner.introduction": "{domain} je deo decentralizovane društvene mreže koju pokreće {mastodon}.",
   "server_banner.learn_more": "Saznajte više",
   "server_banner.server_stats": "Statistike servera:",
-  "sign_in_banner.create_account": "Napravi nalog",
-  "sign_in_banner.sign_in": "Prijavi se",
-  "sign_in_banner.text": "Prijavite se da pratite profile ili heštegove, stavite objave kao omiljene, delite i odgovarate na njih ili komunicirate sa svog naloga sa drugog servera.",
+  "sign_in_banner.create_account": "Napravite nalog",
+  "sign_in_banner.sign_in": "Prijavite se",
+  "sign_in_banner.text": "Prijavite se da biste pratili profile ili heš oznake, označili objave kao omiljene, delili i odgovarali na njih. Takođe možete komunicirati sa svog naloga na drugom serveru.",
   "status.admin_account": "Otvori moderatorsko okruženje za @{name}",
   "status.admin_domain": "Otvori moderatorsko okruženje za {domain}",
   "status.admin_status": "Otvori ovu objavu u moderatorskom okruženju",
@@ -551,7 +559,8 @@
   "status.copy": "Kopiraj vezu u objavu",
   "status.delete": "Izbriši",
   "status.detailed_status": "Detaljan prikaz razgovora",
-  "status.direct": "Direktna poruka @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Uredi",
   "status.edited": "Uređeno {date}",
   "status.edited_x_times": "Uređeno {count, plural, one {{count} put} other {{count} puta}}",
@@ -580,7 +589,7 @@
   "status.remove_bookmark": "Ukloni obeleživač",
   "status.replied_to": "Odgovor za {name}",
   "status.reply": "Odgovori",
-  "status.replyAll": "Odgovori na diskusiju",
+  "status.replyAll": "Odgovori na niz",
   "status.report": "Prijavi @{name}",
   "status.sensitive_warning": "Osetljiv sadržaj",
   "status.share": "Podeli",
@@ -595,7 +604,7 @@
   "status.uncached_media_warning": "Nije dostupno",
   "status.unmute_conversation": "Ne ignoriši razgovor",
   "status.unpin": "Otkači sa profila",
-  "subscribed_languages.lead": "Samo objave na označenim jezicima će se pojavljivati na početnoj liniji i na listama posle ove izmene. Odaberite ništa da primate objave na svim jezicima.",
+  "subscribed_languages.lead": "Samo objave na označenim jezicima će se pojavljivati na početnoj liniji i na listama posle ove izmene. Kada nijedan jezik nije izabran, primaćete objave na svim jezicima.",
   "subscribed_languages.save": "Sačuvaj izmene",
   "subscribed_languages.target": "Promeni jezike na koje je {target} prijavljen",
   "suggestions.dismiss": "Odbaci predlog",
@@ -611,9 +620,9 @@
   "time_remaining.seconds": "Ostalo {number, plural, one {# sekund} few {# sekunde} other {# sekundi}}",
   "timeline_hint.remote_resource_not_displayed": "{resource} sa drugih servera se ne prikazuju.",
   "timeline_hint.resources.followers": "Pratioci",
-  "timeline_hint.resources.follows": "Praćeni",
+  "timeline_hint.resources.follows": "Praćenja",
   "timeline_hint.resources.statuses": "Starije objave",
-  "trends.counter_by_accounts": "{count, plural, one {{counter} osoba} few {{counter} osobe} other {{counter} osoba}} u proteklih {days, plural, one {dan} few {{days} dana} other {{days} dana}}",
+  "trends.counter_by_accounts": "{count, plural, one {{counter} osoba} few {{counter} osobe} other {{counter} osoba}} u proteklih {days, plural, one {dan} other {{days} dana}}",
   "trends.trending_now": "U trendu sada",
   "ui.beforeunload": "Vaš nacrt će biti izgubljen ako napustite Mastodon.",
   "units.short.billion": "{count} mlrd.",
@@ -622,9 +631,9 @@
   "upload_area.title": "Prevucite i otpustite za otpremanje",
   "upload_button.label": "Dodaj slike, video ili audio datoteku",
   "upload_error.limit": "Dostignuto je ograničenje za otpremanje datoteka.",
-  "upload_error.poll": "Otpremanje datoteka nije dozvoljeno sa anketama.",
-  "upload_form.audio_description": "Opišite za osobe sa oštećenim sluhom",
-  "upload_form.description": "Opišite za osobe sa oštećenim vidom",
+  "upload_error.poll": "Otpremanje datoteka nije dozvoljeno kod anketa.",
+  "upload_form.audio_description": "Dodajte opis za osobe sa oštećenim sluhom",
+  "upload_form.description": "Dodajte opis za osobe sa oštećenim vidom",
   "upload_form.description_missing": "Nema dodatog opisa",
   "upload_form.edit": "Uredi",
   "upload_form.thumbnail": "Promeni sličicu",
@@ -635,8 +644,8 @@
   "upload_modal.applying": "Primena…",
   "upload_modal.choose_image": "Odaberite sliku",
   "upload_modal.description_placeholder": "Ljubazni fenjerdžija čađavog lica hoće da mi pokaže štos",
-  "upload_modal.detect_text": "Otkrij tekst sa slike",
-  "upload_modal.edit_media": "Uredi miltimediju",
+  "upload_modal.detect_text": "Pronađi tekst sa slike",
+  "upload_modal.edit_media": "Uredi multimediju",
   "upload_modal.hint": "Kliknite ili prevucite kružić na pregledu za izbor tačke fokusa koja će uvek biti vidljiva na svim sličicama.",
   "upload_modal.preparing_ocr": "Priprema OCR-a…",
   "upload_modal.preview_label": "Pregled ({ratio})",
diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json
index ca0f04b34..bfdd031f8 100644
--- a/app/javascript/mastodon/locales/sr.json
+++ b/app/javascript/mastodon/locales/sr.json
@@ -4,12 +4,12 @@
   "about.disclaimer": "Mastodon је бесплатан софтвер отвореног кода и заштићени знак компаније Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Разлог није наведен",
   "about.domain_blocks.preamble": "Мастодон вам генерално омогућава да видите садржај и комуницирате са корисницима са било ког другог сервера у федиверзуму. Ово су изузеци који су направљени на овом серверу.",
-  "about.domain_blocks.silenced.explanation": "У начелу нећете видети профиле и садржај са овог сервера, осим ако га експлицитно не потражите или се укључите тако што ћете га пратити.",
+  "about.domain_blocks.silenced.explanation": "Нећете видети профиле и садржај са овог сервера осим ако их експлицитно не потражите или не запратите неки профил са сервера.",
   "about.domain_blocks.silenced.title": "Ограничен",
-  "about.domain_blocks.suspended.explanation": "Подаци са овог сервера неће се обрађивати, чувати или размењивати, што онемогућава било какву интеракцију или комуникацију са корисницима са овог сервера.",
+  "about.domain_blocks.suspended.explanation": "Подаци са овог сервера неће се обрађивати, чувати нити размењивати, што ће онемогућити било какву интеракцију или комуникацију са корисницима са овог сервера.",
   "about.domain_blocks.suspended.title": "Суспендован",
   "about.not_available": "Ове информације нису доступне на овом серверу.",
-  "about.powered_by": "Децентрализована друштвена медија коју покреће {mastodon}",
+  "about.powered_by": "Децентрализована друштвена мрежа коју покреће {mastodon}",
   "about.rules": "Правила сервера",
   "account.account_note_header": "Напомена",
   "account.add_or_remove_from_list": "Додај или уклони са листа",
@@ -20,7 +20,7 @@
   "account.blocked": "Блокиран",
   "account.browse_more_on_origin_server": "Прегледајте још на оригиналном профилу",
   "account.cancel_follow_request": "Повуци захтев за праћење",
-  "account.direct": "Директна порука @{name}",
+  "account.direct": "Приватно помени @{name}",
   "account.disable_notifications": "Заустави обавештавање за објаве корисника @{name}",
   "account.domain_blocked": "Домен је блокиран",
   "account.edit_profile": "Уреди профил",
@@ -38,11 +38,11 @@
   "account.follows.empty": "Овај корисник још увек никог не прати.",
   "account.follows_you": "Прати вас",
   "account.go_to_profile": "Иди на профил",
-  "account.hide_reblogs": "Сакриј подржавања од @{name}",
-  "account.joined_short": "Придружио се",
+  "account.hide_reblogs": "Сакриј подржавања @{name}",
+  "account.joined_short": "Датум придруживања",
   "account.languages": "Промени претплаћене језике",
   "account.link_verified_on": "Власништво над овом везом је проверено {date}",
-  "account.locked_info": "Статус приватности овог налога је подешен на закључано. Власник ручно прегледа ко га може пратити.",
+  "account.locked_info": "Статус приватности овог налога је подешен на „закључано”. Власник ручно прегледа ко га може пратити.",
   "account.media": "Мултимедија",
   "account.mention": "Помени корисника @{name}",
   "account.moved_to": "Корисник {name} је назначио да је његов нови налог сада:",
@@ -91,7 +91,7 @@
   "bundle_column_error.routing.body": "Није могуће пронаћи тражену страницу. Да ли сте сигурни да је URL у адресном пољу исправан?",
   "bundle_column_error.routing.title": "404",
   "bundle_modal_error.close": "Затвори",
-  "bundle_modal_error.message": "Нешто није било у реду при учитавању ове компоненте.",
+  "bundle_modal_error.message": "Нешто је пошло наопако током учитавања ове компоненте.",
   "bundle_modal_error.retry": "Покушајте поново",
   "closed_registrations.other_server_instructions": "Пошто је Mastodon децентрализован, можете направити налог на другом серверу али и даље комуницирати са овим.",
   "closed_registrations_modal.description": "Креирање налога на {domain} тренутно није могуће, али имајте у виду да вам не треба налог засебно на {domain} да бисте користили Mastodon.",
@@ -102,7 +102,7 @@
   "column.blocks": "Блокирани корисници",
   "column.bookmarks": "Обележивачи",
   "column.community": "Локална временска линија",
-  "column.direct": "Директне поруке",
+  "column.direct": "Приватна помињања",
   "column.directory": "Прегледај профиле",
   "column.domain_blocks": "Блокирани домени",
   "column.favourites": "Омиљено",
@@ -129,7 +129,7 @@
   "compose_form.direct_message_warning_learn_more": "Сазнајте више",
   "compose_form.encryption_warning": "Објаве на Mastodon-у нису потпуно шифроване. Немојте делити никакве осетљиве информације преко Mastodon-а.",
   "compose_form.hashtag_warning": "Ова објава неће бити наведена ни под једном хеш ознаком јер није јавна. Само јавне објаве се могу претраживати по хеш ознакама.",
-  "compose_form.lock_disclaimer": "Ваш налог није {locked}. Свако може да вас прати и да види ваше објаве намењене само за ваше пратиоце.",
+  "compose_form.lock_disclaimer": "Ваш налог није {locked}. Свако може да Вас запрати и да види објаве намењене само Вашим пратиоцима.",
   "compose_form.lock_disclaimer.lock": "закључан",
   "compose_form.placeholder": "О чему размишљате?",
   "compose_form.poll.add_option": "Додајте избор",
@@ -155,22 +155,24 @@
   "confirmations.cancel_follow_request.confirm": "Повуци захтев",
   "confirmations.cancel_follow_request.message": "Да ли сте сигурни да желите да повучете захтев да пратите {name}?",
   "confirmations.delete.confirm": "Избриши",
-  "confirmations.delete.message": "Да ли сте сигурни да желите изришете ову објаву?",
+  "confirmations.delete.message": "Да ли сте сигурни да желите да избришете ову објаву?",
   "confirmations.delete_list.confirm": "Избриши",
   "confirmations.delete_list.message": "Да ли сте сигурни да желите да трајно избришете ову листу?",
   "confirmations.discard_edit_media.confirm": "Одбаци",
   "confirmations.discard_edit_media.message": "Имате несачуване промене у опису или прегледу медија, да ли ипак хоћете да их одбаците?",
   "confirmations.domain_block.confirm": "Блокирај цео домен",
   "confirmations.domain_block.message": "Да ли сте заиста сигурни да желите да блокирате цео домен {domain}? У већини случајева, довољно је и пожељно неколико циљаних блокирања или игнорисања. Нећете видети садржај са тог домена ни у једној јавној временској линији или у вашим обавештењима. Ваши пратиоци са тог домена ће бити уклоњени.",
-  "confirmations.logout.confirm": "Одјави се",
-  "confirmations.logout.message": "Да ли се сигурни да желите да се одјавите?",
+  "confirmations.edit.confirm": "Уреди",
+  "confirmations.edit.message": "Уређивањем ће се обрисати порука коју тренутно састављате. Да ли сте сигурни да желите да наставите?",
+  "confirmations.logout.confirm": "Одјава",
+  "confirmations.logout.message": "Да ли сте сигурни да желите да се одјавите?",
   "confirmations.mute.confirm": "Игнориши",
-  "confirmations.mute.explanation": "Ово ће сакрити објаве од корисника и објаве које га помињу, али ће му и даље бити дозвољено да види ваше објаве и да вас прати.",
+  "confirmations.mute.explanation": "Ово ће сакрити објаве корисника и објаве које га помињу, али ће му и даље бити дозвољено да види Ваше објаве и да Вас прати.",
   "confirmations.mute.message": "Да ли стварно желите да игноришете корисника {name}?",
   "confirmations.redraft.confirm": "Избриши и преправи",
   "confirmations.redraft.message": "Да ли сте сигурни да желите да избришете ову објаву и да је преправите? Подржавања и ознаке као омиљених ће бити изгубљени, а одговори ће остати без оригиналне објаве.",
   "confirmations.reply.confirm": "Одговори",
-  "confirmations.reply.message": "Одговарањем ћете обрисати поруку коју састављате. Да ли сигурни да желите да наставите?",
+  "confirmations.reply.message": "Одговарањем ћете обрисати поруку коју састављате. Да ли сте сигурни да желите да наставите?",
   "confirmations.unfollow.confirm": "Отпрати",
   "confirmations.unfollow.message": "Да ли сте сигурни да желите да отпратите корисника {name}?",
   "conversation.delete": "Избриши разговор",
@@ -179,7 +181,7 @@
   "conversation.with": "Са {names}",
   "copypaste.copied": "Копирано",
   "copypaste.copy": "Копирај",
-  "directory.federated": "Са знаног фидеверзума",
+  "directory.federated": "Са знаног федиверзума",
   "directory.local": "Само са {domain}",
   "directory.new_arrivals": "Новопридошли",
   "directory.recently_active": "Недавно активни",
@@ -187,13 +189,13 @@
   "disabled_account_banner.text": "Ваш налог {disabledAccount} је тренутно онемогућен.",
   "dismissable_banner.community_timeline": "Ово су најновије јавне објаве људи чије налоге хостује {domain}.",
   "dismissable_banner.dismiss": "Одбаци",
-  "dismissable_banner.explore_links": "О овим вестима управо сада разговарају људи на овом и другим серверима децентрализоване мреже.",
+  "dismissable_banner.explore_links": "О овим вестима тренутно разговарају људи на овом и другим серверима децентрализоване мреже.",
   "dismissable_banner.explore_statuses": "Ове објаве са овог и других сервера у децентрализованој мрежи постају све популарније на овом серверу.",
   "dismissable_banner.explore_tags": "Ове хеш ознаке постају све популарније међу људима на овом и другим серверима децентрализоване мреже.",
   "dismissable_banner.public_timeline": "Ово су најновије јавне објаве људи на овом и другим серверима децентрализоване мреже за које овај сервер зна.",
   "embed.instructions": "Уградите ову објаву на свој веб сајт копирањем кода испод.",
   "embed.preview": "Ево како ће то изгледати:",
-  "emoji_button.activity": "Активност",
+  "emoji_button.activity": "Активности",
   "emoji_button.clear": "Обриши",
   "emoji_button.custom": "Прилагођено",
   "emoji_button.flags": "Заставице",
@@ -214,29 +216,30 @@
   "empty_column.blocks": "Још увек нисте блокирали ниједног корисника.",
   "empty_column.bookmarked_statuses": "Још увек немате објава доданих у обележиваче. Када додате неку, појавиће се овде.",
   "empty_column.community": "Локална временска линија је празна. Напишите нешто јавно да започнете!",
-  "empty_column.direct": "Још увек немате ниједну директну поруку. Када пошаљете или примите неку, појавиће се овде.",
+  "empty_column.direct": "Још увек немате приватних помињања. Када пошаљете или примите једно, оно ће се појавити овде.",
   "empty_column.domain_blocks": "Још увек нема блокираних домена.",
   "empty_column.explore_statuses": "Тренутно ништа није у тренду. Проверите поново касније!",
   "empty_column.favourited_statuses": "Још увек немате објава означених као омиљене. Када означите неку, појавиће се овде.",
   "empty_column.favourites": "Нико још није означио ову објаву као омиљену. Када неко то уради, појавиће се овде.",
   "empty_column.follow_recommendations": "Изгледа да не могу да се генеришу било какви предлози за вас. Можете покушати да користите претрагу како бисте потражили људе које можда познајете или истражили популарне хеш ознаке.",
-  "empty_column.follow_requests": "Још увек немате захтева за праћење. Када примите захтев, појавиће се овде.",
+  "empty_column.follow_requests": "Још увек немате ниједан захтев за праћење. Када примите захтев, он ће се појавити овде.",
+  "empty_column.followed_tags": "Још увек нисте запратили ниједну хеш ознаку. Када то урадите, оне ће се појавити овде.",
   "empty_column.hashtag": "Још увек нема ничега у овој хеш ознаци.",
   "empty_column.home": "Ваша почетна временска линија је празна! Пратите више људи да бисте је попунили. {suggestions}",
   "empty_column.home.suggestions": "Погледајте неке предлоге",
   "empty_column.list": "У овој листи још нема ничега. Када чланови ове листе објаве нешто ново, појавиће се овде.",
-  "empty_column.lists": "Још увек немате ниједну листу. Када направите једну, појавиће се овде.",
+  "empty_column.lists": "Још увек немате ниједну листу. Када направите једну, она ће се појавити овде.",
   "empty_column.mutes": "Још увек не игноришете ниједног корисника.",
   "empty_column.notifications": "Још увек немате никаква обавештења. Када други људи буду у интеракцији са вама, видећете то овде.",
   "empty_column.public": "Овде нема ничега! Напишите нешто јавно или ручно пратите кориснике са других сервера да бисте ово попунили",
   "error.unexpected_crash.explanation": "Због грешке у нашем коду или проблема са компатибилношћу прегледача, ова страница се није могла правилно приказати.",
   "error.unexpected_crash.explanation_addons": "Ова страница се није могла правилно приказати. Ову грешку вероватно узрокују додаци прегледача или алати за аутоматско превођење.",
   "error.unexpected_crash.next_steps": "Покушајте да освежите страницу. Ако то не помогне, можда ћете и даље моћи да користите Mastodon путем другог прегледача или матичне апликације.",
-  "error.unexpected_crash.next_steps_addons": "Покушајте да их онемогућите и освежите страницу. Ако то не помогне, можда ћете и даље моћи да користите Mastodon преко другог прегледача или матичне апликације.",
+  "error.unexpected_crash.next_steps_addons": "Покушајте да их онемогућите и освежите страницу. Ако то не помогне, можете да наставите да користите Mastodon коришћењем другог прегледача или матичне апликације.",
   "errors.unexpected_crash.copy_stacktrace": "Копирај „stacktrace” у клипборд",
   "errors.unexpected_crash.report_issue": "Пријави проблем",
   "explore.search_results": "Резултати претраге",
-  "explore.suggested_follows": "За тебе",
+  "explore.suggested_follows": "За Вас",
   "explore.title": "Истражи",
   "explore.trending_links": "Вести",
   "explore.trending_statuses": "Објаве",
@@ -248,7 +251,7 @@
   "filter_modal.added.review_and_configure": "За преглед и даљу конфигурацију ове категорије филтера, идите на {settings_link}.",
   "filter_modal.added.review_and_configure_title": "Подешавања филтера",
   "filter_modal.added.settings_link": "страница подешавања",
-  "filter_modal.added.short_explanation": "Ова објава је додата у следећу категорију филтера: {title}.",
+  "filter_modal.added.short_explanation": "Ова објава је додата у следећу филтер категорију: {title}.",
   "filter_modal.added.title": "Филтер је додат!",
   "filter_modal.select_filter.context_mismatch": "не односи се на овај контекст",
   "filter_modal.select_filter.expired": "истекло",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Одобри",
   "follow_request.reject": "Одбиј",
   "follow_requests.unlocked_explanation": "Иако ваш налог није закључан, особље {domain} сматра да бисте можда желели да ручно прегледате захтеве за праћење са ових налога.",
+  "followed_tags": "Праћене хеш ознаке",
   "footer.about": "Основни подаци",
   "footer.directory": "Директоријум профила",
   "footer.get_app": "Преузмите апликацију",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Тастерске пречице",
   "footer.privacy_policy": "Политика приватности",
   "footer.source_code": "Прикажи изворни код",
+  "footer.status": "Статус",
   "generic.saved": "Сачувано",
   "getting_started.heading": "Први кораци",
   "hashtag.column_header.tag_mode.all": "и {additional}",
@@ -277,13 +282,13 @@
   "hashtag.column_header.tag_mode.none": "без {additional}",
   "hashtag.column_settings.select.no_options_message": "Нису пронађени предлози",
   "hashtag.column_settings.select.placeholder": "Унесите хеш ознаке…",
-  "hashtag.column_settings.tag_mode.all": "Све оve",
+  "hashtag.column_settings.tag_mode.all": "Све",
   "hashtag.column_settings.tag_mode.any": "Било које од ових",
   "hashtag.column_settings.tag_mode.none": "Ниједан од ових",
   "hashtag.column_settings.tag_toggle": "Укључи додатне ознаке за ову колону",
-  "hashtag.follow": "Прати хеш ознаку",
+  "hashtag.follow": "Запрати хеш ознаку",
   "hashtag.unfollow": "Отпрати хеш ознаку",
-  "home.column_settings.basic": "Основно",
+  "home.column_settings.basic": "Основна",
   "home.column_settings.show_reblogs": "Прикажи подржавања",
   "home.column_settings.show_replies": "Прикажи одговоре",
   "home.hide_announcements": "Сакриј најаве",
@@ -297,7 +302,7 @@
   "interaction_modal.other_server_instructions": "Копирајте и налепите ову URL адресу у поље претраге своје омиљене Mastodon апликације или веб окружење свог Mastodon сервера.",
   "interaction_modal.preamble": "Пошто је Mastodon децентрализован, можете користити свој постојећи налог који хостује други Mastodon сервер или компатибилна платформа ако немате налог на овом.",
   "interaction_modal.title.favourite": "Означи објаву корисника {name} као омиљену",
-  "interaction_modal.title.follow": "Прати {name}",
+  "interaction_modal.title.follow": "Запрати {name}",
   "interaction_modal.title.reblog": "Подржи објаву корисника {name}",
   "interaction_modal.title.reply": "Одговори на објаву корисника {name}",
   "intervals.full.days": "{number, plural, one {# дан} few {# дана} other {# дана}}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Фокусирај колону",
   "keyboard_shortcuts.compose": "Фокусирај поље за састављање објаве",
   "keyboard_shortcuts.description": "Опис",
-  "keyboard_shortcuts.direct": "за отварање колоне директних порука",
+  "keyboard_shortcuts.direct": "за отварање колоне приватних помињања",
   "keyboard_shortcuts.down": "Премести надоле у листи",
   "keyboard_shortcuts.enter": "Отвори објаву",
   "keyboard_shortcuts.favourite": "Означи објаву као омиљену",
@@ -332,7 +337,7 @@
   "keyboard_shortcuts.search": "Фокусирај траку претраге",
   "keyboard_shortcuts.spoilers": "Прикажи/сакриј поље текста упозорења о садржају (CW)",
   "keyboard_shortcuts.start": "Отвори колону „први кораци”",
-  "keyboard_shortcuts.toggle_hidden": "Прикажи/сакриј текст иза CW-а",
+  "keyboard_shortcuts.toggle_hidden": "Прикажи/сакриј текст иза упозорења",
   "keyboard_shortcuts.toggle_sensitivity": "Прикажи/сакриј мултимедију",
   "keyboard_shortcuts.toot": "Започни нову објаву",
   "keyboard_shortcuts.unfocus": "Уклони фокус са поља за унос текста/претраге",
@@ -351,27 +356,27 @@
   "lists.edit.submit": "Промени наслов",
   "lists.new.create": "Додај листу",
   "lists.new.title_placeholder": "Наслов нове листе",
-  "lists.replies_policy.followed": "Сваки праћени корисник",
-  "lists.replies_policy.list": "Чланови листе",
-  "lists.replies_policy.none": "Нико",
-  "lists.replies_policy.title": "Прикажи одговоре на:",
+  "lists.replies_policy.followed": "Сваком праћеном кориснику",
+  "lists.replies_policy.list": "Члановима листе",
+  "lists.replies_policy.none": "Никоме",
+  "lists.replies_policy.title": "Прикажи одговоре:",
   "lists.search": "Претражи међу људима које пратите",
   "lists.subheading": "Ваше листе",
   "load_pending": "{count, plural, one {# нова ставка} few {# нове ставке} other {# нових ставки}}",
   "loading_indicator.label": "Учитавање...",
   "media_gallery.toggle_visible": "{number, plural, one {Сакриј слику} few {Сакриј слике} other {Сакриј слике}}",
   "missing_indicator.label": "Није пронађено",
-  "missing_indicator.sublabel": "Овај ресурс није могуће пронаћи",
+  "missing_indicator.sublabel": "Овај ресурс није било могуће пронаћи",
   "moved_to_account_banner.text": "Ваш налог {disabledAccount} је тренутно онемогућен јер сте прешли на {movedToAccount}.",
   "mute_modal.duration": "Трајање",
   "mute_modal.hide_notifications": "Сакрити обавештења од овог корисника?",
-  "mute_modal.indefinite": "Неодређен",
+  "mute_modal.indefinite": "Неодређено",
   "navigation_bar.about": "Основни подаци",
   "navigation_bar.blocks": "Блокирани корисници",
   "navigation_bar.bookmarks": "Обележивачи",
   "navigation_bar.community_timeline": "Локална временска линија",
   "navigation_bar.compose": "Састави нову објаву",
-  "navigation_bar.direct": "Директне поруке",
+  "navigation_bar.direct": "Приватна помињања",
   "navigation_bar.discover": "Откриј",
   "navigation_bar.domain_blocks": "Блокирани домени",
   "navigation_bar.edit_profile": "Уреди профил",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Омиљено",
   "navigation_bar.filters": "Игнорисане речи",
   "navigation_bar.follow_requests": "Захтеви за праћење",
+  "navigation_bar.followed_tags": "Праћене хеш ознаке",
   "navigation_bar.follows_and_followers": "Праћења и пратиоци",
   "navigation_bar.lists": "Листе",
   "navigation_bar.logout": "Одјава",
@@ -389,9 +395,9 @@
   "navigation_bar.public_timeline": "Здружена временска линија",
   "navigation_bar.search": "Претрага",
   "navigation_bar.security": "Безбедност",
-  "not_signed_in_indicator.not_signed_in": "Морате да се пријавите да приступите овом ресурсу.",
-  "notification.admin.report": "{name} је пријавио {target}",
-  "notification.admin.sign_up": "{name} се регистровао",
+  "not_signed_in_indicator.not_signed_in": "Морате да се пријавите да бисте приступили овом ресурсу.",
+  "notification.admin.report": "{name} је пријавио/-ла {target}",
+  "notification.admin.sign_up": "{name} се регистровао/-ла",
   "notification.favourite": "{name} је означио вашу објаву као омиљену",
   "notification.follow": "{name} вас је запратио",
   "notification.follow_request": "{name} је затражио да вас прати",
@@ -408,7 +414,7 @@
   "notifications.column_settings.alert": "Обавештења на радној површини",
   "notifications.column_settings.favourite": "Омиљени:",
   "notifications.column_settings.filter_bar.advanced": "Прикажи све категорије",
-  "notifications.column_settings.filter_bar.category": "Трака за брзи филтер",
+  "notifications.column_settings.filter_bar.category": "Трака за брзо филтрирање",
   "notifications.column_settings.filter_bar.show_bar": "Прикажи траку са филтерима",
   "notifications.column_settings.follow": "Нови пратиоци:",
   "notifications.column_settings.follow_request": "Нови захтеви за праћење:",
@@ -432,13 +438,13 @@
   "notifications.grant_permission": "Одобри дозволу.",
   "notifications.group": "{count} обавештења",
   "notifications.mark_as_read": "Означи свако обавештење као прочитано",
-  "notifications.permission_denied": "Обавештења на радној површини нису доступна због претходно одбијеног захтева за дозволом прегледача",
+  "notifications.permission_denied": "Обавештења на радној површини нису доступна због претходно одбијеног захтева за дозволу прегледача",
   "notifications.permission_denied_alert": "Обавештења на радној површини не могу бити омогућена, јер је дозвола прегледача раније била одбијена",
   "notifications.permission_required": "Обавештења на радној површини нису доступна јер потребна дозвола није додељена.",
   "notifications_permission_banner.enable": "Омогућити обавештења на радној површини",
   "notifications_permission_banner.how_to_control": "Да бисте примали обавештења када Mastodon није отворен, омогућите обавештења на радној површини. Kада су обавештења на радној површини омогућена врсте интеракција које она генеришу могу се подешавати помоћу дугмета {icon}.",
   "notifications_permission_banner.title": "Никада ништа не пропустите",
-  "picture_in_picture.restore": "Врати то назад",
+  "picture_in_picture.restore": "Врати назад",
   "poll.closed": "Затворено",
   "poll.refresh": "Освежи",
   "poll.total_people": "{count, plural, one {# особа} few {# особе} other {# особа}}",
@@ -449,13 +455,13 @@
   "poll_button.add_poll": "Додај анкету",
   "poll_button.remove_poll": "Уклони анкету",
   "privacy.change": "Промени приватност објаве",
-  "privacy.direct.long": "Видљиво само за поменуте кориснике",
+  "privacy.direct.long": "Видљиво само поменутим корисницима",
   "privacy.direct.short": "Само поменуте особе",
-  "privacy.private.long": "Видљиво само за пратиоце",
-  "privacy.private.short": "Само пратиоци",
+  "privacy.private.long": "Видљиво само пратиоцима",
+  "privacy.private.short": "Само пратиоцима",
   "privacy.public.long": "Видљиво за све",
   "privacy.public.short": "Јавно",
-  "privacy.unlisted.long": "Видљиво за све, али искључено из функције откривања",
+  "privacy.unlisted.long": "Видљиво свима, али искључено из функција откривања",
   "privacy.unlisted.short": "Неизлистано",
   "privacy_policy.last_updated": "Последње ажурирање {date}",
   "privacy_policy.title": "Политика приватности",
@@ -475,34 +481,34 @@
   "relative_time.today": "данас",
   "reply_indicator.cancel": "Откажи",
   "report.block": "Блокирај",
-  "report.block_explanation": "Нећете видети објаве корисника. Ни он неће видети ваше објаве нити ће моћи да вас прати. Такође може да зна да је блокиран.",
+  "report.block_explanation": "Нећете видети објаве корисника. Ни он неће видети Ваше објаве нити ће моћи да Вас прати. Такође ће моћи да сазна да је блокиран.",
   "report.categories.other": "Остало",
   "report.categories.spam": "Спам",
   "report.categories.violation": "Садржај крши једно или више правила сервера",
   "report.category.subtitle": "Одаберите најприближније",
-  "report.category.title": "Реците нам шта је проблем са овим {type}",
-  "report.category.title_account": "профилом",
+  "report.category.title": "Реците нам шта је проблем са {type}",
+  "report.category.title_account": "овим профилом",
   "report.category.title_status": "објавом",
   "report.close": "Готово",
-  "report.comment.title": "Да ли има нешто додатно што треба да знамо?",
+  "report.comment.title": "Да ли има још нешто што бисте хтели да нам дате до знања?",
   "report.forward": "Проследити {target}",
   "report.forward_hint": "Налог је са другог сервера. Послати анонимну копију пријаве и тамо?",
   "report.mute": "Игнориши",
-  "report.mute_explanation": "Нећете видети објаве корисника. Он и и даље може да вас прати и види ваше објаве и неће знати да је игнорисан.",
+  "report.mute_explanation": "Нећете видети објаве корисника. Он ће и даље моћи да Вас прати и види Ваше објаве и неће знати да је игнорисан.",
   "report.next": "Следеће",
   "report.placeholder": "Додатни коментари",
   "report.reasons.dislike": "Не свиђа ми се",
-  "report.reasons.dislike_description": "Ово није нешто што желиш да видите",
+  "report.reasons.dislike_description": "Ово није нешто што желите да видите",
   "report.reasons.other": "Нешто друго",
   "report.reasons.other_description": "Проблем се не уклапа у друге категорије",
   "report.reasons.spam": "Ово је спам",
   "report.reasons.spam_description": "Злонамерне везе, лажно ангажовање или одговори који се понављају",
   "report.reasons.violation": "Крши правила сервера",
-  "report.reasons.violation_description": "Свесни сте да крши нека одређена правила",
-  "report.rules.subtitle": "Изабери све што важи",
+  "report.reasons.violation_description": "Свесни сте да крши одређена правила",
+  "report.rules.subtitle": "Изаберите сва правила која су прекршена",
   "report.rules.title": "Која правила су прекршена?",
-  "report.statuses.subtitle": "Изабери све што важи",
-  "report.statuses.title": "Да ли постоје било какве објаве које подржавају ову пријаву?",
+  "report.statuses.subtitle": "Изаберите све што важи",
+  "report.statuses.title": "Има ли било каквих објава које поткрепљују ову пријаву?",
   "report.submit": "Пошаљи",
   "report.target": "Пријављујем {target}",
   "report.thanks.take_action": "Ово су Ваше опције да контролишете шта видите на Mastodon-у:",
@@ -516,31 +522,33 @@
   "report_notification.categories.spam": "Спам",
   "report_notification.categories.violation": "Кршење правила",
   "report_notification.open": "Отвори пријаву",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Претрага",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Претражите или унесите адресу",
-  "search_popout.search_format": "Напредни формат претраге",
-  "search_popout.tips.full_text": "Једноставан текст враћа објаве које сте написали, додали у омиљене, подржали или у којима сте били поменути, као и подударање корисничких имена, приказана имена и хеш ознаке.",
-  "search_popout.tips.hashtag": "хеш ознака",
-  "search_popout.tips.status": "објава",
-  "search_popout.tips.text": "Једноставан текст враћа подударање имена за приказ, корисничка имена и хеш ознаке",
-  "search_popout.tips.user": "корисник",
-  "search_results.accounts": "Људи",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Профили",
   "search_results.all": "Све",
   "search_results.hashtags": "Хеш ознаке",
   "search_results.nothing_found": "Није могуће пронаћи ништа за ове термине за претрагу",
   "search_results.statuses": "Објаве",
   "search_results.statuses_fts_disabled": "Претрага објава по садржају није омогућена на овом Mastodon серверу.",
-  "search_results.title": "Претражи {q}",
+  "search_results.title": "Тражи {q}",
   "search_results.total": "{count, number} {count, plural, one {резултат} few {резултата} other {резултата}}",
   "server_banner.about_active_users": "Људи који су користили овај сервер у претходних 30 дана (месечно активних корисника)",
   "server_banner.active_users": "активних корисника",
-  "server_banner.administered_by": "Администрирано од стране:",
+  "server_banner.administered_by": "Администрира:",
   "server_banner.introduction": "{domain} је део децентрализоване друштвене мреже коју покреће {mastodon}.",
   "server_banner.learn_more": "Сазнајте више",
   "server_banner.server_stats": "Статистике сервера:",
-  "sign_in_banner.create_account": "Направи налог",
-  "sign_in_banner.sign_in": "Пријави се",
-  "sign_in_banner.text": "Пријавите се да пратите профиле или хештегове, ставите објаве као омиљене, делите и одговарате на њих или комуницирате са свог налога са другог сервера.",
+  "sign_in_banner.create_account": "Направите налог",
+  "sign_in_banner.sign_in": "Пријавите се",
+  "sign_in_banner.text": "Пријавите се да бисте пратили профиле или хеш ознаке, означили објаве као омиљене, делили и одговарали на њих. Такође можете комуницирати са свог налога на другом серверу.",
   "status.admin_account": "Отвори модераторско окружење за @{name}",
   "status.admin_domain": "Отвори модераторско окружење за {domain}",
   "status.admin_status": "Отвори ову објаву у модераторском окружењу",
@@ -551,7 +559,8 @@
   "status.copy": "Копирај везу у објаву",
   "status.delete": "Избриши",
   "status.detailed_status": "Детаљан приказ разговора",
-  "status.direct": "Директна порука @{name}",
+  "status.direct": "Приватно помени @{name}",
+  "status.direct_indicator": "Приватно помињање",
   "status.edit": "Уреди",
   "status.edited": "Уређено {date}",
   "status.edited_x_times": "Уређено {count, plural, one {{count} пут} other {{count} пута}}",
@@ -580,7 +589,7 @@
   "status.remove_bookmark": "Уклони обележивач",
   "status.replied_to": "Одговор за {name}",
   "status.reply": "Одговори",
-  "status.replyAll": "Одговори на дискусију",
+  "status.replyAll": "Одговори на низ",
   "status.report": "Пријави @{name}",
   "status.sensitive_warning": "Осетљив садржај",
   "status.share": "Подели",
@@ -595,7 +604,7 @@
   "status.uncached_media_warning": "Није доступно",
   "status.unmute_conversation": "Не игнориши разговор",
   "status.unpin": "Откачи са профила",
-  "subscribed_languages.lead": "Само објаве на означеним језицима ће се појављивати на почетној линији и на листама после ове измене. Одаберите ништа да примате објаве на свим језицима.",
+  "subscribed_languages.lead": "Само објаве на означеним језицима ће се појављивати на почетној линији и на листама после ове измене. Када ниједан језик није изабран, примаћете објаве на свим језицима.",
   "subscribed_languages.save": "Сачувај измене",
   "subscribed_languages.target": "Промени језике на које је {target} пријављен",
   "suggestions.dismiss": "Одбаци предлог",
@@ -611,9 +620,9 @@
   "time_remaining.seconds": "Остало {number, plural, one {# секунд} few {# секунде} other {# секунди}}",
   "timeline_hint.remote_resource_not_displayed": "{resource} са других сервера се не приказују.",
   "timeline_hint.resources.followers": "Пратиоци",
-  "timeline_hint.resources.follows": "Праћени",
+  "timeline_hint.resources.follows": "Праћења",
   "timeline_hint.resources.statuses": "Старије објаве",
-  "trends.counter_by_accounts": "{count, plural, one {{counter} особа} few {{counter} особе} other {{counter} особа}} у протеклих {days, plural, one {дан} few {{days} дана} other {{days} дана}}",
+  "trends.counter_by_accounts": "{count, plural, one {{counter} особа} few {{counter} особе} other {{counter} особа}} у протеклих {days, plural, one {дан} other {{days} дана}}",
   "trends.trending_now": "У тренду сада",
   "ui.beforeunload": "Ваш нацрт ће бити изгубљен ако напустите Mastodon.",
   "units.short.billion": "{count} млрд.",
@@ -622,9 +631,9 @@
   "upload_area.title": "Превуците и отпустите за отпремање",
   "upload_button.label": "Додај слике, видео или аудио датотеку",
   "upload_error.limit": "Достигнуто је ограничење за отпремање датотека.",
-  "upload_error.poll": "Отпремање датотека није дозвољено са анкетама.",
-  "upload_form.audio_description": "Опишите за особе са оштећеним слухом",
-  "upload_form.description": "Опишите за особе са оштећеним видом",
+  "upload_error.poll": "Отпремање датотека није дозвољено код анкета.",
+  "upload_form.audio_description": "Додајте опис за особе са оштећеним слухом",
+  "upload_form.description": "Додајте опис за особе са оштећеним видом",
   "upload_form.description_missing": "Нема додатог описа",
   "upload_form.edit": "Уреди",
   "upload_form.thumbnail": "Промени сличицу",
@@ -635,8 +644,8 @@
   "upload_modal.applying": "Примена…",
   "upload_modal.choose_image": "Одаберите слику",
   "upload_modal.description_placeholder": "Љубазни фењерџија чађавог лица хоће да ми покаже штос",
-  "upload_modal.detect_text": "Откриј текст са слике",
-  "upload_modal.edit_media": "Уреди милтимедију",
+  "upload_modal.detect_text": "Пронађи текст са слике",
+  "upload_modal.edit_media": "Уреди мултимедију",
   "upload_modal.hint": "Кликните или превуците кружић на прегледу за избор тачке фокуса која ће увек бити видљива на свим сличицама.",
   "upload_modal.preparing_ocr": "Припрема OCR-а…",
   "upload_modal.preview_label": "Преглед ({ratio})",
diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json
index 878911c87..f22670955 100644
--- a/app/javascript/mastodon/locales/sv.json
+++ b/app/javascript/mastodon/locales/sv.json
@@ -3,7 +3,7 @@
   "about.contact": "Kontakt:",
   "about.disclaimer": "Mastodon är fri programvara med öppen källkod och ett varumärke tillhörande Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Okänd orsak",
-  "about.domain_blocks.preamble": "Som regel låter Mastodon dig interagera med användare från andra servrar i fediversumet och se deras innehåll. Detta är de undantag som gjorts på just denna servern.",
+  "about.domain_blocks.preamble": "Som regel låter Mastodon dig interagera med användare från andra servrar i fediversumet och se deras innehåll. Detta är de undantag som gjorts på just denna server.",
   "about.domain_blocks.silenced.explanation": "Såvida du inte uttryckligen söker upp dem eller samtycker till att se dem genom att följa dem kommer du i allmänhet inte se profiler från den här servern, eller deras innehåll.",
   "about.domain_blocks.silenced.title": "Begränsat",
   "about.domain_blocks.suspended.explanation": "Inga data från denna server kommer behandlas, lagras eller bytas ut, vilket omöjliggör kommunikation med användare på denna server.",
@@ -20,7 +20,7 @@
   "account.blocked": "Blockerad",
   "account.browse_more_on_origin_server": "Läs mer på den ursprungliga profilen",
   "account.cancel_follow_request": "Återkalla din begäran om att få följa",
-  "account.direct": "Skicka direktmeddelande till @{name}",
+  "account.direct": "Nämn @{name} privat",
   "account.disable_notifications": "Sluta notifiera mig när @{name} gör inlägg",
   "account.domain_blocked": "Domän blockerad",
   "account.edit_profile": "Redigera profil",
@@ -32,9 +32,9 @@
   "account.follow": "Följ",
   "account.followers": "Följare",
   "account.followers.empty": "Ingen följer denna användare än.",
-  "account.followers_counter": "{count, plural, one {{counter} Följare} other {{counter} Följare}}",
+  "account.followers_counter": "{count, plural, one {{counter} följare} other {{counter} följare}}",
   "account.following": "Följer",
-  "account.following_counter": "{count, plural, one {{counter} Följer} other {{counter} Följer}}",
+  "account.following_counter": "{count, plural, one {{counter} följd} other {{counter} följda}}",
   "account.follows.empty": "Denna användare följer inte någon än.",
   "account.follows_you": "Följer dig",
   "account.go_to_profile": "Gå till profilen",
@@ -42,7 +42,7 @@
   "account.joined_short": "Gick med",
   "account.languages": "Ändra vilka språk du helst vill se i ditt flöde",
   "account.link_verified_on": "Ägarskap för denna länk kontrollerades den {date}",
-  "account.locked_info": "För detta konto har ägaren valt att manuellt godkänna vem som kan följa dem.",
+  "account.locked_info": "För detta konto har ägaren valt att manuellt godkänna vem som kan följa hen.",
   "account.media": "Media",
   "account.mention": "Nämn @{name}",
   "account.moved_to": "{name} har indikerat att hen har ett nytt konto:",
@@ -57,7 +57,7 @@
   "account.requested_follow": "{name} har begärt att följa dig",
   "account.share": "Dela @{name}s profil",
   "account.show_reblogs": "Visa boostar från @{name}",
-  "account.statuses_counter": "{count, plural, one {{counter} Inlägg} other {{counter} Inlägg}}",
+  "account.statuses_counter": "{count, plural, one {{counter} inlägg} other {{counter} inlägg}}",
   "account.unblock": "Avblockera @{name}",
   "account.unblock_domain": "Avblockera {domain}",
   "account.unblock_short": "Avblockera",
@@ -102,7 +102,7 @@
   "column.blocks": "Blockerade användare",
   "column.bookmarks": "Bokmärken",
   "column.community": "Lokal tidslinje",
-  "column.direct": "Direktmeddelanden",
+  "column.direct": "Privata nämningar",
   "column.directory": "Bläddra bland profiler",
   "column.domain_blocks": "Blockerade domäner",
   "column.favourites": "Favoriter",
@@ -159,13 +159,15 @@
   "confirmations.delete_list.confirm": "Radera",
   "confirmations.delete_list.message": "Är du säker på att du vill radera denna lista permanent?",
   "confirmations.discard_edit_media.confirm": "Kasta",
-  "confirmations.discard_edit_media.message": "Du har o-sparade ändringar till mediabeskrivningen eller förhandsgranskningen, kasta bort dem ändå?",
+  "confirmations.discard_edit_media.message": "Du har osparade ändringar till mediabeskrivningen eller förhandsgranskningen, kasta bort dem ändå?",
   "confirmations.domain_block.confirm": "Dölj hela domänen",
   "confirmations.domain_block.message": "Är du verkligen, verkligen säker på att du vill blockera hela {domain}? I de flesta fall är några riktade blockeringar eller nedtystade konton tillräckligt och att föredra. Du kommer inte se innehåll från den domänen i den allmänna tidslinjen eller i dina aviseringar. Dina följare från den domänen komer att tas bort.",
+  "confirmations.edit.confirm": "Redigera",
+  "confirmations.edit.message": "Om du svarar nu kommer det att ersätta meddelandet du håller på att skapa. Är du säker på att du vill fortsätta?",
   "confirmations.logout.confirm": "Logga ut",
   "confirmations.logout.message": "Är du säker på att du vill logga ut?",
   "confirmations.mute.confirm": "Tysta",
-  "confirmations.mute.explanation": "Detta kommer dölja inlägg från dem och inlägg som nämner dem, men de tillåts fortfarande se dina inlägg och följa dig.",
+  "confirmations.mute.explanation": "Detta kommer dölja inlägg från hen och inlägg som nämner hen, men hen tillåts fortfarande se dina inlägg och följa dig.",
   "confirmations.mute.message": "Är du säker på att du vill tysta {name}?",
   "confirmations.redraft.confirm": "Radera & gör om",
   "confirmations.redraft.message": "Är du säker på att du vill radera detta inlägg och göra om det? Favoritmarkeringar, boostar och svar till det ursprungliga inlägget kommer förlora sitt sammanhang.",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Du har ännu ej blockerat några användare.",
   "empty_column.bookmarked_statuses": "Du har inte bokmärkt några inlägg än. När du bokmärker ett inlägg kommer det synas här.",
   "empty_column.community": "Den lokala tidslinjen är tom. Skriv något offentligt för att sätta bollen i rullning!",
-  "empty_column.direct": "Du har inga direktmeddelanden. När du skickar eller tar emot ett direktmeddelande kommer det att visas här.",
+  "empty_column.direct": "Du har inga privata nämningar. När du skickar eller tar emot ett direktmeddelande kommer det att visas här.",
   "empty_column.domain_blocks": "Det finns ännu inga dolda domäner.",
   "empty_column.explore_statuses": "Ingenting är trendigt just nu. Kom tillbaka senare!",
   "empty_column.favourited_statuses": "Du har inga favoritmarkerade inlägg än. När du favoritmarkerar ett inlägg kommer det visas här.",
   "empty_column.favourites": "Ingen har favoritmarkerat detta inlägg än. När någon gör det kommer de synas här.",
   "empty_column.follow_recommendations": "Det ser ut som om inga förslag kan genereras till dig. Du kan prova att använda sök för att leta efter personer som du kanske känner eller utforska trendande hash-taggar.",
   "empty_column.follow_requests": "Du har inga följarförfrågningar än. När du får en kommer den visas här.",
+  "empty_column.followed_tags": "Du följer inga hashtaggar ännu. När du gör det kommer de att dyka upp här.",
   "empty_column.hashtag": "Det finns inget i denna hashtag ännu.",
   "empty_column.home": "Din hemma-tidslinje är tom! Följ fler användare för att fylla den. {suggestions}",
   "empty_column.home.suggestions": "Se några förslag",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Godkänn",
   "follow_request.reject": "Avvisa",
   "follow_requests.unlocked_explanation": "Även om ditt konto inte är låst tror {domain} personalen att du kanske vill granska dessa följares förfrågningar manuellt.",
+  "followed_tags": "Följda hashtags",
   "footer.about": "Om",
   "footer.directory": "Profilkatalog",
   "footer.get_app": "Skaffa appen",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Tangentbordsgenvägar",
   "footer.privacy_policy": "Integritetspolicy",
   "footer.source_code": "Visa källkod",
+  "footer.status": "Status",
   "generic.saved": "Sparad",
   "getting_started.heading": "Kom igång",
   "hashtag.column_header.tag_mode.all": "och {additional}",
@@ -289,7 +294,7 @@
   "home.hide_announcements": "Dölj notiser",
   "home.show_announcements": "Visa notiser",
   "interaction_modal.description.favourite": "Med ett Mastodon-konto kan du favoritmarkera detta inlägg för att visa författaren att du gillar det och för att spara det till senare.",
-  "interaction_modal.description.follow": "Med ett Mastodon-konto kan du följa {name} för att se deras inlägg i ditt hemflöde.",
+  "interaction_modal.description.follow": "Med ett Mastodon-konto kan du följa {name} för att se hens inlägg i ditt hemflöde.",
   "interaction_modal.description.reblog": "Med ett Mastodon-konto kan du boosta detta inlägg för att dela den med dina egna följare.",
   "interaction_modal.description.reply": "Med ett Mastodon-konto kan du svara på detta inlägg.",
   "interaction_modal.on_another_server": "På en annan server",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "för att fokusera en status i en av kolumnerna",
   "keyboard_shortcuts.compose": "för att fokusera skrivfältet",
   "keyboard_shortcuts.description": "Beskrivning",
-  "keyboard_shortcuts.direct": "för att öppna Direktmeddelanden",
+  "keyboard_shortcuts.direct": "för att öppna privata nämningskolumnen",
   "keyboard_shortcuts.down": "för att flytta nedåt i listan",
   "keyboard_shortcuts.enter": "Öppna inlägg",
   "keyboard_shortcuts.favourite": "Favoritmarkera inlägg",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bokmärken",
   "navigation_bar.community_timeline": "Lokal tidslinje",
   "navigation_bar.compose": "Författa nytt inlägg",
-  "navigation_bar.direct": "Direktmeddelanden",
+  "navigation_bar.direct": "Privata nämningar",
   "navigation_bar.discover": "Upptäck",
   "navigation_bar.domain_blocks": "Dolda domäner",
   "navigation_bar.edit_profile": "Redigera profil",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoriter",
   "navigation_bar.filters": "Tystade ord",
   "navigation_bar.follow_requests": "Följförfrågningar",
+  "navigation_bar.followed_tags": "Utvalda hashtags",
   "navigation_bar.follows_and_followers": "Följer och följare",
   "navigation_bar.lists": "Listor",
   "navigation_bar.logout": "Logga ut",
@@ -475,7 +481,7 @@
   "relative_time.today": "idag",
   "reply_indicator.cancel": "Ångra",
   "report.block": "Blockera",
-  "report.block_explanation": "Du kommer inte se deras inlägg. De kommer inte kunna se dina inlägg eller följa dig. De kommer kunna se att de är blockerade.",
+  "report.block_explanation": "Du kommer inte se hens inlägg. Hen kommer inte kunna se dina inlägg eller följa dig. Hen kommer kunna se att hen är blockerad.",
   "report.categories.other": "Övrigt",
   "report.categories.spam": "Skräppost",
   "report.categories.violation": "Innehåll bryter mot en eller flera serverregler",
@@ -488,7 +494,7 @@
   "report.forward": "Vidarebefordra till {target}",
   "report.forward_hint": "Kontot är från en annan server. Skicka även en anonymiserad kopia av anmälan dit?",
   "report.mute": "Tysta",
-  "report.mute_explanation": "Du kommer inte se deras inlägg. De kan fortfarande följa dig och se dina inlägg. De kommer inte veta att de är tystade.",
+  "report.mute_explanation": "Du kommer inte se hens inlägg. Hen kan fortfarande följa dig och se dina inlägg. Hen kommer inte veta att hen är tystad.",
   "report.next": "Nästa",
   "report.placeholder": "Ytterligare kommentarer",
   "report.reasons.dislike": "Jag tycker inte om det",
@@ -510,21 +516,23 @@
   "report.thanks.title": "Vill du inte se det här?",
   "report.thanks.title_actionable": "Tack för att du rapporterar, vi kommer att titta på detta.",
   "report.unfollow": "Sluta följ @{username}",
-  "report.unfollow_explanation": "Du följer detta konto. Avfölj dem för att inte se deras inlägg i ditt hemflöde.",
+  "report.unfollow_explanation": "Du följer detta konto. Avfölj hen för att inte se hens inlägg i ditt hemflöde.",
   "report_notification.attached_statuses": "bifogade {count, plural, one {{count} inlägg} other {{count} inlägg}}",
   "report_notification.categories.other": "Övrigt",
   "report_notification.categories.spam": "Skräppost",
   "report_notification.categories.violation": "Regelöverträdelse",
   "report_notification.open": "Öppna rapport",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Sök",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Sök eller klistra in URL",
-  "search_popout.search_format": "Avancerat sökformat",
-  "search_popout.tips.full_text": "Enkel text returnerar inlägg du har skrivit, favoritmarkerat, boostat eller blivit nämnd i, samt matchar användarnamn, visningsnamn och hashtaggar.",
-  "search_popout.tips.hashtag": "hash-tagg",
-  "search_popout.tips.status": "inlägg",
-  "search_popout.tips.text": "Enkel text returnerar matchande visningsnamn, användarnamn och hashtags",
-  "search_popout.tips.user": "användare",
-  "search_results.accounts": "Människor",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "Alla",
   "search_results.hashtags": "Hashtaggar",
   "search_results.nothing_found": "Kunde inte hitta något för dessa sökord",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Serverstatistik:",
   "sign_in_banner.create_account": "Skapa konto",
   "sign_in_banner.sign_in": "Logga in",
-  "sign_in_banner.text": "Logga in för att följa profiler eller hashtaggar, favoritmarkera, dela och svara på inlägg eller interagera från ditt konto på en annan server.",
+  "sign_in_banner.text": "Logga in för att följa profiler eller hashtags, favoritmarkera, dela och svara på inlägg. Du kan också interagera med ditt konto på en annan server.",
   "status.admin_account": "Öppet modereringsgränssnitt för @{name}",
   "status.admin_domain": "Öppet modereringsgränssnitt för @{domain}",
   "status.admin_status": "Öppna detta inlägg i modereringsgränssnittet",
@@ -551,7 +559,8 @@
   "status.copy": "Kopiera inläggslänk",
   "status.delete": "Radera",
   "status.detailed_status": "Detaljerad samtalsvy",
-  "status.direct": "Direktmeddela @{name}",
+  "status.direct": "Nämn @{name} privat",
+  "status.direct_indicator": "Privat nämning",
   "status.edit": "Redigera",
   "status.edited": "Ändrad {date}",
   "status.edited_x_times": "Redigerad {count, plural, one {{count} gång} other {{count} gånger}}",
diff --git a/app/javascript/mastodon/locales/szl.json b/app/javascript/mastodon/locales/szl.json
index e646ef83c..c8f0337cb 100644
--- a/app/javascript/mastodon/locales/szl.json
+++ b/app/javascript/mastodon/locales/szl.json
@@ -1,28 +1,28 @@
 {
-  "about.blocks": "Moderated servers",
-  "about.contact": "Contact:",
-  "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
-  "about.domain_blocks.no_reason_available": "Reason not available",
-  "about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",
-  "about.domain_blocks.silenced.explanation": "You will generally not see profiles and content from this server, unless you explicitly look it up or opt into it by following.",
-  "about.domain_blocks.silenced.title": "Limited",
-  "about.domain_blocks.suspended.explanation": "No data from this server will be processed, stored or exchanged, making any interaction or communication with users from this server impossible.",
-  "about.domain_blocks.suspended.title": "Suspended",
-  "about.not_available": "This information has not been made available on this server.",
+  "about.blocks": "Moderowane serwery",
+  "about.contact": "Kōntakt:",
+  "about.disclaimer": "Mastodōn je wolnym a ôtwartozdrzōdłowym ôprogramowaniym ôraz znakiym towarowym ôd Mastodon gGmbH.",
+  "about.domain_blocks.no_reason_available": "Grund niydostympny",
+  "about.domain_blocks.preamble": "Mastodōn normalniy pozwŏlŏ na ôglōndaniy treściōw a interakcyje ze używŏczami inkszych serwerōw we fediverse, ale sōm ôd tygo wyjōntki, kere bōły poczyniōne na tym serwerze.",
+  "about.domain_blocks.silenced.explanation": "Normalniy niy bydziesz widzieć profilōw a treściōw ze tygo serwera. Ôboczysz je ino jak specjalniy bydziesz ich szukać abo jak je zaôbserwujesz.",
+  "about.domain_blocks.silenced.title": "Ôgraniczone",
+  "about.domain_blocks.suspended.explanation": "Żŏdne dane ze tygo serwera niy bydōm przetwarzane, przechowywane abo wymieniane, beztoż wszelakŏ interakcyjŏ abo komunikacyjŏ ze używŏczami tygo serwera bydzie niymożliwŏ.",
+  "about.domain_blocks.suspended.title": "Zawiyszōne",
+  "about.not_available": "Ta informacyjŏ niy bōła udostympniōna na tym serwerze.",
   "about.powered_by": "Decentralized social media powered by {mastodon}",
-  "about.rules": "Server rules",
-  "account.account_note_header": "Note",
+  "about.rules": "Zasady serwera",
+  "account.account_note_header": "Notatka",
   "account.add_or_remove_from_list": "Add or Remove from lists",
   "account.badges.bot": "Bot",
-  "account.badges.group": "Group",
-  "account.block": "Block @{name}",
-  "account.block_domain": "Block domain {domain}",
+  "account.badges.group": "Grupa",
+  "account.block": "Zablokuj @{name}",
+  "account.block_domain": "Zablokuj domena {domain}",
   "account.blocked": "Blocked",
-  "account.browse_more_on_origin_server": "Browse more on the original profile",
+  "account.browse_more_on_origin_server": "Ôbocz wiyncyj we ôryginalnym profilu",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
-  "account.domain_blocked": "Domain blocked",
+  "account.domain_blocked": "Domena zablokowanŏ",
   "account.edit_profile": "Edit profile",
   "account.enable_notifications": "Notify me when @{name} posts",
   "account.endorse": "Feature on profile",
@@ -43,11 +43,11 @@
   "account.languages": "Change subscribed languages",
   "account.link_verified_on": "Ownership of this link was checked on {date}",
   "account.locked_info": "This account privacy status is set to locked. The owner manually reviews who can follow them.",
-  "account.media": "Media",
+  "account.media": "Mydia",
   "account.mention": "Mention @{name}",
   "account.moved_to": "{name} has indicated that their new account is now:",
-  "account.mute": "Mute @{name}",
-  "account.mute_notifications": "Mute notifications from @{name}",
+  "account.mute": "Wycisz @{name}",
+  "account.mute_notifications": "Wycisz powiadōmiynia ôd @{name}",
   "account.muted": "Muted",
   "account.open_original_page": "Open original page",
   "account.posts": "Toots",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "Favourites",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "Mute",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite 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_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lists",
   "navigation_bar.logout": "Logout",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/ta.json b/app/javascript/mastodon/locales/ta.json
index 7013fdbb8..76e144747 100644
--- a/app/javascript/mastodon/locales/ta.json
+++ b/app/javascript/mastodon/locales/ta.json
@@ -20,7 +20,7 @@
   "account.blocked": "முடக்கப்பட்டது",
   "account.browse_more_on_origin_server": "மேலும் உலாவ சுயவிவரத்திற்குச் செல்க",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "நேரடி செய்தி @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "@{name} பதிவிட்டல் எனக்கு தெரியபடுத்த வேண்டாம்",
   "account.domain_blocked": "மறைக்கப்பட்டத் தளங்கள்",
   "account.edit_profile": "சுயவிவரத்தை மாற்று",
@@ -102,7 +102,7 @@
   "column.blocks": "தடுக்கப்பட்ட பயனர்கள்",
   "column.bookmarks": "அடையாளக்குறிகள்",
   "column.community": "சுய நிகழ்வு காலவரிசை",
-  "column.direct": "நேரடி செய்திகள்",
+  "column.direct": "Private mentions",
   "column.directory": "சுயவிவரங்களை உலாவு",
   "column.domain_blocks": "மறைந்திருக்கும் திரளங்கள்",
   "column.favourites": "பிடித்தவைகள்",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "சேமிக்கப்படாத மாற்றங்கள் ஊடக விளக்கம் அல்லது முன்னோட்டத்தில் உள்ளது. அவற்றை நிராகரிக்கவா?",
   "confirmations.domain_block.confirm": "முழு களத்தையும் மறை",
   "confirmations.domain_block.message": "நீங்கள் முழு {domain} களத்தையும் நிச்சயமாக, நிச்சயமாகத் தடுக்க விரும்புகிறீர்களா? பெரும்பாலும் சில குறிப்பிட்ட பயனர்களைத் தடுப்பதே போதுமானது. முழு களத்தையும் தடுத்தால், அதிலிருந்து வரும் எந்தப் பதிவையும் உங்களால் காண முடியாது, மேலும் அப்பதிவுகள் குறித்த அறிவிப்புகளும் உங்களுக்கு வராது. அந்தக் களத்தில் இருக்கும் பின்தொடர்பவர்கள் உங்கள் பக்கத்திலிருந்து நீக்கப்படுவார்கள்.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "வெளியேறு",
   "confirmations.logout.message": "நிச்சயமாக நீங்கள் வெளியேற விரும்புகிறீர்களா?",
   "confirmations.mute.confirm": "அமைதியாக்கு",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "நீங்கள் இதுவரை எந்தப் பயனர்களையும் முடக்கியிருக்கவில்லை.",
   "empty_column.bookmarked_statuses": "உங்களிடம் அடையாளக்குறியிட்ட டூட்டுகள் எவையும் இல்லை. அடையாளக்குறியிட்ட பிறகு அவை இங்கே காட்டப்படும்.",
   "empty_column.community": "உங்கள் மாஸ்டடான் முச்சந்தியில் யாரும் இல்லை. எதையேனும் எழுதி ஆட்டத்தைத் துவக்குங்கள்!",
-  "empty_column.direct": "உங்களுக்குத் தனிப்பட்ட செய்திகள் ஏதும் இல்லை. செய்தியை நீங்கள் அனுப்பும்போதோ அல்லது பெறும்போதோ, அது இங்கே காண்பிக்கப்படும்.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "தடுக்கப்பட்டக் களங்கள் இதுவரை இல்லை.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "உங்களுக்குப் பிடித்த டூட்டுகள் இதுவரை இல்லை. ஒரு டூட்டில் நீங்கள் விருப்பக்குறி இட்டால், அது இங்கே காண்பிக்கப்படும்.",
   "empty_column.favourites": "இந்த டூட்டில் இதுவரை யாரும் விருப்பக்குறி இடவில்லை. யாரேனும் விரும்பினால், அது இங்கே காண்பிக்கப்படும்.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "வாசகர் கோரிக்கைகள் இதுவரை ஏதும் இல்லை. யாரேனும் கோரிக்கையை அனுப்பினால், அது இங்கே காண்பிக்கப்படும்.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "இந்த சிட்டையில் இதுவரை ஏதும் இல்லை.",
   "empty_column.home": "உங்கள் மாஸ்டடான் வீட்டில் யாரும் இல்லை. {public} -இல் சென்று பார்க்கவும், அல்லது தேடல் கருவியைப் பயன்படுத்திப் பிற பயனர்களைக் கண்டடையவும்.",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "அனுமதியளி",
   "follow_request.reject": "நிராகரி",
   "follow_requests.unlocked_explanation": "உங்கள் கணக்கு பூட்டப்படவில்லை என்றாலும், இந்தக் கணக்குகளிலிருந்து உங்களைப் பின்தொடர விரும்பும் கோரிக்கைகளை நீங்கள் பரீசீலிப்பது நலம் என்று {domain} ஊழியர் எண்ணுகிறார்.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "சேமிக்கப்பட்டது",
   "getting_started.heading": "முதன்மைப் பக்கம்",
   "hashtag.column_header.tag_mode.all": "மற்றும் {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "அடையாளக்குறிகள்",
   "navigation_bar.community_timeline": "உள்ளூர் காலக்கெடு",
   "navigation_bar.compose": "புதியவற்றை எழுதுக toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "கண்டு பிடி",
   "navigation_bar.domain_blocks": "மறைந்த களங்கள்",
   "navigation_bar.edit_profile": "சுயவிவரத்தைத் திருத்தவும்",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "விருப்பத்துக்குகந்த",
   "navigation_bar.filters": "முடக்கப்பட்ட வார்த்தைகள்",
   "navigation_bar.follow_requests": "கோரிக்கைகளை பின்பற்றவும்",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "பின்பற்றல்கள் மற்றும் பின்பற்றுபவர்கள்",
   "navigation_bar.lists": "குதிரை வீர்ர்கள்",
   "navigation_bar.logout": "விடு பதிகை",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "தேடு",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "மேம்பட்ட தேடல் வடிவம்",
-  "search_popout.tips.full_text": "எளிமையான உரை நீங்கள் எழுதப்பட்ட, புகழ், அதிகரித்தது, அல்லது குறிப்பிட்டுள்ள, அதே போல் பயனர் பெயர்கள், காட்சி பெயர்கள், மற்றும் ஹேஸ்டேகைகளை கொண்டுள்ளது என்று நிலைகளை கொடுக்கிறது.",
-  "search_popout.tips.hashtag": "ஹேஸ்டேக்",
-  "search_popout.tips.status": "நிலைமை",
-  "search_popout.tips.text": "எளிய உரை காட்சி பெயர்கள், பயனர்பெயர்கள் மற்றும் ஹாஷ்டேட்களுடன் பொருந்துகிறது",
-  "search_popout.tips.user": "பயனர்",
-  "search_results.accounts": "மக்கள்",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "ஹாஷ்டேக்குகளைச்",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "மிதமான இடைமுகத்தை திறக்க @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "மிதமான இடைமுகத்தில் இந்த நிலையை திறக்கவும்",
@@ -551,7 +559,8 @@
   "status.copy": "நிலைக்கு இணைப்பை நகலெடு",
   "status.delete": "நீக்கு",
   "status.detailed_status": "விரிவான உரையாடல் காட்சி",
-  "status.direct": "நேரடி செய்தி @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/tai.json b/app/javascript/mastodon/locales/tai.json
index d39ca62eb..e553ac58c 100644
--- a/app/javascript/mastodon/locales/tai.json
+++ b/app/javascript/mastodon/locales/tai.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocked",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Domain blocked",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "Favourites",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "Mute",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite 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_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lists",
   "navigation_bar.logout": "Logout",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json
index 2e199fd39..1bd8a65bd 100644
--- a/app/javascript/mastodon/locales/te.json
+++ b/app/javascript/mastodon/locales/te.json
@@ -20,7 +20,7 @@
   "account.blocked": "బ్లాక్ అయినవి",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "@{name}కు నేరుగా సందేశం పంపు",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "డొమైన్ దాచిపెట్టబడినది",
   "account.edit_profile": "ప్రొఫైల్ని సవరించండి",
@@ -102,7 +102,7 @@
   "column.blocks": "బ్లాక్ చేయబడిన వినియోగదారులు",
   "column.bookmarks": "Bookmarks",
   "column.community": "స్థానిక కాలక్రమం",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "దాచిన డొమైన్లు",
   "column.favourites": "ఇష్టపడినవి",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "మొత్తం డొమైన్ను దాచు",
   "confirmations.domain_block.message": "మీరు నిజంగా నిజంగా మొత్తం {domain} ని బ్లాక్ చేయాలనుకుంటున్నారా? చాలా సందర్భాలలో కొన్ని లక్ష్యంగా ఉన్న బ్లాక్స్ లేదా మ్యూట్స్ సరిపోతాయి మరియు ఉత్తమమైనవి. మీరు ఆ డొమైన్ నుండి కంటెంట్ను ఏ ప్రజా కాలక్రమాలలో లేదా మీ నోటిఫికేషన్లలో చూడలేరు. ఆ డొమైన్ నుండి మీ అనుచరులు తీసివేయబడతారు.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "మ్యూట్ చేయి",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "మీరు ఇంకా ఏ వినియోగదారులనూ బ్లాక్ చేయలేదు.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "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.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "దాచబడిన డొమైన్లు ఇంకా ఏమీ లేవు.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "మీకు ఇష్టపడిన టూట్లు ఇంకా ఎమీ లేవు. మీరు ఒకదానిని ఇష్టపడినప్పుడు, అది ఇక్కడ కనిపిస్తుంది.",
   "empty_column.favourites": "ఈ టూట్ను ఇంకా ఎవరూ ఇష్టపడలేదు. ఎవరైనా అలా చేసినప్పుడు, అవి ఇక్కడ కనబడతాయి.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "మీకు ఇంకా ఫాలో రిక్వెస్టులు ఏమీ రాలేదు. మీకు ఒకటి రాగానే, అది ఇక్కడ కనబడుతుంది.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "ఇంకా హాష్ ట్యాగ్లో ఏమీ లేదు.",
   "empty_column.home": "మీ హోమ్ కాలక్రమం ఖాళీగా ఉంది! {Public} ను సందర్శించండి లేదా ఇతర వినియోగదారులను కలుసుకోవడానికి మరియు అన్వేషణ కోసం శోధనను ఉపయోగించండి.",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "అనుమతించు",
   "follow_request.reject": "తిరస్కరించు",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "మొదలుపెడదాం",
   "hashtag.column_header.tag_mode.all": "మరియు {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "స్థానిక కాలక్రమం",
   "navigation_bar.compose": "కొత్త టూట్ను రాయండి",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "కనుగొను",
   "navigation_bar.domain_blocks": "దాచిన డొమైన్లు",
   "navigation_bar.edit_profile": "ప్రొఫైల్ని సవరించండి",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "ఇష్టపడినవి",
   "navigation_bar.filters": "మ్యూట్ చేయబడిన పదాలు",
   "navigation_bar.follow_requests": "అనుసరించడానికి అభ్యర్ధనలు",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "జాబితాలు",
   "navigation_bar.logout": "లాగ్ అవుట్ చేయండి",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "శోధన",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "search_popout.search_format": "అధునాతన శోధన ఆకృతి",
-  "search_popout.tips.full_text": "సాధారణ వచనం మీరు వ్రాసిన, ఇష్టపడే, పెంచబడిన లేదా పేర్కొనబడిన, అలాగే యూజర్పేర్లు, ప్రదర్శన పేర్లు, మరియు హ్యాష్ట్యాగ్లను నమోదు చేసిన హోదాలను అందిస్తుంది.",
-  "search_popout.tips.hashtag": "హాష్ ట్యాగ్",
-  "search_popout.tips.status": "స్టేటస్",
-  "search_popout.tips.text": "సింపుల్ టెక్స్ట్ ప్రదర్శన పేర్లు, యూజర్ పేర్లు మరియు హ్యాష్ట్యాగ్లను సరిపోలుస్తుంది",
-  "search_popout.tips.user": "వాడుకరి",
-  "search_results.accounts": "వ్యక్తులు",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "హాష్ ట్యాగ్లు",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "@{name} కొరకు సమన్వయ వినిమయసీమను తెరువు",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "సమన్వయ వినిమయసీమలో ఈ స్టేటస్ ను తెరవండి",
@@ -551,7 +559,8 @@
   "status.copy": "లంకెను స్టేటస్కు కాపీ చేయి",
   "status.delete": "తొలగించు",
   "status.detailed_status": "వివరణాత్మక సంభాషణ వీక్షణ",
-  "status.direct": "@{name}కు నేరుగా సందేశం పంపు",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json
index a0247891f..cd4c92351 100644
--- a/app/javascript/mastodon/locales/th.json
+++ b/app/javascript/mastodon/locales/th.json
@@ -20,7 +20,7 @@
   "account.blocked": "ปิดกั้นอยู่",
   "account.browse_more_on_origin_server": "เรียกดูเพิ่มเติมในโปรไฟล์ดั้งเดิม",
   "account.cancel_follow_request": "ถอนคำขอติดตาม",
-  "account.direct": "ส่งข้อความโดยตรงถึง @{name}",
+  "account.direct": "กล่าวถึง @{name} แบบส่วนตัว",
   "account.disable_notifications": "หยุดแจ้งเตือนฉันเมื่อ @{name} โพสต์",
   "account.domain_blocked": "ปิดกั้นโดเมนอยู่",
   "account.edit_profile": "แก้ไขโปรไฟล์",
@@ -67,8 +67,8 @@
   "account.unmute_notifications": "เลิกซ่อนการแจ้งเตือนจาก @{name}",
   "account.unmute_short": "เลิกซ่อน",
   "account_note.placeholder": "คลิกเพื่อเพิ่มหมายเหตุ",
-  "admin.dashboard.daily_retention": "อัตราการเก็บรักษาผู้ใช้ตามวันหลังจากลงทะเบียน",
-  "admin.dashboard.monthly_retention": "อัตราการเก็บรักษาผู้ใช้ตามเดือนหลังจากลงทะเบียน",
+  "admin.dashboard.daily_retention": "อัตราการเก็บรักษาผู้ใช้ตามวันหลังจากการลงทะเบียน",
+  "admin.dashboard.monthly_retention": "อัตราการเก็บรักษาผู้ใช้ตามเดือนหลังจากการลงทะเบียน",
   "admin.dashboard.retention.average": "ค่าเฉลี่ย",
   "admin.dashboard.retention.cohort": "เดือนที่ลงทะเบียน",
   "admin.dashboard.retention.cohort_size": "ผู้ใช้ใหม่",
@@ -102,7 +102,7 @@
   "column.blocks": "ผู้ใช้ที่ปิดกั้นอยู่",
   "column.bookmarks": "ที่คั่นหน้า",
   "column.community": "เส้นเวลาในเซิร์ฟเวอร์",
-  "column.direct": "ข้อความโดยตรง",
+  "column.direct": "การกล่าวถึงแบบส่วนตัว",
   "column.directory": "เรียกดูโปรไฟล์",
   "column.domain_blocks": "โดเมนที่ปิดกั้นอยู่",
   "column.favourites": "รายการโปรด",
@@ -128,7 +128,7 @@
   "compose.language.search": "ค้นหาภาษา...",
   "compose_form.direct_message_warning_learn_more": "เรียนรู้เพิ่มเติม",
   "compose_form.encryption_warning": "โพสต์ใน Mastodon ไม่ได้เข้ารหัสแบบต้นทางถึงปลายทาง อย่าแบ่งปันข้อมูลที่ละเอียดอ่อนใด ๆ ผ่าน Mastodon",
-  "compose_form.hashtag_warning": "จะไม่แสดงรายการโพสต์นี้ภายใต้แฮชแท็กใด ๆ เนื่องจากโพสต์ไม่อยู่ในรายการไม่เป็นสาธารณะ เฉพาะโพสต์สาธารณะเท่านั้นที่สามารถค้นหาได้โดยแฮชแท็ก",
+  "compose_form.hashtag_warning": "จะไม่แสดงรายการโพสต์นี้ภายใต้แฮชแท็กใด ๆ เนื่องจากโพสต์ไม่เป็นสาธารณะ เฉพาะโพสต์สาธารณะเท่านั้นที่สามารถค้นหาได้โดยแฮชแท็ก",
   "compose_form.lock_disclaimer": "บัญชีของคุณไม่ได้ {locked} ใครก็ตามสามารถติดตามคุณเพื่อดูโพสต์สำหรับผู้ติดตามเท่านั้นของคุณ",
   "compose_form.lock_disclaimer.lock": "ล็อคอยู่",
   "compose_form.placeholder": "คุณกำลังคิดอะไรอยู่?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "คุณมีการเปลี่ยนแปลงคำอธิบายหรือตัวอย่างสื่อที่ยังไม่ได้บันทึก ละทิ้งการเปลี่ยนแปลงต่อไป?",
   "confirmations.domain_block.confirm": "ปิดกั้นทั้งโดเมน",
   "confirmations.domain_block.message": "คุณแน่ใจจริง ๆ หรือไม่ว่าต้องการปิดกั้นทั้ง {domain}? ในกรณีส่วนใหญ่ การปิดกั้นหรือการซ่อนแบบกำหนดเป้าหมายไม่กี่รายการนั้นเพียงพอและเป็นที่นิยม คุณจะไม่เห็นเนื้อหาจากโดเมนนั้นในเส้นเวลาสาธารณะใด ๆ หรือการแจ้งเตือนของคุณ จะเอาผู้ติดตามของคุณจากโดเมนนั้นออก",
+  "confirmations.edit.confirm": "แก้ไข",
+  "confirmations.edit.message": "การแก้ไขในตอนนี้จะเขียนทับข้อความที่คุณกำลังเขียนในปัจจุบัน คุณแน่ใจหรือไม่ว่าต้องการดำเนินการต่อ?",
   "confirmations.logout.confirm": "ออกจากระบบ",
   "confirmations.logout.message": "คุณแน่ใจหรือไม่ว่าต้องการออกจากระบบ?",
   "confirmations.mute.confirm": "ซ่อน",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "คุณยังไม่ได้ปิดกั้นผู้ใช้ใด ๆ",
   "empty_column.bookmarked_statuses": "คุณยังไม่มีโพสต์ที่เพิ่มที่คั่นหน้าไว้ใด ๆ เมื่อคุณเพิ่มที่คั่นหน้าโพสต์ โพสต์จะปรากฏที่นี่",
   "empty_column.community": "เส้นเวลาในเซิร์ฟเวอร์ว่างเปล่า เขียนบางอย่างเป็นสาธารณะเพื่อเริ่มต้น!",
-  "empty_column.direct": "คุณยังไม่มีข้อความโดยตรงใด ๆ เมื่อคุณส่งหรือรับข้อความ ข้อความจะปรากฏที่นี่",
+  "empty_column.direct": "คุณยังไม่มีการกล่าวถึงแบบส่วนตัวใด ๆ เมื่อคุณส่งหรือรับการกล่าวถึง การกล่าวถึงจะปรากฏที่นี่",
   "empty_column.domain_blocks": "ยังไม่มีโดเมนที่ปิดกั้นอยู่",
   "empty_column.explore_statuses": "ไม่มีสิ่งใดที่กำลังนิยมในตอนนี้ กลับมาตรวจสอบในภายหลัง!",
   "empty_column.favourited_statuses": "คุณยังไม่มีโพสต์ที่ชื่นชอบใด ๆ เมื่อคุณชื่นชอบโพสต์ โพสต์จะปรากฏที่นี่",
   "empty_column.favourites": "ยังไม่มีใครชื่นชอบโพสต์นี้ เมื่อใครสักคนชื่นชอบ เขาจะปรากฏที่นี่",
   "empty_column.follow_recommendations": "ดูเหมือนว่าจะไม่สามารถสร้างข้อเสนอแนะสำหรับคุณ คุณสามารถลองใช้การค้นหาเพื่อมองหาผู้คนที่คุณอาจรู้จักหรือสำรวจแฮชแท็กที่กำลังนิยม",
   "empty_column.follow_requests": "คุณยังไม่มีคำขอติดตามใด ๆ เมื่อคุณได้รับคำขอ คำขอจะปรากฏที่นี่",
+  "empty_column.followed_tags": "คุณยังไม่ได้ติดตามแฮชแท็กใด ๆ เมื่อคุณติดตาม แฮชแท็กจะปรากฏที่นี่",
   "empty_column.hashtag": "ยังไม่มีสิ่งใดในแฮชแท็กนี้",
   "empty_column.home": "เส้นเวลาหน้าแรกของคุณว่างเปล่า! ติดตามผู้คนเพิ่มเติมเพื่อเติมเส้นเวลาให้เต็ม {suggestions}",
   "empty_column.home.suggestions": "ดูข้อเสนอแนะบางส่วน",
@@ -241,7 +244,7 @@
   "explore.trending_links": "ข่าว",
   "explore.trending_statuses": "โพสต์",
   "explore.trending_tags": "แฮชแท็ก",
-  "filter_modal.added.context_mismatch_explanation": "หมวดหมู่ตัวกรองนี้ไม่ได้นำไปใช้กับบริบทที่คุณได้เข้าถึงโพสต์นี้ หากคุณต้องการกรองโพสต์ในบริบทนี้ด้วย คุณจะต้องแก้ไขตัวกรอง",
+  "filter_modal.added.context_mismatch_explanation": "หมวดหมู่ตัวกรองนี้ไม่นำไปใช้กับบริบทที่คุณได้เข้าถึงโพสต์นี้ หากคุณต้องการกรองโพสต์ในบริบทนี้ด้วย คุณจะต้องแก้ไขตัวกรอง",
   "filter_modal.added.context_mismatch_title": "บริบทไม่ตรงกัน!",
   "filter_modal.added.expired_explanation": "หมวดหมู่ตัวกรองนี้หมดอายุแล้ว คุณจะต้องเปลี่ยนวันหมดอายุสำหรับหมวดหมู่เพื่อนำไปใช้",
   "filter_modal.added.expired_title": "ตัวกรองหมดอายุแล้ว!",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "อนุญาต",
   "follow_request.reject": "ปฏิเสธ",
   "follow_requests.unlocked_explanation": "แม้ว่าไม่มีการล็อคบัญชีของคุณ พนักงานของ {domain} คิดว่าคุณอาจต้องการตรวจทานคำขอติดตามจากบัญชีเหล่านี้ด้วยตนเอง",
+  "followed_tags": "แฮชแท็กที่ติดตาม",
   "footer.about": "เกี่ยวกับ",
   "footer.directory": "ไดเรกทอรีโปรไฟล์",
   "footer.get_app": "รับแอป",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "แป้นพิมพ์ลัด",
   "footer.privacy_policy": "นโยบายความเป็นส่วนตัว",
   "footer.source_code": "ดูโค้ดต้นฉบับ",
+  "footer.status": "สถานะ",
   "generic.saved": "บันทึกแล้ว",
   "getting_started.heading": "เริ่มต้นใช้งาน",
   "hashtag.column_header.tag_mode.all": "และ {additional}",
@@ -288,10 +293,10 @@
   "home.column_settings.show_replies": "แสดงการตอบกลับ",
   "home.hide_announcements": "ซ่อนประกาศ",
   "home.show_announcements": "แสดงประกาศ",
-  "interaction_modal.description.favourite": "เมื่อมีบัญชีใน Mastodon คุณสามารถชื่นชอบโพสต์นี้เพื่อให้ผู้สร้างทราบว่าคุณชื่นชมโพสต์และบันทึกโพสต์ไว้สำหรับภายหลัง",
-  "interaction_modal.description.follow": "เมื่อมีบัญชีใน Mastodon คุณสามารถติดตาม {name} เพื่อรับโพสต์ของเขาในฟีดหน้าแรกของคุณ",
-  "interaction_modal.description.reblog": "เมื่อมีบัญชีใน Mastodon คุณสามารถดันโพสต์นี้เพื่อแบ่งปันโพสต์กับผู้ติดตามของคุณเอง",
-  "interaction_modal.description.reply": "เมื่อมีบัญชีใน Mastodon คุณสามารถตอบกลับโพสต์นี้",
+  "interaction_modal.description.favourite": "ด้วยบัญชีใน Mastodon คุณสามารถชื่นชอบโพสต์นี้เพื่อให้ผู้สร้างทราบว่าคุณชื่นชมโพสต์และบันทึกโพสต์ไว้สำหรับภายหลัง",
+  "interaction_modal.description.follow": "ด้วยบัญชีใน Mastodon คุณสามารถติดตาม {name} เพื่อรับโพสต์ของเขาในฟีดหน้าแรกของคุณ",
+  "interaction_modal.description.reblog": "ด้วยบัญชีใน Mastodon คุณสามารถดันโพสต์นี้เพื่อแบ่งปันโพสต์กับผู้ติดตามของคุณเอง",
+  "interaction_modal.description.reply": "ด้วยบัญชีใน Mastodon คุณสามารถตอบกลับโพสต์นี้",
   "interaction_modal.on_another_server": "ในเซิร์ฟเวอร์อื่น",
   "interaction_modal.on_this_server": "ในเซิร์ฟเวอร์นี้",
   "interaction_modal.other_server_instructions": "คัดลอกแล้ววาง URL นี้ลงในช่องค้นหาของแอป Mastodon โปรดของคุณหรือส่วนติดต่อเว็บของเซิร์ฟเวอร์ Mastodon ของคุณ",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "โฟกัสคอลัมน์",
   "keyboard_shortcuts.compose": "โฟกัสพื้นที่เขียนข้อความ",
   "keyboard_shortcuts.description": "คำอธิบาย",
-  "keyboard_shortcuts.direct": "เพื่อเปิดคอลัมน์ข้อความโดยตรง",
+  "keyboard_shortcuts.direct": "เพื่อเปิดคอลัมน์การกล่าวถึงแบบส่วนตัว",
   "keyboard_shortcuts.down": "ย้ายลงในรายการ",
   "keyboard_shortcuts.enter": "เปิดโพสต์",
   "keyboard_shortcuts.favourite": "ชื่นชอบโพสต์",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "ที่คั่นหน้า",
   "navigation_bar.community_timeline": "เส้นเวลาในเซิร์ฟเวอร์",
   "navigation_bar.compose": "เขียนโพสต์ใหม่",
-  "navigation_bar.direct": "ข้อความโดยตรง",
+  "navigation_bar.direct": "การกล่าวถึงแบบส่วนตัว",
   "navigation_bar.discover": "ค้นพบ",
   "navigation_bar.domain_blocks": "โดเมนที่ปิดกั้นอยู่",
   "navigation_bar.edit_profile": "แก้ไขโปรไฟล์",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "รายการโปรด",
   "navigation_bar.filters": "คำที่ซ่อนอยู่",
   "navigation_bar.follow_requests": "คำขอติดตาม",
+  "navigation_bar.followed_tags": "แฮชแท็กที่ติดตาม",
   "navigation_bar.follows_and_followers": "การติดตามและผู้ติดตาม",
   "navigation_bar.lists": "รายการ",
   "navigation_bar.logout": "ออกจากระบบ",
@@ -478,7 +484,7 @@
   "report.block_explanation": "คุณจะไม่เห็นโพสต์ของเขา เขาจะไม่สามารถเห็นโพสต์ของคุณหรือติดตามคุณ เขาจะสามารถบอกได้ว่ามีการปิดกั้นเขา",
   "report.categories.other": "อื่น ๆ",
   "report.categories.spam": "สแปม",
-  "report.categories.violation": "เนื้อหาละเมิดหนึ่งกฎของเซิร์ฟเวอร์หรือมากกว่า",
+  "report.categories.violation": "เนื้อหาละเมิดกฎของเซิร์ฟเวอร์จำนวนหนึ่งหรือมากกว่า",
   "report.category.subtitle": "เลือกที่ตรงกันที่สุด",
   "report.category.title": "บอกเราถึงสิ่งที่กำลังเกิดขึ้นกับ {type} นี้",
   "report.category.title_account": "โปรไฟล์",
@@ -506,7 +512,7 @@
   "report.submit": "ส่ง",
   "report.target": "กำลังรายงาน {target}",
   "report.thanks.take_action": "นี่คือตัวเลือกของคุณสำหรับการควบคุมสิ่งที่คุณเห็นใน Mastodon:",
-  "report.thanks.take_action_actionable": "ขณะที่เราตรวจทานสิ่งนี้ คุณสามารถใช้การกระทำกับ @{name}:",
+  "report.thanks.take_action_actionable": "ขณะที่เราตรวจทานสิ่งนี้ คุณสามารถใช้การกระทำต่อ @{name}:",
   "report.thanks.title": "ไม่ต้องการเห็นสิ่งนี้?",
   "report.thanks.title_actionable": "ขอบคุณสำหรับการรายงาน เราจะตรวจสอบสิ่งนี้",
   "report.unfollow": "เลิกติดตาม @{name}",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "สแปม",
   "report_notification.categories.violation": "การละเมิดกฎ",
   "report_notification.open": "รายงานที่เปิด",
+  "search.no_recent_searches": "ไม่มีการค้นหาล่าสุด",
   "search.placeholder": "ค้นหา",
+  "search.quick_action.account_search": "โปรไฟล์ที่ตรงกับ {x}",
+  "search.quick_action.go_to_account": "ไปยังโปรไฟล์ {x}",
+  "search.quick_action.go_to_hashtag": "ไปยังแฮชแท็ก {x}",
+  "search.quick_action.open_url": "เปิด URL ใน Mastodon",
+  "search.quick_action.status_search": "โพสต์ที่ตรงกับ {x}",
   "search.search_or_paste": "ค้นหาหรือวาง URL",
-  "search_popout.search_format": "รูปแบบการค้นหาขั้นสูง",
-  "search_popout.tips.full_text": "ข้อความแบบง่ายส่งคืนโพสต์ที่คุณได้เขียน ชื่นชอบ ดัน หรือได้รับการกล่าวถึง เช่นเดียวกับชื่อผู้ใช้, ชื่อที่แสดง และแฮชแท็กที่ตรงกัน",
-  "search_popout.tips.hashtag": "แฮชแท็ก",
-  "search_popout.tips.status": "โพสต์",
-  "search_popout.tips.text": "ข้อความแบบง่ายส่งคืนชื่อที่แสดง, ชื่อผู้ใช้ และแฮชแท็กที่ตรงกัน",
-  "search_popout.tips.user": "ผู้ใช้",
-  "search_results.accounts": "ผู้คน",
+  "search_popout.quick_actions": "การกระทำด่วน",
+  "search_popout.recent": "การค้นหาล่าสุด",
+  "search_results.accounts": "โปรไฟล์",
   "search_results.all": "ทั้งหมด",
   "search_results.hashtags": "แฮชแท็ก",
   "search_results.nothing_found": "ไม่พบสิ่งใดสำหรับคำค้นหาเหล่านี้",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "สถิติเซิร์ฟเวอร์:",
   "sign_in_banner.create_account": "สร้างบัญชี",
   "sign_in_banner.sign_in": "ลงชื่อเข้า",
-  "sign_in_banner.text": "ลงชื่อเข้าเพื่อติดตามโปรไฟล์หรือแฮชแท็ก ชื่นชอบ แบ่งปัน และตอบกลับโพสต์ หรือโต้ตอบจากบัญชีของคุณในเซิร์ฟเวอร์อื่น",
+  "sign_in_banner.text": "ลงชื่อเข้าเพื่อติดตามโปรไฟล์หรือแฮชแท็ก ชื่นชอบ แบ่งปัน และตอบกลับโพสต์ คุณยังสามารถโต้ตอบจากบัญชีของคุณในเซิร์ฟเวอร์อื่น",
   "status.admin_account": "เปิดส่วนติดต่อการควบคุมสำหรับ @{name}",
   "status.admin_domain": "เปิดส่วนติดต่อการควบคุมสำหรับ {domain}",
   "status.admin_status": "เปิดโพสต์นี้ในส่วนติดต่อการควบคุม",
@@ -551,7 +559,8 @@
   "status.copy": "คัดลอกลิงก์ไปยังโพสต์",
   "status.delete": "ลบ",
   "status.detailed_status": "มุมมองการสนทนาโดยละเอียด",
-  "status.direct": "ส่งข้อความโดยตรงถึง @{name}",
+  "status.direct": "กล่าวถึง @{name} แบบส่วนตัว",
+  "status.direct_indicator": "การกล่าวถึงแบบส่วนตัว",
   "status.edit": "แก้ไข",
   "status.edited": "แก้ไขเมื่อ {date}",
   "status.edited_x_times": "แก้ไข {count, plural, other {{count} ครั้ง}}",
@@ -622,10 +631,10 @@
   "upload_area.title": "ลากแล้วปล่อยเพื่ออัปโหลด",
   "upload_button.label": "เพิ่มไฟล์ภาพ, วิดีโอ หรือเสียง",
   "upload_error.limit": "เกินขีดจำกัดการอัปโหลดไฟล์",
-  "upload_error.poll": "ไม่อนุญาตให้อัปโหลดไฟล์กับการลงคะแนน",
+  "upload_error.poll": "ไม่อนุญาตการอัปโหลดไฟล์โดยมีการสำรวจความคิดเห็น",
   "upload_form.audio_description": "อธิบายสำหรับผู้ที่สูญเสียการได้ยิน",
   "upload_form.description": "อธิบายสำหรับผู้บกพร่องทางการมองเห็น",
-  "upload_form.description_missing": "ไม่มีการเพิ่มคำอธิบาย",
+  "upload_form.description_missing": "ไม่ได้เพิ่มคำอธิบาย",
   "upload_form.edit": "แก้ไข",
   "upload_form.thumbnail": "เปลี่ยนภาพขนาดย่อ",
   "upload_form.undo": "ลบ",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index 287ba5e63..a7b8cd916 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -20,7 +20,7 @@
   "account.blocked": "Engellendi",
   "account.browse_more_on_origin_server": "Orijinal profilde daha fazlasına göz atın",
   "account.cancel_follow_request": "Takip isteğini geri çek",
-  "account.direct": "@{name} adlı kişiye mesaj gönder",
+  "account.direct": "@{name} kullanıcısına özelden değin",
   "account.disable_notifications": "@{name} kişisinin gönderi bildirimlerini kapat",
   "account.domain_blocked": "Alan adı engellendi",
   "account.edit_profile": "Profili düzenle",
@@ -58,7 +58,7 @@
   "account.share": "@{name} adlı kişinin profilini paylaş",
   "account.show_reblogs": "@{name} kişisinin boostlarını göster",
   "account.statuses_counter": "{count, plural, one {{counter} Gönderi} other {{counter} Gönderi}}",
-  "account.unblock": "@{name}'in engelini kaldır",
+  "account.unblock": "@{name} adlı kişinin engelini kaldır",
   "account.unblock_domain": "{domain} alan adının engelini kaldır",
   "account.unblock_short": "Engeli kaldır",
   "account.unendorse": "Profilimde öne çıkarma",
@@ -72,8 +72,8 @@
   "admin.dashboard.retention.average": "Ortalama",
   "admin.dashboard.retention.cohort": "Kayıt ayı",
   "admin.dashboard.retention.cohort_size": "Yeni kullanıcılar",
-  "alert.rate_limited.message": "Lütfen {retry_time, time, medium} süresinden sonra tekrar deneyin.",
-  "alert.rate_limited.title": "Oran sınırlıdır",
+  "alert.rate_limited.message": "Lütfen {retry_time, time, medium} saatinden sonra tekrar deneyin.",
+  "alert.rate_limited.title": "Aşırı istek gönderildi",
   "alert.unexpected.message": "Beklenmedik bir hata oluştu.",
   "alert.unexpected.title": "Hay aksi!",
   "announcement.announcement": "Duyuru",
@@ -102,7 +102,7 @@
   "column.blocks": "Engellenen kullanıcılar",
   "column.bookmarks": "Yer İşaretleri",
   "column.community": "Yerel zaman tüneli",
-  "column.direct": "Doğrudan iletiler",
+  "column.direct": "Özel değinmeler",
   "column.directory": "Profillere göz at",
   "column.domain_blocks": "Engellenen alan adları",
   "column.favourites": "Favoriler",
@@ -128,7 +128,7 @@
   "compose.language.search": "Dilleri ara...",
   "compose_form.direct_message_warning_learn_more": "Daha fazla bilgi edinin",
   "compose_form.encryption_warning": "Mastodon gönderileri uçtan uca şifrelemeli değildir. Hassas olabilecek herhangi bir bilgiyi Mastodon'da paylaşmayın.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "Bu gönderi herkese açık olmadığı için hiç bir etikette yer almayacak. Sadece herkese açık gönderiler etiketlerde bulunabilir.",
   "compose_form.lock_disclaimer": "Hesabın {locked} değil. Yalnızca takipçilere özel gönderilerini görüntülemek için herkes seni takip edebilir.",
   "compose_form.lock_disclaimer.lock": "kilitli",
   "compose_form.placeholder": "Aklında ne var?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Medya açıklaması veya ön izlemede kaydedilmemiş değişiklikleriniz var, yine de vazgeçmek istiyor musunuz?",
   "confirmations.domain_block.confirm": "Alanın tamamını engelle",
   "confirmations.domain_block.message": "{domain} alanının tamamını engellemek istediğinden gerçekten emin misin? Genellikle hedeflenen birkaç engelleme veya sessize alma yeterlidir ve tercih edilir. Bu alan adından gelen içeriği herhangi bir genel zaman çizelgesinde veya bildirimlerinde görmezsin. Bu alan adındaki takipçilerin kaldırılır.",
+  "confirmations.edit.confirm": "Düzenle",
+  "confirmations.edit.message": "Şimdi düzenlersen şu an oluşturduğun iletinin üzerine yazılır. Devam etmek istediğine emin misin?",
   "confirmations.logout.confirm": "Oturumu kapat",
   "confirmations.logout.message": "Oturumu kapatmak istediğinden emin misin?",
   "confirmations.mute.confirm": "Sessize al",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Henüz herhangi bir kullanıcıyı engellemedin.",
   "empty_column.bookmarked_statuses": "Henüz yer imine eklediğin toot yok. Bir tanesi yer imine eklendiğinde burada görünür.",
   "empty_column.community": "Yerel zaman çizelgesi boş. Daha fazla eğlence için herkese açık bir gönderi paylaşın!",
-  "empty_column.direct": "Henüz doğrudan iletiniz yok. Bir tane gönderdiğinizde veya aldığınız burada listelenecekler.",
+  "empty_column.direct": "Henüz doğrudan değinmeniz yok. Bir tane gönderdiğinizde veya aldığınızda burada listelenecekler.",
   "empty_column.domain_blocks": "Henüz engellenmiş bir alan adı yok.",
   "empty_column.explore_statuses": "Şu an öne çıkan birşey yok. Daha sonra tekrar bakın!",
   "empty_column.favourited_statuses": "Favori tootun yok. Favori tootun olduğunda burada görünür.",
   "empty_column.favourites": "Kimse bu gönderiyi favorilerine eklememiş. Biri eklediğinde burada görünecek.",
   "empty_column.follow_recommendations": "Öyle görünüyor ki sizin için hiçbir öneri oluşturulamıyor. Tanıdığınız kişileri aramak için aramayı kullanabilir veya öne çıkanlara bakabilirsiniz.",
   "empty_column.follow_requests": "Hiç takip isteğiniz yok. Bir tane aldığınızda burada görünecek.",
+  "empty_column.followed_tags": "Henüz hiç bir etiket takip etmiyorsunuz. Takip ettiğiniz etiketler burada görüntülenecek.",
   "empty_column.hashtag": "Henüz bu etikete sahip hiçbir gönderi yok.",
   "empty_column.home": "Ana zaman tünelin boş! Akışını doldurmak için daha fazla kişiyi takip et. {suggestions}",
   "empty_column.home.suggestions": "Bazı önerileri görün",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "İzin Ver",
   "follow_request.reject": "Reddet",
   "follow_requests.unlocked_explanation": "Hesabınız kilitli olmasa bile, {domain} personeli bu hesaplardan gelen takip isteklerini gözden geçirmek isteyebileceğinizi düşündü.",
+  "followed_tags": "Takip edilen etiketler",
   "footer.about": "Hakkında",
   "footer.directory": "Profil dizini",
   "footer.get_app": "Uygulamayı indir",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Klavye kısayolları",
   "footer.privacy_policy": "Gizlilik politikası",
   "footer.source_code": "Kaynak kodu görüntüle",
+  "footer.status": "Durum",
   "generic.saved": "Kaydedildi",
   "getting_started.heading": "Başlarken",
   "hashtag.column_header.tag_mode.all": "ve {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "sütunlardan birindeki duruma odaklanmak için",
   "keyboard_shortcuts.compose": "yazma alanına odaklanmak için",
   "keyboard_shortcuts.description": "Açıklama",
-  "keyboard_shortcuts.direct": "doğrudan iletiler sütununu açmak için",
+  "keyboard_shortcuts.direct": "özel değinmeler sütununu açmak için",
   "keyboard_shortcuts.down": "listede aşağıya inmek için",
   "keyboard_shortcuts.enter": "gönderiyi aç",
   "keyboard_shortcuts.favourite": "Gönderiyi favorilerine ekle",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Yer İşaretleri",
   "navigation_bar.community_timeline": "Yerel Zaman Tüneli",
   "navigation_bar.compose": "Yeni gönderi yaz",
-  "navigation_bar.direct": "Doğrudan iletiler",
+  "navigation_bar.direct": "Özel değinmeler",
   "navigation_bar.discover": "Keşfet",
   "navigation_bar.domain_blocks": "Engellenen alan adları",
   "navigation_bar.edit_profile": "Profili düzenle",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favoriler",
   "navigation_bar.filters": "Sessize alınmış kelimeler",
   "navigation_bar.follow_requests": "Takip istekleri",
+  "navigation_bar.followed_tags": "Takip edilen etiketler",
   "navigation_bar.follows_and_followers": "Takip edilenler ve takipçiler",
   "navigation_bar.lists": "Listeler",
   "navigation_bar.logout": "Oturumu kapat",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "İstenmeyen",
   "report_notification.categories.violation": "Kural ihlali",
   "report_notification.open": "Bildirim aç",
+  "search.no_recent_searches": "Son arama yok",
   "search.placeholder": "Ara",
+  "search.quick_action.account_search": "Eşleşen profiller {x}",
+  "search.quick_action.go_to_account": "Profile git {x}",
+  "search.quick_action.go_to_hashtag": "Etikete git {x}",
+  "search.quick_action.open_url": "URL'yi Mastodon'da Aç",
+  "search.quick_action.status_search": "Eşleşen gönderiler {x}",
   "search.search_or_paste": "Ara veya URL gir",
-  "search_popout.search_format": "Gelişmiş arama biçimi",
-  "search_popout.tips.full_text": "Basit metin yazdığınız, beğendiğiniz, teşvik ettiğiniz veya söz edilen gönderilerin yanı sıra kullanıcı adlarını, görünen adları ve etiketleri eşleşen gönderileri de döndürür.",
-  "search_popout.tips.hashtag": "etiket",
-  "search_popout.tips.status": "gönderi",
-  "search_popout.tips.text": "Basit metin, eşleşen görünen adları, kullanıcı adlarını ve hashtag'leri döndürür",
-  "search_popout.tips.user": "kullanıcı",
-  "search_results.accounts": "İnsanlar",
+  "search_popout.quick_actions": "Hızlı eylemler",
+  "search_popout.recent": "Son aramalar",
+  "search_results.accounts": "Profiller",
   "search_results.all": "Tümü",
   "search_results.hashtags": "Etiketler",
   "search_results.nothing_found": "Bu arama seçenekleriyle bir sonuç bulunamadı",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Sunucu istatistikleri:",
   "sign_in_banner.create_account": "Hesap oluştur",
   "sign_in_banner.sign_in": "Giriş yap",
-  "sign_in_banner.text": "Profilleri veya etiketleri izlemek, gönderileri beğenmek, paylaşmak ve yanıtlamak için veya başka bir sunucunuzdaki hesabınızla etkileşmek için giriş yapın.",
+  "sign_in_banner.text": "Profilleri veya etiketleri izlemek, gönderileri beğenmek, paylaşmak ve yanıtlamak için giriş yapın. Başka bir sunucudaki hesabınızla da etkileşebilirsiniz.",
   "status.admin_account": "@{name} için denetim arayüzünü açın",
   "status.admin_domain": "{domain} için denetim arayüzünü açın",
   "status.admin_status": "Denetim arayüzünde bu gönderiyi açın",
@@ -551,7 +559,8 @@
   "status.copy": "Bağlantı durumunu kopyala",
   "status.delete": "Sil",
   "status.detailed_status": "Ayrıntılı sohbet görünümü",
-  "status.direct": "@{name} adlı kişiye direkt mesaj",
+  "status.direct": "@{name} kullanıcısına özelden değin",
+  "status.direct_indicator": "Özel değinme",
   "status.edit": "Düzenle",
   "status.edited": "{date} tarihinde düzenlenmiş",
   "status.edited_x_times": "{count, plural, one {{count} kez} other {{count} kez}} düzenlendi",
diff --git a/app/javascript/mastodon/locales/tt.json b/app/javascript/mastodon/locales/tt.json
index d0aaba7e9..64b0840a5 100644
--- a/app/javascript/mastodon/locales/tt.json
+++ b/app/javascript/mastodon/locales/tt.json
@@ -1,226 +1,229 @@
 {
-  "about.blocks": "Moderated servers",
-  "about.contact": "Contact:",
-  "about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
-  "about.domain_blocks.no_reason_available": "Reason not available",
-  "about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the fediverse. These are the exceptions that have been made on this particular server.",
-  "about.domain_blocks.silenced.explanation": "You will generally not see profiles and content from this server, unless you explicitly look it up or opt into it by following.",
-  "about.domain_blocks.silenced.title": "Limited",
-  "about.domain_blocks.suspended.explanation": "No data from this server will be processed, stored or exchanged, making any interaction or communication with users from this server impossible.",
-  "about.domain_blocks.suspended.title": "Suspended",
-  "about.not_available": "This information has not been made available on this server.",
-  "about.powered_by": "Decentralized social media powered by {mastodon}",
-  "about.rules": "Server rules",
+  "about.blocks": "Модератор серверлар",
+  "about.contact": "Бәйләнеш:",
+  "about.disclaimer": "Mastodon-бушлай ачык чыганак программасы һәм Mastodon gmbh сәүдә маркасы.",
+  "about.domain_blocks.no_reason_available": "Сәбәбе юк",
+  "about.domain_blocks.preamble": "Mastodon гадәттә сезгә бүтән fediverse серверыннан эчтәлекне карарга һәм аның белән кулланучылар белән аралашырга мөмкинлек бирә. Бу конкрет серверда ясалган искәрмәләр.",
+  "about.domain_blocks.silenced.explanation": "Гадәттә, сез бу серверның профильләрен һәм эчтәлеген күрмәячәксез, әгәр сез аларны ачыктан-ачык карамасагыз яки бу адымнарны үтәп язылмасагыз.",
+  "about.domain_blocks.silenced.title": "Чикле",
+  "about.domain_blocks.suspended.explanation": "Бу серверның бернинди мәгълүматлары да эшкәртелмәячәк, сакланмаячак яки алмаштырылмаячак, бу сервердан кулланучылар белән үзара бәйләнешне яки аралашуны мөмкин итми.",
+  "about.domain_blocks.suspended.title": "Блокланган",
+  "about.not_available": "Бу серверда бу мәгълүмат юк иде.",
+  "about.powered_by": "{mastodon} нигезендә үзәкчелеге бетерелгән социаль челтәр нигезендә",
+  "about.rules": "Сервер кагыйдәләре",
   "account.account_note_header": "Язма",
-  "account.add_or_remove_from_list": "Исемлеккә кертү я бетерү",
+  "account.add_or_remove_from_list": "Исемлеккә кушу яки бетерү",
   "account.badges.bot": "Бот",
   "account.badges.group": "Төркем",
-  "account.block": "@{name} блоклау",
+  "account.block": "@{name} кулланучыны блоклау",
   "account.block_domain": "{domain} доменын блоклау",
   "account.blocked": "Блокланган",
-  "account.browse_more_on_origin_server": "Тулырак оригинал профилендә карап була",
-  "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "@{name} өчен яңа хат",
+  "account.browse_more_on_origin_server": "Оригиналь профилендә күбрәк карау",
+  "account.cancel_follow_request": "Киләсе сорау",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "@{name} язулары өчен белдерүләр сүндерү",
   "account.domain_blocked": "Домен блокланган",
-  "account.edit_profile": "Профильны үзгәртү",
+  "account.edit_profile": "Профильне үзгәртү",
   "account.enable_notifications": "@{name} язулары өчен белдерүләр яндыру",
-  "account.endorse": "Профильдә рекомендацияләү",
-  "account.featured_tags.last_status_at": "Last post on {date}",
-  "account.featured_tags.last_status_never": "No posts",
-  "account.featured_tags.title": "{name}'s featured hashtags",
-  "account.follow": "Языл",
-  "account.followers": "Язылучылар",
+  "account.endorse": "Профильдә тәкъдим итү",
+  "account.featured_tags.last_status_at": "Соңгы хәбәр {date}",
+  "account.featured_tags.last_status_never": "Хәбәрләр юк",
+  "account.featured_tags.title": "{name} тәкъдим ителгән хэштеглар",
+  "account.follow": "Язылу",
+  "account.followers": "Язылучы",
   "account.followers.empty": "Әле беркем дә язылмаган.",
-  "account.followers_counter": "{count, plural,one {{counter} Язылучы} other {{counter} Язылучы}}",
-  "account.following": "Язылган",
-  "account.following_counter": "{count, plural, one {{counter} Язылган} other {{counter} Язылган}}",
+  "account.followers_counter": "{count, plural,one {{counter} язылучы} other {{counter} язылучы}}",
+  "account.following": "Язылулар",
+  "account.following_counter": "{count, plural, one {{counter} язылу} other {{counter} язылу}}",
   "account.follows.empty": "Беркемгә дә язылмаган әле.",
   "account.follows_you": "Сезгә язылган",
-  "account.go_to_profile": "Go to profile",
-  "account.hide_reblogs": "Hide boosts from @{name}",
-  "account.joined_short": "Joined",
-  "account.languages": "Change subscribed languages",
-  "account.link_verified_on": "Ownership of this link was checked on {date}",
+  "account.go_to_profile": "Профильгә күчү",
+  "account.hide_reblogs": "Скрывать көчен нче @{name}",
+  "account.joined_short": "Кушылды",
+  "account.languages": "Сайланган телләрен үзгәртү",
+  "account.link_verified_on": "Бу сылтамага милек хокукы тикшерелде {date}",
   "account.locked_info": "Бу - ябык аккаунт. Аны язылучылар гына күрә ала.",
   "account.media": "Медиа",
   "account.mention": "@{name} искәртү",
-  "account.moved_to": "{name} has indicated that their new account is now:",
-  "account.mute": "Mute @{name}",
-  "account.mute_notifications": "Mute notifications from @{name}",
-  "account.muted": "Muted",
-  "account.open_original_page": "Open original page",
-  "account.posts": "Toots",
-  "account.posts_with_replies": "Toots and replies",
-  "account.report": "Report @{name}",
+  "account.moved_to": "{name} аларның яңа счеты хәзер күрсәтте:",
+  "account.mute": "@{name} кулланучыга әһәмият бирмәү",
+  "account.mute_notifications": "@{name} кулланучыдан хәбәрләргә әһәмият бирмәү",
+  "account.muted": "Әһәмият бирмәнгән",
+  "account.open_original_page": "Чыганак битен ачу",
+  "account.posts": "Язма",
+  "account.posts_with_replies": "Язма һәм җавап",
+  "account.report": "@{name} кулланучыга шикаять итү",
   "account.requested": "Awaiting approval",
-  "account.requested_follow": "{name} has requested to follow you",
-  "account.share": "Share @{name}'s profile",
-  "account.show_reblogs": "Show boosts from @{name}",
-  "account.statuses_counter": "{count, plural, one {{counter} Toot} other {{counter} Toots}}",
-  "account.unblock": "Unblock @{name}",
-  "account.unblock_domain": "Unblock domain {domain}",
-  "account.unblock_short": "Unblock",
-  "account.unendorse": "Don't feature on profile",
-  "account.unfollow": "Язылынмау",
-  "account.unmute": "Unmute @{name}",
-  "account.unmute_notifications": "Unmute notifications from @{name}",
-  "account.unmute_short": "Unmute",
+  "account.requested_follow": "{name} Сезгә язылу соравын җиберде",
+  "account.share": "@{name} профиле белән уртаклашу",
+  "account.show_reblogs": "Күрсәтергә көчәйтү нче @{name}",
+  "account.statuses_counter": "{count, plural, one {{counter} язма} other {{counter} язма}}",
+  "account.unblock": "@{name} бикләвен чыгу",
+  "account.unblock_domain": "{domain} бикләвен чыгу",
+  "account.unblock_short": "Бикләүне чыгу",
+  "account.unendorse": "Профильдә тәкъдим итмәү",
+  "account.unfollow": "Язылуны туктату",
+  "account.unmute": "Kабызыгыз @{name}",
+  "account.unmute_notifications": "Кертергә хәбәрләр нче @{name}",
+  "account.unmute_short": "Kабызыгыз",
   "account_note.placeholder": "Click to add a note",
-  "admin.dashboard.daily_retention": "User retention rate by day after sign-up",
-  "admin.dashboard.monthly_retention": "User retention rate by month after sign-up",
-  "admin.dashboard.retention.average": "Average",
-  "admin.dashboard.retention.cohort": "Sign-up month",
-  "admin.dashboard.retention.cohort_size": "New users",
-  "alert.rate_limited.message": "Please retry after {retry_time, time, medium}.",
-  "alert.rate_limited.title": "Rate limited",
-  "alert.unexpected.message": "An unexpected error occurred.",
-  "alert.unexpected.title": "Ой!",
-  "announcement.announcement": "Announcement",
-  "attachments_list.unprocessed": "(unprocessed)",
-  "audio.hide": "Hide audio",
-  "autosuggest_hashtag.per_week": "{count} per week",
-  "boost_modal.combo": "You can press {combo} to skip this next time",
-  "bundle_column_error.copy_stacktrace": "Copy error report",
-  "bundle_column_error.error.body": "The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.",
-  "bundle_column_error.error.title": "Oh, no!",
-  "bundle_column_error.network.body": "There was an error when trying to load this page. This could be due to a temporary problem with your internet connection or this server.",
-  "bundle_column_error.network.title": "Network error",
-  "bundle_column_error.retry": "Try again",
-  "bundle_column_error.return": "Go back home",
-  "bundle_column_error.routing.body": "The requested page could not be found. Are you sure the URL in the address bar is correct?",
+  "admin.dashboard.daily_retention": "Теркәлгәннән соң икенче көнне кулланучыларны тоту коэффициенты",
+  "admin.dashboard.monthly_retention": "Теркәлгәннән соң ай саен кулланучыларны тоту күрсәткече",
+  "admin.dashboard.retention.average": "Урта",
+  "admin.dashboard.retention.cohort": "Теркәлү айлыгы",
+  "admin.dashboard.retention.cohort_size": "Яңа кулланучы",
+  "alert.rate_limited.message": "Зинһар, {retry_time, time, medium} соң кабатлап карагыз.",
+  "alert.rate_limited.title": "Тариф чикләнгән",
+  "alert.unexpected.message": "Көтелмәгән хата булды.",
+  "alert.unexpected.title": "Абау!",
+  "announcement.announcement": "Игълан",
+  "attachments_list.unprocessed": "(чимал)",
+  "audio.hide": "Аудионы яшерү",
+  "autosuggest_hashtag.per_week": "{count} атнага",
+  "boost_modal.combo": "Сез баса аласыз {combo} киләсе тапкыр моны сагыну өчен",
+  "bundle_column_error.copy_stacktrace": "Күчереп алу хата турында Отчет",
+  "bundle_column_error.error.body": "Соралган бит күрсәтелә алмый. Бу безнең кодтагы хата яки браузерга туры килү проблемасы аркасында булырга мөмкин.",
+  "bundle_column_error.error.title": "Әй, юк!",
+  "bundle_column_error.network.body": "Бу битне йөкләргә тырышканда хата булды. Бу сезнең Интернетка тоташу яки бу сервер белән вакытлыча проблема аркасында булырга мөмкин.",
+  "bundle_column_error.network.title": "Челтәр хатасы",
+  "bundle_column_error.retry": "Тагын сынап кара",
+  "bundle_column_error.return": "Өйгә кайтыгыз",
+  "bundle_column_error.routing.body": "Сорау бите табылмады. URL адресы дөрес күрсәтелгәненә ышанасызмы?",
   "bundle_column_error.routing.title": "404",
   "bundle_modal_error.close": "Ябу",
-  "bundle_modal_error.message": "Something went wrong while loading this component.",
-  "bundle_modal_error.retry": "Try again",
-  "closed_registrations.other_server_instructions": "Since Mastodon is decentralized, you can create an account on another server and still interact with this one.",
-  "closed_registrations_modal.description": "Creating an account on {domain} is currently not possible, but please keep in mind that you do not need an account specifically on {domain} to use Mastodon.",
-  "closed_registrations_modal.find_another_server": "Find another server",
-  "closed_registrations_modal.preamble": "Mastodon is decentralized, so no matter where you create your account, you will be able to follow and interact with anyone on this server. You can even self-host it!",
-  "closed_registrations_modal.title": "Signing up on Mastodon",
-  "column.about": "About",
-  "column.blocks": "Blocked users",
+  "bundle_modal_error.message": "Бу компонентны Йөкләгәндә нәрсәдер дөрес булмаган.",
+  "bundle_modal_error.retry": "Тагын сынап кара",
+  "closed_registrations.other_server_instructions": "Mastodon үзәкләштерелмәгәнгә, сез бүтән серверда хисап язмасы булдыра аласыз һәм аның белән аралаша аласыз.",
+  "closed_registrations_modal.description": "Хисап язмасы булдыру {domain} бу хәзерге вакытта мөмкин түгел, ләкин зинһар, онытмагыз, сезгә махсус хисап кирәк түгел {domain} Мастодонны куллану өчен.",
+  "closed_registrations_modal.find_another_server": "Башка серверны табыгыз",
+  "closed_registrations_modal.preamble": "Mastodon үзәкләштерелмәгән, шуңа күрә сез счетыгызны кайда гына ясасагыз да, сез бу серверда теләсә кемгә иярә аласыз һәм аның белән аралаша аласыз. Сез аны хәтта үзегез урнаштыра аласыз!",
+  "closed_registrations_modal.title": "Mastodon өчен теркәлү",
+  "column.about": "Проект турында",
+  "column.blocks": "Блокланган кулланучылар",
   "column.bookmarks": "Кыстыргычлар",
-  "column.community": "Local timeline",
-  "column.direct": "Direct messages",
-  "column.directory": "Browse profiles",
-  "column.domain_blocks": "Blocked domains",
-  "column.favourites": "Favourites",
-  "column.follow_requests": "Follow requests",
+  "column.community": "Локаль вакыт сызыгы",
+  "column.direct": "Private mentions",
+  "column.directory": "Профильләрне карау",
+  "column.domain_blocks": "Блокланган доменнар",
+  "column.favourites": "Сайланма",
+  "column.follow_requests": "Язылу сораулары",
   "column.home": "Баш бит",
-  "column.lists": "Lists",
-  "column.mutes": "Muted users",
-  "column.notifications": "Notifications",
+  "column.lists": "Исемлек",
+  "column.mutes": "Инвалид кулланучылар",
+  "column.notifications": "Хәбәрләр",
   "column.pins": "Pinned toot",
-  "column.public": "Federated timeline",
-  "column_back_button.label": "Кире",
-  "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.public": "Глобаль вакыт сызыгы",
+  "column_back_button.label": "Артка",
+  "column_header.hide_settings": "Көйләүләрне яшерү",
+  "column_header.moveLeft_settings": "Багананы сулга күчерегез",
+  "column_header.moveRight_settings": "Багананы уңга күчерегез",
+  "column_header.pin": "Пин",
+  "column_header.show_settings": "Көйләүләрне күрсәтү",
+  "column_header.unpin": "Төзәтү түгел",
   "column_subheading.settings": "Көйләүләр",
-  "community.column_settings.local_only": "Local only",
+  "community.column_settings.local_only": "Җирле генә",
   "community.column_settings.media_only": "Media only",
-  "community.column_settings.remote_only": "Remote only",
-  "compose.language.change": "Change language",
-  "compose.language.search": "Search languages...",
-  "compose_form.direct_message_warning_learn_more": "Learn more",
+  "community.column_settings.remote_only": "Дистанцион гына идарә итү",
+  "compose.language.change": "Телне үзгәртү",
+  "compose.language.search": "Телләр эзләве...",
+  "compose_form.direct_message_warning_learn_more": "Күбрәк белү",
   "compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts 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.hashtag_warning": "Бу язма бернинди хэштег астында да күрсәтелмәячәк, чөнки ул ачык түгел. Хэштег эзләү җәмәгать басмалары аша гына мөмкин.",
+  "compose_form.lock_disclaimer": "Сезнең хисап түгел {locked}. Апуәрбер теләгән кеше сезнең язма өчен иярә ала.",
+  "compose_form.lock_disclaimer.lock": "бикле",
   "compose_form.placeholder": "What is on your mind?",
-  "compose_form.poll.add_option": "Add a choice",
-  "compose_form.poll.duration": "Poll duration",
-  "compose_form.poll.option_placeholder": "Choice {number}",
-  "compose_form.poll.remove_option": "Remove this choice",
-  "compose_form.poll.switch_to_multiple": "Change poll to allow multiple choices",
-  "compose_form.poll.switch_to_single": "Change poll to allow for a single choice",
-  "compose_form.publish": "Publish",
-  "compose_form.publish_form": "Publish",
+  "compose_form.poll.add_option": "Сайлау өстәгез",
+  "compose_form.poll.duration": "Сораштыру озынлыгы",
+  "compose_form.poll.option_placeholder": "Сайлау {number}",
+  "compose_form.poll.remove_option": "бетерү",
+  "compose_form.poll.switch_to_multiple": "Берничә вариантны чишү өчен сораштыруны Үзгәртегез",
+  "compose_form.poll.switch_to_single": "Бердәнбер сайлау өчен сораштыруны Үзгәртегез",
+  "compose_form.publish": "Бастыру",
+  "compose_form.publish_form": "Бастыру",
   "compose_form.publish_loud": "{publish}!",
-  "compose_form.save_changes": "Save changes",
-  "compose_form.sensitive.hide": "{count, plural, one {Mark media as sensitive} other {Mark media as sensitive}}",
-  "compose_form.sensitive.marked": "{count, plural, one {Media is marked as sensitive} other {Media is marked as sensitive}}",
-  "compose_form.sensitive.unmarked": "{count, plural, one {Media is not marked as sensitive} other {Media is not marked as sensitive}}",
+  "compose_form.save_changes": "Үзгәрешләрне саклагыз",
+  "compose_form.sensitive.hide": "{count, plural, one {Медианы сизгер итеп билгеләгез} other {Медианы сизгер итеп билгеләгез}}",
+  "compose_form.sensitive.marked": "{count, plural, one {Ташучы сизгер дип язылган} other {Ташучы сизгер дип язылган}}",
+  "compose_form.sensitive.unmarked": "{count, plural, one {Медиа сизгер буларак билгеле түгел} other {Медиа сизгер буларак билгеле түгел}}",
   "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",
+  "compose_form.spoiler_placeholder": "Кисәтүегезне монда языгыз",
   "confirmation_modal.cancel": "Баш тарту",
-  "confirmations.block.block_and_report": "Block & Report",
+  "confirmations.block.block_and_report": "Блоклау һәм шикаять итү",
   "confirmations.block.confirm": "Блоклау",
-  "confirmations.block.message": "Are you sure you want to block {name}?",
-  "confirmations.cancel_follow_request.confirm": "Withdraw request",
-  "confirmations.cancel_follow_request.message": "Are you sure you want to withdraw your request to follow {name}?",
+  "confirmations.block.message": "Сез {name} кулланучыны блокларга телисезме?",
+  "confirmations.cancel_follow_request.confirm": "Сорауны баш тарту",
+  "confirmations.cancel_follow_request.message": "Сез абонемент соравыгызны кире кайтарырга телисез {name}?",
   "confirmations.delete.confirm": "Бетерү",
   "confirmations.delete.message": "Are you sure you want to delete this status?",
   "confirmations.delete_list.confirm": "Бетерү",
-  "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?",
-  "confirmations.discard_edit_media.confirm": "Discard",
-  "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
+  "confirmations.delete_list.message": "Сез бу исемлекне мәңгегә бетерергә телисезме?",
+  "confirmations.discard_edit_media.confirm": "Баш тарту",
+  "confirmations.discard_edit_media.message": "Сезнең медиа тасвирламасында яки алдан карау өчен сакланмаган үзгәрешләр бармы? ",
   "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.domain_block.message": "Сез чыннан да барысын да блокларга телисез {domain}? Күпчелек очракта берничә максатлы блоклар яки тавышсызлар җитәрлек һәм өстенлекле. Сез бу доменнан эчтәлекне җәмәгать срокларында яки хәбәрләрегездә күрмәячәксез. Бу доменнан сезнең шәкертләр бетереләчәк.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Чыгу",
-  "confirmations.logout.message": "Are you sure you want to log out?",
+  "confirmations.logout.message": "Сез чыгарга телисезме?",
   "confirmations.mute.confirm": "Тавышсыз",
-  "confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
-  "confirmations.mute.message": "Are you sure you want to mute {name}?",
-  "confirmations.redraft.confirm": "Delete & redraft",
+  "confirmations.mute.explanation": "Бу алардан ураза тотуны һәм алар турында искә алуны яшерәчәк, ләкин бу аларга уразаларыгызны күрергә һәм язылырга мөмкинлек бирәчәк.",
+  "confirmations.mute.message": "Сез тавышны сүндерергә телисез {name}?",
+  "confirmations.redraft.confirm": "Бетерү & эшкәртү",
   "confirmations.redraft.message": "Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
-  "confirmations.reply.confirm": "Reply",
-  "confirmations.reply.message": "Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?",
-  "confirmations.unfollow.confirm": "Язылынмау",
-  "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?",
-  "conversation.delete": "Delete conversation",
-  "conversation.mark_as_read": "Mark as read",
-  "conversation.open": "View conversation",
-  "conversation.with": "With {names}",
-  "copypaste.copied": "Copied",
-  "copypaste.copy": "Copy",
-  "directory.federated": "From known fediverse",
-  "directory.local": "From {domain} only",
-  "directory.new_arrivals": "New arrivals",
-  "directory.recently_active": "Recently active",
-  "disabled_account_banner.account_settings": "Account settings",
-  "disabled_account_banner.text": "Your account {disabledAccount} is currently disabled.",
-  "dismissable_banner.community_timeline": "These are the most recent public posts from people whose accounts are hosted by {domain}.",
-  "dismissable_banner.dismiss": "Dismiss",
-  "dismissable_banner.explore_links": "These news stories are being talked about by people on this and other servers of the decentralized network right now.",
-  "dismissable_banner.explore_statuses": "These posts from this and other servers in the decentralized network are gaining traction on this server right now.",
-  "dismissable_banner.explore_tags": "These hashtags are gaining traction among people on this and other servers of the decentralized network right now.",
-  "dismissable_banner.public_timeline": "These are the most recent public posts from people on this and other servers of the decentralized network that this server knows about.",
+  "confirmations.reply.confirm": "Җавап бирү",
+  "confirmations.reply.message": "Тһеавап хәзер сез ясаган хәбәрне яңадан язуга китерәчәк. Сез дәвам итәсегез киләме?",
+  "confirmations.unfollow.confirm": "Язылуны туктату",
+  "confirmations.unfollow.message": "Сез язылудан баш тартырга телисез {name}?",
+  "conversation.delete": "Сөйләшүне бетерегез",
+  "conversation.mark_as_read": "Укылганны Ничек билгеләргә",
+  "conversation.open": "Сөйләшүне карау",
+  "conversation.with": "{names} белән",
+  "copypaste.copied": "Күчереп алынган",
+  "copypaste.copy": "Күчереп алу",
+  "directory.federated": "Билгеле галәмнән",
+  "directory.local": "Кемнән {domain} гына",
+  "directory.new_arrivals": "Яңа килүчеләр",
+  "directory.recently_active": "Күптән түгел актив",
+  "disabled_account_banner.account_settings": "Хисап көйләүләре",
+  "disabled_account_banner.text": "Сезнең хисап {disabledAccount} хәзерге вакытта инвалид.",
+  "dismissable_banner.community_timeline": "Бу счетлары урнаштырылган кешеләрдән иң соңгы җәмәгать хәбәрләре {domain}.",
+  "dismissable_banner.dismiss": "Ябу",
+  "dismissable_banner.explore_links": "Бу яңалыклар турында хәзерге вакытта кешеләр һәм башка үзәкләштерелмәгән челтәр серверларында сөйләшәләр.",
+  "dismissable_banner.explore_statuses": "Бу һәм бүтән серверларның үзәкләштерелмәгән челтәрдәге бу язмалары хәзерге вакытта бу серверда тартыла.",
+  "dismissable_banner.explore_tags": "Бу хэштеглар хәзерге вакытта үзәкләштерелмәгән челтәрнең бүтән серверларында кешеләр арасында кызыксыну уята.",
+  "dismissable_banner.public_timeline": "Бу сервер бу һәм башка үзәкләштерелмәгән челтәр серверларында кешеләрдән иң соңгы хәбәрләр.",
   "embed.instructions": "Embed this status on your website by copying the code below.",
-  "embed.preview": "Here is what it will look like:",
+  "embed.preview": "Менә ул нинди булыр:",
   "emoji_button.activity": "Активлык",
-  "emoji_button.clear": "Clear",
+  "emoji_button.clear": "Чистарту",
   "emoji_button.custom": "Куелган",
   "emoji_button.flags": "Байраклар",
-  "emoji_button.food": "Food & Drink",
-  "emoji_button.label": "Insert emoji",
-  "emoji_button.nature": "Nature",
-  "emoji_button.not_found": "No matching emojis found",
-  "emoji_button.objects": "Objects",
+  "emoji_button.food": "Ашау һәм эчү",
+  "emoji_button.label": "Emoji кертү",
+  "emoji_button.nature": "Табигать",
+  "emoji_button.not_found": "Дөрес эможалар табылмады",
+  "emoji_button.objects": "Объектлар",
   "emoji_button.people": "Кешеләр",
-  "emoji_button.recent": "Frequently used",
+  "emoji_button.recent": "Еш кулланыла",
   "emoji_button.search": "Эзләү...",
-  "emoji_button.search_results": "Search results",
-  "emoji_button.symbols": "Symbols",
-  "emoji_button.travel": "Travel & Places",
-  "empty_column.account_suspended": "Account suspended",
+  "emoji_button.search_results": "Эзләү нәтиҗәләре",
+  "emoji_button.symbols": "Символлар",
+  "emoji_button.travel": "Сәяхәт һәм урыннар",
+  "empty_column.account_suspended": "Аккаунт блокланган",
   "empty_column.account_timeline": "No toots here!",
   "empty_column.account_unavailable": "Profile unavailable",
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite 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_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -234,11 +237,11 @@
   "error.unexpected_crash.next_steps": "Try refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
   "error.unexpected_crash.next_steps_addons": "Try disabling them and refreshing the page. If that does not help, you may still be able to use Mastodon through a different browser or native app.",
   "errors.unexpected_crash.copy_stacktrace": "Copy stacktrace to clipboard",
-  "errors.unexpected_crash.report_issue": "Report issue",
+  "errors.unexpected_crash.report_issue": "Хата турында белдерү",
   "explore.search_results": "Search results",
-  "explore.suggested_follows": "For you",
+  "explore.suggested_follows": "Сез өчен",
   "explore.title": "Explore",
-  "explore.trending_links": "News",
+  "explore.trending_links": "Яңалыклар",
   "explore.trending_statuses": "Posts",
   "explore.trending_tags": "Hashtags",
   "filter_modal.added.context_mismatch_explanation": "This filter category does not apply to the context in which you have accessed this post. If you want the post to be filtered in this context too, you will have to edit the filter.",
@@ -257,52 +260,54 @@
   "filter_modal.select_filter.subtitle": "Use an existing category or create a new one",
   "filter_modal.select_filter.title": "Filter this post",
   "filter_modal.title.status": "Filter a post",
-  "follow_recommendations.done": "Done",
+  "follow_recommendations.done": "Булды",
   "follow_recommendations.heading": "Follow people you'd like to see posts from! Here are some suggestions.",
   "follow_recommendations.lead": "Posts from people you follow will show up in chronological order on your home feed. Don't be afraid to make mistakes, you can unfollow people just as easily any time!",
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
-  "footer.about": "About",
-  "footer.directory": "Profiles directory",
-  "footer.get_app": "Get the app",
-  "footer.invite": "Invite people",
-  "footer.keyboard_shortcuts": "Keyboard shortcuts",
-  "footer.privacy_policy": "Privacy policy",
-  "footer.source_code": "View source code",
+  "followed_tags": "Имзаланган хэштеглар",
+  "footer.about": "Проект турында",
+  "footer.directory": "Профильләр каталогы",
+  "footer.get_app": "Кушымта алыгыз",
+  "footer.invite": "Кешеләрне чакырыгыз",
+  "footer.keyboard_shortcuts": "Клавиатура кыска юллары",
+  "footer.privacy_policy": "Хосусыйлык сәясәте",
+  "footer.source_code": "Чыганак кодын карау",
+  "footer.status": "Status",
   "generic.saved": "Сакланды",
-  "getting_started.heading": "Getting started",
-  "hashtag.column_header.tag_mode.all": "and {additional}",
-  "hashtag.column_header.tag_mode.any": "or {additional}",
-  "hashtag.column_header.tag_mode.none": "without {additional}",
-  "hashtag.column_settings.select.no_options_message": "No suggestions found",
-  "hashtag.column_settings.select.placeholder": "Enter hashtags…",
-  "hashtag.column_settings.tag_mode.all": "All of these",
-  "hashtag.column_settings.tag_mode.any": "Any of these",
-  "hashtag.column_settings.tag_mode.none": "None of these",
+  "getting_started.heading": "Эшкә урнашу",
+  "hashtag.column_header.tag_mode.all": "һәм {additional}",
+  "hashtag.column_header.tag_mode.any": "яки {additional}",
+  "hashtag.column_header.tag_mode.none": "гайре {additional}",
+  "hashtag.column_settings.select.no_options_message": "Тәкъдимнәр табылмады",
+  "hashtag.column_settings.select.placeholder": "Хэштегларны кертегез…",
+  "hashtag.column_settings.tag_mode.all": "Боларның барысы",
+  "hashtag.column_settings.tag_mode.any": "Һәрберсе",
+  "hashtag.column_settings.tag_mode.none": "Аларның берсе дә",
   "hashtag.column_settings.tag_toggle": "Include additional tags in this column",
-  "hashtag.follow": "Follow hashtag",
-  "hashtag.unfollow": "Unfollow hashtag",
-  "home.column_settings.basic": "Basic",
-  "home.column_settings.show_reblogs": "Show boosts",
-  "home.column_settings.show_replies": "Show replies",
-  "home.hide_announcements": "Hide announcements",
-  "home.show_announcements": "Show announcements",
-  "interaction_modal.description.favourite": "With an account on Mastodon, you can favourite this post to let the author know you appreciate it and save it for later.",
-  "interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.",
-  "interaction_modal.description.reblog": "With an account on Mastodon, you can boost this post to share it with your own followers.",
-  "interaction_modal.description.reply": "With an account on Mastodon, you can respond to this post.",
-  "interaction_modal.on_another_server": "On a different server",
-  "interaction_modal.on_this_server": "On this server",
-  "interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favourite Mastodon app or the web interface of your Mastodon server.",
-  "interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.",
-  "interaction_modal.title.favourite": "Favourite {name}'s post",
-  "interaction_modal.title.follow": "Follow {name}",
-  "interaction_modal.title.reblog": "Boost {name}'s post",
-  "interaction_modal.title.reply": "Reply to {name}'s post",
-  "intervals.full.days": "{number, plural, one {# day} other {# days}}",
-  "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
-  "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
+  "hashtag.follow": "Хэштегка язылу",
+  "hashtag.unfollow": "Хэштегка язылу юк",
+  "home.column_settings.basic": "Төп",
+  "home.column_settings.show_reblogs": "Табышмаклау",
+  "home.column_settings.show_replies": "Җаваплар күрсәтү",
+  "home.hide_announcements": "Игъланнарны яшерү",
+  "home.show_announcements": "Белдерүләр бирегез",
+  "interaction_modal.description.favourite": "Була торып, аккаунт бу Mastodon, сез куярга, бу постны выбранное авторы белсен өчен, сез цените аны, һәм Саклап калу соңрак.",
+  "interaction_modal.description.follow": "Mastodon аккаунты белән сез иярә аласыз {name} аларның язмаларын өй тасмасында алу өчен.",
+  "interaction_modal.description.reblog": "Mastodon аккаунты ярдәмендә сез бу язманы үз шәкертләрегез белән уртаклашу өчен арттыра аласыз.",
+  "interaction_modal.description.reply": "Mastodon аккаунты белән сез бу язмага җавап бирә аласыз.",
+  "interaction_modal.on_another_server": "Башка серверда",
+  "interaction_modal.on_this_server": "Бу серверда",
+  "interaction_modal.other_server_instructions": "Бу URL-ны яраткан Mastodon кушымтасының эзләү тартмасына яки Mastodon серверының веб-интерфейсына күчереп языгыз.",
+  "interaction_modal.preamble": "Mastodon үзәкләштерелмәгәнгә, Сез үзегезнең Mastodon серверына урнаштырылган счетыгызны яки бу серверда счетыгыз булмаса, платформага туры килгән платформаны куллана аласыз.",
+  "interaction_modal.title.favourite": "Яраткан {name} сак",
+  "interaction_modal.title.follow": "Иярү {name}",
+  "interaction_modal.title.reblog": "Арттыру {name} сак",
+  "interaction_modal.title.reply": "Җавап {name} сак",
+  "intervals.full.days": "{number, plural, one {# көн} other {# көн}}",
+  "intervals.full.hours": "{number, plural, one {# сәгать} other {# сәгать}}",
+  "intervals.full.minutes": "{number, plural, one {# минут} other {# минут}}",
   "keyboard_shortcuts.back": "to navigate back",
   "keyboard_shortcuts.blocked": "to open blocked users list",
   "keyboard_shortcuts.boost": "to boost",
@@ -317,7 +322,7 @@
   "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.hotkey": "Кайнар Ачкыч",
   "keyboard_shortcuts.legend": "to display this legend",
   "keyboard_shortcuts.local": "to open local timeline",
   "keyboard_shortcuts.mention": "to mention author",
@@ -338,18 +343,18 @@
   "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
   "keyboard_shortcuts.up": "to move up in the list",
   "lightbox.close": "Ябу",
-  "lightbox.compress": "Compress image view box",
-  "lightbox.expand": "Expand image view box",
+  "lightbox.compress": "Кысылган рәсемне карау тәрәзәсе",
+  "lightbox.expand": "Рәсемне карау тәрәзәсен ачыгыз",
   "lightbox.next": "Киләсе",
   "lightbox.previous": "Алдагы",
-  "limited_account_hint.action": "Show profile anyway",
-  "limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
-  "lists.account.add": "Add to list",
+  "limited_account_hint.action": "Барыбер профильне күрсәтергә",
+  "limited_account_hint.title": "Бу профильне модераторлар яшергән {domain}.",
+  "lists.account.add": "Исемлеккә өстәргә",
   "lists.account.remove": "Исемлектән бетерергә",
-  "lists.delete": "Delete list",
-  "lists.edit": "Edit list",
-  "lists.edit.submit": "Change title",
-  "lists.new.create": "Add list",
+  "lists.delete": "Исемлекне бетерегез",
+  "lists.edit": "Исемлекне үзгәртү",
+  "lists.edit.submit": "Исемен үзгәртү",
+  "lists.new.create": "Исемлек өстәгез",
   "lists.new.title_placeholder": "New list title",
   "lists.replies_policy.followed": "Any followed user",
   "lists.replies_policy.list": "Members of the list",
@@ -358,20 +363,20 @@
   "lists.search": "Search among people you follow",
   "lists.subheading": "Your lists",
   "load_pending": "{count, plural, one {# new item} other {# new items}}",
-  "loading_indicator.label": "Loading...",
+  "loading_indicator.label": "Йөкләү...",
   "media_gallery.toggle_visible": "{number, plural, one {Hide image} other {Hide images}}",
-  "missing_indicator.label": "Not found",
+  "missing_indicator.label": "Табылмады",
   "missing_indicator.sublabel": "This resource could not be found",
   "moved_to_account_banner.text": "Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.",
   "mute_modal.duration": "Дәвамлык",
   "mute_modal.hide_notifications": "Hide notifications from this user?",
   "mute_modal.indefinite": "Indefinite",
-  "navigation_bar.about": "About",
-  "navigation_bar.blocks": "Blocked users",
+  "navigation_bar.about": "Проект турында",
+  "navigation_bar.blocks": "Блокланган кулланучылар",
   "navigation_bar.bookmarks": "Кыстыргычлар",
-  "navigation_bar.community_timeline": "Local timeline",
+  "navigation_bar.community_timeline": "Локаль вакыт сызыгы",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Профильны үзгәртү",
@@ -379,15 +384,16 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
-  "navigation_bar.lists": "Lists",
-  "navigation_bar.logout": "Logout",
+  "navigation_bar.lists": "Исемлекләр",
+  "navigation_bar.logout": "Чыгу",
   "navigation_bar.mutes": "Muted users",
   "navigation_bar.personal": "Personal",
   "navigation_bar.pins": "Pinned toots",
   "navigation_bar.preferences": "Caylaw",
   "navigation_bar.public_timeline": "Federated timeline",
-  "navigation_bar.search": "Search",
+  "navigation_bar.search": "Эзләү",
   "navigation_bar.security": "Хәвефсезлек",
   "not_signed_in_indicator.not_signed_in": "You need to sign in to access this resource.",
   "notification.admin.report": "{name} reported {target}",
@@ -443,7 +449,7 @@
   "poll.refresh": "Яңарту",
   "poll.total_people": "{count, plural, one {# person} other {# people}}",
   "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
-  "poll.vote": "Vote",
+  "poll.vote": "Тавыш бирү",
   "poll.voted": "You voted for this answer",
   "poll.votes": "{votes, plural, one {# vote} other {# votes}}",
   "poll_button.add_poll": "Add a poll",
@@ -474,7 +480,7 @@
   "relative_time.seconds": "{number}сек",
   "relative_time.today": "бүген",
   "reply_indicator.cancel": "Баш тарту",
-  "report.block": "Block",
+  "report.block": "Блоклау",
   "report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.",
   "report.categories.other": "Other",
   "report.categories.spam": "Spam",
@@ -483,7 +489,7 @@
   "report.category.title": "Tell us what's going on with this {type}",
   "report.category.title_account": "профиль",
   "report.category.title_status": "post",
-  "report.close": "Done",
+  "report.close": "Булды",
   "report.comment.title": "Is there anything else you think we should know?",
   "report.forward": "Forward to {target}",
   "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
@@ -491,7 +497,7 @@
   "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.",
   "report.next": "Next",
   "report.placeholder": "Type or paste additional comments",
-  "report.reasons.dislike": "I don't like it",
+  "report.reasons.dislike": "Миңа бу ошамый",
   "report.reasons.dislike_description": "It is not something you want to see",
   "report.reasons.other": "It's something else",
   "report.reasons.other_description": "The issue does not fit into other categories",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Эзләү",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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": "кулланучы",
-  "search_results.accounts": "Кешеләр",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -536,11 +544,11 @@
   "server_banner.active_users": "active users",
   "server_banner.administered_by": "Administered by:",
   "server_banner.introduction": "{domain} is part of the decentralized social network powered by {mastodon}.",
-  "server_banner.learn_more": "Learn more",
+  "server_banner.learn_more": "Күбрәк белү",
   "server_banner.server_stats": "Server stats:",
-  "sign_in_banner.create_account": "Create account",
-  "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.create_account": "Аккаунтны ясау",
+  "sign_in_banner.sign_in": "Керү",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,8 +559,9 @@
   "status.copy": "Copy link to status",
   "status.delete": "Бетерү",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
-  "status.edit": "Edit",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
+  "status.edit": "Үзгәртү",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
   "status.embed": "Embed",
@@ -579,7 +588,7 @@
   "status.redraft": "Delete & re-draft",
   "status.remove_bookmark": "Remove bookmark",
   "status.replied_to": "Replied to {name}",
-  "status.reply": "Reply",
+  "status.reply": "Җавап бирү",
   "status.replyAll": "Reply to thread",
   "status.report": "Report @{name}",
   "status.sensitive_warning": "Sensitive content",
@@ -590,7 +599,7 @@
   "status.show_more": "Күбрәк күрсәтү",
   "status.show_more_all": "Show more for all",
   "status.show_original": "Show original",
-  "status.translate": "Translate",
+  "status.translate": "Тәрҗемә итү",
   "status.translated_from_with": "Translated from {lang} using {provider}",
   "status.uncached_media_warning": "Not available",
   "status.unmute_conversation": "Unmute conversation",
@@ -632,7 +641,7 @@
   "upload_form.video_description": "Describe for people with hearing loss or visual impairment",
   "upload_modal.analyzing_picture": "Analyzing picture…",
   "upload_modal.apply": "Куллан",
-  "upload_modal.applying": "Applying…",
+  "upload_modal.applying": "Куллану…",
   "upload_modal.choose_image": "Choose image",
   "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
   "upload_modal.detect_text": "Detect text from picture",
diff --git a/app/javascript/mastodon/locales/ug.json b/app/javascript/mastodon/locales/ug.json
index e646ef83c..d330c136c 100644
--- a/app/javascript/mastodon/locales/ug.json
+++ b/app/javascript/mastodon/locales/ug.json
@@ -20,7 +20,7 @@
   "account.blocked": "Blocked",
   "account.browse_more_on_origin_server": "Browse more on the original profile",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "Direct message @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "Domain blocked",
   "account.edit_profile": "Edit profile",
@@ -102,7 +102,7 @@
   "column.blocks": "Blocked users",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "Favourites",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "Log out",
   "confirmations.logout.message": "Are you sure you want to log out?",
   "confirmations.mute.confirm": "Mute",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite 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_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "Reject",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "and {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "Edit profile",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "Lists",
   "navigation_bar.logout": "Logout",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json
index 8276908b1..5278f845f 100644
--- a/app/javascript/mastodon/locales/uk.json
+++ b/app/javascript/mastodon/locales/uk.json
@@ -20,7 +20,7 @@
   "account.blocked": "Заблоковані",
   "account.browse_more_on_origin_server": "Переглянути більше в оригінальному профілі",
   "account.cancel_follow_request": "Відкликати запит на стеження",
-  "account.direct": "Надіслати пряме повідомлення @{name}",
+  "account.direct": "Особиста згадка @{name}",
   "account.disable_notifications": "Не повідомляти мене про дописи @{name}",
   "account.domain_blocked": "Домен заблоковано",
   "account.edit_profile": "Редагувати профіль",
@@ -102,7 +102,7 @@
   "column.blocks": "Заблоковані користувачі",
   "column.bookmarks": "Закладки",
   "column.community": "Локальна стрічка",
-  "column.direct": "Особисті повідомлення",
+  "column.direct": "Особисті згадки",
   "column.directory": "Переглянути профілі",
   "column.domain_blocks": "Заблоковані домени",
   "column.favourites": "Вподобане",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "У вас є незбережені зміни в описі медіа або попереднього перегляду, все одно відкинути їх?",
   "confirmations.domain_block.confirm": "Заблокувати весь домен",
   "confirmations.domain_block.message": "Ви точно, точно впевнені, що хочете заблокувати весь домен {domain}? У більшості випадків для нормальної роботи краще заблокувати або приховати лише деяких користувачів. Ви не зможете бачити контент з цього домену у будь-яких стрічках або ваших сповіщеннях. Ваші підписники з цього домену будуть відписані від вас.",
+  "confirmations.edit.confirm": "Змінити",
+  "confirmations.edit.message": "Редагування перезапише повідомлення, яке ви зараз пишете. Ви впевнені, що хочете продовжити?",
   "confirmations.logout.confirm": "Вийти",
   "confirmations.logout.message": "Ви впевнені, що хочете вийти?",
   "confirmations.mute.confirm": "Приховати",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "Ви ще не заблокували жодного користувача.",
   "empty_column.bookmarked_statuses": "У вас ще немає дописів у закладках. Коли ви щось додасте до закладок, воно з'явиться тут.",
   "empty_column.community": "Локальна стрічка пуста. Напишіть щось, щоб розігріти народ!",
-  "empty_column.direct": "У вас ще немає прямих повідомлень. Коли ви щось отримаєте чи надішлете, воно з'явиться тут.",
+  "empty_column.direct": "У вас ще немає жодних особистих згадок. Коли ви надсилаєте чи отримуєте повідомлення, воно з'явиться тут.",
   "empty_column.domain_blocks": "Тут поки немає прихованих доменів.",
   "empty_column.explore_statuses": "Нема нічого популярного. Подивіться пізніше!",
   "empty_column.favourited_statuses": "У вас ще немає вподобаних дописів. Коли ви щось вподобаєте, воно з'явиться тут.",
   "empty_column.favourites": "Ніхто ще не вподобав цей допис. Коли хтось це зробить, він з'явиться тут.",
   "empty_column.follow_recommendations": "Схоже, для вас не було створено жодної пропозиції. Ви можете спробувати скористатися пошуком людей, яких ви можете знати, або переглянути популярні гештеґи.",
   "empty_column.follow_requests": "У вас ще немає запитів на підписку. Коли ви їх отримаєте, вони з'являться тут.",
+  "empty_column.followed_tags": "Ви ще не підписані на хештеґи. Коли ви це зробите, вони з'являться тут.",
   "empty_column.hashtag": "Дописів з цим гештеґом поки не існує.",
   "empty_column.home": "Ваша стрічка порожня! Підпишіться на інших, щоб її заповнити. {suggestions}",
   "empty_column.home.suggestions": "Переглянути пропозиції",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Авторизувати",
   "follow_request.reject": "Відмовити",
   "follow_requests.unlocked_explanation": "Хоча ваш обліковий запис не заблоковано, персонал {domain} припускає, що, можливо, ви хотіли б переглянути ці запити на підписку.",
+  "followed_tags": "Відстежувані хештеґи",
   "footer.about": "Про проєкт",
   "footer.directory": "Каталог профілів",
   "footer.get_app": "Завантажити застосунок",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Комбінації клавіш",
   "footer.privacy_policy": "Політика приватності",
   "footer.source_code": "Перегляд програмного коду",
+  "footer.status": "Статус",
   "generic.saved": "Збережено",
   "getting_started.heading": "Розпочати",
   "hashtag.column_header.tag_mode.all": "та {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "Фокусуватися на одній з колонок",
   "keyboard_shortcuts.compose": "Фокусуватися на полі введення",
   "keyboard_shortcuts.description": "Опис",
-  "keyboard_shortcuts.direct": "щоб відкрити колонку прямих повідомлень",
+  "keyboard_shortcuts.direct": "щоб відкрити стовпець особистих згадок",
   "keyboard_shortcuts.down": "Рухатися вниз стрічкою",
   "keyboard_shortcuts.enter": "Відкрити допис",
   "keyboard_shortcuts.favourite": "Вподобати допис",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Закладки",
   "navigation_bar.community_timeline": "Локальна стрічка",
   "navigation_bar.compose": "Написати новий допис",
-  "navigation_bar.direct": "Особисті повідомлення",
+  "navigation_bar.direct": "Особисті згадки",
   "navigation_bar.discover": "Дослідити",
   "navigation_bar.domain_blocks": "Заблоковані домени",
   "navigation_bar.edit_profile": "Редагувати профіль",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Вподобане",
   "navigation_bar.filters": "Приховані слова",
   "navigation_bar.follow_requests": "Запити на підписку",
+  "navigation_bar.followed_tags": "Відстежувані хештеґи",
   "navigation_bar.follows_and_followers": "Підписки та підписники",
   "navigation_bar.lists": "Списки",
   "navigation_bar.logout": "Вийти",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Спам",
   "report_notification.categories.violation": "Порушення правил",
   "report_notification.open": "Відкрити скаргу",
+  "search.no_recent_searches": "Немає останніх пошуків",
   "search.placeholder": "Пошук",
+  "search.quick_action.account_search": "Збіг профілів {x}",
+  "search.quick_action.go_to_account": "Перейти до профілю {x}",
+  "search.quick_action.go_to_hashtag": "Перейти до хештегу {x}",
+  "search.quick_action.open_url": "Відкрити URL-адресу в Mastodon",
+  "search.quick_action.status_search": "Збіг дописів {x}",
   "search.search_or_paste": "Введіть адресу або пошуковий запит",
-  "search_popout.search_format": "Розширений формат пошуку",
-  "search_popout.tips.full_text": "Пошук за текстом знаходить дописи, які ви написали, вподобали, поширили, або в яких вас згадували. Також він знаходить імена користувачів, реальні імена та гештеґи.",
-  "search_popout.tips.hashtag": "хештеґ",
-  "search_popout.tips.status": "допис",
-  "search_popout.tips.text": "Пошук за текстом знаходить імена користувачів, реальні імена та хештеґи",
-  "search_popout.tips.user": "користувач",
-  "search_results.accounts": "Люди",
+  "search_popout.quick_actions": "Швидкі дії",
+  "search_popout.recent": "Нещодавні запити",
+  "search_results.accounts": "Профілі",
   "search_results.all": "Усі",
   "search_results.hashtags": "Хештеґи",
   "search_results.nothing_found": "Нічого не вдалося знайти за цими пошуковими термінами",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Статистика сервера:",
   "sign_in_banner.create_account": "Створити обліковий запис",
   "sign_in_banner.sign_in": "Увійти",
-  "sign_in_banner.text": "Увійдіть, щоб слідкувати за профілями або хештеґами, вподобаними, ділитися і відповідати на повідомлення або взаємодіяти з вашого облікового запису на іншому сервері.",
+  "sign_in_banner.text": "Увійдіть, щоб слідкувати за профілями або хештеґами, вподобаними, ділитися і відповідати на дописи. Ви також взаємодіяти з вашого облікового запису на іншому сервері.",
   "status.admin_account": "Відкрити інтерфейс модерації для @{name}",
   "status.admin_domain": "Відкрити інтерфейс модерації для {domain}",
   "status.admin_status": "Відкрити цей допис в інтерфейсі модерації",
@@ -551,7 +559,8 @@
   "status.copy": "Копіювати посилання на допис",
   "status.delete": "Видалити",
   "status.detailed_status": "Детальний вигляд бесіди",
-  "status.direct": "Пряме повідомлення до @{name}",
+  "status.direct": "Особиста згадка @{name}",
+  "status.direct_indicator": "Особиста згадка",
   "status.edit": "Редагувати",
   "status.edited": "Відредаговано {date}",
   "status.edited_x_times": "Відредаговано {count, plural, one {{count} раз} few {{count} рази} many {{counter} разів} other {{counter} разів}}",
diff --git a/app/javascript/mastodon/locales/ur.json b/app/javascript/mastodon/locales/ur.json
index e411e6015..ffb6e1068 100644
--- a/app/javascript/mastodon/locales/ur.json
+++ b/app/javascript/mastodon/locales/ur.json
@@ -20,7 +20,7 @@
   "account.blocked": "مسدود کردہ",
   "account.browse_more_on_origin_server": "اصل پروفائل پر مزید براؤز کریں",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "راست پیغام @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "جب @{name} پوسٹ کرے تو مجھ مطلع نہ کریں",
   "account.domain_blocked": "پوشیدہ ڈومین",
   "account.edit_profile": "مشخص ترمیم کریں",
@@ -102,7 +102,7 @@
   "column.blocks": "مسدود صارفین",
   "column.bookmarks": "بُک مارکس",
   "column.community": "مقامی زمانی جدول",
-  "column.direct": "براہ راست پیغامات",
+  "column.direct": "Private mentions",
   "column.directory": "مشخصات کا مطالعہ کریں",
   "column.domain_blocks": "پوشیدہ ڈومین",
   "column.favourites": "پسندیدہ",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "confirmations.domain_block.confirm": "Hide entire domain",
   "confirmations.domain_block.message": "کیا آپ واقعی، واقعی یقین رکھتے ہیں کہ آپ پورے {domain} کو بلاک کرنا چاہتے ہیں؟ زیادہ تر معاملات میں چند ٹارگٹیڈ بلاکس یا خاموش کرنا کافی اور افضل ہیں۔ آپ اس ڈومین کا مواد کسی بھی عوامی ٹائم لائنز یا اپنی اطلاعات میں نہیں دیکھیں گے۔ اس ڈومین سے آپ کے پیروکاروں کو ہٹا دیا جائے گا۔",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "لاگ آؤٹ",
   "confirmations.logout.message": "کیا واقعی آپ لاگ آؤٹ ہونا چاہتے ہیں؟",
   "confirmations.mute.confirm": "خاموش",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "آپ نے ابھی کسی صارف کو مسدود نہیں کیا ہے.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "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.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "ابھی تک کوئی چھپا ہوا ڈومین نہیں ہے.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "آپ کا کوئی پسندیدہ ٹوٹ نہیں ہے. جب آپ پسند کریں گے، یہاں نظر آئےگا.",
   "empty_column.favourites": "ابھی تک کسی نے بھی اس ٹوٹ کو پسند نہیں کیا ہے. جب بھی کوئی اسے پسند کرے گا، ان کا نام یہاں نظر آئے گا.",
   "empty_column.follow_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "ابھی تک آپ کی پیری کرنے کی درخواست نہیں کی ہے. جب کوئی درخواست کرے گا، ان کا نام یہاں نظر آئے گا.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "ابھی یہ ہیش ٹیگ خالی ہے.",
   "empty_column.home": "آپ کا خانگی جدول خالی ہے! {public} دیکھیں یا شروعات کیلئے تلاش کریں اور دیگر صارفین سے ملیں.",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "اجازت دیں",
   "follow_request.reject": "انکار کریں",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "آغاز کریں",
   "hashtag.column_header.tag_mode.all": "اور {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "بُک مارکس",
   "navigation_bar.community_timeline": "مقامی ٹائم لائن",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "دریافت کریں",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "پروفائل میں ترمیم کریں",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "پسندیدہ",
   "navigation_bar.filters": "خاموش کردہ الفاظ",
   "navigation_bar.follow_requests": "پیروی کی درخواستیں",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "پیروی کردہ اور پیروکار",
   "navigation_bar.lists": "فہرستیں",
   "navigation_bar.logout": "لاگ آؤٹ",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "Hashtags",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "Delete",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "Direct message @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/uz.json b/app/javascript/mastodon/locales/uz.json
new file mode 100644
index 000000000..e7502bf23
--- /dev/null
+++ b/app/javascript/mastodon/locales/uz.json
@@ -0,0 +1,664 @@
+{
+  "about.blocks": "Moderatsiya qilingan serverlar",
+  "about.contact": "Ulanish:",
+  "about.disclaimer": "Mastodon bepul, ochiq kodli dastur va Mastodon gGmbH kompaniyasining savdo belgisidir.",
+  "about.domain_blocks.no_reason_available": "Sabab mavjud emas",
+  "about.domain_blocks.preamble": "Mastodon odatda fediversedagi istalgan boshqa serverdagi foydalanuvchilar tarkibini ko'rish va ular bilan muloqot qilish imkonini beradi. Bu alohida serverda qilingan istisnolar.",
+  "about.domain_blocks.silenced.explanation": "Bu serverdagi profillar va kontentni koʻrmaysiz, agar siz uni aniq koʻrib chiqmasangiz yoki unga amal qilish orqali kirishni xohlamasangiz.",
+  "about.domain_blocks.silenced.title": "Cheklangan",
+  "about.domain_blocks.suspended.explanation": "Ushbu serverdan hech qanday ma'lumot qayta ishlanmaydi, saqlanmaydi yoki almashtirilmaydi, bu esa ushbu serverdagi foydalanuvchilar bilan hech qanday o'zaro aloqa yoki aloqani imkonsiz qiladi.",
+  "about.domain_blocks.suspended.title": "To‘xtatildi",
+  "about.not_available": "Ushbu ma'lumot ushbu serverda mavjud emas.",
+  "about.powered_by": "{mastodon} tomonidan boshqariladigan markazlashtirilmagan ijtimoiy media",
+  "about.rules": "Server qoidalari",
+  "account.account_note_header": "Eslatma",
+  "account.add_or_remove_from_list": "Roʻyxatlarga qoʻshish yoki oʻchirish",
+  "account.badges.bot": "Bo't",
+  "account.badges.group": "Guruhlar",
+  "account.block": "Blok @{name}",
+  "account.block_domain": "{domain} domenini bloklash",
+  "account.blocked": "Bloklangan",
+  "account.browse_more_on_origin_server": "Asl profilda ko'proq ko'rish",
+  "account.cancel_follow_request": "Kuzatuv so‘rovini bekor qilish",
+  "account.direct": "Privately mention @{name}",
+  "account.disable_notifications": "@{name} post qo‘yganida menga xabar berishni to‘xtating",
+  "account.domain_blocked": "Domen bloklangan",
+  "account.edit_profile": "Profilni tahrirlash",
+  "account.enable_notifications": "@{name} post qo‘yganida menga xabar olish",
+  "account.endorse": "Profildagi xususiyat",
+  "account.featured_tags.last_status_at": "Oxirgi post: {date}",
+  "account.featured_tags.last_status_never": "Habarlar yo'q",
+  "account.featured_tags.title": "{name} ning taniqli hashtaglari",
+  "account.follow": "Obuna bo‘lish",
+  "account.followers": "Obunachilar",
+  "account.followers.empty": "Bu foydalanuvchini hali hech kim kuzatmaydi.",
+  "account.followers_counter": "{count, plural, one {{counter} Muxlis} other {{counter} Muxlislar}}",
+  "account.following": "Kuzatish",
+  "account.following_counter": "{count, plural, one {{counter} ga Muxlis} other {{counter} larga muxlis}}",
+  "account.follows.empty": "Bu foydalanuvchi hali hech kimni kuzatmagan.",
+  "account.follows_you": "Sizga obuna",
+  "account.go_to_profile": "Profilga o'tish",
+  "account.hide_reblogs": "@{name} dan boostlarni yashirish",
+  "account.joined_short": "Qo'shilgan",
+  "account.languages": "Obuna boʻlgan tillarni oʻzgartirish",
+  "account.link_verified_on": "Bu havolaning egaligi {date} kuni tekshirilgan",
+  "account.locked_info": "Bu hisobning maxfiylik holati qulflangan qilib sozlangan. Egasi ularni kim kuzatishi mumkinligini qo'lda ko'rib chiqadi.",
+  "account.media": "Media",
+  "account.mention": "@{name} ni zikr qiling",
+  "account.moved_to": "{name} oʻzining yangi hisobi endi ekanligini aytdi:",
+  "account.mute": "@{name} ovozini o‘chirish",
+  "account.mute_notifications": "@{name} bildirishnomalarini o‘chirish",
+  "account.muted": "Ovozsiz",
+  "account.open_original_page": "Original post sahifasi",
+  "account.posts": "Postlar",
+  "account.posts_with_replies": "Xabarlar va javoblar",
+  "account.report": "@{name} xabar berish",
+  "account.requested": "Tasdiqlash kutilmoqda. Kuzatuv soʻrovini bekor qilish uchun bosing",
+  "account.requested_follow": "{name} sizni kuzatishni soʻradi",
+  "account.share": "@{name} profilini ulashing",
+  "account.show_reblogs": "@{name} dan bootlarni ko'rsatish",
+  "account.statuses_counter": "{count, plural, one {{counter} Post} other {{counter} Postlar}}",
+  "account.unblock": "@{name} ni blokdan chiqarish",
+  "account.unblock_domain": "{domain} domenini blokdan chiqarish",
+  "account.unblock_short": "Blokdan chiqarish",
+  "account.unendorse": "Profilda ko'rsatilmasin",
+  "account.unfollow": "Kuzatishni To'xtatish",
+  "account.unmute": "@{name} ovozini yoqish",
+  "account.unmute_notifications": "@{name} bildirishnomalarining ovozini yoqish",
+  "account.unmute_short": "Ovozni yoqish",
+  "account_note.placeholder": "Eslatma qo'shish uchun bosing",
+  "admin.dashboard.daily_retention": "Ro'yxatdan o'tgandan keyingi kun bo'yicha foydalanuvchini ushlab turish darajasi",
+  "admin.dashboard.monthly_retention": "Ro'yxatdan o'tgandan keyin oy bo'yicha foydalanuvchini ushlab turish darajasi",
+  "admin.dashboard.retention.average": "O‘rtacha",
+  "admin.dashboard.retention.cohort": "Ro'yxatdan o'tish oyi",
+  "admin.dashboard.retention.cohort_size": "Yangi foydalanuvchilar",
+  "alert.rate_limited.message": "{retry_time, time, media} keyin qayta urinib koʻring.",
+  "alert.rate_limited.title": "Rate cheklangan",
+  "alert.unexpected.message": "Kutilmagan xatolik yuz berdi.",
+  "alert.unexpected.title": "Voy!",
+  "announcement.announcement": "E'lonlar",
+  "attachments_list.unprocessed": "(qayta ishlanmagan)",
+  "audio.hide": "Audioni yashirish",
+  "autosuggest_hashtag.per_week": "{count} haftasiga",
+  "boost_modal.combo": "Keyingi safar buni oʻtkazib yuborish uchun {combo} tugmasini bosishingiz mumkin",
+  "bundle_column_error.copy_stacktrace": "Xato hisobotini nusxalash",
+  "bundle_column_error.error.body": "Soʻralgan sahifani koʻrsatib boʻlmadi. Buning sababi bizning kodimizdagi xato yoki brauzer mosligi muammosi bo'lishi mumkin.",
+  "bundle_column_error.error.title": "Voy yo'q!",
+  "bundle_column_error.network.body": "Ushbu sahifani yuklashda xatolik yuz berdi. Buning sababi internet ulanishingiz yoki ushbu serverdagi vaqtinchalik muammo bo'lishi mumkin.",
+  "bundle_column_error.network.title": "Tarmoq xatosi",
+  "bundle_column_error.retry": "Qayta urinib ko'rish",
+  "bundle_column_error.return": "Bosh sahifaga qaytish",
+  "bundle_column_error.routing.body": "Soʻralgan sahifani topib boʻlmadi. Manzil satridagi URL to'g'ri ekanligiga ishonchingiz komilmi?",
+  "bundle_column_error.routing.title": "404",
+  "bundle_modal_error.close": "Yopish",
+  "bundle_modal_error.message": "Ushbu mahsulotni qayta belgilashda xatolik yuz berdi.",
+  "bundle_modal_error.retry": "Qayta urinib ko'rish",
+  "closed_registrations.other_server_instructions": "Mastodon markazlashtirilmaganligi sababli, siz boshqa serverda hisob yaratishingiz va u bilan o'zaro aloqada bo'lishingiz mumkin.",
+  "closed_registrations_modal.description": "{domain} da hisob yaratish hozircha imkonsiz, lekin Mastodondan foydalanish uchun maxsus {domain} hisob qaydnomasi kerak emasligini yodda tuting.",
+  "closed_registrations_modal.find_another_server": "Boshqa server topish",
+  "closed_registrations_modal.preamble": "Mastodon markazlashtirilmagan, shuning uchun hisob qaydnomangizni qayerda yaratmasligingizdan qat'iy nazar, siz ushbu serverdagi har kimni kuzatishingiz va ular bilan muloqot qilishingiz mumkin bo'ladi. Siz hatto uni o'zingiz ham joylashtirishingiz mumkin!",
+  "closed_registrations_modal.title": "Mastodonda ro'yxatdan o'tish",
+  "column.about": "Haqida",
+  "column.blocks": "Bloklangan foydalanuvchilar",
+  "column.bookmarks": "Xatcho‘plar",
+  "column.community": "Mahalliy",
+  "column.direct": "Private mentions",
+  "column.directory": "Profillarni ko'rish",
+  "column.domain_blocks": "Bloklangan domenlar",
+  "column.favourites": "Sevimlilar",
+  "column.follow_requests": "So'rovlarni kuzatib boring",
+  "column.home": "Bosh sahifa",
+  "column.lists": "Ro‘yxat",
+  "column.mutes": "Ovozsiz foydalanuvchilar",
+  "column.notifications": "Bildirishnomalar",
+  "column.pins": "Belgilangan post",
+  "column.public": "Federatsiyalangan vaqt jadvali",
+  "column_back_button.label": "Orqaga",
+  "column_header.hide_settings": "Sozlamalarni yashirish",
+  "column_header.moveLeft_settings": "Ustunni chapga siljiting",
+  "column_header.moveRight_settings": "Ustunni o'nga siljiting",
+  "column_header.pin": "Yopishtirish",
+  "column_header.show_settings": "Sozlamalarni ko'rsatish",
+  "column_header.unpin": "Olib qo‘yish",
+  "column_subheading.settings": "Sozlamalar",
+  "community.column_settings.local_only": "Faqat mahalliy",
+  "community.column_settings.media_only": "Faqat media",
+  "community.column_settings.remote_only": "Faqat masofaviy",
+  "compose.language.change": "Tilni o‘zgartirish",
+  "compose.language.search": "Tillarni izlash...",
+  "compose_form.direct_message_warning_learn_more": "Batafsil ma’lumot",
+  "compose_form.encryption_warning": "Mastodondagi xabarlar uchdan uchgacha shifrlanmagan. Mastodon orqali hech qanday nozik ma'lumotni baham ko'rmang.",
+  "compose_form.hashtag_warning": "Bu post hech qanday xeshteg ostida ko‘rsatilmaydi, chunki u ochiq emas. Faqat ochiq xabarlarni heshteg orqali qidirish mumkin.",
+  "compose_form.lock_disclaimer": "Hisobingiz {locked}. Faqat obunachilarga moʻljallangan postlaringizni koʻrish uchun har kim sizni kuzatishi mumkin.",
+  "compose_form.lock_disclaimer.lock": "yopilgan",
+  "compose_form.placeholder": "Xalolizda nima?",
+  "compose_form.poll.add_option": "Tanlov qo'shing",
+  "compose_form.poll.duration": "So‘rov muddati",
+  "compose_form.poll.option_placeholder": "Tanlov {number}",
+  "compose_form.poll.remove_option": "Olib tashlash",
+  "compose_form.poll.switch_to_multiple": "Bir nechta tanlovga ruxsat berish uchun so'rovnomani o'zgartirish",
+  "compose_form.poll.switch_to_single": "Yagona tanlovga ruxsat berish uchun so‘rovnomani o‘zgartirish",
+  "compose_form.publish": "Nashr qilish",
+  "compose_form.publish_form": "Nashr qilish",
+  "compose_form.publish_loud": "{publish}!",
+  "compose_form.save_changes": "O‘zgarishlarni saqlash",
+  "compose_form.sensitive.hide": "{count, plural, one {Mediani sezgir deb belgilang} other {Medialarni sezgir deb belgilang}}",
+  "compose_form.sensitive.marked": "{count, plural, one {Mediani sezgir deb belgilang} other {Medialarni sezgir deb belgilang}}",
+  "compose_form.sensitive.unmarked": "{count, plural, one {Mediani sezgir deb belgilang} other {Medialarni sezgir deb belgilang}}",
+  "compose_form.spoiler.marked": "Kontent ogohlantirishini olib tashlang",
+  "compose_form.spoiler.unmarked": "Kontent haqida ogohlantirish qo'shing",
+  "compose_form.spoiler_placeholder": "Sharhingizni bu erga yozing",
+  "confirmation_modal.cancel": "Bekor qilish",
+  "confirmations.block.block_and_report": "Bloklash va hisobot berish",
+  "confirmations.block.confirm": "Bloklash",
+  "confirmations.block.message": "Haqiqatan ham {name}ni bloklamoqchimisiz?",
+  "confirmations.cancel_follow_request.confirm": "Bekor qilish",
+  "confirmations.cancel_follow_request.message": "Haqiqatan ham {name}ga obuna boʻlish soʻrovingizni qaytarib olmoqchimisiz?",
+  "confirmations.delete.confirm": "Oʻchirish",
+  "confirmations.delete.message": "Haqiqatan ham bu postni oʻchirib tashlamoqchimisiz?",
+  "confirmations.delete_list.confirm": "Oʻchirish",
+  "confirmations.delete_list.message": "Haqiqatan ham bu roʻyxatni butunlay oʻchirib tashlamoqchimisiz?",
+  "confirmations.discard_edit_media.confirm": "Bekor qilish",
+  "confirmations.discard_edit_media.message": "Sizda media tavsifi yoki oldindan ko‘rishda saqlanmagan o‘zgarishlar bor, ular baribir bekor qilinsinmi?",
+  "confirmations.domain_block.confirm": "Butun domenni bloklash",
+  "confirmations.domain_block.message": "Haqiqatan ham, {domain} ni butunlay bloklamoqchimisiz? Ko'pgina hollarda bir nechta maqsadli bloklar yoki ovozni o'chirish etarli va afzaldir. Siz oʻsha domendagi kontentni hech qanday umumiy vaqt jadvallarida yoki bildirishnomalaringizda koʻrmaysiz. Bu domendagi obunachilaringiz olib tashlanadi.",
+  "confirmations.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
+  "confirmations.logout.confirm": "Chiqish",
+  "confirmations.logout.message": "Chiqishingizga aminmisiz?",
+  "confirmations.mute.confirm": "Ovozsiz",
+  "confirmations.mute.explanation": "Bu ulardagi postlar va ular haqida eslatib o'tilgan postlarni yashiradi, ammo bu ularga sizning postlaringizni ko'rish va sizni kuzatish imkonini beradi.",
+  "confirmations.mute.message": "Haqiqatan ham {name} ovozini o‘chirib qo‘ymoqchimisiz?",
+  "confirmations.redraft.confirm": "O'chirish va qayta loyihalash",
+  "confirmations.redraft.message": "Haqiqatan ham bu postni o‘chirib tashlab, uni qayta loyihalashni xohlaysizmi? Sevimlilar va yuksalishlar yo'qoladi va asl postga javoblar yetim qoladi.",
+  "confirmations.reply.confirm": "Javob berish",
+  "confirmations.reply.message": "Hozir javob bersangiz, hozir yozayotgan xabaringiz ustidan yoziladi. Davom etishni xohlaysizmi?",
+  "confirmations.unfollow.confirm": "Kuzatishni To'xtatish",
+  "confirmations.unfollow.message": "Haqiqatan ham {name} obunasini bekor qilmoqchimisiz?",
+  "conversation.delete": "Suhbatni o'chirish",
+  "conversation.mark_as_read": "O'qilgan deb belgilang",
+  "conversation.open": "Suhbatni ko'rish",
+  "conversation.with": "{names} bilan",
+  "copypaste.copied": "Ko‘chirildi",
+  "copypaste.copy": "Nusxa olish",
+  "directory.federated": "Faqat bilingan fediversdan",
+  "directory.local": "Faqat {domain}dan",
+  "directory.new_arrivals": "Yangi kelganlar",
+  "directory.recently_active": "Yaqinda faol",
+  "disabled_account_banner.account_settings": "Profil sozlamalari",
+  "disabled_account_banner.text": "{disabledAccount} hisobingiz hozirda oʻchirib qoʻyilgan.",
+  "dismissable_banner.community_timeline": "Bular akkauntlari {domain} tomonidan joylashtirilgan odamlarning eng soʻnggi ochiq postlari.",
+  "dismissable_banner.dismiss": "Bekor qilish",
+  "dismissable_banner.explore_links": "Ushbu yangiliklar haqida hozirda markazlashtirilmagan tarmoqning ushbu va boshqa serverlarida odamlar gaplashmoqda.",
+  "dismissable_banner.explore_statuses": "Markazlashtirilmagan tarmoqdagi ushbu va boshqa serverlarning ushbu xabarlari hozirda ushbu serverda qiziqish uyg'otmoqda.",
+  "dismissable_banner.explore_tags": "Ushbu hashtaglar hozirda markazlashtirilmagan tarmoqning ushbu va boshqa serverlarida odamlar orasida qiziqish uyg'otmoqda.",
+  "dismissable_banner.public_timeline": "Bular ushbu va markazlashtirilmagan tarmoqning boshqa serverlaridagi odamlarning ushbu serverga ma'lum bo'lgan eng so'nggi ommaviy xabarlari.",
+  "embed.instructions": "Quyidagi kodni nusxalash orqali ushbu postni veb-saytingizga joylashtiring.",
+  "embed.preview": "Bu qanday ko'rinishda bo'ladi:",
+  "emoji_button.activity": "Faoliyat",
+  "emoji_button.clear": "Tozalash",
+  "emoji_button.custom": "Boshqa",
+  "emoji_button.flags": "Belgilar",
+  "emoji_button.food": "Ovqat va Ichimlik",
+  "emoji_button.label": "Emoji kiritish",
+  "emoji_button.nature": "Tabiat",
+  "emoji_button.not_found": "Mos emoji topilmadi",
+  "emoji_button.objects": "Ob'ektlar",
+  "emoji_button.people": "Odamlar",
+  "emoji_button.recent": "Ko'p ishlatilgan",
+  "emoji_button.search": "Izlash...",
+  "emoji_button.search_results": "Qidiruv natijalari",
+  "emoji_button.symbols": "Belgilar",
+  "emoji_button.travel": "Sayohat va Joylar",
+  "empty_column.account_suspended": "Hisob to‘xtatildi",
+  "empty_column.account_timeline": "Bu yerda post yo'q!",
+  "empty_column.account_unavailable": "Profil mavjud emas",
+  "empty_column.blocks": "Siz hali hech qanday foydalanuvchini bloklamagansiz.",
+  "empty_column.bookmarked_statuses": "Sizda hali xatcho‘p qo‘yilgan postlar yo‘q. Biriga xatcho‘p qo‘ysangiz, u shu yerda ko‘rinadi.",
+  "empty_column.community": "Mahalliy vaqt jadvali boʻsh. To'pni aylantirish uchun hammaga ochiq narsa yozing!",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
+  "empty_column.domain_blocks": "Hali bloklangan domenlar mavjud emas.",
+  "empty_column.explore_statuses": "Hozir hech narsa trendda emas. Keyinroq tekshiring!",
+  "empty_column.favourited_statuses": "Sizda hali sevimli postlar yoʻq. Sizga yoqqanida, u shu yerda chiqadi.",
+  "empty_column.favourites": "Bu postni hali hech kim yoqtirmagan. Kimdir buni qilsa, ular shu yerda paydo bo'ladi.",
+  "empty_column.follow_recommendations": "Siz uchun hech qanday taklif yaratilmaganga o‘xshaydi. Siz tanish bo'lgan odamlarni qidirish yoki trendli hashtaglarni o'rganish uchun qidiruvdan foydalanib ko'rishingiz mumkin.",
+  "empty_column.follow_requests": "Sizda hali kuzatuv soʻrovlari yoʻq. Bittasini olganingizda, u shu yerda paydo bo'ladi.",
+  "empty_column.followed_tags": "Siz hali hech qanday hashtagga amal qilmagansiz. Qachonki, ular shu yerda paydo bo'ladi.",
+  "empty_column.hashtag": "Ushbu hashtagda hali hech narsa yo'q.",
+  "empty_column.home": "Bosh sahifa yilnomangiz boʻsh! Uni to'ldirish uchun ko'proq odamlarni kuzatib boring. {suggestions}",
+  "empty_column.home.suggestions": "Ba'zi takliflarni ko'ring",
+  "empty_column.list": "Bu ro'yxatda hali hech narsa yo'q. Ushbu roʻyxat aʼzolari yangi xabarlarni nashr qilganda, ular shu yerda paydo boʻladi.",
+  "empty_column.lists": "Sizda hali hech qanday roʻyxat yoʻq. Uni yaratganingizda, u shu yerda paydo bo'ladi.",
+  "empty_column.mutes": "Siz hali hech bir foydalanuvchining ovozini o‘chirmagansiz.",
+  "empty_column.notifications": "Sizda hali hech qanday bildirishnoma yo‘q. Boshqa odamlar siz bilan muloqot qilganda, buni shu yerda ko'rasiz.",
+  "empty_column.public": "Bu erda hech narsa yo'q! Biror narsani ochiq yozing yoki uni toʻldirish uchun boshqa serverlardagi foydalanuvchilarni qoʻlda kuzatib boring",
+  "error.unexpected_crash.explanation": "Kodimizdagi xatolik yoki brauzer mosligi muammosi tufayli bu sahifani toʻgʻri koʻrsatib boʻlmadi.",
+  "error.unexpected_crash.explanation_addons": "Bu sahifani toʻgʻri koʻrsatib boʻlmadi. Bu xato brauzer qoʻshimchasi yoki avtomatik tarjima vositalaridan kelib chiqqan boʻlishi mumkin.",
+  "error.unexpected_crash.next_steps": "Sahifani yangilab ko‘ring. Agar bu yordam bermasa, siz boshqa brauzer yoki mahalliy ilova orqali Mastodondan foydalanishingiz mumkin.",
+  "error.unexpected_crash.next_steps_addons": "Ularni o'chirib ko'ring va sahifani yangilang. Agar bu yordam bermasa, siz boshqa brauzer yoki mahalliy ilova orqali Mastodondan foydalanishingiz mumkin.",
+  "errors.unexpected_crash.copy_stacktrace": "Stacktrace-ni vaqtinchalik xotiraga nusxalash",
+  "errors.unexpected_crash.report_issue": "Muammo haqida xabar berish",
+  "explore.search_results": "Qidiruv natijalari",
+  "explore.suggested_follows": "Siz uchun",
+  "explore.title": "Ko'rib chiqish",
+  "explore.trending_links": "Yangiliklar",
+  "explore.trending_statuses": "Postlar",
+  "explore.trending_tags": "Hashteglar",
+  "filter_modal.added.context_mismatch_explanation": "Ushbu filtr toifasi siz ushbu postga kirgan kontekstga taalluqli emas. Agar siz post ham shu kontekstda filtrlanishini istasangiz, filtrni tahrirlashingiz kerak bo'ladi.",
+  "filter_modal.added.context_mismatch_title": "Kontekst mos kelmadi!",
+  "filter_modal.added.expired_explanation": "Ushbu filtr toifasi muddati tugagan, uni qo‘llash uchun amal qilish muddatini o‘zgartirishingiz kerak bo‘ladi.",
+  "filter_modal.added.expired_title": "Muddati tugagan filtr!",
+  "filter_modal.added.review_and_configure": "Ushbu filtr toifasini koʻrib chiqish va sozlash uchun {settings_link} sahifasiga oʻting.",
+  "filter_modal.added.review_and_configure_title": "Filtr sozlamalari",
+  "filter_modal.added.settings_link": "sozlamalar sahifasi",
+  "filter_modal.added.short_explanation": "Bu post quyidagi filtr turkumiga qo‘shildi: {title}.",
+  "filter_modal.added.title": "Filter qo'shildi!",
+  "filter_modal.select_filter.context_mismatch": "ushbu kontekstga taalluqli emas",
+  "filter_modal.select_filter.expired": "muddati tugagan",
+  "filter_modal.select_filter.prompt_new": "Yangi turkum: {name}",
+  "filter_modal.select_filter.search": "Qidiring yoki yarating",
+  "filter_modal.select_filter.subtitle": "Mavjud toifadan foydalaning yoki yangisini yarating",
+  "filter_modal.select_filter.title": "Ushbu postni filtrlang",
+  "filter_modal.title.status": "Postni filtrlash",
+  "follow_recommendations.done": "Tayyor",
+  "follow_recommendations.heading": "Xabarlarni ko'rishni istagan odamlarni kuzatib boring! Bu erda ba'zi takliflar mavjud.",
+  "follow_recommendations.lead": "Siz kuzatayotgan odamlarning postlari uy tasmangizda xronologik tartibda ko‘rsatiladi. Xato qilishdan qo'rqmang, istalgan vaqtda odamlarni kuzatishdan osongina voz kechishingiz mumkin!",
+  "follow_request.authorize": "Ruxsat berish",
+  "follow_request.reject": "Rad etish",
+  "follow_requests.unlocked_explanation": "Hisobingiz bloklanmagan boʻlsa ham, {domain} xodimlari ushbu hisoblardan kuzatuv soʻrovlarini qoʻlda koʻrib chiqishingiz mumkin deb oʻylashdi.",
+  "followed_tags": "Kuzatilgan hashtaglar",
+  "footer.about": "Haqida",
+  "footer.directory": "Profillar katalogi",
+  "footer.get_app": "Ilovani yuklab oling",
+  "footer.invite": "Odamlarni taklif qilish",
+  "footer.keyboard_shortcuts": "Klaviatura yorliqlari",
+  "footer.privacy_policy": "Maxfiylik siyosati",
+  "footer.source_code": "Kodini ko'rish",
+  "footer.status": "Status",
+  "generic.saved": "Saqlandi",
+  "getting_started.heading": "Ishni boshlash",
+  "hashtag.column_header.tag_mode.all": "va {additional}",
+  "hashtag.column_header.tag_mode.any": "yoki {additional}",
+  "hashtag.column_header.tag_mode.none": "{additional}siz",
+  "hashtag.column_settings.select.no_options_message": "Hech qanday taklif topilmadi",
+  "hashtag.column_settings.select.placeholder": "Hashtaglarni kiriting…",
+  "hashtag.column_settings.tag_mode.all": "Bularning hammasi",
+  "hashtag.column_settings.tag_mode.any": "Bularning har biri",
+  "hashtag.column_settings.tag_mode.none": "Bularning hech biri",
+  "hashtag.column_settings.tag_toggle": "Ushbu ustun uchun qo'shimcha teglarni qo'shing",
+  "hashtag.follow": "Hashtagni kuzatish",
+  "hashtag.unfollow": "Hashtagni kuzatishni to'xtatish",
+  "home.column_settings.basic": "Asos",
+  "home.column_settings.show_reblogs": "Boostlarni ko'rish",
+  "home.column_settings.show_replies": "Javoblarni ko'rish",
+  "home.hide_announcements": "E'lonlarni yashirish",
+  "home.show_announcements": "E'lonlarni ko'rsatish",
+  "interaction_modal.description.favourite": "Mastodonda akkaunt bilan siz muallifga buni qadrlayotganingizni bildirish uchun ushbu postni yoqtirishingiz va keyinroq saqlashingiz mumkin.",
+  "interaction_modal.description.follow": "Mastodon’da akkauntga ega bo‘lgan holda, siz {name} ga obuna bo‘lib, ularning postlarini bosh sahifangizga olishingiz mumkin.",
+  "interaction_modal.description.reblog": "Mastodon-dagi akkaunt yordamida siz ushbu postni o'z izdoshlaringiz bilan baham ko'rish uchun oshirishingiz mumkin.",
+  "interaction_modal.description.reply": "Mastodondagi akkaunt bilan siz ushbu xabarga javob berishingiz mumkin.",
+  "interaction_modal.on_another_server": "Boshqa serverda",
+  "interaction_modal.on_this_server": "Shu serverda",
+  "interaction_modal.other_server_instructions": "Ushbu URL manzilidan nusxa ko‘chiring va sevimli Mastodon ilovangizning qidirish maydoniga yoki Mastodon serveringiz veb-interfeysiga joylashtiring.",
+  "interaction_modal.preamble": "Mastodon markazlashtirilmaganligi sababli, boshqa Mastodon serverida joylashgan mavjud hisob qaydnomangizdan yoki bu serverda akkauntingiz bo'lmasa, unga mos platformadan foydalanishingiz mumkin.",
+  "interaction_modal.title.favourite": "{name} posti yoqdi",
+  "interaction_modal.title.follow": "{name} ga ergashing",
+  "interaction_modal.title.reblog": "{name}ning postini boost qilish",
+  "interaction_modal.title.reply": "{name} postiga javob bering",
+  "intervals.full.days": "{number, plural, one {# day} other {# days}}",
+  "intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
+  "intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
+  "keyboard_shortcuts.back": "Orqaga qaytish",
+  "keyboard_shortcuts.blocked": "to open blocked users list",
+  "keyboard_shortcuts.boost": "Postni boost qilish",
+  "keyboard_shortcuts.column": "Fokus ustuni",
+  "keyboard_shortcuts.compose": "to focus the compose textarea",
+  "keyboard_shortcuts.description": "Tavsif",
+  "keyboard_shortcuts.direct": "to open direct messages column",
+  "keyboard_shortcuts.down": "to move down in the list",
+  "keyboard_shortcuts.enter": "Yozuvni ochish",
+  "keyboard_shortcuts.favourite": "Yozuv yoqdi",
+  "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": "Qaynoq klavishlar",
+  "keyboard_shortcuts.legend": "Ushbu afsonani ko'rsatish",
+  "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": "Profilingizni ochish",
+  "keyboard_shortcuts.notifications": "to open notifications column",
+  "keyboard_shortcuts.open_media": "Mediani ochish",
+  "keyboard_shortcuts.pinned": "Qattiqlangan postlar roʻyxatini oching",
+  "keyboard_shortcuts.profile": "Muallif profilini ochish",
+  "keyboard_shortcuts.reply": "Postga javob berish",
+  "keyboard_shortcuts.requests": "Kuzatuv soʻrovlari roʻyxatini ochish",
+  "keyboard_shortcuts.search": "Qidiruv paneliga fokus",
+  "keyboard_shortcuts.spoilers": "CW maydonini ko'rsatish/yashirish",
+  "keyboard_shortcuts.start": "\"Boshlash\" ustunini oching",
+  "keyboard_shortcuts.toggle_hidden": "CW orqasida matnni ko'rsatish/yashirish",
+  "keyboard_shortcuts.toggle_sensitivity": "Mediani ko'rsatish/yashirish",
+  "keyboard_shortcuts.toot": "Yangi post boshlang",
+  "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
+  "keyboard_shortcuts.up": "Roʻyxatda yuqoriga koʻtarish",
+  "lightbox.close": "Yopish",
+  "lightbox.compress": "Compress image view box",
+  "lightbox.expand": "Expand image view box",
+  "lightbox.next": "Next",
+  "lightbox.previous": "Previous",
+  "limited_account_hint.action": "Baribir profilni ko'rsatish",
+  "limited_account_hint.title": "This profile has been hidden by the moderators of {domain}.",
+  "lists.account.add": "Ro‘yxatga qo‘shish",
+  "lists.account.remove": "Roʻyxatdan o'chirish",
+  "lists.delete": "Roʻyxatni o'chirish",
+  "lists.edit": "Roʻyxatni tahrirlash",
+  "lists.edit.submit": "Change title",
+  "lists.new.create": "Ro‘yxatga qo‘shish",
+  "lists.new.title_placeholder": "New list title",
+  "lists.replies_policy.followed": "Any followed user",
+  "lists.replies_policy.list": "Ro'yxat a'zolari",
+  "lists.replies_policy.none": "Hech kim",
+  "lists.replies_policy.title": "Javoblarni ko'rsatish:",
+  "lists.search": "Siz kuzatadigan odamlar orasidan qidiring",
+  "lists.subheading": "Sizning ro'yxatlaringiz",
+  "load_pending": "{count, plural, one {# yangi element} other {# yangi elementlar}}",
+  "loading_indicator.label": "Yuklanmoqda...",
+  "media_gallery.toggle_visible": "{number, plural, one {Rasmni yashirish} other {Rasmlarni yashirish}}",
+  "missing_indicator.label": "Topilmadi",
+  "missing_indicator.sublabel": "Bu resurs topilmadi",
+  "moved_to_account_banner.text": "{movedToAccount} hisobiga koʻchganingiz uchun {disabledAccount} hisobingiz hozirda oʻchirib qoʻyilgan.",
+  "mute_modal.duration": "Davomiyligi",
+  "mute_modal.hide_notifications": "Bu foydalanuvchidan bildirishnomalar berkitilsinmi?",
+  "mute_modal.indefinite": "Cheksiz",
+  "navigation_bar.about": "Haqida",
+  "navigation_bar.blocks": "Bloklangan foydalanuvchilar",
+  "navigation_bar.bookmarks": "Xatcho‘plar",
+  "navigation_bar.community_timeline": "Mahalliy",
+  "navigation_bar.compose": "Yangi post yozing",
+  "navigation_bar.direct": "Private mentions",
+  "navigation_bar.discover": "Kashf qilish",
+  "navigation_bar.domain_blocks": "Bloklangan domenlar",
+  "navigation_bar.edit_profile": "Profilni tahrirlash",
+  "navigation_bar.explore": "O‘rganish",
+  "navigation_bar.favourites": "Sevimlilar",
+  "navigation_bar.filters": "E'tiborga olinmagan so'zlar",
+  "navigation_bar.follow_requests": "Follow requests",
+  "navigation_bar.followed_tags": "Kuzatilgan hashtaglar",
+  "navigation_bar.follows_and_followers": "Kuzatuvchilar va izdoshlar",
+  "navigation_bar.lists": "Ro‘yxat",
+  "navigation_bar.logout": "Chiqish",
+  "navigation_bar.mutes": "Ovozsiz foydalanuvchilar",
+  "navigation_bar.personal": "Shaxsiy",
+  "navigation_bar.pins": "Belgilangan postlar",
+  "navigation_bar.preferences": "Sozlamalar",
+  "navigation_bar.public_timeline": "Federatsiyalangan vaqt jadvali",
+  "navigation_bar.search": "Izlash",
+  "navigation_bar.security": "Xavfsizlik",
+  "not_signed_in_indicator.not_signed_in": "Ushbu manbaga kirish uchun tizimga kirishingiz kerak.",
+  "notification.admin.report": "{name} reported {target}",
+  "notification.admin.sign_up": "{name} signed up",
+  "notification.favourite": "{name} favourited your status",
+  "notification.follow": "{name} followed you",
+  "notification.follow_request": "{name} has requested to follow you",
+  "notification.mention": "{name} mentioned you",
+  "notification.own_poll": "So‘rovingiz tugadi",
+  "notification.poll": "Siz ovoz bergan soʻrovnoma yakunlandi",
+  "notification.reblog": "{name} boosted your status",
+  "notification.status": "{name} just posted",
+  "notification.update": "{name} edited a post",
+  "notifications.clear": "Clear notifications",
+  "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
+  "notifications.column_settings.admin.report": "New reports:",
+  "notifications.column_settings.admin.sign_up": "New sign-ups:",
+  "notifications.column_settings.alert": "Desktop notifications",
+  "notifications.column_settings.favourite": "Favourites:",
+  "notifications.column_settings.filter_bar.advanced": "Display all categories",
+  "notifications.column_settings.filter_bar.category": "Quick filter bar",
+  "notifications.column_settings.filter_bar.show_bar": "Show filter bar",
+  "notifications.column_settings.follow": "New followers:",
+  "notifications.column_settings.follow_request": "New follow requests:",
+  "notifications.column_settings.mention": "Mentions:",
+  "notifications.column_settings.poll": "Poll results:",
+  "notifications.column_settings.push": "Push notifications",
+  "notifications.column_settings.reblog": "Boosts:",
+  "notifications.column_settings.show": "Show in column",
+  "notifications.column_settings.sound": "Play sound",
+  "notifications.column_settings.status": "New posts:",
+  "notifications.column_settings.unread_notifications.category": "Unread notifications",
+  "notifications.column_settings.unread_notifications.highlight": "Highlight unread notifications",
+  "notifications.column_settings.update": "Edits:",
+  "notifications.filter.all": "All",
+  "notifications.filter.boosts": "Boosts",
+  "notifications.filter.favourites": "Favourites",
+  "notifications.filter.follows": "Follows",
+  "notifications.filter.mentions": "Mentions",
+  "notifications.filter.polls": "Poll results",
+  "notifications.filter.statuses": "Updates from people you follow",
+  "notifications.grant_permission": "Grant permission.",
+  "notifications.group": "{count} notifications",
+  "notifications.mark_as_read": "Mark every notification as read",
+  "notifications.permission_denied": "Desktop notifications are unavailable due to previously denied browser permissions request",
+  "notifications.permission_denied_alert": "Desktop notifications can't be enabled, as browser permission has been denied before",
+  "notifications.permission_required": "Desktop notifications are unavailable because the required permission has not been granted.",
+  "notifications_permission_banner.enable": "Enable desktop notifications",
+  "notifications_permission_banner.how_to_control": "To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled.",
+  "notifications_permission_banner.title": "Never miss a thing",
+  "picture_in_picture.restore": "Put it back",
+  "poll.closed": "Closed",
+  "poll.refresh": "Refresh",
+  "poll.total_people": "{count, plural, one {# person} other {# people}}",
+  "poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
+  "poll.vote": "Vote",
+  "poll.voted": "You voted for this answer",
+  "poll.votes": "{votes, plural, one {# vote} other {# votes}}",
+  "poll_button.add_poll": "Add a poll",
+  "poll_button.remove_poll": "Remove poll",
+  "privacy.change": "Adjust status privacy",
+  "privacy.direct.long": "Visible for mentioned users only",
+  "privacy.direct.short": "Direct",
+  "privacy.private.long": "Visible for followers only",
+  "privacy.private.short": "Followers-only",
+  "privacy.public.long": "Visible for all",
+  "privacy.public.short": "Public",
+  "privacy.unlisted.long": "Visible for all, but opted-out of discovery features",
+  "privacy.unlisted.short": "Unlisted",
+  "privacy_policy.last_updated": "Last updated {date}",
+  "privacy_policy.title": "Privacy Policy",
+  "refresh": "Refresh",
+  "regeneration_indicator.label": "Loading…",
+  "regeneration_indicator.sublabel": "Your home feed is being prepared!",
+  "relative_time.days": "{number}d",
+  "relative_time.full.days": "{number, plural, one {# day} other {# days}} ago",
+  "relative_time.full.hours": "{number, plural, one {# hour} other {# hours}} ago",
+  "relative_time.full.just_now": "just now",
+  "relative_time.full.minutes": "{number, plural, one {# minute} other {# minutes}} ago",
+  "relative_time.full.seconds": "{number, plural, one {# second} other {# seconds}} ago",
+  "relative_time.hours": "{number}h",
+  "relative_time.just_now": "now",
+  "relative_time.minutes": "{number}m",
+  "relative_time.seconds": "{number}s",
+  "relative_time.today": "today",
+  "reply_indicator.cancel": "Cancel",
+  "report.block": "Block",
+  "report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.",
+  "report.categories.other": "Other",
+  "report.categories.spam": "Spam",
+  "report.categories.violation": "Content violates one or more server rules",
+  "report.category.subtitle": "Choose the best match",
+  "report.category.title": "Tell us what's going on with this {type}",
+  "report.category.title_account": "profile",
+  "report.category.title_status": "post",
+  "report.close": "Done",
+  "report.comment.title": "Is there anything else you think we should know?",
+  "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.mute": "Mute",
+  "report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.",
+  "report.next": "Next",
+  "report.placeholder": "Type or paste additional comments",
+  "report.reasons.dislike": "I don't like it",
+  "report.reasons.dislike_description": "It is not something you want to see",
+  "report.reasons.other": "It's something else",
+  "report.reasons.other_description": "The issue does not fit into other categories",
+  "report.reasons.spam": "It's spam",
+  "report.reasons.spam_description": "Malicious links, fake engagement, or repetitive replies",
+  "report.reasons.violation": "It violates server rules",
+  "report.reasons.violation_description": "You are aware that it breaks specific rules",
+  "report.rules.subtitle": "Select all that apply",
+  "report.rules.title": "Which rules are being violated?",
+  "report.statuses.subtitle": "Select all that apply",
+  "report.statuses.title": "Are there any posts that back up this report?",
+  "report.submit": "Submit report",
+  "report.target": "Report {target}",
+  "report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:",
+  "report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:",
+  "report.thanks.title": "Don't want to see this?",
+  "report.thanks.title_actionable": "Thanks for reporting, we'll look into this.",
+  "report.unfollow": "Unfollow @{name}",
+  "report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.",
+  "report_notification.attached_statuses": "{count, plural, one {{count} post} other {{count} posts}} attached",
+  "report_notification.categories.other": "Other",
+  "report_notification.categories.spam": "Spam",
+  "report_notification.categories.violation": "Rule violation",
+  "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
+  "search.placeholder": "Search",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
+  "search.search_or_paste": "Search or paste URL",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
+  "search_results.all": "All",
+  "search_results.hashtags": "Hashtags",
+  "search_results.nothing_found": "Could not find anything for these search terms",
+  "search_results.statuses": "Posts",
+  "search_results.statuses_fts_disabled": "Searching posts by their content is not enabled on this Mastodon server.",
+  "search_results.title": "Search for {q}",
+  "search_results.total": "{count, number} {count, plural, one {result} other {results}}",
+  "server_banner.about_active_users": "People using this server during the last 30 days (Monthly Active Users)",
+  "server_banner.active_users": "active users",
+  "server_banner.administered_by": "Administered by:",
+  "server_banner.introduction": "{domain} is part of the decentralized social network powered by {mastodon}.",
+  "server_banner.learn_more": "Learn more",
+  "server_banner.server_stats": "Server stats:",
+  "sign_in_banner.create_account": "Create account",
+  "sign_in_banner.sign_in": "Sign in",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
+  "status.admin_account": "Open moderation interface for @{name}",
+  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_status": "Open this status in the moderation interface",
+  "status.block": "Block @{name}",
+  "status.bookmark": "Bookmark",
+  "status.cancel_reblog_private": "Unboost",
+  "status.cannot_reblog": "This post cannot be boosted",
+  "status.copy": "Copy link to status",
+  "status.delete": "Delete",
+  "status.detailed_status": "Detailed conversation view",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
+  "status.edit": "Edit",
+  "status.edited": "Edited {date}",
+  "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
+  "status.embed": "Embed",
+  "status.favourite": "Favourite",
+  "status.filter": "Filter this post",
+  "status.filtered": "Filtered",
+  "status.hide": "Hide post",
+  "status.history.created": "{name} created {date}",
+  "status.history.edited": "{name} edited {date}",
+  "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 post",
+  "status.read_more": "Read more",
+  "status.reblog": "Boost",
+  "status.reblog_private": "Boost with original visibility",
+  "status.reblogged_by": "{name} boosted",
+  "status.reblogs.empty": "No one has boosted this post yet. When someone does, they will show up here.",
+  "status.redraft": "Delete & re-draft",
+  "status.remove_bookmark": "Remove bookmark",
+  "status.replied_to": "Replied to {name}",
+  "status.reply": "Reply",
+  "status.replyAll": "Reply to thread",
+  "status.report": "Report @{name}",
+  "status.sensitive_warning": "Sensitive content",
+  "status.share": "Share",
+  "status.show_filter_reason": "Show anyway",
+  "status.show_less": "Show less",
+  "status.show_less_all": "Show less for all",
+  "status.show_more": "Show more",
+  "status.show_more_all": "Show more for all",
+  "status.show_original": "Show original",
+  "status.translate": "Translate",
+  "status.translated_from_with": "Translated from {lang} using {provider}",
+  "status.uncached_media_warning": "Not available",
+  "status.unmute_conversation": "Unmute conversation",
+  "status.unpin": "Unpin from profile",
+  "subscribed_languages.lead": "Only posts in selected languages will appear on your home and list timelines after the change. Select none to receive posts in all languages.",
+  "subscribed_languages.save": "Save changes",
+  "subscribed_languages.target": "Change subscribed languages for {target}",
+  "suggestions.dismiss": "Dismiss suggestion",
+  "suggestions.header": "You might be interested in…",
+  "tabs_bar.federated_timeline": "Federated",
+  "tabs_bar.home": "Home",
+  "tabs_bar.local_timeline": "Local",
+  "tabs_bar.notifications": "Notifications",
+  "time_remaining.days": "{number, plural, one {# day} other {# days}} left",
+  "time_remaining.hours": "{number, plural, one {# hour} other {# hours}} left",
+  "time_remaining.minutes": "{number, plural, one {# minute} other {# minutes}} left",
+  "time_remaining.moments": "Moments remaining",
+  "time_remaining.seconds": "{number, plural, one {# second} other {# seconds}} left",
+  "timeline_hint.remote_resource_not_displayed": "{resource} from other servers are not displayed.",
+  "timeline_hint.resources.followers": "Followers",
+  "timeline_hint.resources.follows": "Follows",
+  "timeline_hint.resources.statuses": "Older posts",
+  "trends.counter_by_accounts": "{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {{days} days}}",
+  "trends.trending_now": "Trending now",
+  "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
+  "units.short.billion": "{count}B",
+  "units.short.million": "{count}M",
+  "units.short.thousand": "{count}K",
+  "upload_area.title": "Drag & drop to upload",
+  "upload_button.label": "Add images, a video or an audio file",
+  "upload_error.limit": "File upload limit exceeded.",
+  "upload_error.poll": "File upload not allowed with polls.",
+  "upload_form.audio_description": "Describe for people who are hard of hearing",
+  "upload_form.description": "Describe for people who are blind or have low vision",
+  "upload_form.description_missing": "No description added",
+  "upload_form.edit": "Edit",
+  "upload_form.thumbnail": "Change thumbnail",
+  "upload_form.undo": "Delete",
+  "upload_form.video_description": "Describe for people who are deaf, hard of hearing, blind or have low vision",
+  "upload_modal.analyzing_picture": "Analyzing picture…",
+  "upload_modal.apply": "Apply",
+  "upload_modal.applying": "Applying…",
+  "upload_modal.choose_image": "Choose image",
+  "upload_modal.description_placeholder": "A quick brown fox jumps over the lazy dog",
+  "upload_modal.detect_text": "Detect text from picture",
+  "upload_modal.edit_media": "Edit media",
+  "upload_modal.hint": "Click or drag the circle on the preview to choose the focal point which will always be in view on all thumbnails.",
+  "upload_modal.preparing_ocr": "Preparing OCR…",
+  "upload_modal.preview_label": "Preview ({ratio})",
+  "upload_progress.label": "Uploading…",
+  "upload_progress.processing": "Processing…",
+  "video.close": "Close video",
+  "video.download": "Download file",
+  "video.exit_fullscreen": "Exit full screen",
+  "video.expand": "Expand video",
+  "video.fullscreen": "Full screen",
+  "video.hide": "Hide video",
+  "video.mute": "Mute sound",
+  "video.pause": "Pause",
+  "video.play": "Play",
+  "video.unmute": "Unmute sound"
+}
diff --git a/app/javascript/mastodon/locales/vi.json b/app/javascript/mastodon/locales/vi.json
index 7bc505474..7d51c7055 100644
--- a/app/javascript/mastodon/locales/vi.json
+++ b/app/javascript/mastodon/locales/vi.json
@@ -1,7 +1,7 @@
 {
   "about.blocks": "Giới hạn chung",
   "about.contact": "Liên lạc:",
-  "about.disclaimer": "Mastodon là phần mềm tự do mã nguồn mở, một thương hiệu của Mastodon gGmbH.",
+  "about.disclaimer": "Mastodon là phần mềm tự do nguồn mở của Mastodon gGmbH.",
   "about.domain_blocks.no_reason_available": "Lý do không được cung cấp",
   "about.domain_blocks.preamble": "Mastodon cho phép bạn tương tác nội dung và giao tiếp với mọi người từ bất kỳ máy chủ nào khác trong mạng liên hợp. Còn máy chủ này có những ngoại lệ riêng.",
   "about.domain_blocks.silenced.explanation": "Nói chung, bạn sẽ không thấy người và nội dung từ máy chủ này, trừ khi bạn tự tìm kiếm hoặc tự theo dõi.",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "Bạn chưa lưu thay đổi đối với phần mô tả hoặc bản xem trước của media, vẫn bỏ luôn?",
   "confirmations.domain_block.confirm": "Ẩn toàn bộ máy chủ",
   "confirmations.domain_block.message": "Bạn thật sự muốn ẩn toàn bộ nội dung từ {domain}? Sẽ hợp lý hơn nếu bạn chỉ chặn hoặc ẩn một vài tài khoản cụ thể. Ẩn toàn bộ nội dung từ máy chủ sẽ khiến bạn không còn thấy nội dung từ máy chủ đó ở bất kỳ nơi nào, kể cả thông báo. Người quan tâm bạn từ máy chủ đó cũng sẽ bị xóa luôn.",
+  "confirmations.edit.confirm": "Sửa",
+  "confirmations.edit.message": "Nội dung tút cũ sẽ bị ghi đè, bạn có tiếp tục?",
   "confirmations.logout.confirm": "Đăng xuất",
   "confirmations.logout.message": "Bạn có thật sự muốn thoát?",
   "confirmations.mute.confirm": "Ẩn",
@@ -221,7 +223,8 @@
   "empty_column.favourites": "Chưa có ai thích tút này.",
   "empty_column.follow_recommendations": "Bạn chưa có gợi ý theo dõi nào. Hãy thử tìm kiếm những người thú vị hoặc khám phá những hashtag nổi bật.",
   "empty_column.follow_requests": "Bạn chưa có yêu cầu theo dõi nào.",
-  "empty_column.hashtag": "Chưa có bài đăng nào dùng hashtag này.",
+  "empty_column.followed_tags": "Bạn chưa theo dõi hashtag nào. Khi bạn theo dõi, chúng sẽ hiện lên ở đây.",
+  "empty_column.hashtag": "Chưa có tút nào dùng hashtag này.",
   "empty_column.home": "Bảng tin của bạn đang trống! Hãy theo dõi nhiều người hơn. {suggestions}",
   "empty_column.home.suggestions": "Gợi ý dành cho bạn",
   "empty_column.list": "Chưa có tút. Khi những người trong danh sách này đăng tút mới, chúng sẽ xuất hiện ở đây.",
@@ -263,13 +266,15 @@
   "follow_request.authorize": "Cho phép",
   "follow_request.reject": "Từ chối",
   "follow_requests.unlocked_explanation": "Mặc dù tài khoản của bạn đang ở chế độ công khai, quản trị viên của {domain} vẫn tin rằng bạn sẽ muốn xem lại yêu cầu theo dõi từ những người khác.",
+  "followed_tags": "Hashtag theo dõi",
   "footer.about": "Giới thiệu",
   "footer.directory": "Cộng đồng",
   "footer.get_app": "Ứng dụng",
   "footer.invite": "Mời bạn bè",
   "footer.keyboard_shortcuts": "Phím tắt",
-  "footer.privacy_policy": "Chính sách bảo mật",
+  "footer.privacy_policy": "Bảo mật",
   "footer.source_code": "Mã nguồn",
+  "footer.status": "Trạng thái",
   "generic.saved": "Đã lưu",
   "getting_started.heading": "Quản lý",
   "hashtag.column_header.tag_mode.all": "và {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "mở các mục",
   "keyboard_shortcuts.compose": "mở khung soạn tút",
   "keyboard_shortcuts.description": "Mô tả",
-  "keyboard_shortcuts.direct": "để mở cột tin nhắn",
+  "keyboard_shortcuts.direct": "mở mục nhắn riêng",
   "keyboard_shortcuts.down": "di chuyển xuống dưới danh sách",
   "keyboard_shortcuts.enter": "viết tút mới",
   "keyboard_shortcuts.favourite": "thích",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Thích",
   "navigation_bar.filters": "Bộ lọc từ ngữ",
   "navigation_bar.follow_requests": "Yêu cầu theo dõi",
+  "navigation_bar.followed_tags": "Hashtag theo dõi",
   "navigation_bar.follows_and_followers": "Quan hệ",
   "navigation_bar.lists": "Danh sách",
   "navigation_bar.logout": "Đăng xuất",
@@ -516,14 +522,16 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Vi phạm nội quy",
   "report_notification.open": "Mở báo cáo",
+  "search.no_recent_searches": "Không có tìm kiếm gần đây",
   "search.placeholder": "Tìm kiếm",
+  "search.quick_action.account_search": "Người trùng khớp {x}",
+  "search.quick_action.go_to_account": "Xem trang {x}",
+  "search.quick_action.go_to_hashtag": "Xem hashtag {x}",
+  "search.quick_action.open_url": "Mở liên kết trong Mastodon",
+  "search.quick_action.status_search": "Tút trùng khớp {x}",
   "search.search_or_paste": "Tìm kiếm hoặc nhập URL",
-  "search_popout.search_format": "Gợi ý",
-  "search_popout.tips.full_text": "Nội dung trả về bao gồm những tút mà bạn đã viết, thích, đăng lại hoặc những tút có nhắc đến bạn. Bạn cũng có thể tìm tên người dùng, biệt danh và hashtag.",
-  "search_popout.tips.hashtag": "hashtag",
-  "search_popout.tips.status": "tút",
-  "search_popout.tips.text": "Nội dung trả về là tên người dùng, biệt danh và hashtag",
-  "search_popout.tips.user": "mọi người",
+  "search_popout.quick_actions": "Thao tác nhanh",
+  "search_popout.recent": "Tìm kiếm gần đây",
   "search_results.accounts": "Mọi người",
   "search_results.all": "Toàn bộ",
   "search_results.hashtags": "Hashtags",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Thống kê:",
   "sign_in_banner.create_account": "Đăng ký",
   "sign_in_banner.sign_in": "Đăng nhập",
-  "sign_in_banner.text": "Đăng nhập để theo dõi người hoặc hashtag; cũng như thích, chia sẻ và trả lời tút.",
+  "sign_in_banner.text": "Đăng nhập để theo dõi người hoặc hashtag, thích, chia sẻ và trả lời tút. Bạn cũng có thể tương tác từ tài khoản của mình trên một máy chủ khác.",
   "status.admin_account": "Mở giao diện quản trị @{name}",
   "status.admin_domain": "Mở giao diện quản trị @{domain}",
   "status.admin_status": "Mở tút này trong giao diện quản trị",
@@ -552,6 +560,7 @@
   "status.delete": "Xóa",
   "status.detailed_status": "Xem chi tiết thêm",
   "status.direct": "Nhắn riêng @{name}",
+  "status.direct_indicator": "Nhắn riêng",
   "status.edit": "Sửa",
   "status.edited": "Đã sửa {date}",
   "status.edited_x_times": "Đã sửa {count, plural, other {{count} lần}}",
diff --git a/app/javascript/mastodon/locales/whitelist_csb.json b/app/javascript/mastodon/locales/whitelist_csb.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_csb.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/mastodon/locales/whitelist_uz.json b/app/javascript/mastodon/locales/whitelist_uz.json
new file mode 100644
index 000000000..0d4f101c7
--- /dev/null
+++ b/app/javascript/mastodon/locales/whitelist_uz.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/app/javascript/mastodon/locales/zgh.json b/app/javascript/mastodon/locales/zgh.json
index 9b620cde8..f02d1a15d 100644
--- a/app/javascript/mastodon/locales/zgh.json
+++ b/app/javascript/mastodon/locales/zgh.json
@@ -20,7 +20,7 @@
   "account.blocked": "ⵉⵜⵜⵓⴳⴷⵍ",
   "account.browse_more_on_origin_server": "ⵙⵜⴰⵔⴰ ⵓⴳⴳⴰⵔ ⴳ ⵉⴼⵔⵙ ⴰⵏⵚⵍⵉ",
   "account.cancel_follow_request": "Withdraw follow request",
-  "account.direct": "ⵜⵓⵣⵉⵏⵜ ⵜⵓⵙⵔⵉⴷⵜ @{name}",
+  "account.direct": "Privately mention @{name}",
   "account.disable_notifications": "Stop notifying me when @{name} posts",
   "account.domain_blocked": "ⵉⵜⵜⵓⴳⴷⵍ ⵉⴳⵔ",
   "account.edit_profile": "ⵙⵏⴼⵍ ⵉⴼⵔⵙ",
@@ -102,7 +102,7 @@
   "column.blocks": "ⵉⵏⵙⵙⵎⵔⵙⵏ ⵜⵜⵓⴳⴷⵍⵏⵉⵏ",
   "column.bookmarks": "Bookmarks",
   "column.community": "Local timeline",
-  "column.direct": "Direct messages",
+  "column.direct": "Private mentions",
   "column.directory": "Browse profiles",
   "column.domain_blocks": "Blocked domains",
   "column.favourites": "ⵜⵓⴼⵓⵜⵉⵏ",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "You have unsaved changes to the media description or preview, discard them anyway?",
   "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.edit.confirm": "Edit",
+  "confirmations.edit.message": "Editing now will overwrite the message you are currently composing. Are you sure you want to proceed?",
   "confirmations.logout.confirm": "ⴼⴼⵖ",
   "confirmations.logout.message": "ⵉⵙ ⵏⵉⵜ ⵜⵅⵙⴷ ⴰⴷ ⵜⴼⴼⵖⴷ?",
   "confirmations.mute.confirm": "ⵥⵥⵉⵥⵏ",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "You haven't blocked any users yet.",
   "empty_column.bookmarked_statuses": "You don't have any bookmarked toots yet. When you bookmark one, it will show up here.",
   "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!",
-  "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.",
+  "empty_column.direct": "You don't have any private mentions yet. When you send or receive one, it will show up here.",
   "empty_column.domain_blocks": "There are no blocked domains yet.",
   "empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
   "empty_column.favourited_statuses": "You don't have any favourite 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_recommendations": "Looks like no suggestions could be generated for you. You can try using search to look for people you might know or explore trending hashtags.",
   "empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
+  "empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
   "empty_column.hashtag": "There is nothing in this hashtag yet.",
   "empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
   "empty_column.home.suggestions": "See some suggestions",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "Authorize",
   "follow_request.reject": "ⴰⴳⵢ",
   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.",
+  "followed_tags": "Followed hashtags",
   "footer.about": "About",
   "footer.directory": "Profiles directory",
   "footer.get_app": "Get the app",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "Keyboard shortcuts",
   "footer.privacy_policy": "Privacy policy",
   "footer.source_code": "View source code",
+  "footer.status": "Status",
   "generic.saved": "Saved",
   "getting_started.heading": "Getting started",
   "hashtag.column_header.tag_mode.all": "ⴷ {additional}",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "Bookmarks",
   "navigation_bar.community_timeline": "Local timeline",
   "navigation_bar.compose": "Compose new toot",
-  "navigation_bar.direct": "Direct messages",
+  "navigation_bar.direct": "Private mentions",
   "navigation_bar.discover": "Discover",
   "navigation_bar.domain_blocks": "Hidden domains",
   "navigation_bar.edit_profile": "ⵙⵏⴼⵍ ⵉⴼⵔⵙ",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "Favourites",
   "navigation_bar.filters": "Muted words",
   "navigation_bar.follow_requests": "ⵜⵓⵜⵔⴰⵡⵉⵏ ⵏ ⵓⴹⴼⴰⵕ",
+  "navigation_bar.followed_tags": "Followed hashtags",
   "navigation_bar.follows_and_followers": "Follows and followers",
   "navigation_bar.lists": "ⵜⵉⵍⴳⴰⵎⵉⵏ",
   "navigation_bar.logout": "ⴼⴼⵖ",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "Spam",
   "report_notification.categories.violation": "Rule violation",
   "report_notification.open": "Open report",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "ⵔⵣⵓ",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "Search or paste URL",
-  "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": "ⵎⵉⴷⴷⵏ",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "All",
   "search_results.hashtags": "ⵀⴰⵛⵟⴰⴳ",
   "search_results.nothing_found": "Could not find anything for these search terms",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "Server stats:",
   "sign_in_banner.create_account": "Create account",
   "sign_in_banner.sign_in": "Sign in",
-  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts, or interact from your account on a different server.",
+  "sign_in_banner.text": "Sign in to follow profiles or hashtags, favourite, share and reply to posts. You can also interact from your account on a different server.",
   "status.admin_account": "Open moderation interface for @{name}",
   "status.admin_domain": "Open moderation interface for {domain}",
   "status.admin_status": "Open this status in the moderation interface",
@@ -551,7 +559,8 @@
   "status.copy": "Copy link to status",
   "status.delete": "ⴽⴽⵙ",
   "status.detailed_status": "Detailed conversation view",
-  "status.direct": "ⵜⵓⵣⵉⵏⵜ ⵜⵓⵙⵔⵉⴷⵜ ⵉ @{name}",
+  "status.direct": "Privately mention @{name}",
+  "status.direct_indicator": "Private mention",
   "status.edit": "Edit",
   "status.edited": "Edited {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index 195c63198..f9e31c450 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -9,7 +9,7 @@
   "about.domain_blocks.suspended.explanation": "此服务器的数据将不会被处理、存储或者交换,本站也将无法和来自此服务器的用户互动或者交流。",
   "about.domain_blocks.suspended.title": "已封禁",
   "about.not_available": "此信息在当前服务器尚不可用。",
-  "about.powered_by": "由 {mastodon} 驱动的分布式社交媒体",
+  "about.powered_by": "由 {mastodon} 驱动的去中心化社交媒体",
   "about.rules": "站点规则",
   "account.account_note_header": "备注",
   "account.add_or_remove_from_list": "从列表中添加或移除",
@@ -20,7 +20,7 @@
   "account.blocked": "已屏蔽",
   "account.browse_more_on_origin_server": "在原始个人资料页面上浏览详情",
   "account.cancel_follow_request": "撤回关注请求",
-  "account.direct": "发送私信给 @{name}",
+  "account.direct": "私下提及 @{name}",
   "account.disable_notifications": "当 @{name} 发嘟时不要通知我",
   "account.domain_blocked": "域名已屏蔽",
   "account.edit_profile": "修改个人资料",
@@ -37,7 +37,7 @@
   "account.following_counter": "正在关注 {counter} 人",
   "account.follows.empty": "此用户目前尚未关注任何人。",
   "account.follows_you": "关注了你",
-  "account.go_to_profile": "转到个人资料界面",
+  "account.go_to_profile": "转到个人资料页",
   "account.hide_reblogs": "隐藏来自 @{name} 的转贴",
   "account.joined_short": "加入于",
   "account.languages": "更改订阅语言",
@@ -53,7 +53,7 @@
   "account.posts": "嘟文",
   "account.posts_with_replies": "嘟文和回复",
   "account.report": "举报 @{name}",
-  "account.requested": "正在等待对方同意。点击以取消发送关注请求",
+  "account.requested": "正在等待对方同意。点击取消发送关注请求",
   "account.requested_follow": "{name} 已经向你发送了关注请求",
   "account.share": "分享 @{name} 的个人资料页",
   "account.show_reblogs": "显示来自 @{name} 的转嘟",
@@ -102,7 +102,7 @@
   "column.blocks": "屏蔽的用户",
   "column.bookmarks": "书签",
   "column.community": "本站时间轴",
-  "column.direct": "私信",
+  "column.direct": "私下提及",
   "column.directory": "浏览用户资料",
   "column.domain_blocks": "已屏蔽的域名",
   "column.favourites": "喜欢",
@@ -127,9 +127,9 @@
   "compose.language.change": "更改语言",
   "compose.language.search": "搜索语言...",
   "compose_form.direct_message_warning_learn_more": "了解详情",
-  "compose_form.encryption_warning": "Mastodon 上的嘟文并未端到端加密。请不要在 Mastodon 上分享敏感信息。",
+  "compose_form.encryption_warning": "Mastodon 上的嘟文未经端到端加密。请勿在 Mastodon 上分享敏感信息。",
   "compose_form.hashtag_warning": "这条嘟文被设置为“不公开”,因此它不会出现在任何话题标签的列表下。只有公开的嘟文才能通过话题标签进行搜索。",
-  "compose_form.lock_disclaimer": "你的帐户没有{locked}。任何人都可以在关注你后立即查看仅关注者可见的嘟文。",
+  "compose_form.lock_disclaimer": "你的账户没有{locked}。任何人都可以在关注你后立即查看仅关注者可见的嘟文。",
   "compose_form.lock_disclaimer.lock": "开启保护",
   "compose_form.placeholder": "在想些什么?",
   "compose_form.poll.add_option": "添加一个选项",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "您还有未保存的媒体描述或预览修改,仍然丢弃它们吗?",
   "confirmations.domain_block.confirm": "屏蔽整个域名",
   "confirmations.domain_block.message": "你真的确定要屏蔽所有来自 {domain} 的内容吗?多数情况下,屏蔽或隐藏几个特定的用户就已经足够了。来自该网站的内容将不再出现在你的任何公共时间轴或通知列表里。来自该网站的关注者将会被移除。",
+  "confirmations.edit.confirm": "编辑",
+  "confirmations.edit.message": "编辑此消息将会覆盖当前正在撰写的信息。仍要继续吗?",
   "confirmations.logout.confirm": "登出",
   "confirmations.logout.message": "你确定要登出吗?",
   "confirmations.mute.confirm": "隐藏",
@@ -200,7 +202,7 @@
   "emoji_button.food": "食物和饮料",
   "emoji_button.label": "插入表情符号",
   "emoji_button.nature": "自然",
-  "emoji_button.not_found": "木有这个表情符号!(╯°□°)╯︵ ┻━┻",
+  "emoji_button.not_found": "未找到匹配的表情符号",
   "emoji_button.objects": "物体",
   "emoji_button.people": "人物",
   "emoji_button.recent": "常用",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "你还未屏蔽任何用户。",
   "empty_column.bookmarked_statuses": "你还没有给任何嘟文添加过书签。在你添加书签后,嘟文就会显示在这里。",
   "empty_column.community": "本站时间轴暂时没有内容,快写点什么让它动起来吧!",
-  "empty_column.direct": "你还未使用过私信。当你发出或者收到私信时,它将显示在此。",
+  "empty_column.direct": "你还未使用过私下提及。当你发出或者收到私下提及时,它将显示在此。",
   "empty_column.domain_blocks": "暂且没有被屏蔽的站点。",
   "empty_column.explore_statuses": "目前没有热门话题,稍后再来看看吧!",
   "empty_column.favourited_statuses": "你还没有喜欢过任何嘟文。喜欢过的嘟文会显示在这里。",
   "empty_column.favourites": "没有人喜欢过这条嘟文。如果有人喜欢了,就会显示在这里。",
   "empty_column.follow_recommendations": "似乎无法为你生成任何建议。你可以尝试使用搜索寻找你可能知道的人或探索热门标签。",
   "empty_column.follow_requests": "你没有收到新的关注请求。收到后将显示在此。",
+  "empty_column.followed_tags": "您还没有关注任何话题标签。 当您关注后,它们会出现在这里。",
   "empty_column.hashtag": "这个话题标签下暂时没有内容。",
   "empty_column.home": "你的主页时间线是空的!快去关注更多人吧。 {suggestions}",
   "empty_column.home.suggestions": "查看一些建议",
@@ -243,18 +246,18 @@
   "explore.trending_tags": "话题标签",
   "filter_modal.added.context_mismatch_explanation": "此过滤器分类不适用访问过嘟文的环境中。如果你想要在环境中过滤嘟文,你必须编辑此过滤器。",
   "filter_modal.added.context_mismatch_title": "环境不匹配!",
-  "filter_modal.added.expired_explanation": "此过滤器分类已过期,你需要修改到期日期才能应用。",
+  "filter_modal.added.expired_explanation": "此过滤器类别已过期,你需要修改到期日期才能应用。",
   "filter_modal.added.expired_title": "过滤器已过期!",
   "filter_modal.added.review_and_configure": "要审核并进一步配置此过滤器分类,请前往{settings_link}。",
   "filter_modal.added.review_and_configure_title": "过滤器设置",
   "filter_modal.added.settings_link": "设置页面",
-  "filter_modal.added.short_explanation": "此嘟文已添加到以下过滤器分类:{title}。",
+  "filter_modal.added.short_explanation": "此嘟文已添加到以下过滤器类别:{title}。",
   "filter_modal.added.title": "过滤器已添加 !",
-  "filter_modal.select_filter.context_mismatch": "不适用于此环境",
+  "filter_modal.select_filter.context_mismatch": "不适用于这个情境",
   "filter_modal.select_filter.expired": "已过期",
-  "filter_modal.select_filter.prompt_new": "新分类:{name}",
+  "filter_modal.select_filter.prompt_new": "新类别:{name}",
   "filter_modal.select_filter.search": "搜索或创建",
-  "filter_modal.select_filter.subtitle": "使用一个已存在分类,或创建一个新分类",
+  "filter_modal.select_filter.subtitle": "使用一个已存在分类,或创建一个新类别",
   "filter_modal.select_filter.title": "过滤此嘟文",
   "filter_modal.title.status": "过滤一条嘟文",
   "follow_recommendations.done": "完成",
@@ -263,13 +266,15 @@
   "follow_request.authorize": "授权",
   "follow_request.reject": "拒绝",
   "follow_requests.unlocked_explanation": "尽管你没有锁嘟,但是 {domain} 的工作人员认为你也许会想手动审核审核这些账号的关注请求。",
+  "followed_tags": "关注的话题标签",
   "footer.about": "关于",
   "footer.directory": "用户目录",
-  "footer.get_app": "获取应用程序",
+  "footer.get_app": "获取应用",
   "footer.invite": "邀请",
   "footer.keyboard_shortcuts": "快捷键列表",
   "footer.privacy_policy": "隐私政策",
   "footer.source_code": "查看源代码",
+  "footer.status": "状态",
   "generic.saved": "已保存",
   "getting_started.heading": "开始使用",
   "hashtag.column_header.tag_mode.all": "以及 {additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "选择某栏",
   "keyboard_shortcuts.compose": "选择输入框",
   "keyboard_shortcuts.description": "说明",
-  "keyboard_shortcuts.direct": "打开私信栏",
+  "keyboard_shortcuts.direct": "打开私下提及栏",
   "keyboard_shortcuts.down": "在列表中让光标下移",
   "keyboard_shortcuts.enter": "展开嘟文",
   "keyboard_shortcuts.favourite": "喜欢嘟文",
@@ -358,8 +363,8 @@
   "lists.search": "搜索你关注的人",
   "lists.subheading": "你的列表",
   "load_pending": "{count} 项",
-  "loading_indicator.label": "加载中……",
-  "media_gallery.toggle_visible": "隐藏图片",
+  "loading_indicator.label": "加载中…",
+  "media_gallery.toggle_visible": "{number, plural, other {隐藏图像}}",
   "missing_indicator.label": "找不到内容",
   "missing_indicator.sublabel": "无法找到此资源",
   "moved_to_account_banner.text": "您的账号 {disabledAccount} 已停用,因为您已迁移到 {movedToAccount} 。",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "书签",
   "navigation_bar.community_timeline": "本站时间轴",
   "navigation_bar.compose": "撰写新嘟文",
-  "navigation_bar.direct": "私信",
+  "navigation_bar.direct": "私下提及",
   "navigation_bar.discover": "发现",
   "navigation_bar.domain_blocks": "已屏蔽的域名",
   "navigation_bar.edit_profile": "修改个人资料",
@@ -379,9 +384,10 @@
   "navigation_bar.favourites": "喜欢",
   "navigation_bar.filters": "忽略的关键词",
   "navigation_bar.follow_requests": "关注请求",
+  "navigation_bar.followed_tags": "关注的话题标签",
   "navigation_bar.follows_and_followers": "关注管理",
   "navigation_bar.lists": "列表",
-  "navigation_bar.logout": "登出",
+  "navigation_bar.logout": "退出登录",
   "navigation_bar.mutes": "已隐藏的用户",
   "navigation_bar.personal": "个人",
   "navigation_bar.pins": "置顶嘟文",
@@ -497,7 +503,7 @@
   "report.reasons.other_description": "该问题不符合其他类别",
   "report.reasons.spam": "它是垃圾信息",
   "report.reasons.spam_description": "恶意链接,虚假互动和重复回复",
-  "report.reasons.violation": "它违反了服务器规则",
+  "report.reasons.violation": "违反服务器规则",
   "report.reasons.violation_description": "你清楚它违反了特定的规则",
   "report.rules.subtitle": "选择所有适用选项",
   "report.rules.title": "哪些规则被违反了?",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "骚扰",
   "report_notification.categories.violation": "违反规则",
   "report_notification.open": "打开举报",
+  "search.no_recent_searches": "无最近搜索",
   "search.placeholder": "搜索",
+  "search.quick_action.account_search": "匹配 {x} 的个人资料",
+  "search.quick_action.go_to_account": "转到 {x} 个人资料",
+  "search.quick_action.go_to_hashtag": "转到标签 {x}",
+  "search.quick_action.open_url": "在 Mastodon 中打开链接",
+  "search.quick_action.status_search": "匹配 {x} 的帖子",
   "search.search_or_paste": "搜索或输入链接",
-  "search_popout.search_format": "高级搜索格式",
-  "search_popout.tips.full_text": "输入关键词检索所有你发送、喜欢、转嘟过或提及到你的帖子,以及其他用户公开的用户名、昵称和话题标签。",
-  "search_popout.tips.hashtag": "话题标签",
-  "search_popout.tips.status": "嘟文",
-  "search_popout.tips.text": "输入关键词检索昵称、用户名和话题标签",
-  "search_popout.tips.user": "用户",
-  "search_results.accounts": "用户",
+  "search_popout.quick_actions": "快捷操作",
+  "search_popout.recent": "最近搜索",
+  "search_results.accounts": "个人资料",
   "search_results.all": "全部",
   "search_results.hashtags": "话题标签",
   "search_results.nothing_found": "无法找到符合这些搜索词的任何内容",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "服务器统计数据:",
   "sign_in_banner.create_account": "创建账户",
   "sign_in_banner.sign_in": "登录",
-  "sign_in_banner.text": "登录以关注个人资料或话题标签、喜欢、分享和嘟文,或与在不同服务器上的账号进行互动。",
+  "sign_in_banner.text": "登录以关注个人资料、话题标签或喜欢、分享和回复嘟文。您还能用您的账户在另一个服务器上进行互动。",
   "status.admin_account": "打开 @{name} 的管理界面",
   "status.admin_domain": "打开 {domain} 的管理界面",
   "status.admin_status": "打开此帖的管理界面",
@@ -551,7 +559,8 @@
   "status.copy": "复制嘟文链接",
   "status.delete": "删除",
   "status.detailed_status": "详细的对话视图",
-  "status.direct": "私信 @{name}",
+  "status.direct": "私下提及 @{name}",
+  "status.direct_indicator": "私下提及",
   "status.edit": "编辑",
   "status.edited": "编辑于 {date}",
   "status.edited_x_times": "共编辑 {count, plural, one {{count} 次} other {{count} 次}}",
diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json
index aa0530da3..d130662fc 100644
--- a/app/javascript/mastodon/locales/zh-HK.json
+++ b/app/javascript/mastodon/locales/zh-HK.json
@@ -20,7 +20,7 @@
   "account.blocked": "已封鎖",
   "account.browse_more_on_origin_server": "前往原始的個人檔案頁瀏覽更多",
   "account.cancel_follow_request": "撤回追蹤請求",
-  "account.direct": "私訊 @{name}",
+  "account.direct": "私下提及 @{name}",
   "account.disable_notifications": "當 @{name} 發文時不要再通知我",
   "account.domain_blocked": "網域被封鎖",
   "account.edit_profile": "修改個人檔案",
@@ -102,7 +102,7 @@
   "column.blocks": "封鎖名單",
   "column.bookmarks": "書籤",
   "column.community": "本站時間軸",
-  "column.direct": "私訊",
+  "column.direct": "私人提及",
   "column.directory": "瀏覽個人資料",
   "column.domain_blocks": "封鎖的服務站",
   "column.favourites": "最愛的文章",
@@ -128,7 +128,7 @@
   "compose.language.search": "搜尋語言...",
   "compose_form.direct_message_warning_learn_more": "了解更多",
   "compose_form.encryption_warning": "Mastodon 上的帖文並未端對端加密。請不要透過 Mastodon 分享任何敏感資訊。",
-  "compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
+  "compose_form.hashtag_warning": "由於此帖文並非公開,因此它不會列在標籤下。只有公開帖文才可以經標籤搜尋。",
   "compose_form.lock_disclaimer": "你的用戶狀態沒有{locked},任何人都能立即關注你,然後看到「只有關注者能看」的文章。",
   "compose_form.lock_disclaimer.lock": "鎖定",
   "compose_form.placeholder": "你在想甚麼?",
@@ -162,6 +162,8 @@
   "confirmations.discard_edit_media.message": "您在媒體描述或預覽有尚未儲存的變更。確定要捨棄它們嗎?",
   "confirmations.domain_block.confirm": "封鎖整個網站",
   "confirmations.domain_block.message": "你真的真的確定要封鎖整個 {domain} ?多數情況下,封鎖或靜音幾個特定目標就已經有效,也是比較建議的做法。若然封鎖全站,你將不會再在這裏看到該站的內容和通知。來自該站的關注者亦會被移除。",
+  "confirmations.edit.confirm": "編輯",
+  "confirmations.edit.message": "現在編輯將會覆蓋你目前正在撰寫的訊息。你確定要繼續嗎?",
   "confirmations.logout.confirm": "登出",
   "confirmations.logout.message": "確定要登出嗎?",
   "confirmations.mute.confirm": "靜音",
@@ -214,13 +216,14 @@
   "empty_column.blocks": "你還沒有封鎖任何使用者。",
   "empty_column.bookmarked_statuses": "你還沒建立任何書籤。這裡將會顯示你建立的書籤。",
   "empty_column.community": "本站時間軸暫時未有內容,快寫一點東西來搶頭香啊!",
-  "empty_column.direct": "您還沒有任何私訊。當您私訊別人或收到私訊時,它將在此顯示。",
+  "empty_column.direct": "你還沒有私人提及。當你發送或收到時,它將顯示在這裏。",
   "empty_column.domain_blocks": "尚未隱藏任何網域。",
   "empty_column.explore_statuses": "目前沒有熱門話題,請稍候再回來看看!",
   "empty_column.favourited_statuses": "你還沒收藏任何文章。這裡將會顯示你收藏的嘟文。",
   "empty_column.favourites": "還沒有人收藏這則文章。這裡將會顯示被收藏的嘟文。",
   "empty_column.follow_recommendations": "似乎未能替您產生任何建議。您可以試著搜尋您知道的帳戶或者探索熱門主題標籤",
   "empty_column.follow_requests": "您尚未收到任何追蹤請求。這裡將會顯示收到的追蹤請求。",
+  "empty_column.followed_tags": "你還沒有追蹤標籤。當你追蹤後,標籤將顯示在此處。",
   "empty_column.hashtag": "這個標籤暫時未有內容。",
   "empty_column.home": "你還沒有關注任何使用者。快看看{public},向其他使用者搭訕吧。",
   "empty_column.home.suggestions": "檢視部份建議",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "批准",
   "follow_request.reject": "拒絕",
   "follow_requests.unlocked_explanation": "即使您的帳號未上鎖,{domain} 的工作人員認為您可能會想手動審核來自這些帳號的追蹤請求。",
+  "followed_tags": "已追蹤標籤",
   "footer.about": "關於",
   "footer.directory": "個人檔案目錄",
   "footer.get_app": "取得應用程式",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "鍵盤快速鍵",
   "footer.privacy_policy": "私隱政策",
   "footer.source_code": "查看原始碼",
+  "footer.status": "狀態",
   "generic.saved": "已儲存",
   "getting_started.heading": "開始使用",
   "hashtag.column_header.tag_mode.all": "以及{additional}",
@@ -309,7 +314,7 @@
   "keyboard_shortcuts.column": "把標示移動到其中一列",
   "keyboard_shortcuts.compose": "把標示移動到文字輸入區",
   "keyboard_shortcuts.description": "描述",
-  "keyboard_shortcuts.direct": "開啟私訊欄",
+  "keyboard_shortcuts.direct": "以打開私人提及欄",
   "keyboard_shortcuts.down": "在列表往下移動",
   "keyboard_shortcuts.enter": "打開文章",
   "keyboard_shortcuts.favourite": "收藏文章",
@@ -371,7 +376,7 @@
   "navigation_bar.bookmarks": "書籤",
   "navigation_bar.community_timeline": "本站時間軸",
   "navigation_bar.compose": "撰寫新文章",
-  "navigation_bar.direct": "私訊",
+  "navigation_bar.direct": "私人提及",
   "navigation_bar.discover": "探索",
   "navigation_bar.domain_blocks": "封鎖的服務站",
   "navigation_bar.edit_profile": "修改個人資料",
@@ -379,6 +384,7 @@
   "navigation_bar.favourites": "最愛的內容",
   "navigation_bar.filters": "靜音詞彙",
   "navigation_bar.follow_requests": "追蹤請求",
+  "navigation_bar.followed_tags": "已追蹤標籤",
   "navigation_bar.follows_and_followers": "追蹤及追蹤者",
   "navigation_bar.lists": "列表",
   "navigation_bar.logout": "登出",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "垃圾訊息",
   "report_notification.categories.violation": "違反規則",
   "report_notification.open": "打開檢舉報告",
+  "search.no_recent_searches": "No recent searches",
   "search.placeholder": "搜尋",
+  "search.quick_action.account_search": "Profiles matching {x}",
+  "search.quick_action.go_to_account": "Go to profile {x}",
+  "search.quick_action.go_to_hashtag": "Go to hashtag {x}",
+  "search.quick_action.open_url": "Open URL in Mastodon",
+  "search.quick_action.status_search": "Posts matching {x}",
   "search.search_or_paste": "搜尋或貼上網址",
-  "search_popout.search_format": "高級搜索格式",
-  "search_popout.tips.full_text": "輸入簡單的文字,搜索由你發放、收藏、轉推和提及你的文章,以及符合的使用者名稱,顯示名稱和標籤。",
-  "search_popout.tips.hashtag": "標籤",
-  "search_popout.tips.status": "文章",
-  "search_popout.tips.text": "輸入簡單的文字,搜索符合的顯示名稱、使用者名稱和標籤",
-  "search_popout.tips.user": "使用者",
-  "search_results.accounts": "使用者",
+  "search_popout.quick_actions": "Quick actions",
+  "search_popout.recent": "Recent searches",
+  "search_results.accounts": "Profiles",
   "search_results.all": "全部",
   "search_results.hashtags": "標籤",
   "search_results.nothing_found": "找不到與搜尋字詞相關的內容",
@@ -540,9 +548,9 @@
   "server_banner.server_stats": "伺服器統計:",
   "sign_in_banner.create_account": "建立帳號",
   "sign_in_banner.sign_in": "登入",
-  "sign_in_banner.text": "登入以追蹤個人檔案、標籤、最愛、分享和回覆帖文,或用你在其他伺服器的帳號進行互動。",
+  "sign_in_banner.text": "登入以追蹤個人檔案和標籤,或最愛、分享和回覆帖文。你也可以使用帳戶在其他伺服器上互動。",
   "status.admin_account": "開啟 @{name} 的管理介面",
-  "status.admin_domain": "Open moderation interface for {domain}",
+  "status.admin_domain": "打開 {domain} 管理介面",
   "status.admin_status": "在管理介面開啟這篇文章",
   "status.block": "封鎖 @{name}",
   "status.bookmark": "書籤",
@@ -551,7 +559,8 @@
   "status.copy": "將連結複製到文章中",
   "status.delete": "刪除",
   "status.detailed_status": "詳細對話內容",
-  "status.direct": "私訊 @{name}",
+  "status.direct": "私下提及 @{name}",
+  "status.direct_indicator": "私人提及",
   "status.edit": "編輯",
   "status.edited": "編輯於 {date}",
   "status.edited_x_times": "Edited {count, plural, one {{count} 次} other {{count} 次}}",
@@ -559,7 +568,7 @@
   "status.favourite": "最愛",
   "status.filter": "篩選此帖文",
   "status.filtered": "已過濾",
-  "status.hide": "Hide post",
+  "status.hide": "隱藏帖文",
   "status.history.created": "{name} 於 {date} 建立",
   "status.history.edited": "{name} 於 {date} 編輯",
   "status.load_more": "載入更多",
diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json
index 75e423d1c..55f709340 100644
--- a/app/javascript/mastodon/locales/zh-TW.json
+++ b/app/javascript/mastodon/locales/zh-TW.json
@@ -1,15 +1,15 @@
 {
-  "about.blocks": "受管制的伺服器",
+  "about.blocks": "被限制的伺服器",
   "about.contact": "聯絡我們:",
   "about.disclaimer": "Mastodon 是一個自由的開源軟體,是 Mastodon gGmbH 的註冊商標。",
-  "about.domain_blocks.no_reason_available": "無法存取之原因",
-  "about.domain_blocks.preamble": "Mastodon 一般來說允許您閱讀並和聯邦宇宙上任何伺服器的使用者互動。這些伺服器是這個站台設下的例外。",
-  "about.domain_blocks.silenced.explanation": "一般來說您不會看到來自這個伺服器的個人檔案和內容,除非您明確地打開或著跟隨此個人檔案。",
-  "about.domain_blocks.silenced.title": "受限的",
-  "about.domain_blocks.suspended.explanation": "來自此伺服器的資料都不會被處理、儲存或交換,也無法和此伺服器上的使用者互動與溝通。",
+  "about.domain_blocks.no_reason_available": "無法存取的原因",
+  "about.domain_blocks.preamble": "Mastodon 基本上允許您瀏覽聯邦宇宙中任何伺服器的內容並與使用者互動。以下是在本伺服器上設定的例外。",
+  "about.domain_blocks.silenced.explanation": "一般來說您不會看到來自這個伺服器的個人檔案和內容,除非您明確搜尋或主動跟隨對方。",
+  "about.domain_blocks.silenced.title": "已受限",
+  "about.domain_blocks.suspended.explanation": "來自此伺服器的資料都不會被處理、儲存或交換,也無法與此伺服器上的使用者互動或交流。",
   "about.domain_blocks.suspended.title": "已停權",
-  "about.not_available": "這個資料於此伺服器上不可存取。",
-  "about.powered_by": "由 {mastodon} 提供之去中心化社群媒體",
+  "about.not_available": "無法在本伺服器上使用此資訊。",
+  "about.powered_by": "由 {mastodon} 提供的去中心化社群媒體",
   "about.rules": "伺服器規則",
   "account.account_note_header": "備註",
   "account.add_or_remove_from_list": "從列表中新增或移除",
@@ -18,9 +18,9 @@
   "account.block": "封鎖 @{name}",
   "account.block_domain": "封鎖來自 {domain} 網域的所有內容",
   "account.blocked": "已封鎖",
-  "account.browse_more_on_origin_server": "於該伺服器的個人檔案頁上瀏覽更多",
+  "account.browse_more_on_origin_server": "在該伺服器上的個人檔案頁面瀏覽更多",
   "account.cancel_follow_request": "收回跟隨請求",
-  "account.direct": "傳私訊給 @{name}",
+  "account.direct": "私訊 @{name}",
   "account.disable_notifications": "取消來自 @{name} 嘟文的通知",
   "account.domain_blocked": "已封鎖網域",
   "account.edit_profile": "編輯個人檔案",
@@ -42,7 +42,7 @@
   "account.joined_short": "加入時間",
   "account.languages": "變更訂閱的語言",
   "account.link_verified_on": "已於 {date} 檢查此連結的擁有者權限",
-  "account.locked_info": "此帳號的隱私狀態被設為鎖定。該擁有者會手動審核能跟隨此帳號的人。",
+  "account.locked_info": "此帳號的隱私狀態設定為鎖定。該擁有者會手動審核能跟隨此帳號的人。",
   "account.media": "媒體",
   "account.mention": "提及 @{name}",
   "account.moved_to": "{name} 現在的新帳號為:",
@@ -58,15 +58,15 @@
   "account.share": "分享 @{name} 的個人檔案",
   "account.show_reblogs": "顯示來自 @{name} 的嘟文",
   "account.statuses_counter": "{count, plural,one {{counter} 則}other {{counter} 則}}嘟文",
-  "account.unblock": "取消封鎖 @{name}",
-  "account.unblock_domain": "取消封鎖域名 {domain}",
+  "account.unblock": "解除封鎖 @{name}",
+  "account.unblock_domain": "解除封鎖網域 {domain}",
   "account.unblock_short": "解除封鎖",
-  "account.unendorse": "不再於個人檔案頁面推薦對方",
+  "account.unendorse": "取消在個人檔案推薦對方",
   "account.unfollow": "取消跟隨",
-  "account.unmute": "取消靜音 @{name}",
+  "account.unmute": "解除靜音 @{name}",
   "account.unmute_notifications": "重新接收來自 @{name} 的通知",
   "account.unmute_short": "解除靜音",
-  "account_note.placeholder": "按此添加備注",
+  "account_note.placeholder": "按此新增備註",
   "admin.dashboard.daily_retention": "註冊後使用者存留率(日)",
   "admin.dashboard.monthly_retention": "註冊後使用者存留率(月)",
   "admin.dashboard.retention.average": "平均",
@@ -93,10 +93,10 @@
   "bundle_modal_error.close": "關閉",
   "bundle_modal_error.message": "載入此元件時發生錯誤。",
   "bundle_modal_error.retry": "重試",
-  "closed_registrations.other_server_instructions": "因為 Mastodon 是去中心化的,所以您也能於其他伺服器上建立帳號,並仍然與這個伺服器互動。",
+  "closed_registrations.other_server_instructions": "因為 Mastodon 是去中心化的,您可以在其他伺服器上也建立帳號,並繼續與這個伺服器互動。",
   "closed_registrations_modal.description": "目前無法在 {domain} 建立新帳號,但也請別忘了,您並不一定需要有 {domain} 伺服器的帳號,也能使用 Mastodon 。",
   "closed_registrations_modal.find_another_server": "尋找另一個伺服器",
-  "closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以無論您在哪個伺服器新增帳號,都可以與此伺服器上的任何人追蹤及互動。您甚至能自行架一個自己的伺服器!",
+  "closed_registrations_modal.preamble": "Mastodon 是去中心化的,所以無論您在哪個伺服器上建立帳號,都可以跟隨並與此伺服器上的任何人互動。您甚至能架一個自己的伺服器!",
   "closed_registrations_modal.title": "註冊 Mastodon",
   "column.about": "關於",
   "column.blocks": "已封鎖的使用者",
@@ -104,14 +104,14 @@
   "column.community": "本站時間軸",
   "column.direct": "私訊",
   "column.directory": "瀏覽個人檔案",
-  "column.domain_blocks": "已封鎖的網域",
+  "column.domain_blocks": "已封鎖網域",
   "column.favourites": "最愛",
   "column.follow_requests": "跟隨請求",
   "column.home": "首頁",
   "column.lists": "列表",
   "column.mutes": "已靜音的使用者",
   "column.notifications": "通知",
-  "column.pins": "釘選嘟文",
+  "column.pins": "釘選的嘟文",
   "column.public": "聯邦時間軸",
   "column_back_button.label": "上一頁",
   "column_header.hide_settings": "隱藏設定",
@@ -128,8 +128,8 @@
   "compose.language.search": "搜尋語言...",
   "compose_form.direct_message_warning_learn_more": "了解更多",
   "compose_form.encryption_warning": "Mastodon 上的嘟文並未進行端到端加密。請不要透過 Mastodon 分享任何敏感資訊。",
-  "compose_form.hashtag_warning": "由於這則嘟文設定為「不公開」,它將不被列於任何主題標籤下。只有公開的嘟文才能藉由主題標籤被找到。",
-  "compose_form.lock_disclaimer": "您的帳號尚未 {locked}。任何人皆能跟隨您並看到您設定成只有跟隨者能看的嘟文。",
+  "compose_form.hashtag_warning": "由於這則嘟文設定為非公開,將不會列於任何主題標籤下。只有公開的嘟文才能藉由主題標籤被找到。",
+  "compose_form.lock_disclaimer": "您的帳號尚未 {locked}。任何人皆能跟隨您並看到您設定成只對跟隨者顯示的嘟文。",
   "compose_form.lock_disclaimer.lock": "上鎖",
   "compose_form.placeholder": "正在想些什麼嗎?",
   "compose_form.poll.add_option": "新增選項",
@@ -151,26 +151,28 @@
   "confirmation_modal.cancel": "取消",
   "confirmations.block.block_and_report": "封鎖並檢舉",
   "confirmations.block.confirm": "封鎖",
-  "confirmations.block.message": "您確定要封鎖 {name} ?",
-  "confirmations.cancel_follow_request.confirm": "收回請求",
+  "confirmations.block.message": "您確定要封鎖 {name} 嗎?",
+  "confirmations.cancel_follow_request.confirm": "收回跟隨請求",
   "confirmations.cancel_follow_request.message": "您確定要收回跟隨 {name} 的請求嗎?",
   "confirmations.delete.confirm": "刪除",
-  "confirmations.delete.message": "您確定要刪除這則嘟文?",
+  "confirmations.delete.message": "您確定要刪除這則嘟文嗎?",
   "confirmations.delete_list.confirm": "刪除",
-  "confirmations.delete_list.message": "您確定要永久刪除此列表?",
+  "confirmations.delete_list.message": "您確定要永久刪除此列表嗎?",
   "confirmations.discard_edit_media.confirm": "捨棄",
   "confirmations.discard_edit_media.message": "您在媒體描述或預覽區塊有未儲存的變更。是否要捨棄這些變更?",
-  "confirmations.domain_block.confirm": "隱藏整個域名",
-  "confirmations.domain_block.message": "真的非常確定封鎖整個 {domain} 網域嗎?大部分情況下,您只需要封鎖或靜音少數特定的帳號能滿足需求了。您將不能在任何公開的時間軸及通知中看到來自此網域的內容。您來自該網域的跟隨者也將被移除。",
+  "confirmations.domain_block.confirm": "封鎖整個網域",
+  "confirmations.domain_block.message": "您真的非常確定要封鎖整個 {domain} 網域嗎?大部分情況下,封鎖或靜音少數特定的帳號就能滿足需求了。您將不能在任何公開的時間軸及通知中看到來自此網域的內容。您來自該網域的跟隨者也將被移除。",
+  "confirmations.edit.confirm": "編輯",
+  "confirmations.edit.message": "編輯嘟文將覆蓋掉您目前正在撰寫的訊息。是否仍要繼續?",
   "confirmations.logout.confirm": "登出",
   "confirmations.logout.message": "您確定要登出嗎?",
   "confirmations.mute.confirm": "靜音",
   "confirmations.mute.explanation": "這將會隱藏來自他們的嘟文與通知,但是他們還是可以查閱您的嘟文與跟隨您。",
-  "confirmations.mute.message": "您確定要靜音 {name} ?",
+  "confirmations.mute.message": "您確定要靜音 {name} 嗎?",
   "confirmations.redraft.confirm": "刪除並重新編輯",
-  "confirmations.redraft.message": "您確定要刪掉這則嘟文並重新編輯嗎?將會失去這則嘟文的轉嘟及最愛,且回覆這則的嘟文將會變成獨立的嘟文。",
+  "confirmations.redraft.message": "您確定要刪除這則嘟文並重新編輯嗎?您將失去這則嘟文的轉嘟及最愛,且對原始嘟文的回覆都會變成獨立的嘟文。",
   "confirmations.reply.confirm": "回覆",
-  "confirmations.reply.message": "現在回覆將蓋掉您目前正在撰寫的訊息。是否仍要回覆?",
+  "confirmations.reply.message": "回覆嘟文將覆蓋掉您目前正在撰寫的訊息。是否仍要繼續?",
   "confirmations.unfollow.confirm": "取消跟隨",
   "confirmations.unfollow.message": "您確定要取消跟隨 {name} 嗎?",
   "conversation.delete": "刪除對話",
@@ -185,10 +187,10 @@
   "directory.recently_active": "最近活躍",
   "disabled_account_banner.account_settings": "帳號設定",
   "disabled_account_banner.text": "您的帳號 {disabledAccount} 目前已停用。",
-  "dismissable_banner.community_timeline": "這些是 {domain} 上面託管帳號之最新公開嘟文。",
+  "dismissable_banner.community_timeline": "這些是託管於 {domain} 上帳號之最新公開嘟文。",
   "dismissable_banner.dismiss": "關閉",
   "dismissable_banner.explore_links": "這些新聞故事正在被此伺服器以及去中心化網路上的人們熱烈討論著。",
-  "dismissable_banner.explore_statuses": "這些於這個伺服器以及去中心化網路中其他伺服器發出的嘟文正在被此伺服器上的人們熱烈討論著。",
+  "dismissable_banner.explore_statuses": "這些於此伺服器以及去中心化網路中其他伺服器發出的嘟文正在被此伺服器上的人們熱烈討論著。",
   "dismissable_banner.explore_tags": "這些主題標籤正在被此伺服器以及去中心化網路上的人們熱烈討論著。",
   "dismissable_banner.public_timeline": "這些是來自這裡以及去中心化網路中其他已知伺服器之最新公開嘟文。",
   "embed.instructions": "要在您的網站嵌入此嘟文,請複製以下程式碼。",
@@ -208,27 +210,28 @@
   "emoji_button.search_results": "搜尋結果",
   "emoji_button.symbols": "符號",
   "emoji_button.travel": "旅遊與地點",
-  "empty_column.account_suspended": "帳號被暫停",
+  "empty_column.account_suspended": "帳號已被停權",
   "empty_column.account_timeline": "這裡還沒有嘟文!",
   "empty_column.account_unavailable": "無法取得個人檔案",
-  "empty_column.blocks": "您還沒有封鎖任何使用者。",
+  "empty_column.blocks": "您尚未封鎖任何使用者。",
   "empty_column.bookmarked_statuses": "您還沒有建立任何書籤。當您建立書籤時,它將於此顯示。",
   "empty_column.community": "本站時間軸是空的。快公開嘟些文搶頭香啊!",
-  "empty_column.direct": "您還沒有任何私訊。當您私訊別人或收到私訊時,它將於此顯示。",
+  "empty_column.direct": "您還沒有收到任何私訊。當您私訊別人或收到私訊時,它將於此顯示。",
   "empty_column.domain_blocks": "尚未封鎖任何網域。",
   "empty_column.explore_statuses": "目前沒有熱門討論,請稍候再回來看看!",
   "empty_column.favourited_statuses": "您還沒有加過任何嘟文至最愛。當您收藏嘟文時,它將於此顯示。",
-  "empty_column.favourites": "還沒有人加過這則嘟文至最愛。當有人收藏嘟文時,它將於此顯示。",
+  "empty_column.favourites": "還沒有人加過這則嘟文至最愛。當有人收藏嘟文時,它們將於此顯示。",
   "empty_column.follow_recommendations": "似乎未能為您產生任何建議。您可以嘗試使用搜尋來尋找您可能認識的人,或是探索熱門主題標籤。",
-  "empty_column.follow_requests": "您還沒有收到任何跟隨請求。這裡將會顯示收到的跟隨請求。",
+  "empty_column.follow_requests": "您還沒有收到任何跟隨請求。當您收到的跟隨請求時,它將於此顯示。",
+  "empty_column.followed_tags": "您還沒有跟隨任何主題標籤。當您跟隨主題標籤時,它們將於此顯示。",
   "empty_column.hashtag": "這個主題標籤下什麼也沒有。",
   "empty_column.home": "您的首頁時間軸是空的!前往 {suggestions} 或使用搜尋功能來認識其他人。",
   "empty_column.home.suggestions": "檢視部份建議",
   "empty_column.list": "這份列表下什麼也沒有。當此列表的成員嘟出了新的嘟文時,它們就會顯示於此。",
   "empty_column.lists": "您還沒有建立任何列表。當您建立列表時,它將於此顯示。",
-  "empty_column.mutes": "您還沒有靜音任何使用者。",
+  "empty_column.mutes": "您尚未靜音任何使用者。",
   "empty_column.notifications": "您還沒有收到任何通知,當您和別人開始互動時,它將於此顯示。",
-  "empty_column.public": "這裡什麼都沒有!嘗試寫些公開的嘟文,或著自己跟隨其他伺服器的使用者後就會有嘟文出現了",
+  "empty_column.public": "這裡什麼都沒有!嘗試寫些公開的嘟文,或者跟隨其他伺服器的使用者後,就會有嘟文出現了",
   "error.unexpected_crash.explanation": "由於發生系統故障或瀏覽器相容性問題,無法正常顯示此頁面。",
   "error.unexpected_crash.explanation_addons": "此頁面無法被正常顯示,這可能是由瀏覽器附加元件或網頁自動翻譯工具造成的。",
   "error.unexpected_crash.next_steps": "請嘗試重新整理頁面。如果狀況沒有改善,您可以使用不同的瀏覽器或應用程式來檢視來使用 Mastodon。",
@@ -245,7 +248,7 @@
   "filter_modal.added.context_mismatch_title": "不符合情境!",
   "filter_modal.added.expired_explanation": "此過濾器類別已失效,您需要更新過期日期以套用。",
   "filter_modal.added.expired_title": "過期的過濾器!",
-  "filter_modal.added.review_and_configure": "若欲檢視和進一步設定此過濾器類別,請至 {settings_link}。",
+  "filter_modal.added.review_and_configure": "要檢視和進一步設定此過濾器類別,請至 {settings_link}。",
   "filter_modal.added.review_and_configure_title": "過濾器設定",
   "filter_modal.added.settings_link": "設定頁面",
   "filter_modal.added.short_explanation": "此嘟文已被新增至以下過濾器類別:{title}。",
@@ -263,6 +266,7 @@
   "follow_request.authorize": "授權",
   "follow_request.reject": "拒絕",
   "follow_requests.unlocked_explanation": "即便您的帳號未被鎖定,{domain} 的管理員認為您可能想要自己審核這些帳號的跟隨請求。",
+  "followed_tags": "已跟隨主題標籤",
   "footer.about": "關於",
   "footer.directory": "個人檔案目錄",
   "footer.get_app": "取得應用程式",
@@ -270,6 +274,7 @@
   "footer.keyboard_shortcuts": "鍵盤快速鍵",
   "footer.privacy_policy": "隱私權政策",
   "footer.source_code": "檢視原始碼",
+  "footer.status": "狀態",
   "generic.saved": "已儲存",
   "getting_started.heading": "開始使用",
   "hashtag.column_header.tag_mode.all": "以及 {additional}",
@@ -303,13 +308,13 @@
   "intervals.full.days": "{number, plural, one {# 天} other {# 天}}",
   "intervals.full.hours": "{number, plural, one {# 小時} other {# 小時}}",
   "intervals.full.minutes": "{number, plural, one {# 分鐘} other {# 分鐘}}",
-  "keyboard_shortcuts.back": "返回上一頁",
-  "keyboard_shortcuts.blocked": "開啟「封鎖使用者」名單",
+  "keyboard_shortcuts.back": "上一頁",
+  "keyboard_shortcuts.blocked": "開啟「封鎖使用者」列表",
   "keyboard_shortcuts.boost": "轉嘟",
-  "keyboard_shortcuts.column": "聚焦至其中一欄的嘟文",
-  "keyboard_shortcuts.compose": "聚焦至撰寫文字區塊",
+  "keyboard_shortcuts.column": "將游標移至其中一欄",
+  "keyboard_shortcuts.compose": "將游標移至文字撰寫區塊",
   "keyboard_shortcuts.description": "說明",
-  "keyboard_shortcuts.direct": "開啟私訊欄",
+  "keyboard_shortcuts.direct": "開啟私訊對話欄",
   "keyboard_shortcuts.down": "往下移動",
   "keyboard_shortcuts.enter": "檢視嘟文",
   "keyboard_shortcuts.favourite": "加到最愛",
@@ -325,17 +330,17 @@
   "keyboard_shortcuts.my_profile": "開啟個人檔案頁面",
   "keyboard_shortcuts.notifications": "開啟通知欄",
   "keyboard_shortcuts.open_media": "開啟媒體",
-  "keyboard_shortcuts.pinned": "開啟釘選嘟文列表",
+  "keyboard_shortcuts.pinned": "開啟釘選的嘟文列表",
   "keyboard_shortcuts.profile": "開啟作者的個人檔案頁面",
   "keyboard_shortcuts.reply": "回應嘟文",
   "keyboard_shortcuts.requests": "開啟跟隨請求列表",
-  "keyboard_shortcuts.search": "聚焦至搜尋框",
+  "keyboard_shortcuts.search": "將游標移至搜尋框",
   "keyboard_shortcuts.spoilers": "顯示或隱藏內容警告之嘟文",
   "keyboard_shortcuts.start": "開啟「開始使用」欄位",
   "keyboard_shortcuts.toggle_hidden": "顯示或隱藏在內容警告之後的嘟文",
   "keyboard_shortcuts.toggle_sensitivity": "顯示或隱藏媒體",
   "keyboard_shortcuts.toot": "發個新嘟文",
-  "keyboard_shortcuts.unfocus": "取消輸入文字區塊或搜尋之焦點",
+  "keyboard_shortcuts.unfocus": "跳離文字撰寫區塊或搜尋框",
   "keyboard_shortcuts.up": "往上移動",
   "lightbox.close": "關閉",
   "lightbox.compress": "折疊圖片檢視框",
@@ -367,32 +372,33 @@
   "mute_modal.hide_notifications": "是否隱藏來自這位使用者的通知?",
   "mute_modal.indefinite": "無期限",
   "navigation_bar.about": "關於",
-  "navigation_bar.blocks": "封鎖使用者",
+  "navigation_bar.blocks": "已封鎖的使用者",
   "navigation_bar.bookmarks": "書籤",
   "navigation_bar.community_timeline": "本站時間軸",
   "navigation_bar.compose": "撰寫新嘟文",
   "navigation_bar.direct": "私訊",
   "navigation_bar.discover": "探索",
-  "navigation_bar.domain_blocks": "隱藏的網域",
+  "navigation_bar.domain_blocks": "已封鎖網域",
   "navigation_bar.edit_profile": "編輯個人檔案",
   "navigation_bar.explore": "探索",
   "navigation_bar.favourites": "最愛",
-  "navigation_bar.filters": "靜音詞彙",
+  "navigation_bar.filters": "已靜音的關鍵字",
   "navigation_bar.follow_requests": "跟隨請求",
+  "navigation_bar.followed_tags": "已跟隨的主題標籤",
   "navigation_bar.follows_and_followers": "跟隨中與跟隨者",
   "navigation_bar.lists": "列表",
   "navigation_bar.logout": "登出",
-  "navigation_bar.mutes": "靜音的使用者",
+  "navigation_bar.mutes": "已靜音的使用者",
   "navigation_bar.personal": "個人",
-  "navigation_bar.pins": "釘選嘟文",
+  "navigation_bar.pins": "釘選的嘟文",
   "navigation_bar.preferences": "偏好設定",
   "navigation_bar.public_timeline": "聯邦時間軸",
   "navigation_bar.search": "搜尋",
   "navigation_bar.security": "安全性",
   "not_signed_in_indicator.not_signed_in": "您需要登入才能存取此資源。",
-  "notification.admin.report": "{name} 檢舉了 {target}",
+  "notification.admin.report": "{name} 已檢舉 {target}",
   "notification.admin.sign_up": "{name} 已經註冊",
-  "notification.favourite": "{name} 把您的嘟文加入了最愛",
+  "notification.favourite": "{name} 已將您的嘟文加入最愛",
   "notification.follow": "{name} 跟隨了您",
   "notification.follow_request": "{name} 要求跟隨您",
   "notification.mention": "{name} 提到了您",
@@ -400,7 +406,7 @@
   "notification.poll": "您曾投過的投票已經結束",
   "notification.reblog": "{name} 轉嘟了您的嘟文",
   "notification.status": "{name} 剛剛嘟文",
-  "notification.update": "{name} 編輯了嘟文",
+  "notification.update": "{name} 已編輯嘟文",
   "notifications.clear": "清除通知",
   "notifications.clear_confirmation": "您確定要永久清除您的通知嗎?",
   "notifications.column_settings.admin.report": "新檢舉報告:",
@@ -416,7 +422,7 @@
   "notifications.column_settings.poll": "投票結果:",
   "notifications.column_settings.push": "推播通知",
   "notifications.column_settings.reblog": "轉嘟:",
-  "notifications.column_settings.show": "在欄位中顯示",
+  "notifications.column_settings.show": "於欄位中顯示",
   "notifications.column_settings.sound": "播放聲音",
   "notifications.column_settings.status": "新嘟文:",
   "notifications.column_settings.unread_notifications.category": "未讀通知",
@@ -449,13 +455,13 @@
   "poll_button.add_poll": "建立投票",
   "poll_button.remove_poll": "移除投票",
   "privacy.change": "調整嘟文隱私狀態",
-  "privacy.direct.long": "只有被提及的使用者能看到",
+  "privacy.direct.long": "只對被提及的使用者顯示",
   "privacy.direct.short": "僅限提及的人",
-  "privacy.private.long": "只有跟隨您的使用者能看到",
+  "privacy.private.long": "只對跟隨者顯示",
   "privacy.private.short": "僅限跟隨者",
-  "privacy.public.long": "對所有人可見",
+  "privacy.public.long": "對所有人顯示",
   "privacy.public.short": "公開",
-  "privacy.unlisted.long": "對所有人可見,但選擇退出探索功能",
+  "privacy.unlisted.long": "對所有人顯示,但關閉探索功能",
   "privacy.unlisted.short": "不公開",
   "privacy_policy.last_updated": "最後更新:{date}",
   "privacy_policy.title": "隱私權政策",
@@ -516,15 +522,17 @@
   "report_notification.categories.spam": "垃圾訊息",
   "report_notification.categories.violation": "違反規則",
   "report_notification.open": "開啟檢舉報告",
+  "search.no_recent_searches": "尚無最近的搜尋紀錄",
   "search.placeholder": "搜尋",
+  "search.quick_action.account_search": "符合的個人檔案 {x}",
+  "search.quick_action.go_to_account": "前往個人檔案 {x}",
+  "search.quick_action.go_to_hashtag": "前往主題標籤 {x}",
+  "search.quick_action.open_url": "於 Mastodon 中開啟連結",
+  "search.quick_action.status_search": "符合的嘟文 {x}",
   "search.search_or_paste": "搜尋或輸入網址",
-  "search_popout.search_format": "進階搜尋格式",
-  "search_popout.tips.full_text": "輸入簡單的文字,搜尋由您撰寫、最愛、轉嘟或提您的嘟文,以及與關鍵詞匹配的使用者名稱、帳號顯示名稱和主題標籤。",
-  "search_popout.tips.hashtag": "主題標籤",
-  "search_popout.tips.status": "嘟文",
-  "search_popout.tips.text": "輸入簡單的文字,搜尋符合的使用者名稱,帳號名稱與標籤",
-  "search_popout.tips.user": "使用者",
-  "search_results.accounts": "使用者",
+  "search_popout.quick_actions": "快捷操作",
+  "search_popout.recent": "最近的搜尋紀錄",
+  "search_results.accounts": "個人檔案",
   "search_results.all": "全部",
   "search_results.hashtags": "主題標籤",
   "search_results.nothing_found": "無法找到符合搜尋條件之結果",
@@ -540,7 +548,7 @@
   "server_banner.server_stats": "伺服器統計:",
   "sign_in_banner.create_account": "新增帳號",
   "sign_in_banner.sign_in": "登入",
-  "sign_in_banner.text": "登入以追蹤個人檔案、主題標籤、最愛,分享和回覆嘟文,或以您其他伺服器之帳號進行互動:",
+  "sign_in_banner.text": "登入以跟隨個人檔案和主題標籤,或收藏、分享和回覆嘟文。您也可以使用您的帳號在其他伺服器上進行互動。",
   "status.admin_account": "開啟 @{name} 的管理介面",
   "status.admin_domain": "開啟 {domain} 的管理介面",
   "status.admin_status": "在管理介面開啟此嘟文",
@@ -551,7 +559,8 @@
   "status.copy": "複製嘟文連結",
   "status.delete": "刪除",
   "status.detailed_status": "詳細的對話內容",
-  "status.direct": "發送私訊給 @{name}",
+  "status.direct": "私訊 @{name}",
+  "status.direct_indicator": "私訊",
   "status.edit": "編輯",
   "status.edited": "編輯於 {date}",
   "status.edited_x_times": "已編輯 {count, plural, one {{count} 次} other {{count} 次}}",
@@ -563,7 +572,7 @@
   "status.history.created": "{name} 於 {date} 建立",
   "status.history.edited": "{name} 於 {date} 修改",
   "status.load_more": "載入更多",
-  "status.media_hidden": "隱藏媒體內容",
+  "status.media_hidden": "隱藏的媒體內容",
   "status.mention": "提及 @{name}",
   "status.more": "更多",
   "status.mute": "靜音 @{name}",
diff --git a/app/javascript/mastodon/main.js b/app/javascript/mastodon/main.jsx
index 69a7ee91f..69a7ee91f 100644
--- a/app/javascript/mastodon/main.js
+++ b/app/javascript/mastodon/main.jsx
diff --git a/app/javascript/mastodon/performance.js b/app/javascript/mastodon/performance.js
index 450a90626..95cf962d6 100644
--- a/app/javascript/mastodon/performance.js
+++ b/app/javascript/mastodon/performance.js
@@ -12,6 +12,7 @@ if (process.env.NODE_ENV === 'development') {
     // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1331135
     performance.setResourceTimingBufferSize(Infinity);
   }
+
   marky = require('marky');
   // allows us to easily do e.g. ReactPerf.printWasted() while debugging
   //window.ReactPerf = require('react-addons-perf');
diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js
index 9ce7e97ed..842b7af51 100644
--- a/app/javascript/mastodon/reducers/compose.js
+++ b/app/javascript/mastodon/reducers/compose.js
@@ -186,11 +186,12 @@ const ignoreSuggestion = (state, position, token, completion, path) => {
 };
 
 const sortHashtagsByUse = (state, tags) => {
-  const personalHistory = state.get('tagHistory');
+  const personalHistory = state.get('tagHistory').map(tag => tag.toLowerCase());
 
-  return tags.sort((a, b) => {
-    const usedA = personalHistory.includes(a.name);
-    const usedB = personalHistory.includes(b.name);
+  const tagsWithLowercase = tags.map(t => ({ ...t, lowerName: t.name.toLowerCase() }));
+  const sorted = tagsWithLowercase.sort((a, b) => {
+    const usedA = personalHistory.includes(a.lowerName);
+    const usedB = personalHistory.includes(b.lowerName);
 
     if (usedA === usedB) {
       return 0;
@@ -200,6 +201,8 @@ const sortHashtagsByUse = (state, tags) => {
       return 1;
     }
   });
+  sorted.forEach(tag => delete tag.lowerName);
+  return sorted;
 };
 
 const insertEmoji = (state, position, emojiData, needsSpace) => {
@@ -222,8 +225,8 @@ const privacyPreference = (a, b) => {
 const hydrate = (state, hydratedState) => {
   state = clearAll(state.merge(hydratedState));
 
-  if (hydratedState.has('text')) {
-    state = state.set('text', hydratedState.get('text'));
+  if (hydratedState.get('text')) {
+    state = state.set('text', hydratedState.get('text')).set('focusDate', new Date());
   }
 
   return state;
@@ -330,6 +333,8 @@ export default function compose(state = initialState, action) {
       map.set('preselectDate', new Date());
       map.set('idempotencyKey', uuid());
 
+      map.update('media_attachments', list => list.filter(media => media.get('unattached')));
+
       if (action.status.get('language') && !action.status.has('translation')) {
         map.set('language', action.status.get('language'));
       } else {
@@ -444,7 +449,7 @@ export default function compose(state = initialState, action) {
       .setIn(['media_modal', 'dirty'], false)
       .update('media_attachments', list => list.map(item => {
         if (item.get('id') === action.media.id) {
-          return fromJS(action.media).set('unattached', true);
+          return fromJS(action.media).set('unattached', !action.attached);
         }
 
         return item;
diff --git a/app/javascript/mastodon/reducers/followed_tags.js b/app/javascript/mastodon/reducers/followed_tags.js
new file mode 100644
index 000000000..da20b7b12
--- /dev/null
+++ b/app/javascript/mastodon/reducers/followed_tags.js
@@ -0,0 +1,42 @@
+import {
+  FOLLOWED_HASHTAGS_FETCH_REQUEST,
+  FOLLOWED_HASHTAGS_FETCH_SUCCESS,
+  FOLLOWED_HASHTAGS_FETCH_FAIL,
+  FOLLOWED_HASHTAGS_EXPAND_REQUEST,
+  FOLLOWED_HASHTAGS_EXPAND_SUCCESS,
+  FOLLOWED_HASHTAGS_EXPAND_FAIL,
+} from 'mastodon/actions/tags';
+import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
+
+const initialState = ImmutableMap({
+  items: ImmutableList(),
+  isLoading: false,
+  next: null,
+});
+
+export default function followed_tags(state = initialState, action) {
+  switch(action.type) {
+  case FOLLOWED_HASHTAGS_FETCH_REQUEST:
+    return state.set('isLoading', true);
+  case FOLLOWED_HASHTAGS_FETCH_SUCCESS:
+    return state.withMutations(map => {
+      map.set('items', fromJS(action.followed_tags));
+      map.set('isLoading', false);
+      map.set('next', action.next);
+    });
+  case FOLLOWED_HASHTAGS_FETCH_FAIL:
+    return state.set('isLoading', false);
+  case FOLLOWED_HASHTAGS_EXPAND_REQUEST:
+    return state.set('isLoading', true);
+  case FOLLOWED_HASHTAGS_EXPAND_SUCCESS:
+    return state.withMutations(map => {
+      map.update('items', set => set.concat(fromJS(action.followed_tags)));
+      map.set('isLoading', false);
+      map.set('next', action.next);
+    });
+  case FOLLOWED_HASHTAGS_EXPAND_FAIL:
+    return state.set('isLoading', false);
+  default:
+    return state;
+  }
+}
diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js
index bccdc1865..69771ad1b 100644
--- a/app/javascript/mastodon/reducers/index.js
+++ b/app/javascript/mastodon/reducers/index.js
@@ -40,6 +40,7 @@ import picture_in_picture from './picture_in_picture';
 import accounts_map from './accounts_map';
 import history from './history';
 import tags from './tags';
+import followed_tags from './followed_tags';
 
 const reducers = {
   announcements,
@@ -83,6 +84,7 @@ const reducers = {
   picture_in_picture,
   history,
   tags,
+  followed_tags,
 };
 
 export default combineReducers(reducers);
diff --git a/app/javascript/mastodon/reducers/search.js b/app/javascript/mastodon/reducers/search.js
index d3e71da9d..e545f430c 100644
--- a/app/javascript/mastodon/reducers/search.js
+++ b/app/javascript/mastodon/reducers/search.js
@@ -6,13 +6,15 @@ import {
   SEARCH_FETCH_SUCCESS,
   SEARCH_SHOW,
   SEARCH_EXPAND_SUCCESS,
+  SEARCH_RESULT_CLICK,
+  SEARCH_RESULT_FORGET,
 } from '../actions/search';
 import {
   COMPOSE_MENTION,
   COMPOSE_REPLY,
   COMPOSE_DIRECT,
 } from '../actions/compose';
-import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
+import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrderedSet, fromJS } from 'immutable';
 
 const initialState = ImmutableMap({
   value: '',
@@ -21,6 +23,7 @@ const initialState = ImmutableMap({
   results: ImmutableMap(),
   isLoading: false,
   searchTerm: '',
+  recent: ImmutableOrderedSet(),
 });
 
 export default function search(state = initialState, action) {
@@ -61,6 +64,10 @@ export default function search(state = initialState, action) {
   case SEARCH_EXPAND_SUCCESS:
     const results = action.searchType === 'hashtags' ? fromJS(action.results.hashtags) : action.results[action.searchType].map(item => item.id);
     return state.updateIn(['results', action.searchType], list => list.concat(results));
+  case SEARCH_RESULT_CLICK:
+    return state.update('recent', set => set.add(fromJS(action.result)));
+  case SEARCH_RESULT_FORGET:
+    return state.update('recent', set => set.filterNot(result => result.get('q') === action.q));
   default:
     return state;
   }
diff --git a/app/javascript/mastodon/reducers/server.js b/app/javascript/mastodon/reducers/server.js
index db9f2b5e6..909ab2a66 100644
--- a/app/javascript/mastodon/reducers/server.js
+++ b/app/javascript/mastodon/reducers/server.js
@@ -2,6 +2,9 @@ import {
   SERVER_FETCH_REQUEST,
   SERVER_FETCH_SUCCESS,
   SERVER_FETCH_FAIL,
+  SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST,
+  SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS,
+  SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL,
   EXTENDED_DESCRIPTION_REQUEST,
   EXTENDED_DESCRIPTION_SUCCESS,
   EXTENDED_DESCRIPTION_FAIL,
@@ -35,6 +38,12 @@ export default function server(state = initialState, action) {
     return state.set('server', fromJS(action.server)).setIn(['server', 'isLoading'], false);
   case SERVER_FETCH_FAIL:
     return state.setIn(['server', 'isLoading'], false);
+  case SERVER_TRANSLATION_LANGUAGES_FETCH_REQUEST:
+    return state.setIn(['translationLanguages', 'isLoading'], true);
+  case SERVER_TRANSLATION_LANGUAGES_FETCH_SUCCESS:
+    return state.setIn(['translationLanguages', 'items'], fromJS(action.translationLanguages)).setIn(['translationLanguages', 'isLoading'], false);
+  case SERVER_TRANSLATION_LANGUAGES_FETCH_FAIL:
+    return state.setIn(['translationLanguages', 'isLoading'], false);
   case EXTENDED_DESCRIPTION_REQUEST:
     return state.setIn(['extendedDescription', 'isLoading'], true);
   case EXTENDED_DESCRIPTION_SUCCESS:
diff --git a/app/javascript/mastodon/selectors/index.js b/app/javascript/mastodon/selectors/index.js
index bf46c810e..58972bdf7 100644
--- a/app/javascript/mastodon/selectors/index.js
+++ b/app/javascript/mastodon/selectors/index.js
@@ -121,8 +121,8 @@ export const getAccountGallery = createSelector([
   let medias = ImmutableList();
 
   statusIds.forEach(statusId => {
-    const status = statuses.get(statusId);
-    medias = medias.concat(status.get('media_attachments').map(media => media.set('status', status).set('account', account)));
+    const status = statuses.get(statusId).set('account', account);
+    medias = medias.concat(status.get('media_attachments').map(media => media.set('status', status)));
   });
 
   return medias;
diff --git a/app/javascript/mastodon/service_worker/web_push_notifications.js b/app/javascript/mastodon/service_worker/web_push_notifications.js
index f12595777..b9d626694 100644
--- a/app/javascript/mastodon/service_worker/web_push_notifications.js
+++ b/app/javascript/mastodon/service_worker/web_push_notifications.js
@@ -1,6 +1,6 @@
 import IntlMessageFormat from 'intl-messageformat';
-import locales from './web_push_locales';
 import { unescape } from 'lodash';
+import locales from './web_push_locales';
 
 const MAX_NOTIFICATIONS = 5;
 const GROUP_TAG = 'tag';
@@ -90,7 +90,13 @@ export const handlePush = (event) => {
       options.tag       = notification.id;
       options.badge     = '/badge.png';
       options.image     = notification.status && notification.status.media_attachments.length > 0 && notification.status.media_attachments[0].preview_url || undefined;
-      options.data      = { access_token, preferred_locale, id: notification.status ? notification.status.id : notification.account.id, url: notification.status ? `/@${notification.account.acct}/${notification.status.id}` : `/@${notification.account.acct}` };
+      options.data      = { access_token, preferred_locale, id: notification.status ? notification.status.id : notification.account.id };
+
+      if (notification.status) {
+        options.data.url = `/@${notification.status.account.acct}/${notification.status.id}`;
+      } else {
+        options.data.url = `/@${notification.account.acct}`;
+      }
 
       if (notification.status && notification.status.spoiler_text || notification.status.sensitive) {
         options.data.hiddenBody  = htmlToPlainText(notification.status.content);
diff --git a/app/javascript/mastodon/stream.js b/app/javascript/mastodon/stream.js
index c6d12cd6f..95e0359d0 100644
--- a/app/javascript/mastodon/stream.js
+++ b/app/javascript/mastodon/stream.js
@@ -59,6 +59,7 @@ const subscribe = ({ channelName, params, onConnect }) => {
   subscriptionCounters[key] = subscriptionCounters[key] || 0;
 
   if (subscriptionCounters[key] === 0) {
+    // @ts-expect-error
     sharedConnection.send(JSON.stringify({ type: 'subscribe', stream: channelName, ...params }));
   }
 
@@ -74,7 +75,9 @@ const unsubscribe = ({ channelName, params, onDisconnect }) => {
 
   subscriptionCounters[key] = subscriptionCounters[key] || 1;
 
+  // @ts-expect-error
   if (subscriptionCounters[key] === 1 && sharedConnection.readyState === WebSocketClient.OPEN) {
+    // @ts-expect-error
     sharedConnection.send(JSON.stringify({ type: 'unsubscribe', stream: channelName, ...params }));
   }
 
@@ -83,11 +86,12 @@ const unsubscribe = ({ channelName, params, onDisconnect }) => {
 };
 
 const sharedCallbacks = {
-  connected () {
+  connected() {
     subscriptions.forEach(subscription => subscribe(subscription));
   },
 
-  received (data) {
+  // @ts-expect-error
+  received(data) {
     const { stream } = data;
 
     subscriptions.filter(({ channelName, params }) => {
@@ -111,11 +115,11 @@ const sharedCallbacks = {
     });
   },
 
-  disconnected () {
+  disconnected() {
     subscriptions.forEach(subscription => unsubscribe(subscription));
   },
 
-  reconnected () {
+  reconnected() {
   },
 };
 
@@ -138,6 +142,7 @@ const channelNameWithInlineParams = (channelName, params) => {
  * @param {function(Function, Function): { onConnect: (function(): void), onReceive: (function(StreamEvent): void), onDisconnect: (function(): void) }} callbacks
  * @return {function(): void}
  */
+// @ts-expect-error
 export const connectStream = (channelName, params, callbacks) => (dispatch, getState) => {
   const streamingAPIBaseURL = getState().getIn(['meta', 'streaming_api_base_url']);
   const accessToken = getState().getIn(['meta', 'access_token']);
@@ -147,19 +152,19 @@ export const connectStream = (channelName, params, callbacks) => (dispatch, getS
   // to using individual connections for each channel
   if (!streamingAPIBaseURL.startsWith('ws')) {
     const connection = createConnection(streamingAPIBaseURL, accessToken, channelNameWithInlineParams(channelName, params), {
-      connected () {
+      connected() {
         onConnect();
       },
 
-      received (data) {
+      received(data) {
         onReceive(data);
       },
 
-      disconnected () {
+      disconnected() {
         onDisconnect();
       },
 
-      reconnected () {
+      reconnected() {
         onConnect();
       },
     });
@@ -227,14 +232,19 @@ const handleEventSourceMessage = (e, received) => {
 const createConnection = (streamingAPIBaseURL, accessToken, channelName, { connected, received, disconnected, reconnected }) => {
   const params = channelName.split('&');
 
+  // @ts-expect-error
   channelName = params.shift();
 
   if (streamingAPIBaseURL.startsWith('ws')) {
+    // @ts-expect-error
     const ws = new WebSocketClient(`${streamingAPIBaseURL}/api/v1/streaming/?${params.join('&')}`, accessToken);
 
-    ws.onopen      = connected;
-    ws.onmessage   = e => received(JSON.parse(e.data));
-    ws.onclose     = disconnected;
+    // @ts-expect-error
+    ws.onopen = connected;
+    ws.onmessage = e => received(JSON.parse(e.data));
+    // @ts-expect-error
+    ws.onclose = disconnected;
+    // @ts-expect-error
     ws.onreconnect = reconnected;
 
     return ws;
@@ -256,7 +266,7 @@ const createConnection = (streamingAPIBaseURL, accessToken, channelName, { conne
   };
 
   KNOWN_EVENT_TYPES.forEach(type => {
-    es.addEventListener(type, e => handleEventSourceMessage(/** @type {MessageEvent} */ (e), received));
+    es.addEventListener(type, e => handleEventSourceMessage(/** @type {MessageEvent} */(e), received));
   });
 
   es.onerror = /** @type {function(): void} */ (disconnected);
diff --git a/app/javascript/mastodon/utils/hashtags.js b/app/javascript/mastodon/utils/hashtags.js
new file mode 100644
index 000000000..358ce37f5
--- /dev/null
+++ b/app/javascript/mastodon/utils/hashtags.js
@@ -0,0 +1,47 @@
+const HASHTAG_SEPARATORS = '_\\u00b7\\u200c';
+const ALPHA = '\\p{L}\\p{M}';
+const WORD = '\\p{L}\\p{M}\\p{N}\\p{Pc}';
+
+const buildHashtagPatternRegex = () => {
+  try {
+    return new RegExp(
+      '(?:^|[^\\/\\)\\w])#((' +
+      '[' + WORD + '_]' +
+      '[' + WORD + HASHTAG_SEPARATORS + ']*' +
+      '[' + ALPHA + HASHTAG_SEPARATORS + ']' +
+      '[' + WORD + HASHTAG_SEPARATORS +']*' +
+      '[' + WORD + '_]' +
+      ')|(' +
+      '[' + WORD + '_]*' +
+      '[' + ALPHA + ']' +
+      '[' + WORD + '_]*' +
+      '))', 'iu',
+    );
+  } catch {
+    return /(?:^|[^/)\w])#(\w*[a-zA-Z·]\w*)/i;
+  }
+};
+
+const buildHashtagRegex = () => {
+  try {
+    return new RegExp(
+      '^((' +
+      '[' + WORD + '_]' +
+      '[' + WORD + HASHTAG_SEPARATORS + ']*' +
+      '[' + ALPHA + HASHTAG_SEPARATORS + ']' +
+      '[' + WORD + HASHTAG_SEPARATORS +']*' +
+      '[' + WORD + '_]' +
+      ')|(' +
+      '[' + WORD + '_]*' +
+      '[' + ALPHA + ']' +
+      '[' + WORD + '_]*' +
+      '))$', 'iu',
+    );
+  } catch {
+    return /^(\w*[a-zA-Z·]\w*)$/i;
+  }
+};
+
+export const HASHTAG_PATTERN_REGEX = buildHashtagPatternRegex();
+
+export const HASHTAG_REGEX = buildHashtagRegex();
diff --git a/app/javascript/mastodon/utils/icons.js b/app/javascript/mastodon/utils/icons.jsx
index c3e362e39..c3e362e39 100644
--- a/app/javascript/mastodon/utils/icons.js
+++ b/app/javascript/mastodon/utils/icons.jsx
diff --git a/app/javascript/mastodon/utils/notifications.js b/app/javascript/mastodon/utils/notifications.js
index 7634cac21..42623ac7c 100644
--- a/app/javascript/mastodon/utils/notifications.js
+++ b/app/javascript/mastodon/utils/notifications.js
@@ -3,7 +3,7 @@
 
 const checkNotificationPromise = () => {
   try {
-    // eslint-disable-next-line promise/catch-or-return
+    // eslint-disable-next-line promise/valid-params, promise/catch-or-return
     Notification.requestPermission().then();
   } catch(e) {
     return false;
diff --git a/app/javascript/mastodon/utils/resize_image.js b/app/javascript/mastodon/utils/resize_image.js
deleted file mode 100644
index fb8c3c11e..000000000
--- a/app/javascript/mastodon/utils/resize_image.js
+++ /dev/null
@@ -1,189 +0,0 @@
-import EXIF from 'exif-js';
-
-const MAX_IMAGE_PIXELS = 2073600; // 1920x1080px
-
-const _browser_quirks = {};
-
-// Some browsers will automatically draw images respecting their EXIF orientation
-// while others won't, and the safest way to detect that is to examine how it
-// is done on a known image.
-// See https://github.com/w3c/csswg-drafts/issues/4666
-// and https://github.com/blueimp/JavaScript-Load-Image/commit/1e4df707821a0afcc11ea0720ee403b8759f3881
-const dropOrientationIfNeeded = (orientation) => new Promise(resolve => {
-  switch (_browser_quirks['image-orientation-automatic']) {
-  case true:
-    resolve(1);
-    break;
-  case false:
-    resolve(orientation);
-    break;
-  default:
-    // black 2x1 JPEG, with the following meta information set:
-    // - EXIF Orientation: 6 (Rotated 90° CCW)
-    const testImageURL =
-      'data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' +
-      'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' +
-      'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' +
-      'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/x' +
-      'ABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAA' +
-      'AAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==';
-    const img = new Image();
-    img.onload = () => {
-      const automatic = (img.width === 1 && img.height === 2);
-      _browser_quirks['image-orientation-automatic'] = automatic;
-      resolve(automatic ? 1 : orientation);
-    };
-    img.onerror = () => {
-      _browser_quirks['image-orientation-automatic'] = false;
-      resolve(orientation);
-    };
-    img.src = testImageURL;
-  }
-});
-
-// Some browsers don't allow reading from a canvas and instead return all-white
-// or randomized data. Use a pre-defined image to check if reading the canvas
-// works.
-const checkCanvasReliability = () => new Promise((resolve, reject) => {
-  switch(_browser_quirks['canvas-read-unreliable']) {
-  case true:
-    reject('Canvas reading unreliable');
-    break;
-  case false:
-    resolve();
-    break;
-  default:
-    // 2×2 GIF with white, red, green and blue pixels
-    const testImageURL =
-      'data:image/gif;base64,R0lGODdhAgACAKEDAAAA//8AAAD/AP///ywAAAAAAgACAAACA1wEBQA7';
-    const refData =
-      [255, 255, 255, 255,  255, 0, 0, 255,  0, 255, 0, 255,  0, 0, 255, 255];
-    const img = new Image();
-    img.onload = () => {
-      const canvas  = document.createElement('canvas');
-      const context = canvas.getContext('2d');
-      context.drawImage(img, 0, 0, 2, 2);
-      const imageData = context.getImageData(0, 0, 2, 2);
-      if (imageData.data.every((x, i) => refData[i] === x)) {
-        _browser_quirks['canvas-read-unreliable'] = false;
-        resolve();
-      } else {
-        _browser_quirks['canvas-read-unreliable'] = true;
-        reject('Canvas reading unreliable');
-      }
-    };
-    img.onerror = () => {
-      _browser_quirks['canvas-read-unreliable'] = true;
-      reject('Failed to load test image');
-    };
-    img.src = testImageURL;
-  }
-});
-
-const getImageUrl = inputFile => new Promise((resolve, reject) => {
-  if (window.URL && URL.createObjectURL) {
-    try {
-      resolve(URL.createObjectURL(inputFile));
-    } catch (error) {
-      reject(error);
-    }
-    return;
-  }
-
-  const reader = new FileReader();
-  reader.onerror = (...args) => reject(...args);
-  reader.onload  = ({ target }) => resolve(target.result);
-
-  reader.readAsDataURL(inputFile);
-});
-
-const loadImage = inputFile => new Promise((resolve, reject) => {
-  getImageUrl(inputFile).then(url => {
-    const img = new Image();
-
-    img.onerror = (...args) => reject(...args);
-    img.onload  = () => resolve(img);
-
-    img.src = url;
-  }).catch(reject);
-});
-
-const getOrientation = (img, type = 'image/png') => new Promise(resolve => {
-  if (!['image/jpeg', 'image/webp'].includes(type)) {
-    resolve(1);
-    return;
-  }
-
-  EXIF.getData(img, () => {
-    const orientation = EXIF.getTag(img, 'Orientation');
-    if (orientation !== 1) {
-      dropOrientationIfNeeded(orientation).then(resolve).catch(() => resolve(orientation));
-    } else {
-      resolve(orientation);
-    }
-  });
-});
-
-const processImage = (img, { width, height, orientation, type = 'image/png' }) => new Promise(resolve => {
-  const canvas  = document.createElement('canvas');
-
-  if (4 < orientation && orientation < 9) {
-    canvas.width  = height;
-    canvas.height = width;
-  } else {
-    canvas.width  = width;
-    canvas.height = height;
-  }
-
-  const context = canvas.getContext('2d');
-
-  switch (orientation) {
-  case 2: context.transform(-1, 0, 0, 1, width, 0); break;
-  case 3: context.transform(-1, 0, 0, -1, width, height); break;
-  case 4: context.transform(1, 0, 0, -1, 0, height); break;
-  case 5: context.transform(0, 1, 1, 0, 0, 0); break;
-  case 6: context.transform(0, 1, -1, 0, height, 0); break;
-  case 7: context.transform(0, -1, -1, 0, height, width); break;
-  case 8: context.transform(0, -1, 1, 0, 0, width); break;
-  }
-
-  context.drawImage(img, 0, 0, width, height);
-
-  canvas.toBlob(resolve, type);
-});
-
-const resizeImage = (img, type = 'image/png') => new Promise((resolve, reject) => {
-  const { width, height } = img;
-
-  const newWidth  = Math.round(Math.sqrt(MAX_IMAGE_PIXELS * (width / height)));
-  const newHeight = Math.round(Math.sqrt(MAX_IMAGE_PIXELS * (height / width)));
-
-  checkCanvasReliability()
-    .then(getOrientation(img, type))
-    .then(orientation => processImage(img, {
-      width: newWidth,
-      height: newHeight,
-      orientation,
-      type,
-    }))
-    .then(resolve)
-    .catch(reject);
-});
-
-export default inputFile => new Promise((resolve) => {
-  if (!inputFile.type.match(/image.*/) || inputFile.type === 'image/gif') {
-    resolve(inputFile);
-    return;
-  }
-
-  loadImage(inputFile).then(img => {
-    if (img.width * img.height < MAX_IMAGE_PIXELS) {
-      resolve(inputFile);
-      return;
-    }
-
-    resizeImage(img, inputFile.type)
-      .then(resolve)
-      .catch(() => resolve(inputFile));
-  }).catch(() => resolve(inputFile));
-});
diff --git a/app/javascript/mastodon/uuid.js b/app/javascript/mastodon/uuid.js
deleted file mode 100644
index 0d2cfaa77..000000000
--- a/app/javascript/mastodon/uuid.js
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function uuid(a) {
-  return a ? (a^Math.random() * 16 >> a / 4).toString(16) : ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, uuid);
-}
diff --git a/app/javascript/mastodon/uuid.ts b/app/javascript/mastodon/uuid.ts
new file mode 100644
index 000000000..01b57ee99
--- /dev/null
+++ b/app/javascript/mastodon/uuid.ts
@@ -0,0 +1,8 @@
+export default function uuid(a?: string): string {
+  return a
+    ? (
+      (a as any as number) ^
+        ((Math.random() * 16) >> ((a as any as number) / 4))
+    ).toString(16)
+    : ('' + 1e7 + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid);
+}
diff --git a/app/javascript/packs/admin.js b/app/javascript/packs/admin.jsx
index 599015000..599015000 100644
--- a/app/javascript/packs/admin.js
+++ b/app/javascript/packs/admin.jsx
diff --git a/app/javascript/packs/public-path.js b/app/javascript/packs/public-path.js
index f96109f4f..f4d166a77 100644
--- a/app/javascript/packs/public-path.js
+++ b/app/javascript/packs/public-path.js
@@ -17,5 +17,5 @@ function formatPublicPath(host = '', path = '') {
 
 const cdnHost = document.querySelector('meta[name=cdn-host]');
 
-// eslint-disable-next-line camelcase, no-undef, no-unused-vars
+// eslint-disable-next-line no-undef
 __webpack_public_path__ = formatPublicPath(cdnHost ? cdnHost.content : '', process.env.PUBLIC_OUTPUT_PATH);
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.jsx
index 8017734d5..ad6bf237f 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.jsx
@@ -100,11 +100,12 @@ function main() {
       const datetime = new Date(content.getAttribute('datetime'));
       const now      = new Date();
 
-      content.title = dateTimeFormat.format(datetime);
+      const timeGiven = content.getAttribute('datetime').includes('T');
+      content.title = timeGiven ? dateTimeFormat.format(datetime) : dateFormat.format(datetime);
       content.textContent = timeAgoString({
         formatMessage: ({ id, defaultMessage }, values) => (new IntlMessageFormat(messages[id] || defaultMessage, locale)).format(values),
         formatDate: (date, options) => (new Intl.DateTimeFormat(locale, options)).format(date),
-      }, datetime, now, now.getFullYear(), content.getAttribute('datetime').includes('T'));
+      }, datetime, now, now.getFullYear(), timeGiven);
     });
 
     const reactComponents = document.querySelectorAll('[data-component]');
@@ -188,10 +189,10 @@ function main() {
 
     if (sidebar.classList.contains('visible')) {
       document.body.style.overflow = null;
-      toggleButton.setAttribute('aria-expanded', false);
+      toggleButton.setAttribute('aria-expanded', 'false');
     } else {
       document.body.style.overflow = 'hidden';
-      toggleButton.setAttribute('aria-expanded', true);
+      toggleButton.setAttribute('aria-expanded', 'true');
     }
 
     toggleButton.classList.toggle('active');
diff --git a/app/javascript/packs/share.js b/app/javascript/packs/share.jsx
index 1225d7b52..1225d7b52 100644
--- a/app/javascript/packs/share.js
+++ b/app/javascript/packs/share.jsx
diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss
index 81a040108..1b2969c23 100644
--- a/app/javascript/styles/application.scss
+++ b/app/javascript/styles/application.scss
@@ -23,3 +23,4 @@
 @import 'mastodon/dashboard';
 @import 'mastodon/rtl';
 @import 'mastodon/accessibility';
+@import 'mastodon/rich_text';
diff --git a/app/javascript/styles/fonts/roboto-mono.scss b/app/javascript/styles/fonts/roboto-mono.scss
index 909d1a13e..66f3eed9f 100644
--- a/app/javascript/styles/fonts/roboto-mono.scss
+++ b/app/javascript/styles/fonts/roboto-mono.scss
@@ -1,11 +1,12 @@
 @font-face {
   font-family: mastodon-font-monospace;
-  src:
-    local('Roboto Mono'),
+  src: local('Roboto Mono'),
     url('~fonts/roboto-mono/robotomono-regular-webfont.woff2') format('woff2'),
     url('~fonts/roboto-mono/robotomono-regular-webfont.woff') format('woff'),
-    url('~fonts/roboto-mono/robotomono-regular-webfont.ttf') format('truetype'),
-    url('~fonts/roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular') format('svg');
+    url('~fonts/roboto-mono/robotomono-regular-webfont.ttf')
+      format('truetype'),
+    url('~fonts/roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular')
+      format('svg');
   font-weight: 400;
   font-display: swap;
   font-style: normal;
diff --git a/app/javascript/styles/fonts/roboto.scss b/app/javascript/styles/fonts/roboto.scss
index 0ccc43094..07cf0cb00 100644
--- a/app/javascript/styles/fonts/roboto.scss
+++ b/app/javascript/styles/fonts/roboto.scss
@@ -1,11 +1,11 @@
 @font-face {
   font-family: mastodon-font-sans-serif;
-  src:
-    local('Roboto Italic'),
+  src: local('Roboto Italic'),
     url('~fonts/roboto/roboto-italic-webfont.woff2') format('woff2'),
     url('~fonts/roboto/roboto-italic-webfont.woff') format('woff'),
     url('~fonts/roboto/roboto-italic-webfont.ttf') format('truetype'),
-    url('~fonts/roboto/roboto-italic-webfont.svg#roboto-italic-webfont') format('svg');
+    url('~fonts/roboto/roboto-italic-webfont.svg#roboto-italic-webfont')
+      format('svg');
   font-weight: normal;
   font-display: swap;
   font-style: italic;
@@ -13,12 +13,12 @@
 
 @font-face {
   font-family: mastodon-font-sans-serif;
-  src:
-    local('Roboto Bold'),
+  src: local('Roboto Bold'),
     url('~fonts/roboto/roboto-bold-webfont.woff2') format('woff2'),
     url('~fonts/roboto/roboto-bold-webfont.woff') format('woff'),
     url('~fonts/roboto/roboto-bold-webfont.ttf') format('truetype'),
-    url('~fonts/roboto/roboto-bold-webfont.svg#roboto-bold-webfont') format('svg');
+    url('~fonts/roboto/roboto-bold-webfont.svg#roboto-bold-webfont')
+      format('svg');
   font-weight: bold;
   font-display: swap;
   font-style: normal;
@@ -26,12 +26,12 @@
 
 @font-face {
   font-family: mastodon-font-sans-serif;
-  src:
-    local('Roboto Medium'),
+  src: local('Roboto Medium'),
     url('~fonts/roboto/roboto-medium-webfont.woff2') format('woff2'),
     url('~fonts/roboto/roboto-medium-webfont.woff') format('woff'),
     url('~fonts/roboto/roboto-medium-webfont.ttf') format('truetype'),
-    url('~fonts/roboto/roboto-medium-webfont.svg#roboto-medium-webfont') format('svg');
+    url('~fonts/roboto/roboto-medium-webfont.svg#roboto-medium-webfont')
+      format('svg');
   font-weight: 500;
   font-display: swap;
   font-style: normal;
@@ -39,12 +39,12 @@
 
 @font-face {
   font-family: mastodon-font-sans-serif;
-  src:
-    local('Roboto'),
+  src: local('Roboto'),
     url('~fonts/roboto/roboto-regular-webfont.woff2') format('woff2'),
     url('~fonts/roboto/roboto-regular-webfont.woff') format('woff'),
     url('~fonts/roboto/roboto-regular-webfont.ttf') format('truetype'),
-    url('~fonts/roboto/roboto-regular-webfont.svg#roboto-regular-webfont') format('svg');
+    url('~fonts/roboto/roboto-regular-webfont.svg#roboto-regular-webfont')
+      format('svg');
   font-weight: normal;
   font-display: swap;
   font-style: normal;
diff --git a/app/javascript/styles/mastodon-light/diff.scss b/app/javascript/styles/mastodon-light/diff.scss
index 7449cc785..40856cc04 100644
--- a/app/javascript/styles/mastodon-light/diff.scss
+++ b/app/javascript/styles/mastodon-light/diff.scss
@@ -12,6 +12,14 @@ html {
   &.button-alternative-2 {
     color: $white;
   }
+
+  &.button-tertiary {
+    color: $highlight-text-color;
+  }
+}
+
+.simple_form .button.button-tertiary {
+  color: $highlight-text-color;
 }
 
 .status-card__actions button,
@@ -144,7 +152,7 @@ html {
 }
 
 .compose-form__autosuggest-wrapper,
-.poll__option input[type="text"],
+.poll__option input[type='text'],
 .compose-form .spoiler-input__input,
 .compose-form__poll-wrapper select,
 .search__input,
@@ -171,7 +179,9 @@ html {
 }
 
 .compose-form__poll-wrapper select {
-  background: $simple-background-color url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 8%))}'/></svg>") no-repeat right 8px center / auto 16px;
+  background: $simple-background-color
+    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 8%))}'/></svg>")
+    no-repeat right 8px center / auto 16px;
 }
 
 .compose-form__poll-wrapper,
@@ -197,7 +207,9 @@ html {
 }
 
 .drawer__inner__mastodon {
-  background: $white 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($ui-base-color)}"/></svg>') no-repeat bottom / 100% auto;
+  background: $white
+    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($ui-base-color)}"/></svg>')
+    no-repeat bottom / 100% auto;
 }
 
 // Change the colors used in compose-form
@@ -246,9 +258,13 @@ html {
   border-color: $ui-base-color;
 }
 
+.upload-progress__backdrop {
+  background: $ui-base-color;
+}
+
 // Change the background colors of statuses
 .focusable:focus {
-  background: $ui-base-color;
+  background: lighten($white, 4%);
 }
 
 .detailed-status,
@@ -324,11 +340,13 @@ html {
   color: $white;
 }
 
-.language-dropdown__dropdown__results__item .language-dropdown__dropdown__results__item__common-name {
+.language-dropdown__dropdown__results__item
+  .language-dropdown__dropdown__results__item__common-name {
   color: lighten($ui-base-color, 8%);
 }
 
-.language-dropdown__dropdown__results__item.active .language-dropdown__dropdown__results__item__common-name {
+.language-dropdown__dropdown__results__item.active
+  .language-dropdown__dropdown__results__item__common-name {
   color: darken($ui-base-color, 12%);
 }
 
@@ -482,7 +500,8 @@ html {
   background: darken($ui-secondary-color, 10%);
 }
 
-.react-toggle.react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track {
+.react-toggle.react-toggle--checked:hover:not(.react-toggle--disabled)
+  .react-toggle-track {
   background: lighten($ui-highlight-color, 10%);
 }
 
@@ -514,10 +533,10 @@ html {
 }
 
 .simple_form {
-  input[type="text"],
-  input[type="number"],
-  input[type="email"],
-  input[type="password"],
+  input[type='text'],
+  input[type='number'],
+  input[type='email'],
+  input[type='password'],
   textarea {
     &:hover {
       border-color: lighten($ui-base-color, 12%);
@@ -543,25 +562,6 @@ html {
   }
 }
 
-.directory__tag.active > a,
-.directory__tag.active > div {
-  border-color: $ui-highlight-color;
-
-  &,
-  h4,
-  h4 small,
-  .fa,
-  .trends__item__current {
-    color: $white;
-  }
-
-  &:hover,
-  &:active,
-  &:focus {
-    background: $ui-highlight-color;
-  }
-}
-
 .batch-table {
   &__toolbar,
   &__row,
@@ -687,5 +687,15 @@ html {
 
 .mute-modal select {
   border: 1px solid lighten($ui-base-color, 8%);
-  background: $simple-background-color url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 8%))}'/></svg>") no-repeat right 8px center / auto 16px;
+  background: $simple-background-color
+    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14.933 18.467' height='19.698' width='15.929'><path d='M3.467 14.967l-3.393-3.5H14.86l-3.392 3.5c-1.866 1.925-3.666 3.5-4 3.5-.335 0-2.135-1.575-4-3.5zm.266-11.234L7.467 0 11.2 3.733l3.733 3.734H0l3.733-3.734z' fill='#{hex-color(lighten($ui-base-color, 8%))}'/></svg>")
+    no-repeat right 8px center / auto 16px;
+}
+
+.status__wrapper-direct {
+  background-color: rgba($ui-highlight-color, 0.1);
+
+  &:focus {
+    background-color: rgba($ui-highlight-color, 0.15);
+  }
 }
diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss
index 0183c43d5..0f02563b4 100644
--- a/app/javascript/styles/mastodon/about.scss
+++ b/app/javascript/styles/mastodon/about.scss
@@ -28,14 +28,14 @@ $fluid-breakpoint: $maximum-width + 20px;
     position: relative;
     border-bottom: 1px solid lighten($ui-base-color, 8%);
     padding: 1em 1.75em;
-    padding-left: 3em;
+    padding-inline-start: 3em;
     font-weight: 500;
     counter-increment: list-counter;
 
     &::before {
       content: counter(list-counter);
       position: absolute;
-      left: 0;
+      inset-inline-start: 0;
       top: 50%;
       transform: translateY(-50%);
       background: $highlight-text-color;
diff --git a/app/javascript/styles/mastodon/accessibility.scss b/app/javascript/styles/mastodon/accessibility.scss
index c5bcb5941..deaa0afda 100644
--- a/app/javascript/styles/mastodon/accessibility.scss
+++ b/app/javascript/styles/mastodon/accessibility.scss
@@ -1,4 +1,7 @@
-$emojis-requiring-inversion: 'back' 'copyright' 'curly_loop' 'currency_exchange' 'end' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign' 'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'on' 'registered' 'soon' 'spider' 'telephone_receiver' 'tm' 'top' 'wavy_dash' !default;
+$emojis-requiring-inversion: 'back' 'copyright' 'curly_loop' 'currency_exchange'
+  'end' 'heavy_check_mark' 'heavy_division_sign' 'heavy_dollar_sign'
+  'heavy_minus_sign' 'heavy_multiplication_x' 'heavy_plus_sign' 'on'
+  'registered' 'soon' 'spider' 'telephone_receiver' 'tm' 'top' 'wavy_dash' !default;
 
 %emoji-color-inversion {
   filter: invert(1);
diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss
index 0497d51d5..71bd20ebf 100644
--- a/app/javascript/styles/mastodon/accounts.scss
+++ b/app/javascript/styles/mastodon/accounts.scss
@@ -36,10 +36,6 @@
     @media screen and (max-width: 600px) {
       height: 200px;
     }
-
-    @media screen and (max-width: $no-gap-breakpoint) {
-      display: none;
-    }
   }
 
   &__bar {
@@ -73,8 +69,8 @@
     }
 
     .display-name {
-      margin-left: 15px;
-      text-align: left;
+      margin-inline-start: 15px;
+      text-align: start;
 
       i[data-hidden] {
         display: none;
@@ -139,21 +135,21 @@
 
   .older {
     float: left;
-    padding-left: 0;
+    padding-inline-start: 0;
 
     .fa {
       display: inline-block;
-      margin-right: 5px;
+      margin-inline-end: 5px;
     }
   }
 
   .newer {
     float: right;
-    padding-right: 0;
+    padding-inline-end: 0;
 
     .fa {
       display: inline-block;
-      margin-left: 5px;
+      margin-inline-start: 5px;
     }
   }
 
diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss
index 4250cf2b6..acb4baf4f 100644
--- a/app/javascript/styles/mastodon/admin.scss
+++ b/app/javascript/styles/mastodon/admin.scss
@@ -1,4 +1,4 @@
-@use "sass:math";
+@use 'sass:math';
 
 $no-columns-breakpoint: 600px;
 $sidebar-width: 240px;
@@ -117,7 +117,7 @@ $content-width: 840px;
         text-overflow: ellipsis;
 
         i.fa {
-          margin-right: 5px;
+          margin-inline-end: 5px;
         }
 
         &:hover {
@@ -186,7 +186,10 @@ $content-width: 840px;
   }
 
   .content {
-    padding: 55px 15px 20px 25px;
+    padding-top: 55px;
+    padding-bottom: 20px;
+    padding-inline-start: 25px;
+    padding-inline-end: 15px;
 
     @media screen and (max-width: $no-columns-breakpoint) {
       max-width: none;
@@ -202,11 +205,12 @@ $content-width: 840px;
         flex-wrap: wrap;
         align-items: center;
         justify-content: space-between;
-        margin: -15px -15px 0 0;
+        margin-top: -15px;
+        margin-inline-end: -15px;
 
         & > * {
           margin-top: 15px;
-          margin-right: 15px;
+          margin-inline-end: 15px;
         }
       }
 
@@ -384,8 +388,8 @@ $content-width: 840px;
           position: fixed;
           z-index: 10;
           width: 100%;
-          height: calc(100vh - 56px);
-          left: 0;
+          height: calc(100% - 56px);
+          inset-inline-start: 0;
           bottom: 0;
           overflow-y: auto;
           background: $ui-base-color;
@@ -470,10 +474,11 @@ body,
 .filters {
   display: flex;
   flex-wrap: wrap;
+  gap: 40px;
 
   .filter-subset {
     flex: 0 0 auto;
-    margin: 0 40px 20px 0;
+    margin-bottom: 20px;
 
     &:last-child {
       margin-bottom: 30px;
@@ -485,7 +490,7 @@ body,
 
       li {
         display: inline-block;
-        margin-right: 5px;
+        margin-inline-end: 5px;
       }
     }
 
@@ -588,7 +593,7 @@ body,
 
   .activity-stream {
     flex: 2 0 0;
-    margin-right: 20px;
+    margin-inline-end: 20px;
     max-width: calc(100% - 60px);
 
     .entry {
@@ -641,7 +646,7 @@ body,
   }
 
   .media-spoiler-toggle-buttons {
-    margin-left: auto;
+    margin-inline-start: auto;
 
     .button {
       overflow: visible;
@@ -667,7 +672,7 @@ body,
 
 .special-action-button,
 .back-link {
-  text-align: right;
+  text-align: end;
   flex: 1 1 auto;
 }
 
@@ -685,7 +690,7 @@ body,
   display: block;
   line-height: 20px;
   padding: 15px;
-  padding-left: 15px * 2 + 40px;
+  padding-inline-start: 15px * 2 + 40px;
   background: $ui-base-color;
   border-bottom: 1px solid darken($ui-base-color, 8%);
   position: relative;
@@ -712,7 +717,7 @@ body,
 
   &__avatar {
     position: absolute;
-    left: 15px;
+    inset-inline-start: 15px;
     top: 15px;
 
     .avatar {
@@ -780,7 +785,7 @@ a.name-tag,
   .avatar {
     display: block;
     margin: 0;
-    margin-right: 5px;
+    margin-inline-end: 5px;
     border-radius: 50%;
   }
 
@@ -794,7 +799,7 @@ a.name-tag,
 
 .speech-bubble {
   margin-bottom: 20px;
-  border-left: 4px solid $ui-highlight-color;
+  border-inline-start: 4px solid $ui-highlight-color;
 
   &.positive {
     border-left-color: $success-green;
@@ -810,7 +815,7 @@ a.name-tag,
 
   &__bubble {
     padding: 16px;
-    padding-left: 14px;
+    padding-inline-start: 14px;
     font-size: 15px;
     line-height: 20px;
     border-radius: 4px 4px 4px 0;
@@ -824,7 +829,7 @@ a.name-tag,
 
   &__owner {
     padding: 8px;
-    padding-left: 12px;
+    padding-inline-start: 12px;
   }
 
   time {
@@ -848,7 +853,7 @@ a.name-tag,
       border: 0;
 
       &__avatar-wrapper {
-        margin-left: 0;
+        margin-inline-start: 0;
       }
     }
 
@@ -857,7 +862,7 @@ a.name-tag,
       font-weight: 500;
       color: $darker-text-color;
       text-transform: uppercase;
-      text-align: right;
+      text-align: end;
 
       a {
         color: inherit;
@@ -908,7 +913,7 @@ a.name-tag,
 
         &__icon {
           color: $dark-text-color;
-          margin-right: 4px;
+          margin-inline-end: 4px;
           font-weight: 500;
         }
       }
@@ -1106,7 +1111,7 @@ a.name-tag,
 
   > h4 {
     position: sticky;
-    left: 0;
+    inset-inline-start: 0;
   }
 
   &__table {
@@ -1118,7 +1123,7 @@ a.name-tag,
     &__date {
       white-space: nowrap;
       padding: 10px 0;
-      text-align: left;
+      text-align: start;
       min-width: 120px;
 
       &.retention__table__average {
@@ -1147,7 +1152,10 @@ a.name-tag,
 
       @for $i from 0 through 10 {
         &--#{10 * $i} {
-          background-color: rgba($ui-highlight-color, 1 * (math.div(max(1, $i), 10)));
+          background-color: rgba(
+            $ui-highlight-color,
+            1 * (math.div(max(1, $i), 10))
+          );
         }
       }
     }
@@ -1173,7 +1181,7 @@ a.name-tag,
 
     &__total {
       display: block;
-      margin-right: 10px;
+      margin-inline-end: 10px;
       font-weight: 500;
       font-size: 28px;
       color: $primary-text-color;
@@ -1236,7 +1244,12 @@ a.sparkline {
 
 .skeleton {
   background-color: lighten($ui-base-color, 8%);
-  background-image: linear-gradient(90deg, lighten($ui-base-color, 8%), lighten($ui-base-color, 12%), lighten($ui-base-color, 8%));
+  background-image: linear-gradient(
+    90deg,
+    lighten($ui-base-color, 8%),
+    lighten($ui-base-color, 12%),
+    lighten($ui-base-color, 8%)
+  );
   background-size: 200px 100%;
   background-repeat: no-repeat;
   border-radius: 4px;
@@ -1270,7 +1283,7 @@ a.sparkline {
     }
 
     &__value {
-      text-align: right;
+      text-align: end;
       color: $darker-text-color;
       padding: 11px 10px;
     }
@@ -1281,11 +1294,14 @@ a.sparkline {
       height: 8px;
       border-radius: 50%;
       background: $ui-highlight-color;
-      margin-right: 10px;
+      margin-inline-end: 10px;
 
       @for $i from 0 through 10 {
         &--#{10 * $i} {
-          background-color: rgba($ui-highlight-color, 1 * (math.div(max(1, $i), 10)));
+          background-color: rgba(
+            $ui-highlight-color,
+            1 * (math.div(max(1, $i), 10))
+          );
         }
       }
     }
@@ -1314,7 +1330,7 @@ a.sparkline {
     }
 
     &__rules {
-      margin-left: 30px;
+      margin-inline-start: 30px;
     }
   }
 
@@ -1431,12 +1447,12 @@ a.sparkline {
 
     &::after {
       display: block;
-      content: "";
+      content: '';
       width: 50px;
       height: 21px;
       position: absolute;
       bottom: 0;
-      right: 15px;
+      inset-inline-end: 15px;
       background: linear-gradient(to left, $ui-base-color, transparent);
       pointer-events: none;
     }
@@ -1516,7 +1532,7 @@ a.sparkline {
     background: $ui-base-color;
     position: relative;
     padding: 15px;
-    padding-left: 15px * 2 + 40px;
+    padding-inline-start: 15px * 2 + 40px;
     border-bottom: 1px solid darken($ui-base-color, 8%);
 
     &:first-child {
@@ -1536,7 +1552,7 @@ a.sparkline {
 
     &__avatar {
       position: absolute;
-      left: 15px;
+      inset-inline-start: 15px;
       top: 15px;
       border-radius: 4px;
       width: 40px;
@@ -1552,7 +1568,7 @@ a.sparkline {
       .username {
         color: $primary-text-color;
         font-weight: 500;
-        margin-right: 5px;
+        margin-inline-end: 5px;
 
         a {
           color: inherit;
@@ -1567,7 +1583,7 @@ a.sparkline {
       }
 
       time {
-        margin-left: 5px;
+        margin-inline-start: 5px;
         vertical-align: baseline;
       }
     }
@@ -1588,13 +1604,22 @@ a.sparkline {
           margin-bottom: 0;
         }
       }
+
+      a {
+        color: $highlight-text-color;
+        text-decoration: none;
+
+        &:hover {
+          text-decoration: underline;
+        }
+      }
     }
 
     &__actions {
       position: absolute;
       top: 15px;
-      right: 15px;
-      text-align: right;
+      inset-inline-end: 15px;
+      text-align: end;
     }
   }
 }
@@ -1617,7 +1642,7 @@ a.sparkline {
       flex: 0 0 auto;
       width: 200px;
       padding: 15px;
-      padding-right: 0;
+      padding-inline-end: 0;
 
       .button {
         display: block;
@@ -1703,7 +1728,7 @@ a.sparkline {
 
   &__rules {
     list-style: disc;
-    padding-left: 15px;
+    padding-inline-start: 15px;
     margin-bottom: 20px;
     color: $darker-text-color;
 
@@ -1792,7 +1817,7 @@ a.sparkline {
 
   li {
     counter-increment: step 1;
-    padding-left: 2.5rem;
+    padding-inline-start: 2.5rem;
     padding-bottom: 8px;
     position: relative;
     margin-bottom: 8px;
@@ -1802,7 +1827,7 @@ a.sparkline {
       content: counter(step);
       font-size: 0.625rem;
       font-weight: 500;
-      left: 0;
+      inset-inline-start: 0;
       display: flex;
       justify-content: center;
       align-items: center;
@@ -1816,12 +1841,12 @@ a.sparkline {
 
     &::after {
       position: absolute;
-      content: "";
+      content: '';
       width: 1px;
       background: $highlight-text-color;
       bottom: 0;
       top: calc(1.875rem + 1px);
-      left: 0.6875rem;
+      inset-inline-start: 0.6875rem;
     }
 
     &:last-child {
diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss
index 413a1cdd6..a344c7fa4 100644
--- a/app/javascript/styles/mastodon/basics.scss
+++ b/app/javascript/styles/mastodon/basics.scss
@@ -14,7 +14,7 @@ body {
   font-weight: 400;
   color: $primary-text-color;
   text-rendering: optimizelegibility;
-  font-feature-settings: "kern";
+  font-feature-settings: 'kern';
   text-size-adjust: none;
   -webkit-tap-highlight-color: rgba(0, 0, 0, 0%);
   -webkit-tap-highlight-color: transparent;
@@ -31,7 +31,9 @@ body {
     // Droid Sans => Older Androids (<4.0)
     // Helvetica Neue => Older macOS <10.11
     // $font-sans-serif => web-font (Roboto) fallback and newer Androids (>=4.0)
-    font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", $font-sans-serif, sans-serif;
+    font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
+      Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+      $font-sans-serif, sans-serif;
   }
 
   &.app-body {
@@ -265,7 +267,7 @@ button {
   overflow: hidden;
   position: absolute;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   z-index: -1000;
 }
 
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index d5937643f..862252781 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -309,7 +309,7 @@
   &__counter {
     display: inline-block;
     width: auto;
-    margin-left: 4px;
+    margin-inline-start: 4px;
     font-size: 12px;
     font-weight: 500;
   }
@@ -387,7 +387,7 @@ body > [data-popper-placement] {
 
 .ellipsis {
   &::after {
-    content: "…";
+    content: '…';
   }
 }
 
@@ -404,11 +404,8 @@ body > [data-popper-placement] {
       color: $highlight-text-color;
     }
 
-    input[type="checkbox"] {
-      display: none;
-    }
-
-    .checkbox {
+    input[type='checkbox'] {
+      appearance: none;
       display: inline-block;
       position: relative;
       border: 1px solid $ui-primary-color;
@@ -416,14 +413,17 @@ body > [data-popper-placement] {
       width: 18px;
       height: 18px;
       flex: 0 0 auto;
-      margin-right: 10px;
+      margin-inline-end: 10px;
       top: -1px;
       border-radius: 4px;
       vertical-align: middle;
+      cursor: inherit;
 
-      &.active {
+      &:checked {
         border-color: $highlight-text-color;
-        background: $highlight-text-color;
+        background: $highlight-text-color
+          url("data:image/svg+xml;utf8,<svg width='18' height='18' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M4.5 8.5L8 12l6-6' stroke='white' stroke-width='1.5'/></svg>")
+          center center no-repeat;
       }
     }
   }
@@ -465,7 +465,7 @@ body > [data-popper-placement] {
   .emoji-picker-dropdown {
     position: absolute;
     top: 0;
-    right: 0;
+    inset-inline-end: 0;
   }
 
   .compose-form__autosuggest-wrapper {
@@ -527,7 +527,7 @@ body > [data-popper-placement] {
     min-height: 100px;
     border-radius: 4px 4px 0 0;
     padding-bottom: 0;
-    padding-right: 10px + 22px;
+    padding-right: 10px + 22px; // Cannot use inline-end because of dir=auto
     resize: none;
     scrollbar-color: initial;
 
@@ -536,7 +536,7 @@ body > [data-popper-placement] {
     }
 
     @media screen and (max-width: 600px) {
-      height: 100px !important; // prevent auto-resize textarea
+      height: 100px !important; // Prevent auto-resize textarea
       resize: vertical;
     }
   }
@@ -605,7 +605,7 @@ body > [data-popper-placement] {
 
     &__uses {
       flex: 0 0 auto;
-      text-align: right;
+      text-align: end;
       overflow: hidden;
       text-overflow: ellipsis;
       white-space: nowrap;
@@ -615,7 +615,7 @@ body > [data-popper-placement] {
   .autosuggest-account-icon,
   .autosuggest-emoji img {
     display: block;
-    margin-right: 8px;
+    margin-inline-end: 8px;
     width: 16px;
     height: 16px;
   }
@@ -647,7 +647,12 @@ body > [data-popper-placement] {
       margin: 5px;
 
       &__actions {
-        background: linear-gradient(180deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 80%, transparent);
+        background: linear-gradient(
+          180deg,
+          rgba($base-shadow-color, 0.8) 0,
+          rgba($base-shadow-color, 0.35) 80%,
+          transparent
+        );
         display: flex;
         align-items: flex-start;
         justify-content: space-between;
@@ -672,10 +677,15 @@ body > [data-popper-placement] {
         position: absolute;
         z-index: 2;
         bottom: 0;
-        left: 0;
-        right: 0;
+        inset-inline-start: 0;
+        inset-inline-end: 0;
         box-sizing: border-box;
-        background: linear-gradient(0deg, rgba($base-shadow-color, 0.8) 0, rgba($base-shadow-color, 0.35) 80%, transparent);
+        background: linear-gradient(
+          0deg,
+          rgba($base-shadow-color, 0.8) 0,
+          rgba($base-shadow-color, 0.35) 80%,
+          transparent
+        );
       }
     }
 
@@ -727,7 +737,7 @@ body > [data-popper-placement] {
 
     .character-counter__wrapper {
       align-self: center;
-      margin-right: 4px;
+      margin-inline-end: 4px;
     }
   }
 
@@ -826,13 +836,13 @@ body > [data-popper-placement] {
   max-width: 100%;
   line-height: 24px;
   overflow: hidden;
-  padding-right: 25px;
+  padding-inline-end: 25px;
   text-decoration: none;
 }
 
 .reply-indicator__display-avatar {
   float: left;
-  margin-right: 5px;
+  margin-inline-end: 5px;
 }
 
 .status__content--with-action {
@@ -1080,8 +1090,13 @@ body > [data-popper-placement] {
   cursor: auto;
 
   @keyframes fade {
-    0% { opacity: 0; }
-    100% { opacity: 1; }
+    0% {
+      opacity: 0;
+    }
+
+    100% {
+      opacity: 1;
+    }
   }
 
   opacity: 1;
@@ -1183,11 +1198,12 @@ body > [data-popper-placement] {
 
 .status__info {
   font-size: 15px;
-  margin-bottom: 10px;
+  padding-bottom: 10px;
   display: flex;
   align-items: center;
   justify-content: space-between;
   gap: 10px;
+  cursor: pointer;
 }
 
 .status-check-box__status {
@@ -1228,7 +1244,7 @@ body > [data-popper-placement] {
 .status__prepend {
   padding: 16px;
   padding-bottom: 0;
-  display: flex;
+  display: inline-flex;
   gap: 10px;
   font-size: 15px;
   line-height: 22px;
@@ -1246,6 +1262,18 @@ body > [data-popper-placement] {
   }
 }
 
+.status__wrapper-direct {
+  background: mix($ui-base-color, $ui-highlight-color, 95%);
+
+  &:focus {
+    background: mix(lighten($ui-base-color, 4%), $ui-highlight-color, 95%);
+  }
+
+  .status__prepend {
+    color: $highlight-text-color;
+  }
+}
+
 .status__action-bar {
   display: flex;
   justify-content: space-between;
@@ -1299,6 +1327,11 @@ body > [data-popper-placement] {
   .audio-player {
     margin-top: 16px;
   }
+
+  .status__prepend {
+    padding: 0;
+    margin-bottom: 16px;
+  }
 }
 
 .detailed-status__meta {
@@ -1317,6 +1350,32 @@ body > [data-popper-placement] {
   padding: 10px 0;
 }
 
+.detailed-status__wrapper-direct {
+  .detailed-status,
+  .detailed-status__action-bar {
+    background: mix($ui-base-color, $ui-highlight-color, 95%);
+  }
+
+  &:focus {
+    .detailed-status,
+    .detailed-status__action-bar {
+      background: mix(lighten($ui-base-color, 4%), $ui-highlight-color, 95%);
+    }
+  }
+
+  .detailed-status__action-bar {
+    border-top-color: mix(
+      lighten($ui-base-color, 8%),
+      $ui-highlight-color,
+      95%
+    );
+  }
+
+  .status__prepend {
+    color: $highlight-text-color;
+  }
+}
+
 .detailed-status__link {
   color: inherit;
   text-decoration: none;
@@ -1328,7 +1387,7 @@ body > [data-popper-placement] {
   font-weight: 500;
   font-size: 12px;
   line-height: 17px;
-  margin-left: 6px;
+  margin-inline-start: 6px;
 }
 
 .reply-indicator__content {
@@ -1368,15 +1427,6 @@ body > [data-popper-placement] {
   padding: 16px;
   border-bottom: 1px solid lighten($ui-base-color, 8%);
 
-  &.compact {
-    padding: 0;
-    border-bottom: 0;
-
-    .account__avatar-wrapper {
-      margin-left: 0;
-    }
-  }
-
   .account__display-name {
     flex: 1 1 auto;
     display: flex;
@@ -1387,9 +1437,23 @@ body > [data-popper-placement] {
     text-decoration: none;
     font-size: 14px;
 
-    &--with-note {
-      strong {
-        display: inline;
+    .display-name {
+      margin-bottom: 4px;
+    }
+
+    .display-name strong {
+      display: inline;
+    }
+  }
+
+  &--minimal {
+    .account__display-name {
+      .display-name {
+        margin-bottom: 0;
+      }
+
+      .display-name strong {
+        display: block;
       }
     }
   }
@@ -1434,7 +1498,7 @@ body > [data-popper-placement] {
   &-inline {
     display: inline-block;
     vertical-align: middle;
-    margin-right: 5px;
+    margin-inline-end: 5px;
   }
 
   &-composite {
@@ -1457,7 +1521,7 @@ body > [data-popper-placement] {
       display: block;
       position: absolute;
       top: 50%;
-      left: 50%;
+      inset-inline-start: 50%;
       transform: translate(-50%, -50%);
       color: $primary-text-color;
       text-shadow: 1px 1px 2px $base-shadow-color;
@@ -1477,7 +1541,7 @@ a .account__avatar {
   &-overlay {
     position: absolute;
     bottom: 0;
-    right: 0;
+    inset-inline-end: 0;
     z-index: 1;
   }
 }
@@ -1534,15 +1598,15 @@ a .account__avatar {
 
   .dropdown--active {
     .dropdown__content.dropdown__right {
-      left: 6px;
-      right: initial;
+      inset-inline-start: 6px;
+      inset-inline-end: initial;
     }
 
     &::after {
       bottom: initial;
-      margin-left: 11px;
+      margin-inline-start: 11px;
       margin-top: -7px;
-      right: initial;
+      inset-inline-end: initial;
     }
   }
 }
@@ -1558,7 +1622,7 @@ a .account__avatar {
   text-decoration: none;
   overflow: hidden;
   flex: 0 1 100%;
-  border-right: 1px solid lighten($ui-base-color, 8%);
+  border-inline-end: 1px solid lighten($ui-base-color, 8%);
   padding: 10px 0;
   border-bottom: 4px solid transparent;
 
@@ -1599,7 +1663,7 @@ a .account__avatar {
 
 .account-authorize__avatar {
   float: left;
-  margin-right: 10px;
+  margin-inline-end: 10px;
 }
 
 .status__display-name,
@@ -1613,7 +1677,7 @@ a .account__avatar {
 
 .status__display-name,
 .account__display-name {
-  strong {
+  .display-name strong {
     color: $primary-text-color;
   }
 }
@@ -1628,12 +1692,12 @@ a .account__avatar {
 .reply-indicator__display-name,
 .detailed-status__display-name,
 a.account__display-name {
-  &:hover strong {
+  &:hover .display-name strong {
     text-decoration: underline;
   }
 }
 
-.account__display-name strong {
+.account__display-name .display-name strong {
   display: block;
   overflow: hidden;
   text-overflow: ellipsis;
@@ -1827,11 +1891,11 @@ a.account__display-name {
   justify-content: center;
   flex-direction: column;
   scrollbar-width: none; /* Firefox */
-  -ms-overflow-style: none;  /* IE 10+ */
+  -ms-overflow-style: none; /* IE 10+ */
 
   * {
     scrollbar-width: none; /* Firefox */
-    -ms-overflow-style: none;  /* IE 10+ */
+    -ms-overflow-style: none; /* IE 10+ */
   }
 
   &::-webkit-scrollbar,
@@ -2071,7 +2135,7 @@ a.account__display-name {
   }
 
   &.right {
-    left: -9px;
+    inset-inline-start: -9px;
 
     &::before {
       transform: rotate(-90deg);
@@ -2083,7 +2147,7 @@ a.account__display-name {
   }
 
   &.left {
-    right: -9px;
+    inset-inline-end: -9px;
 
     &::before {
       transform: rotate(90deg);
@@ -2153,7 +2217,7 @@ a.account__display-name {
   vertical-align: top;
 
   .account__avatar {
-    margin-right: 5px;
+    margin-inline-end: 5px;
     border-radius: 50%;
   }
 
@@ -2166,8 +2230,8 @@ a.account__display-name {
   display: block;
   line-height: 18px;
   max-width: 311px;
-  right: 0;
-  text-align: left;
+  inset-inline-end: 0;
+  text-align: start;
   z-index: 9999;
 
   & > ul {
@@ -2181,12 +2245,12 @@ a.account__display-name {
   }
 
   &.dropdown__right {
-    right: 0;
+    inset-inline-end: 0;
   }
 
   &.dropdown__left {
     & > ul {
-      left: -98px;
+      inset-inline-start: -98px;
     }
   }
 
@@ -2405,23 +2469,23 @@ $ui-header-height: 55px;
   .drawer {
     flex: 0 0 auto;
     padding: 10px;
-    padding-left: 5px;
-    padding-right: 5px;
+    padding-inline-start: 5px;
+    padding-inline-end: 5px;
 
     &:first-child {
-      padding-left: 10px;
+      padding-inline-start: 10px;
     }
 
     &:last-child {
-      padding-right: 10px;
+      padding-inline-end: 10px;
     }
   }
 
   .columns-area > div {
     .column,
     .drawer {
-      padding-left: 5px;
-      padding-right: 5px;
+      padding-inline-start: 5px;
+      padding-inline-end: 5px;
     }
   }
 }
@@ -2468,7 +2532,7 @@ $ui-header-height: 55px;
   }
 
   span {
-    margin-left: 5px;
+    margin-inline-start: 5px;
     display: none;
   }
 }
@@ -2510,7 +2574,7 @@ $ui-header-height: 55px;
     line-height: 18px;
     font-size: 16px;
     padding: 15px;
-    padding-right: 30px;
+    padding-inline-end: 30px;
   }
 
   .search__icon .fa {
@@ -2578,7 +2642,7 @@ $ui-header-height: 55px;
     .navigation-panel {
       margin: 0;
       background: $ui-base-color;
-      border-left: 1px solid lighten($ui-base-color, 8%);
+      border-inline-start: 1px solid lighten($ui-base-color, 8%);
       height: 100vh;
     }
 
@@ -2650,7 +2714,7 @@ $ui-header-height: 55px;
 
   &__badge {
     position: absolute;
-    left: 9px;
+    inset-inline-start: 9px;
     top: -13px;
     background: $ui-highlight-color;
     border: 2px solid lighten($ui-base-color, 8%);
@@ -2664,7 +2728,7 @@ $ui-header-height: 55px;
 
   &__issue-badge {
     position: absolute;
-    left: 11px;
+    inset-inline-start: 11px;
     bottom: 1px;
     display: block;
     background: $error-red;
@@ -2720,7 +2784,7 @@ $ui-header-height: 55px;
 
   &__background {
     position: absolute;
-    left: 0;
+    inset-inline-start: 0;
     bottom: 0;
     height: 220px;
     width: auto;
@@ -2846,7 +2910,7 @@ $ui-header-height: 55px;
 .drawer__inner {
   position: absolute;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   background: lighten($ui-base-color, 13%);
   box-sizing: border-box;
   padding: 0;
@@ -2863,7 +2927,9 @@ $ui-header-height: 55px;
 }
 
 .drawer__inner__mastodon {
-  background: lighten($ui-base-color, 13%) 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($ui-base-color)}"/></svg>') no-repeat bottom / 100% auto;
+  background: lighten($ui-base-color, 13%)
+    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($ui-base-color)}"/></svg>')
+    no-repeat bottom / 100% auto;
   flex: 1;
   min-height: 47px;
   display: none;
@@ -2875,7 +2941,6 @@ $ui-header-height: 55px;
     width: 85%;
     height: 100%;
     pointer-events: none;
-    user-drag: none;
     user-select: none;
   }
 
@@ -2887,7 +2952,7 @@ $ui-header-height: 55px;
 .pseudo-drawer {
   background: lighten($ui-base-color, 13%);
   font-size: 13px;
-  text-align: left;
+  text-align: start;
 }
 
 .drawer__header {
@@ -2897,6 +2962,8 @@ $ui-header-height: 55px;
   margin-bottom: 10px;
   display: flex;
   flex-direction: row;
+  border-radius: 4px;
+  overflow: hidden;
 
   a {
     transition: background 100ms ease-in;
@@ -2918,7 +2985,8 @@ $ui-header-height: 55px;
     overflow-y: auto;
   }
 
-  @supports (display: grid) { // hack to fix Chrome <57
+  @supports (display: grid) {
+    // hack to fix Chrome <57
     contain: strict;
   }
 
@@ -2939,7 +3007,8 @@ $ui-header-height: 55px;
 }
 
 .scrollable.fullscreen {
-  @supports (display: grid) { // hack to fix Chrome <57
+  @supports (display: grid) {
+    // hack to fix Chrome <57
     contain: none;
   }
 }
@@ -2988,7 +3057,7 @@ $ui-header-height: 55px;
 
 .column-back-button__icon {
   display: inline-block;
-  margin-right: 5px;
+  margin-inline-end: 5px;
 }
 
 .column-back-button--slim {
@@ -3001,7 +3070,7 @@ $ui-header-height: 55px;
   font-size: 16px;
   padding: 15px;
   position: absolute;
-  right: 0;
+  inset-inline-end: 0;
   top: -48px;
 }
 
@@ -3043,7 +3112,8 @@ $ui-header-height: 55px;
   transition: background-color 0.2s ease;
 }
 
-.react-toggle:is(:hover, :focus-within):not(.react-toggle--disabled) .react-toggle-track {
+.react-toggle:is(:hover, :focus-within):not(.react-toggle--disabled)
+  .react-toggle-track {
   background-color: darken($ui-base-color, 10%);
 }
 
@@ -3051,7 +3121,8 @@ $ui-header-height: 55px;
   background-color: darken($ui-highlight-color, 2%);
 }
 
-.react-toggle--checked:is(:hover, :focus-within):not(.react-toggle--disabled) .react-toggle-track {
+.react-toggle--checked:is(:hover, :focus-within):not(.react-toggle--disabled)
+  .react-toggle-track {
   background-color: $ui-highlight-color;
 }
 
@@ -3064,7 +3135,7 @@ $ui-header-height: 55px;
   margin-top: auto;
   margin-bottom: auto;
   line-height: 0;
-  left: 8px;
+  inset-inline-start: 8px;
   opacity: 0;
   transition: opacity 0.25s ease;
 }
@@ -3083,7 +3154,7 @@ $ui-header-height: 55px;
   margin-top: auto;
   margin-bottom: auto;
   line-height: 0;
-  right: 10px;
+  inset-inline-end: 10px;
   opacity: 1;
   transition: opacity 0.25s ease;
 }
@@ -3095,7 +3166,7 @@ $ui-header-height: 55px;
 .react-toggle-thumb {
   position: absolute;
   top: 1px;
-  left: 1px;
+  inset-inline-start: 1px;
   width: 22px;
   height: 22px;
   border: 1px solid $ui-base-color;
@@ -3107,7 +3178,7 @@ $ui-header-height: 55px;
 }
 
 .react-toggle--checked .react-toggle-thumb {
-  left: 27px;
+  inset-inline-start: 27px;
   border-color: $ui-highlight-color;
 }
 
@@ -3161,7 +3232,7 @@ $ui-header-height: 55px;
 
 .column-link__icon {
   display: inline-block;
-  margin-right: 5px;
+  margin-inline-end: 5px;
 }
 
 .column-link__badge {
@@ -3255,7 +3326,7 @@ $ui-header-height: 55px;
 
   thead {
     position: absolute;
-    left: -9999px;
+    inset-inline-start: -9999px;
   }
 
   td {
@@ -3359,9 +3430,9 @@ button.icon-button.active i.fa-retweet {
 
   &__actions {
     bottom: 0;
-    left: 0;
+    inset-inline-start: 0;
     position: absolute;
-    right: 0;
+    inset-inline-end: 0;
     top: 0;
     display: flex;
     justify-content: center;
@@ -3467,7 +3538,7 @@ a.status-card {
     position: absolute;
     transform-origin: 50% 50%;
     top: 50%;
-    left: 50%;
+    inset-inline-start: 50%;
     transform: translate(-50%, -50%);
   }
 }
@@ -3534,7 +3605,7 @@ a.status-card.compact:hover {
   object-fit: fill;
   position: absolute;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   z-index: 0;
   background: $base-overlay-background;
 
@@ -3646,17 +3717,21 @@ a.status-card.compact:hover {
 
     &::before {
       display: block;
-      content: "";
+      content: '';
       position: absolute;
       bottom: -13px;
-      left: 0;
-      right: 0;
+      inset-inline-start: 0;
+      inset-inline-end: 0;
       margin: 0 auto;
       width: 60%;
       pointer-events: none;
       height: 28px;
       z-index: 1;
-      background: radial-gradient(ellipse, rgba($ui-highlight-color, 0.23) 0%, rgba($ui-highlight-color, 0) 60%);
+      background: radial-gradient(
+        ellipse,
+        rgba($ui-highlight-color, 0.23) 0%,
+        rgba($ui-highlight-color, 0) 60%
+      );
     }
   }
 
@@ -3681,11 +3756,12 @@ a.status-card.compact:hover {
   & > button {
     margin: 0;
     border: 0;
-    padding: 15px 0 15px 15px;
+    padding: 15px;
+    padding-inline-end: 0;
     color: inherit;
     background: transparent;
     font: inherit;
-    text-align: left;
+    text-align: start;
     text-overflow: ellipsis;
     overflow: hidden;
     white-space: nowrap;
@@ -3719,7 +3795,7 @@ a.status-card.compact:hover {
 }
 
 .column-header__links .text-btn {
-  margin-right: 10px;
+  margin-inline-end: 10px;
 }
 
 .column-header__button {
@@ -3808,12 +3884,12 @@ a.status-card.compact:hover {
     padding: 5px;
 
     &:first-child {
-      padding-right: 7px;
+      padding-inline-end: 7px;
     }
 
     &:last-child {
-      padding-left: 7px;
-      margin-left: 5px;
+      padding-inline-start: 7px;
+      margin-inline-start: 5px;
     }
   }
 }
@@ -3840,7 +3916,7 @@ a.status-card.compact:hover {
 
 .column-header__icon {
   display: inline-block;
-  margin-right: 5px;
+  margin-inline-end: 5px;
 }
 
 .loading-indicator {
@@ -3851,7 +3927,7 @@ a.status-card.compact:hover {
   overflow: visible;
   position: absolute;
   top: 50%;
-  left: 50%;
+  inset-inline-start: 50%;
   transform: translate(-50%, -50%);
   display: flex;
   align-items: center;
@@ -3987,7 +4063,7 @@ a.status-card.compact:hover {
 
 .spoiler-button {
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   width: 100%;
   height: 100%;
   position: absolute;
@@ -3995,7 +4071,7 @@ a.status-card.compact:hover {
 
   &--minified {
     display: block;
-    left: 4px;
+    inset-inline-start: 4px;
     top: 4px;
     width: auto;
     height: auto;
@@ -4112,12 +4188,12 @@ a.status-card.compact:hover {
 
     &__placeholder {
       color: $dark-text-color;
-      padding-left: 2px;
+      padding-inline-start: 2px;
       font-size: 12px;
     }
 
     &__value-container {
-      padding-left: 6px;
+      padding-inline-start: 6px;
     }
 
     &__multi-value {
@@ -4214,7 +4290,7 @@ a.status-card.compact:hover {
   color: $darker-text-color;
   display: inline-block;
   margin-bottom: 14px;
-  margin-left: 8px;
+  margin-inline-start: 8px;
   vertical-align: middle;
 }
 
@@ -4241,7 +4317,8 @@ a.status-card.compact:hover {
   align-items: center;
   justify-content: center;
 
-  @supports (display: grid) { // hack to fix Chrome <57
+  @supports (display: grid) {
+    // hack to fix Chrome <57
     contain: strict;
   }
 
@@ -4262,6 +4339,7 @@ a.status-card.compact:hover {
 .follow_requests-unlocked_explanation {
   background: darken($ui-base-color, 4%);
   contain: initial;
+  flex-grow: 0;
 }
 
 .error-column {
@@ -4398,7 +4476,7 @@ a.status-card.compact:hover {
 .emoji-picker-dropdown__modifiers {
   position: absolute;
   top: 60px;
-  right: 11px;
+  inset-inline-end: 11px;
   cursor: pointer;
 }
 
@@ -4406,7 +4484,7 @@ a.status-card.compact:hover {
   position: absolute;
   z-index: 4;
   top: -4px;
-  left: -8px;
+  inset-inline-start: -8px;
   background: $simple-background-color;
   border-radius: 4px;
   box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
@@ -4441,14 +4519,14 @@ a.status-card.compact:hover {
   align-items: center;
   background: rgba($base-overlay-background, 0.8);
   display: flex;
-  height: 100%;
+  height: 100vh;
   justify-content: center;
-  left: 0;
+  inset-inline-start: 0;
   opacity: 0;
-  position: absolute;
+  position: fixed;
   top: 0;
   visibility: hidden;
-  width: 100%;
+  width: 100vw;
   z-index: 2000;
 
   * {
@@ -4468,9 +4546,9 @@ a.status-card.compact:hover {
 .upload-area__background {
   position: absolute;
   top: 0;
-  right: 0;
+  inset-inline-end: 0;
   bottom: 0;
-  left: 0;
+  inset-inline-start: 0;
   z-index: -1;
   border-radius: 4px;
   background: $ui-base-color;
@@ -4482,6 +4560,7 @@ a.status-card.compact:hover {
   display: flex;
   align-items: center;
   justify-content: center;
+  text-align: center;
   color: $secondary-text-color;
   font-size: 18px;
   font-weight: 500;
@@ -4497,7 +4576,7 @@ a.status-card.compact:hover {
 
   .fa {
     font-size: 34px;
-    margin-right: 10px;
+    margin-inline-end: 10px;
   }
 
   span {
@@ -4516,14 +4595,14 @@ a.status-card.compact:hover {
   width: 100%;
   height: 6px;
   border-radius: 6px;
-  background: $ui-base-lighter-color;
+  background: darken($simple-background-color, 8%);
   position: relative;
   margin-top: 5px;
 }
 
 .upload-progress__tracker {
   position: absolute;
-  left: 0;
+  inset-inline-start: 0;
   top: 0;
   height: 6px;
   background: $ui-highlight-color;
@@ -4532,7 +4611,10 @@ a.status-card.compact:hover {
 
 .emoji-button {
   display: block;
-  padding: 5px 5px 2px 2px;
+  padding-top: 5px;
+  padding-bottom: 2px;
+  padding-inline-start: 2px;
+  padding-inline-end: 5px;
   outline: 0;
   cursor: pointer;
 
@@ -4620,7 +4702,7 @@ a.status-card.compact:hover {
   display: flex;
   align-items: center;
   justify-content: center;
-  margin-right: 10px;
+  margin-inline-end: 10px;
 }
 
 .privacy-dropdown__option__content {
@@ -4686,11 +4768,11 @@ a.status-card.compact:hover {
     }
 
     .emoji-mart-search {
-      padding-right: 10px;
+      padding-inline-end: 10px;
     }
 
     .emoji-mart-search-icon {
-      right: 10px + 5px;
+      inset-inline-end: 10px + 5px;
     }
 
     .emoji-mart-scroll {
@@ -4736,6 +4818,86 @@ a.status-card.compact:hover {
 .search {
   margin-bottom: 10px;
   position: relative;
+
+  &__popout {
+    box-sizing: border-box;
+    display: none;
+    position: absolute;
+    inset-inline-start: 0;
+    margin-top: -2px;
+    width: 100%;
+    background: $ui-base-color;
+    border-radius: 0 0 4px 4px;
+    box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
+    z-index: 2;
+    font-size: 13px;
+    padding: 15px 5px;
+
+    h4 {
+      text-transform: uppercase;
+      color: $dark-text-color;
+      font-weight: 500;
+      padding: 0 10px;
+      margin-bottom: 10px;
+    }
+
+    &__menu {
+      &__message {
+        color: $dark-text-color;
+        padding: 0 10px;
+      }
+
+      &__item {
+        display: block;
+        box-sizing: border-box;
+        width: 100%;
+        border: 0;
+        font: inherit;
+        background: transparent;
+        color: $darker-text-color;
+        padding: 10px;
+        cursor: pointer;
+        border-radius: 4px;
+        text-align: start;
+        text-overflow: ellipsis;
+        overflow: hidden;
+        white-space: nowrap;
+
+        &--flex {
+          display: flex;
+          justify-content: space-between;
+        }
+
+        .icon-button {
+          transition: none;
+        }
+
+        &:hover,
+        &:focus,
+        &:active,
+        &.selected {
+          background: $ui-highlight-color;
+          color: $primary-text-color;
+
+          .icon-button {
+            color: $primary-text-color;
+          }
+        }
+
+        mark {
+          background: transparent;
+          font-weight: 700;
+          color: $primary-text-color;
+        }
+      }
+    }
+  }
+
+  &.active {
+    .search__popout {
+      display: block;
+    }
+  }
 }
 
 .search__input {
@@ -4743,7 +4905,7 @@ a.status-card.compact:hover {
 
   display: block;
   padding: 15px;
-  padding-right: 30px;
+  padding-inline-end: 30px;
   line-height: 18px;
   font-size: 16px;
 
@@ -4779,7 +4941,7 @@ a.status-card.compact:hover {
   .fa {
     position: absolute;
     top: 16px;
-    right: 10px;
+    inset-inline-end: 10px;
     z-index: 2;
     display: inline-block;
     opacity: 0;
@@ -4833,7 +4995,7 @@ a.status-card.compact:hover {
 
   .fa {
     display: inline-block;
-    margin-right: 5px;
+    margin-inline-end: 5px;
   }
 }
 
@@ -4852,7 +5014,7 @@ a.status-card.compact:hover {
 
     .fa {
       display: inline-block;
-      margin-right: 5px;
+      margin-inline-end: 5px;
     }
   }
 
@@ -4890,8 +5052,8 @@ a.status-card.compact:hover {
 .modal-root__overlay {
   position: fixed;
   top: 0;
-  left: 0;
-  right: 0;
+  inset-inline-start: 0;
+  inset-inline-end: 0;
   bottom: 0;
   background: rgba($base-overlay-background, 0.7);
   transition: background 0.5s;
@@ -4900,7 +5062,7 @@ a.status-card.compact:hover {
 .modal-root__container {
   position: fixed;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   width: 100%;
   height: 100%;
   box-sizing: border-box;
@@ -4953,16 +5115,16 @@ a.status-card.compact:hover {
 .media-modal__closer {
   position: absolute;
   top: 0;
-  left: 0;
-  right: 0;
+  inset-inline-start: 0;
+  inset-inline-end: 0;
   bottom: 0;
 }
 
 .media-modal__navigation {
   position: absolute;
   top: 0;
-  left: 0;
-  right: 0;
+  inset-inline-start: 0;
+  inset-inline-end: 0;
   bottom: 0;
   pointer-events: none;
   transition: opacity 0.3s linear;
@@ -5005,18 +5167,18 @@ a.status-card.compact:hover {
 }
 
 .media-modal__nav--left {
-  left: 0;
+  inset-inline-start: 0;
 }
 
 .media-modal__nav--right {
-  right: 0;
+  inset-inline-end: 0;
 }
 
 .media-modal__overlay {
   max-width: 600px;
   position: absolute;
-  left: 0;
-  right: 0;
+  inset-inline-start: 0;
+  inset-inline-end: 0;
   bottom: 0;
   margin: 0 auto;
 
@@ -5103,14 +5265,14 @@ a.status-card.compact:hover {
 
 .media-modal__close {
   position: absolute;
-  right: 8px;
+  inset-inline-end: 8px;
   top: 8px;
   z-index: 100;
 }
 
 .media-modal__zoom-button {
   position: absolute;
-  right: 64px;
+  inset-inline-end: 64px;
   top: 8px;
   z-index: 100;
   pointer-events: auto;
@@ -5144,7 +5306,7 @@ a.status-card.compact:hover {
   & > div {
     position: absolute;
     top: 0;
-    left: 0;
+    inset-inline-start: 0;
     width: 100%;
     height: 100%;
     box-sizing: border-box;
@@ -5240,7 +5402,7 @@ a.status-card.compact:hover {
   display: inline-block;
   max-width: 30px;
   max-height: auto;
-  margin-left: 10px;
+  margin-inline-start: 10px;
 }
 
 .boost-modal,
@@ -5295,9 +5457,9 @@ a.status-card.compact:hover {
 
   & > div {
     flex: 1 1 auto;
-    text-align: right;
+    text-align: end;
     color: $lighter-text-color;
-    padding-right: 10px;
+    padding-inline-end: 10px;
   }
 
   .button {
@@ -5500,7 +5662,7 @@ a.status-card.compact:hover {
     & > span {
       font-size: 17px;
       font-weight: 500;
-      margin-left: 10px;
+      margin-inline-start: 10px;
     }
   }
 
@@ -5524,11 +5686,11 @@ a.status-card.compact:hover {
   }
 
   .emoji-mart-search {
-    padding-right: 10px;
+    padding-inline-end: 10px;
   }
 
   .emoji-mart-search-icon {
-    right: 10px + 5px;
+    inset-inline-end: 10px + 5px;
   }
 }
 
@@ -5591,7 +5753,7 @@ a.status-card.compact:hover {
 
 .report-modal__comment {
   padding: 20px;
-  border-right: 1px solid $ui-secondary-color;
+  border-inline-end: 1px solid $ui-secondary-color;
   max-width: 320px;
 
   p {
@@ -5689,7 +5851,7 @@ a.status-card.compact:hover {
         }
 
         button:first-child {
-          margin-right: 10px;
+          margin-inline-end: 10px;
         }
       }
     }
@@ -5747,11 +5909,13 @@ a.status-card.compact:hover {
     width: auto;
     outline: 0;
     font-family: inherit;
-    background: $simple-background-color 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(darken($simple-background-color, 14%))}'/></svg>") no-repeat right 8px center / auto 16px;
+    background: $simple-background-color
+      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(darken($simple-background-color, 14%))}'/></svg>")
+      no-repeat right 8px center / auto 16px;
     border: 1px solid darken($simple-background-color, 14%);
     border-radius: 4px;
     padding: 6px 10px;
-    padding-right: 30px;
+    padding-inline-end: 30px;
   }
 }
 
@@ -5775,7 +5939,7 @@ a.status-card.compact:hover {
     &__label {
       color: $inverted-text-color;
       margin: 0;
-      margin-left: 8px;
+      margin-inline-start: 8px;
     }
   }
 }
@@ -5786,7 +5950,7 @@ a.status-card.compact:hover {
   .report-modal__close {
     position: absolute;
     top: 10px;
-    right: 10px;
+    inset-inline-end: 10px;
   }
 }
 
@@ -5798,6 +5962,7 @@ a.status-card.compact:hover {
   &__container {
     padding: 30px;
     pointer-events: all;
+    overflow-y: auto;
   }
 
   .status__content {
@@ -5836,7 +6001,7 @@ a.status-card.compact:hover {
   height: 3px;
   position: fixed;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   z-index: 9999;
 }
 
@@ -5846,7 +6011,7 @@ a.status-card.compact:hover {
   color: $primary-text-color;
   background: rgba($base-overlay-background, 0.5);
   bottom: 6px;
-  left: 6px;
+  inset-inline-start: 6px;
   padding: 2px 6px;
   border-radius: 2px;
   font-size: 11px;
@@ -5879,7 +6044,7 @@ a.status-card.compact:hover {
     color: $dark-text-color;
     padding: 8px 18px;
     cursor: default;
-    border-right: 1px solid lighten($ui-base-color, 8%);
+    border-inline-end: 1px solid lighten($ui-base-color, 8%);
     display: flex;
     flex-direction: column;
     align-items: center;
@@ -5894,7 +6059,7 @@ a.status-card.compact:hover {
   &__list {
     list-style: none;
     padding: 4px 0;
-    padding-left: 8px;
+    padding-inline-start: 8px;
     display: flex;
     flex-direction: column;
     justify-content: center;
@@ -5982,7 +6147,7 @@ a.status-card.compact:hover {
   object-fit: cover;
   position: absolute;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   z-index: 0;
   background: $base-overlay-background;
 
@@ -6150,10 +6315,15 @@ a.status-card.compact:hover {
     position: absolute;
     z-index: 2;
     bottom: 0;
-    left: 0;
-    right: 0;
+    inset-inline-start: 0;
+    inset-inline-end: 0;
     box-sizing: border-box;
-    background: linear-gradient(0deg, rgba($base-shadow-color, 0.85) 0, rgba($base-shadow-color, 0.45) 60%, transparent);
+    background: linear-gradient(
+      0deg,
+      rgba($base-shadow-color, 0.85) 0,
+      rgba($base-shadow-color, 0.45) 60%,
+      transparent
+    );
     padding: 0 15px;
     opacity: 0;
     transition: opacity 0.1s ease;
@@ -6174,7 +6344,7 @@ a.status-card.compact:hover {
     display: none;
     position: absolute;
     top: 0;
-    left: 0;
+    inset-inline-start: 0;
     width: 100%;
     height: 100%;
     z-index: 4;
@@ -6290,18 +6460,18 @@ a.status-card.compact:hover {
     &.active {
       overflow: visible;
       width: 50px;
-      margin-right: 16px;
+      margin-inline-end: 16px;
     }
 
     &::before {
-      content: "";
+      content: '';
       width: 50px;
       background: rgba($white, 0.35);
       border-radius: 4px;
       display: block;
       position: absolute;
       height: 4px;
-      left: 0;
+      inset-inline-start: 0;
       top: 50%;
       transform: translate(0, -50%);
     }
@@ -6311,7 +6481,7 @@ a.status-card.compact:hover {
       position: absolute;
       height: 4px;
       border-radius: 4px;
-      left: 0;
+      inset-inline-start: 0;
       top: 50%;
       transform: translate(0, -50%);
       background: lighten($ui-highlight-color, 8%);
@@ -6324,8 +6494,8 @@ a.status-card.compact:hover {
       width: 12px;
       height: 12px;
       top: 50%;
-      left: 0;
-      margin-left: -6px;
+      inset-inline-start: 0;
+      margin-inline-start: -6px;
       transform: translate(0, -50%);
       background: lighten($ui-highlight-color, 8%);
       box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
@@ -6364,7 +6534,7 @@ a.status-card.compact:hover {
     position: relative;
 
     &::before {
-      content: "";
+      content: '';
       width: 100%;
       background: rgba($white, 0.35);
       border-radius: 4px;
@@ -6396,7 +6566,7 @@ a.status-card.compact:hover {
       width: 12px;
       height: 12px;
       top: 10px;
-      margin-left: -6px;
+      margin-inline-start: -6px;
       background: lighten($ui-highlight-color, 8%);
       box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
 
@@ -6447,7 +6617,11 @@ a.status-card.compact:hover {
 }
 
 .scrollable .account-card__bio::after {
-  background: linear-gradient(to left, lighten($ui-base-color, 8%), transparent);
+  background: linear-gradient(
+    to left,
+    lighten($ui-base-color, 8%),
+    transparent
+  );
 }
 
 .account-gallery__container {
@@ -6468,7 +6642,7 @@ a.status-card.compact:hover {
   &__icons {
     position: absolute;
     top: 50%;
-    left: 50%;
+    inset-inline-start: 50%;
     transform: translate(-50%, -50%);
     font-size: 24px;
   }
@@ -6508,10 +6682,10 @@ a.status-card.compact:hover {
       &::before,
       &::after {
         display: block;
-        content: "";
+        content: '';
         position: absolute;
         bottom: 0;
-        left: 50%;
+        inset-inline-start: 50%;
         width: 0;
         height: 0;
         transform: translateX(-50%);
@@ -6574,8 +6748,8 @@ a.status-card.compact:hover {
   text-overflow: ellipsis;
   cursor: pointer;
 
-  input[type="radio"],
-  input[type="checkbox"] {
+  input[type='radio'],
+  input[type='checkbox'] {
     display: none;
   }
 
@@ -6587,7 +6761,7 @@ a.status-card.compact:hover {
     width: 18px;
     height: 18px;
     flex: 0 0 auto;
-    margin-right: 10px;
+    margin-inline-end: 10px;
     top: -1px;
     border-radius: 50%;
     vertical-align: middle;
@@ -6603,10 +6777,6 @@ a.status-card.compact:hover {
   border-radius: 0;
 }
 
-.search-popout {
-  @include search-popout;
-}
-
 noscript {
   text-align: center;
 
@@ -6634,9 +6804,17 @@ noscript {
 }
 
 @keyframes flicker {
-  0% { opacity: 1; }
-  30% { opacity: 0.75; }
-  100% { opacity: 1; }
+  0% {
+    opacity: 1;
+  }
+
+  30% {
+    opacity: 0.75;
+  }
+
+  100% {
+    opacity: 1;
+  }
 }
 
 @media screen and (max-width: 630px) and (max-height: 400px) {
@@ -6656,8 +6834,10 @@ noscript {
 
   .navigation-bar {
     & > a:first-child {
-      will-change: margin-top, margin-left, margin-right, width;
-      transition: margin-top $duration $delay, margin-left $duration ($duration + $delay), margin-right $duration ($duration + $delay);
+      will-change: margin-top, margin-inline-start, margin-inline-end, width;
+      transition: margin-top $duration $delay,
+        margin-inline-start $duration ($duration + $delay),
+        margin-inline-end $duration ($duration + $delay);
     }
 
     & > .navigation-bar__profile-edit {
@@ -6668,15 +6848,12 @@ noscript {
     .navigation-bar__actions {
       & > .icon-button.close {
         will-change: opacity transform;
-        transition:
-          opacity $duration * 0.5 $delay,
-          transform $duration $delay;
+        transition: opacity $duration * 0.5 $delay, transform $duration $delay;
       }
 
       & > .compose__action-bar .icon-button {
         will-change: opacity transform;
-        transition:
-          opacity $duration * 0.5 $delay + $duration * 0.5,
+        transition: opacity $duration * 0.5 $delay + $duration * 0.5,
           transform $duration $delay;
       }
     }
@@ -6842,7 +7019,7 @@ noscript {
   cursor: pointer;
   position: absolute;
   top: 0;
-  left: 0;
+  inset-inline-start: 0;
   width: 100%;
   height: 100%;
   background: rgba($base-overlay-background, 0.5);
@@ -6871,6 +7048,7 @@ noscript {
 
   .drawer__pager {
     height: 50vh;
+    border-radius: 4px;
   }
 
   .drawer__inner {
@@ -6980,13 +7158,13 @@ noscript {
     width: 100%;
     height: 100%;
     top: 0;
-    left: 0;
+    inset-inline-start: 0;
   }
 
   &__preview {
     position: absolute;
     bottom: 10px;
-    right: 10px;
+    inset-inline-end: 10px;
     z-index: 2;
     cursor: move;
     transition: opacity 0.1s ease;
@@ -7063,7 +7241,7 @@ noscript {
   &__info {
     position: absolute;
     top: 10px;
-    left: 10px;
+    inset-inline-start: 10px;
   }
 
   &__image {
@@ -7106,7 +7284,7 @@ noscript {
     padding-top: 10px;
     gap: 8px;
     overflow: hidden;
-    margin-left: -2px; // aligns the pfp with content below
+    margin-inline-start: -2px; // aligns the pfp with content below
 
     &__buttons {
       display: flex;
@@ -7309,6 +7487,19 @@ noscript {
   }
 }
 
+.verified-badge {
+  display: inline-flex;
+  align-items: center;
+  color: $valid-value-color;
+  gap: 4px;
+
+  a {
+    color: inherit;
+    font-weight: 500;
+    text-decoration: none;
+  }
+}
+
 .trends {
   &__header {
     color: $dark-text-color;
@@ -7321,7 +7512,7 @@ noscript {
 
     .fa {
       display: inline-block;
-      margin-right: 5px;
+      margin-inline-end: 5px;
     }
   }
 
@@ -7371,7 +7562,7 @@ noscript {
       flex: 0 0 auto;
       font-size: 24px;
       font-weight: 500;
-      text-align: right;
+      text-align: end;
       color: $secondary-text-color;
       text-decoration: none;
     }
@@ -7476,7 +7667,7 @@ noscript {
   &__content {
     flex: 1 1 auto;
     padding: 10px 5px;
-    padding-right: 15px;
+    padding-inline-end: 15px;
     overflow: hidden;
 
     &__info {
@@ -7489,7 +7680,7 @@ noscript {
     &__relative-time {
       font-size: 15px;
       color: $darker-text-color;
-      padding-left: 15px;
+      padding-inline-start: 15px;
     }
 
     &__names {
@@ -7579,13 +7770,13 @@ noscript {
       display: block;
       font-weight: 500;
       margin-bottom: 10px;
-      padding-right: 18px;
+      padding-inline-end: 18px;
     }
 
     &__unread {
       position: absolute;
       top: 19px;
-      right: 19px;
+      inset-inline-end: 19px;
       display: block;
       background: $highlight-text-color;
       border-radius: 50%;
@@ -7599,7 +7790,7 @@ noscript {
     color: $darker-text-color;
     position: absolute;
     bottom: 3px;
-    right: 0;
+    inset-inline-end: 0;
   }
 }
 
@@ -7616,7 +7807,7 @@ noscript {
   flex-wrap: wrap;
   align-items: center;
   margin-top: 15px;
-  margin-left: -2px;
+  margin-inline-start: -2px;
   width: calc(100% - (90px - 33px));
 
   &__item {
@@ -7657,7 +7848,7 @@ noscript {
       font-size: 13px;
       font-weight: 500;
       text-align: center;
-      margin-left: 6px;
+      margin-inline-start: 6px;
       color: $darker-text-color;
     }
 
@@ -7676,7 +7867,11 @@ noscript {
     &.active {
       transition: all 100ms ease-in;
       transition-property: background-color, color;
-      background-color: mix(lighten($ui-base-color, 12%), $ui-highlight-color, 80%);
+      background-color: mix(
+        lighten($ui-base-color, 12%),
+        $ui-highlight-color,
+        80%
+      );
 
       .reactions-bar__item__count {
         color: lighten($highlight-text-color, 8%);
@@ -7729,13 +7924,13 @@ noscript {
 
   &.unread {
     &::before {
-      content: "";
+      content: '';
       position: absolute;
       top: 0;
-      left: 0;
+      inset-inline-start: 0;
       width: 100%;
       height: 100%;
-      border-left: 4px solid $highlight-text-color;
+      border-inline-start: 4px solid $highlight-text-color;
       pointer-events: none;
     }
   }
@@ -7744,7 +7939,7 @@ noscript {
 .picture-in-picture {
   position: fixed;
   bottom: 20px;
-  right: 20px;
+  inset-inline-end: 20px;
   width: 300px;
 
   &__footer {
@@ -7770,7 +7965,7 @@ noscript {
     }
 
     .account__avatar {
-      margin-right: 10px;
+      margin-inline-end: 10px;
     }
 
     .display-name {
@@ -7836,7 +8031,7 @@ noscript {
   &__close {
     position: absolute;
     top: 10px;
-    right: 10px;
+    inset-inline-end: 10px;
   }
 
   h2 {
@@ -7869,9 +8064,13 @@ noscript {
     padding: 10px;
   }
 
+  .search__popout {
+    border: 1px solid lighten($ui-base-color, 8%);
+  }
+
   .search .fa {
     top: 10px;
-    right: 10px;
+    inset-inline-end: 10px;
     color: $dark-text-color;
   }
 
@@ -7955,7 +8154,7 @@ noscript {
       object-fit: fill;
       position: absolute;
       top: 0;
-      left: 0;
+      inset-inline-start: 0;
       z-index: 0;
 
       &--hidden {
@@ -8041,7 +8240,7 @@ noscript {
   }
 
   .account__avatar-wrapper {
-    margin-left: 0;
+    margin-inline-start: 0;
   }
 
   .spacer {
@@ -8198,11 +8397,13 @@ noscript {
   img {
     margin-top: 2em;
     margin-bottom: 2em;
+    max-width: 100%;
   }
 
   video {
     margin-top: 2em;
     margin-bottom: 2em;
+    max-width: 100%;
   }
 
   figure {
@@ -8257,27 +8458,27 @@ noscript {
     counter-increment: list-counter;
 
     &::before {
-      content: counter(list-counter) ".";
+      content: counter(list-counter) '.';
       position: absolute;
-      left: 0;
+      inset-inline-start: 0;
     }
   }
 
   ul > li::before {
-    content: "";
+    content: '';
     position: absolute;
     background-color: $darker-text-color;
     border-radius: 50%;
     width: 0.375em;
     height: 0.375em;
     top: 0.5em;
-    left: 0.25em;
+    inset-inline-start: 0.25em;
   }
 
   ul > li,
   ol > li {
     position: relative;
-    padding-left: 1.75em;
+    padding-inline-start: 1.75em;
   }
 
   & > ul > li p {
@@ -8410,7 +8611,7 @@ noscript {
   &__preview {
     position: absolute;
     top: 0;
-    left: 0;
+    inset-inline-start: 0;
     width: 100%;
     height: 100%;
     object-fit: cover;
@@ -8616,7 +8817,7 @@ noscript {
   }
 
   .account__avatar-wrapper {
-    margin-left: 0;
+    margin-inline-start: 0;
   }
 
   .account__relationship {
diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss
index b49b93984..fb71ad034 100644
--- a/app/javascript/styles/mastodon/containers.scss
+++ b/app/javascript/styles/mastodon/containers.scss
@@ -18,7 +18,7 @@
 
     .logo {
       height: 42px;
-      margin-right: 10px;
+      margin-inline-end: 10px;
     }
 
     a {
@@ -73,7 +73,7 @@
   .avatar {
     width: 40px;
     height: 40px;
-    margin-right: 10px;
+    margin-inline-end: 10px;
 
     img {
       width: 100%;
@@ -101,6 +101,6 @@
     display: block;
     font-size: 32px;
     line-height: 40px;
-    margin-left: 10px;
+    margin-inline-start: 10px;
   }
 }
diff --git a/app/javascript/styles/mastodon/emoji_picker.scss b/app/javascript/styles/mastodon/emoji_picker.scss
index 1042ddda8..c7247c3a5 100644
--- a/app/javascript/styles/mastodon/emoji_picker.scss
+++ b/app/javascript/styles/mastodon/emoji_picker.scss
@@ -71,7 +71,7 @@
 .emoji-mart-anchor-bar {
   position: absolute;
   bottom: -5px;
-  left: 0;
+  inset-inline-start: 0;
   width: 100%;
   height: 4px;
   background-color: $highlight-text-color;
@@ -106,7 +106,7 @@
 
 .emoji-mart-search {
   padding: 10px;
-  padding-right: 45px;
+  padding-inline-end: 45px;
   background: $simple-background-color;
   position: relative;
 
@@ -114,7 +114,7 @@
     font-size: 16px;
     font-weight: 400;
     padding: 7px 9px;
-    padding-right: 25px;
+    padding-inline-end: 25px;
     font-family: inherit;
     display: block;
     width: 100%;
@@ -142,7 +142,7 @@
 .emoji-mart-search-icon {
   position: absolute;
   top: 18px;
-  right: 45px + 5px;
+  inset-inline-end: 45px + 5px;
   z-index: 2;
   padding: 2px 5px 1px;
   border: 0;
@@ -174,10 +174,10 @@
 
   &:hover::before {
     z-index: -1;
-    content: "";
+    content: '';
     position: absolute;
     top: 0;
-    left: 0;
+    inset-inline-start: 0;
     width: 100%;
     height: 100%;
     background-color: rgba($ui-secondary-color, 0.7);
diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss
index 0d5cdf382..129a836f3 100644
--- a/app/javascript/styles/mastodon/forms.scss
+++ b/app/javascript/styles/mastodon/forms.scss
@@ -54,12 +54,12 @@ code {
 
       .radio > label {
         position: relative;
-        padding-left: 28px;
+        padding-inline-start: 28px;
 
         input {
           position: absolute;
           top: -2px;
-          left: 0;
+          inset-inline-start: 0;
         }
       }
     }
@@ -79,7 +79,7 @@ code {
 
       .label_input,
       .hint {
-        padding-left: 28px;
+        padding-inline-start: 28px;
       }
 
       .label_input__wrapper {
@@ -89,7 +89,7 @@ code {
       label.checkbox {
         position: absolute;
         top: 2px;
-        left: 0;
+        inset-inline-start: 0;
       }
 
       label a {
@@ -152,7 +152,7 @@ code {
 
     li {
       list-style: disc;
-      margin-left: 18px;
+      margin-inline-start: 18px;
     }
   }
 
@@ -218,7 +218,7 @@ code {
 
     &.select .hint {
       margin-top: 6px;
-      margin-left: 150px;
+      margin-inline-start: 150px;
     }
   }
 
@@ -292,7 +292,7 @@ code {
       max-width: 100%;
       height: auto;
       border-radius: 4px;
-      background: url("images/void.png");
+      background: url('images/void.png');
 
       &:last-child {
         margin-bottom: 0;
@@ -373,13 +373,13 @@ code {
         width: auto;
         position: relative;
         padding-top: 5px;
-        padding-left: 25px;
+        padding-inline-start: 25px;
         flex: 1 1 auto;
       }
 
-      input[type="checkbox"] {
+      input[type='checkbox'] {
         position: absolute;
-        left: 0;
+        inset-inline-start: 0;
         top: 5px;
         margin: 0;
       }
@@ -393,12 +393,12 @@ code {
     border-radius: 4px;
   }
 
-  input[type="text"],
-  input[type="number"],
-  input[type="email"],
-  input[type="password"],
-  input[type="url"],
-  input[type="datetime-local"],
+  input[type='text'],
+  input[type='number'],
+  input[type='email'],
+  input[type='password'],
+  input[type='url'],
+  input[type='datetime-local'],
   textarea {
     box-sizing: border-box;
     font-size: 16px;
@@ -436,11 +436,11 @@ code {
     }
   }
 
-  input[type="text"],
-  input[type="number"],
-  input[type="email"],
-  input[type="password"],
-  input[type="datetime-local"] {
+  input[type='text'],
+  input[type='number'],
+  input[type='email'],
+  input[type='password'],
+  input[type='datetime-local'] {
     &:focus:invalid:not(:placeholder-shown),
     &:required:invalid:not(:placeholder-shown) {
       border-color: lighten($error-red, 12%);
@@ -452,11 +452,11 @@ code {
       color: lighten($error-red, 12%);
     }
 
-    input[type="text"],
-    input[type="number"],
-    input[type="email"],
-    input[type="password"],
-    input[type="datetime-local"],
+    input[type='text'],
+    input[type='number'],
+    input[type='email'],
+    input[type='password'],
+    input[type='datetime-local'],
     textarea,
     select {
       border-color: lighten($error-red, 12%);
@@ -510,10 +510,10 @@ code {
     font-weight: 500;
     outline: 0;
     margin-bottom: 10px;
-    margin-right: 10px;
+    margin-inline-end: 10px;
 
     &:last-child {
-      margin-right: 0;
+      margin-inline-end: 0;
     }
 
     &:active,
@@ -560,11 +560,13 @@ code {
     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;
+    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-left: 10px;
-    padding-right: 30px;
+    padding-inline-start: 10px;
+    padding-inline-end: 30px;
     height: 41px;
   }
 
@@ -579,7 +581,7 @@ code {
 
     &__append {
       position: absolute;
-      right: 3px;
+      inset-inline-end: 3px;
       top: 1px;
       padding: 10px;
       padding-bottom: 9px;
@@ -597,10 +599,14 @@ code {
         display: block;
         position: absolute;
         top: 0;
-        right: 0;
+        inset-inline-end: 0;
         bottom: 1px;
         width: 5px;
-        background-image: linear-gradient(to right, rgba(darken($ui-base-color, 10%), 0), darken($ui-base-color, 10%));
+        background-image: linear-gradient(
+          to right,
+          rgba(darken($ui-base-color, 10%), 0),
+          darken($ui-base-color, 10%)
+        );
       }
     }
   }
@@ -767,7 +773,7 @@ code {
 
   li {
     display: inline-block;
-    margin-right: 10px;
+    margin-inline-end: 10px;
   }
 
   a {
@@ -926,7 +932,7 @@ code {
 
   .actions {
     padding: 30px 0;
-    padding-right: 20px;
+    padding-inline-end: 20px;
     flex: 0 0 auto;
   }
 }
@@ -979,7 +985,7 @@ code {
   border-radius: 4px;
   display: flex;
   align-items: center;
-  padding-right: 4px;
+  padding-inline-end: 4px;
   position: relative;
   top: 1px;
   transition: border-color 300ms linear;
@@ -988,7 +994,7 @@ code {
     flex: 1 1 auto;
   }
 
-  input[type="text"] {
+  input[type='text'] {
     background: transparent;
     border: 0;
     padding: 10px;
diff --git a/app/javascript/styles/mastodon/modal.scss b/app/javascript/styles/mastodon/modal.scss
index a333926dd..29b1f162b 100644
--- a/app/javascript/styles/mastodon/modal.scss
+++ b/app/javascript/styles/mastodon/modal.scss
@@ -1,5 +1,7 @@
 .modal-layout {
-  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($ui-base-lighter-color)}33"/></svg>') repeat-x bottom fixed;
+  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($ui-base-lighter-color)}33"/></svg>')
+    repeat-x bottom fixed;
   display: flex;
   flex-direction: column;
   height: 100vh;
@@ -23,7 +25,7 @@
       height: 100%;
       position: absolute;
       bottom: 0;
-      left: 0;
+      inset-inline-start: 0;
     }
   }
 }
diff --git a/app/javascript/styles/mastodon/polls.scss b/app/javascript/styles/mastodon/polls.scss
index 6812d5462..bdb87d7cf 100644
--- a/app/javascript/styles/mastodon/polls.scss
+++ b/app/javascript/styles/mastodon/polls.scss
@@ -64,8 +64,8 @@
       max-width: calc(100% - 45px - 25px);
     }
 
-    input[type="radio"],
-    input[type="checkbox"] {
+    input[type='radio'],
+    input[type='checkbox'] {
       display: none;
     }
 
@@ -73,7 +73,7 @@
       flex: 1 1 auto;
     }
 
-    input[type="text"] {
+    input[type='text'] {
       display: block;
       box-sizing: border-box;
       width: 100%;
@@ -109,7 +109,7 @@
     box-sizing: border-box;
     width: 18px;
     height: 18px;
-    margin-right: 10px;
+    margin-inline-end: 10px;
     top: -1px;
     border-radius: 50%;
     vertical-align: middle;
@@ -204,7 +204,7 @@
   .button {
     height: 36px;
     padding: 0 16px;
-    margin-right: 10px;
+    margin-inline-end: 10px;
     font-size: 14px;
   }
 }
@@ -240,7 +240,7 @@
     line-height: inherit;
     color: $action-button-color;
     border-color: $action-button-color;
-    margin-right: 5px;
+    margin-inline-end: 5px;
   }
 
   li {
@@ -250,7 +250,7 @@
     .poll__option {
       flex: 0 0 auto;
       width: calc(100% - (23px + 6px));
-      margin-right: 6px;
+      margin-inline-end: 6px;
     }
   }
 
@@ -263,11 +263,13 @@
     width: auto;
     outline: 0;
     font-family: inherit;
-    background: $simple-background-color 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(darken($simple-background-color, 14%))}'/></svg>") no-repeat right 8px center / auto 16px;
+    background: $simple-background-color
+      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(darken($simple-background-color, 14%))}'/></svg>")
+      no-repeat right 8px center / auto 16px;
     border: 1px solid darken($simple-background-color, 14%);
     border-radius: 4px;
     padding: 6px 10px;
-    padding-right: 30px;
+    padding-inline-end: 30px;
   }
 
   .icon-button.disabled {
diff --git a/app/javascript/styles/mastodon/rich_text.scss b/app/javascript/styles/mastodon/rich_text.scss
new file mode 100644
index 000000000..c77c23bc4
--- /dev/null
+++ b/app/javascript/styles/mastodon/rich_text.scss
@@ -0,0 +1,64 @@
+.status__content__text,
+.e-content,
+.reply-indicator__content {
+  pre,
+  blockquote {
+    margin-bottom: 20px;
+    white-space: pre-wrap;
+    unicode-bidi: plaintext;
+
+    &:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  blockquote {
+    padding-inline-start: 10px;
+    border-inline-start: 3px solid $darker-text-color;
+    color: $darker-text-color;
+    white-space: normal;
+
+    p:last-child {
+      margin-bottom: 0;
+    }
+  }
+
+  & > ul,
+  & > ol {
+    margin-bottom: 20px;
+  }
+
+  b,
+  strong {
+    font-weight: 700;
+  }
+
+  em,
+  i {
+    font-style: italic;
+  }
+
+  ul,
+  ol {
+    margin-inline-start: 2em;
+
+    p {
+      margin: 0;
+    }
+  }
+
+  ul {
+    list-style-type: disc;
+  }
+
+  ol {
+    list-style-type: decimal;
+  }
+}
+
+.reply-indicator__content {
+  blockquote {
+    border-left-color: $inverted-text-color;
+    color: $inverted-text-color;
+  }
+}
diff --git a/app/javascript/styles/mastodon/rtl.scss b/app/javascript/styles/mastodon/rtl.scss
index ccec8e95e..8d3d32665 100644
--- a/app/javascript/styles/mastodon/rtl.scss
+++ b/app/javascript/styles/mastodon/rtl.scss
@@ -1,398 +1,61 @@
 body.rtl {
   direction: rtl;
 
-  .column-header > button {
-    text-align: right;
-    padding-left: 0;
-    padding-right: 15px;
-  }
-
-  .radio-button__input {
-    margin-right: 0;
-    margin-left: 10px;
-  }
-
-  .display-name,
-  .announcements__item {
-    text-align: right;
-  }
-
-  .announcements__item__range {
-    padding-right: 0;
-    padding-left: 18px;
-  }
-
   .reactions-bar {
-    margin-left: auto;
-    margin-right: -2px;
     direction: rtl;
   }
 
-  .reactions-bar__item__count {
-    margin-left: 0;
-    margin-right: 6px;
-  }
-
-  .announcements__pagination {
-    right: auto;
-    left: 0;
-  }
-
-  .notification__message {
-    margin-left: 0;
-    margin-right: 68px;
-  }
-
   .announcements__mastodon,
   .drawer__inner__mastodon > img {
     transform: scaleX(-1);
   }
 
-  .notification__favourite-icon-wrapper {
-    left: auto;
-    right: -26px;
-  }
-
-  .column-link__icon,
-  .column-header__icon {
-    margin-right: 0;
-    margin-left: 5px;
-  }
-
-  .compose-form .compose-form__buttons-wrapper .character-counter__wrapper {
-    margin-right: 0;
-    margin-left: 4px;
-  }
-
-  .navigation-bar__profile {
-    margin-left: 0;
-    margin-right: 8px;
-  }
-
-  .search__input {
+  .compose-form .autosuggest-textarea__textarea {
     padding-right: 10px;
-    padding-left: 30px;
-  }
-
-  .search__icon .fa {
-    right: auto;
-    left: 10px;
+    padding-left: 10px + 22px;
   }
 
   .columns-area {
     direction: rtl;
   }
 
-  .column-header__buttons {
-    left: 0;
-    right: auto;
-    margin-left: 0;
-    margin-right: -15px;
-  }
-
-  .column-inline-form .icon-button {
-    margin-left: 0;
-    margin-right: 5px;
-  }
-
-  .column-header__links .text-btn {
-    margin-left: 10px;
-    margin-right: 0;
-  }
-
   .account__avatar-wrapper {
     float: right;
   }
 
-  .column-header__back-button {
-    padding-left: 5px;
-    padding-right: 0;
-  }
-
   .column-header__setting-arrows {
     float: left;
-
-    .column-header__setting-btn {
-      &:first-child {
-        padding-left: 7px;
-        padding-right: 5px;
-      }
-
-      &:last-child {
-        padding-right: 7px;
-        padding-left: 5px;
-        margin-right: 5px;
-        margin-left: 0;
-      }
-    }
-  }
-
-  .setting-toggle__label {
-    margin-left: 0;
-    margin-right: 8px;
-  }
-
-  .status__avatar {
-    left: auto;
-    right: 10px;
-  }
-
-  .status,
-  .activity-stream .status.light {
-    padding-left: 10px;
-    padding-right: 68px;
-  }
-
-  .status__info .status__display-name,
-  .activity-stream .status.light .status__display-name {
-    padding-left: 25px;
-    padding-right: 0;
-  }
-
-  .activity-stream .pre-header {
-    padding-right: 68px;
-    padding-left: 0;
-  }
-
-  .status__prepend {
-    margin-left: 0;
-    margin-right: 68px;
-  }
-
-  .status__prepend-icon-wrapper {
-    left: auto;
-    right: -26px;
-  }
-
-  .activity-stream .pre-header .pre-header__icon {
-    left: auto;
-    right: 42px;
-  }
-
-  .account__header__tabs__buttons > .icon-button {
-    margin-right: 0;
-    margin-left: 8px;
-  }
-
-  .account__avatar-overlay-overlay {
-    right: auto;
-    left: 0;
-  }
-
-  .column-back-button--slim-button {
-    right: auto;
-    left: 0;
-  }
-
-  .status__relative-time,
-  .status__visibility-icon,
-  .activity-stream .status.light .status__header .status__meta {
-    float: left;
-  }
-
-  .status__action-bar {
-    &__counter {
-      margin-right: 0;
-      margin-left: 11px;
-
-      .status__action-bar-button {
-        margin-right: 0;
-        margin-left: 4px;
-      }
-    }
-  }
-
-  .status__action-bar-button {
-    float: right;
-    margin-right: 0;
-    margin-left: 18px;
-  }
-
-  .status__action-bar-dropdown {
-    float: right;
-  }
-
-  .privacy-dropdown__dropdown {
-    margin-left: 0;
-    margin-right: 40px;
-  }
-
-  .privacy-dropdown__option__icon {
-    margin-left: 10px;
-    margin-right: 0;
-  }
-
-  .picture-in-picture__header__account .display-name,
-  .detailed-status__display-name .display-name {
-    text-align: right;
-  }
-
-  .detailed-status__display-avatar {
-    margin-right: 0;
-    margin-left: 10px;
-    float: right;
-  }
-
-  .picture-in-picture__header__account .account__avatar {
-    margin-right: 0;
-    margin-left: 10px;
-  }
-
-  .icon-button__counter {
-    margin-left: 0;
-    margin-right: 4px;
-  }
-
-  .notifications-permission-banner__close {
-    right: auto;
-    left: 10px;
-  }
-
-  .detailed-status__favorites,
-  .detailed-status__reblogs {
-    margin-left: 0;
-    margin-right: 6px;
-  }
-
-  .fa-ul {
-    margin-left: 2.14285714em;
-  }
-
-  .fa-li {
-    left: auto;
-    right: -2.14285714em;
   }
 
   .admin-wrapper {
     direction: rtl;
   }
 
-  .admin-wrapper .sidebar ul a i.fa,
-  a.table-action-link i.fa {
-    margin-right: 0;
-    margin-left: 5px;
-  }
-
-  .simple_form .check_boxes .checkbox label {
-    padding-left: 0;
-    padding-right: 25px;
-  }
-
-  .simple_form .input.with_label.boolean label.checkbox {
-    padding-left: 25px;
-    padding-right: 0;
-  }
-
-  .simple_form .check_boxes .checkbox input[type="checkbox"],
-  .simple_form .input.boolean input[type="checkbox"] {
-    left: auto;
-    right: 0;
-  }
-
-  .simple_form .input.radio_buttons .radio {
-    left: auto;
-    right: 0;
-  }
-
-  .simple_form .input.radio_buttons .radio > label {
-    padding-right: 28px;
-    padding-left: 0;
-  }
-
-  .simple_form .input-with-append .input input {
-    padding-left: 142px;
-    padding-right: 0;
-  }
-
-  .simple_form .input.boolean label.checkbox {
-    left: auto;
-    right: 0;
-  }
-
-  .simple_form .input.boolean .label_input,
-  .simple_form .input.boolean .hint {
-    padding-left: 0;
-    padding-right: 28px;
+  .react-swipeable-view-container > * {
+    direction: rtl;
   }
 
   .simple_form .label_input__append {
-    right: auto;
-    left: 3px;
-
     &::after {
-      right: auto;
-      left: 0;
-      background-image: linear-gradient(to left, rgba(darken($ui-base-color, 10%), 0), darken($ui-base-color, 10%));
+      background-image: linear-gradient(
+        to left,
+        rgba(darken($ui-base-color, 10%), 0),
+        darken($ui-base-color, 10%)
+      );
     }
   }
 
   .simple_form select {
-    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 left 8px center / auto 16px;
-  }
-
-  .table th,
-  .table td {
-    text-align: right;
-  }
-
-  .filters .filter-subset {
-    margin-right: 0;
-    margin-left: 45px;
-  }
-
-  @media screen and (min-width: 631px) {
-    .column,
-    .drawer {
-      padding-left: 5px;
-      padding-right: 5px;
-
-      &:first-child {
-        padding-left: 5px;
-        padding-right: 10px;
-      }
-    }
-
-    .columns-area > div {
-      .column,
-      .drawer {
-        padding-left: 5px;
-        padding-right: 5px;
-      }
-    }
-  }
-
-  .columns-area--mobile .column,
-  .columns-area--mobile .drawer {
-    padding-left: 0;
-    padding-right: 0;
-  }
-
-  .card__bar .display-name {
-    margin-left: 0;
-    margin-right: 15px;
-    text-align: right;
+    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 left 8px center / auto 16px;
   }
 
   .fa-chevron-left::before {
-    content: "\F054";
+    content: '\F054';
   }
 
   .fa-chevron-right::before {
-    content: "\F053";
-  }
-
-  .column-back-button__icon {
-    margin-right: 0;
-    margin-left: 5px;
-  }
-
-  .simple_form .input.radio_buttons .radio > label input {
-    left: auto;
-    right: 0;
-  }
-
-  .picture-in-picture {
-    right: auto;
-    left: 20px;
+    content: '\F053';
   }
 }
diff --git a/app/javascript/styles/mastodon/statuses.scss b/app/javascript/styles/mastodon/statuses.scss
index ce71d11e4..6c9ea916a 100644
--- a/app/javascript/styles/mastodon/statuses.scss
+++ b/app/javascript/styles/mastodon/statuses.scss
@@ -97,7 +97,7 @@
     width: 20px;
     height: auto;
     vertical-align: middle;
-    margin-right: 5px;
+    margin-inline-end: 5px;
     fill: $primary-text-color;
   }
 
@@ -138,7 +138,7 @@ a.button.logo-button {
 }
 
 .embed {
-  .status__content[data-spoiler="folded"] {
+  .status__content[data-spoiler='folded'] {
     .e-content {
       display: none;
     }
@@ -162,7 +162,7 @@ a.button.logo-button {
     min-height: 48px + 2px;
 
     &__avatar {
-      left: 15px;
+      inset-inline-start: 15px;
       top: 17px;
 
       .account__avatar {
@@ -176,12 +176,12 @@ a.button.logo-button {
     }
 
     &__prepend {
-      margin-left: 48px + 15px * 2;
+      margin-inline-start: 48px + 15px * 2;
       padding-top: 15px;
     }
 
     &__prepend-icon-wrapper {
-      left: -32px;
+      inset-inline-start: -32px;
     }
 
     .media-gallery,
diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss
index b644b38f1..49e5bbf9f 100644
--- a/app/javascript/styles/mastodon/tables.scss
+++ b/app/javascript/styles/mastodon/tables.scss
@@ -10,7 +10,7 @@
     line-height: 18px;
     vertical-align: top;
     border-top: 1px solid $ui-base-color;
-    text-align: left;
+    text-align: start;
     background: darken($ui-base-color, 4%);
   }
 
@@ -91,12 +91,12 @@
 
       &:first-child {
         border-radius: 4px 0 0;
-        border-left: 1px solid darken($ui-base-color, 8%);
+        border-inline-start: 1px solid darken($ui-base-color, 8%);
       }
 
       &:last-child {
         border-radius: 0 4px 0 0;
-        border-right: 1px solid darken($ui-base-color, 8%);
+        border-inline-end: 1px solid darken($ui-base-color, 8%);
       }
     }
   }
@@ -125,7 +125,7 @@ button.table-action-link,
 a.table-action-link {
   text-decoration: none;
   display: inline-block;
-  margin-right: 5px;
+  margin-inline-end: 5px;
   padding: 0 10px;
   color: $darker-text-color;
   font-weight: 500;
@@ -136,11 +136,11 @@ a.table-action-link {
 
   i.fa {
     font-weight: 400;
-    margin-right: 5px;
+    margin-inline-end: 5px;
   }
 
   &:first-child {
-    padding-left: 0;
+    padding-inline-start: 0;
   }
 }
 
@@ -172,7 +172,7 @@ a.table-action-link {
     &__actions,
     &__content {
       padding: 8px 0;
-      padding-right: 16px;
+      padding-inline-end: 16px;
       flex: 1 1 auto;
     }
   }
@@ -188,8 +188,8 @@ a.table-action-link {
     align-items: center;
 
     &__actions {
-      text-align: right;
-      padding-right: 16px - 5px;
+      text-align: end;
+      padding-inline-end: 16px - 5px;
     }
   }
 
@@ -296,7 +296,7 @@ a.table-action-link {
         display: flex;
         justify-content: center;
         align-items: center;
-        margin-right: 10px;
+        margin-inline-end: 10px;
 
         .emojione {
           width: 32px;
@@ -315,7 +315,7 @@ a.table-action-link {
 
       &__extra {
         flex: 0 0 auto;
-        text-align: right;
+        text-align: end;
         color: $darker-text-color;
         font-weight: 500;
       }
diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss
index 2f6c41d5f..7de25f8fd 100644
--- a/app/javascript/styles/mastodon/variables.scss
+++ b/app/javascript/styles/mastodon/variables.scss
@@ -1,18 +1,18 @@
 // Commonly used web colors
-$black: #000000;            // Black
-$white: #ffffff;            // White
-$success-green: #79bd9a !default;    // Padua
-$error-red: #df405a !default;        // Cerise
-$warning-red: #ff5050 !default;      // Sunset Orange
-$gold-star: #ca8f04 !default;        // Dark Goldenrod
+$black: #000000; // Black
+$white: #ffffff; // White
+$success-green: #79bd9a !default; // Padua
+$error-red: #df405a !default; // Cerise
+$warning-red: #ff5050 !default; // Sunset Orange
+$gold-star: #ca8f04 !default; // Dark Goldenrod
 
 $red-bookmark: $warning-red;
 
 // Values from the classic Mastodon UI
-$classic-base-color: #282c37;         // Midnight Express
-$classic-primary-color: #9baec8;      // Echo Blue
-$classic-secondary-color: #d9e1e8;    // Pattens Blue
-$classic-highlight-color: #6364ff;    // Brand purple
+$classic-base-color: #282c37; // Midnight Express
+$classic-primary-color: #9baec8; // Echo Blue
+$classic-secondary-color: #d9e1e8; // Pattens Blue
+$classic-highlight-color: #6364ff; // Brand purple
 
 // Variables for defaults in UI
 $base-shadow-color: $black !default;
@@ -23,10 +23,13 @@ $valid-value-color: $success-green !default;
 $error-value-color: $error-red !default;
 
 // Tell UI to use selected colors
-$ui-base-color: $classic-base-color !default;                  // Darkest
-$ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest
-$ui-primary-color: $classic-primary-color !default;            // Lighter
-$ui-secondary-color: $classic-secondary-color !default;        // Lightest
+$ui-base-color: $classic-base-color !default; // Darkest
+$ui-base-lighter-color: lighten(
+  $ui-base-color,
+  26%
+) !default; // Lighter darkest
+$ui-primary-color: $classic-primary-color !default; // Lighter
+$ui-secondary-color: $classic-secondary-color !default; // Lightest
 $ui-highlight-color: $classic-highlight-color !default;
 
 // Variables for texts
diff --git a/app/javascript/styles/mastodon/widgets.scss b/app/javascript/styles/mastodon/widgets.scss
index 7a25d121b..1f69f0cf0 100644
--- a/app/javascript/styles/mastodon/widgets.scss
+++ b/app/javascript/styles/mastodon/widgets.scss
@@ -1,4 +1,4 @@
-@use "sass:math";
+@use 'sass:math';
 
 .hero-widget {
   margin-bottom: 10px;
@@ -39,8 +39,8 @@
       width: 20px;
       height: 20px;
       margin: -3px 0 0;
-      margin-left: 0.075em;
-      margin-right: 0.075em;
+      margin-inline-start: 0.075em;
+      margin-inline-end: 0.075em;
     }
 
     p {
@@ -171,7 +171,7 @@
     margin-bottom: 15px;
 
     .fa {
-      margin-right: 5px;
+      margin-inline-end: 5px;
       color: $darker-text-color;
     }
   }
@@ -284,7 +284,7 @@
     }
 
     .trends__item__current {
-      padding-right: 0;
+      padding-inline-end: 0;
     }
   }
 }
@@ -309,7 +309,7 @@
     padding: 10px;
 
     &:first-child {
-      text-align: left;
+      text-align: start;
     }
   }
 
@@ -340,9 +340,9 @@
 
   tbody td.accounts-table__extra {
     width: 120px;
-    text-align: right;
+    text-align: end;
     color: $darker-text-color;
-    padding-right: 16px;
+    padding-inline-end: 16px;
 
     a {
       text-decoration: none;
@@ -363,7 +363,7 @@
 
   tbody td.accounts-table__interrelationships {
     width: 21px;
-    padding-right: 16px;
+    padding-inline-end: 16px;
   }
 
   .fa {
diff --git a/app/javascript/types/resources.ts b/app/javascript/types/resources.ts
new file mode 100644
index 000000000..372ff7523
--- /dev/null
+++ b/app/javascript/types/resources.ts
@@ -0,0 +1,13 @@
+interface MastodonMap<T> {
+  get<K extends keyof T>(key: K): T[K];
+  has<K extends keyof T>(key: K): boolean;
+  set<K extends keyof T>(key: K, value: T[K]): this;
+}
+
+type AccountValues = {
+  id: number;
+  avatar: string;
+  avatar_static: string;
+  [key: string]: any;
+};
+export type Account = MastodonMap<AccountValues>;
diff --git a/app/lib/activity_tracker.rb b/app/lib/activity_tracker.rb
index 6d3401b37..8829d8605 100644
--- a/app/lib/activity_tracker.rb
+++ b/app/lib/activity_tracker.rb
@@ -27,14 +27,12 @@ class ActivityTracker
     (start_at.to_date...end_at.to_date).map do |date|
       key = key_at(date.to_time(:utc))
 
-      value = begin
-        case @type
-        when :basic
-          redis.get(key).to_i
-        when :unique
-          redis.pfcount(key)
-        end
-      end
+      value = case @type
+              when :basic
+                redis.get(key).to_i
+              when :unique
+                redis.pfcount(key)
+              end
 
       [date, value]
     end
diff --git a/app/lib/activitypub/activity.rb b/app/lib/activitypub/activity.rb
index f4c67cccd..5d9596254 100644
--- a/app/lib/activitypub/activity.rb
+++ b/app/lib/activitypub/activity.rb
@@ -106,7 +106,8 @@ class ActivityPub::Activity
       actor_id = value_or_id(first_of_value(@object['attributedTo']))
 
       if actor_id == @account.uri
-        return ActivityPub::Activity.factory({ 'type' => 'Create', 'actor' => actor_id, 'object' => @object }, @account).perform
+        virtual_object = { 'type' => 'Create', 'actor' => actor_id, 'object' => @object }
+        return ActivityPub::Activity.factory(virtual_object, @account, request_id: @options[:request_id]).perform
       end
     end
 
@@ -152,9 +153,10 @@ class ActivityPub::Activity
   def fetch_remote_original_status
     if object_uri.start_with?('http')
       return if ActivityPub::TagManager.instance.local_uri?(object_uri)
-      ActivityPub::FetchRemoteStatusService.new.call(object_uri, id: true, on_behalf_of: @account.followers.local.first)
+
+      ActivityPub::FetchRemoteStatusService.new.call(object_uri, id: true, on_behalf_of: @account.followers.local.first, request_id: @options[:request_id])
     elsif @object['url'].present?
-      ::FetchRemoteStatusService.new.call(@object['url'])
+      ::FetchRemoteStatusService.new.call(@object['url'], request_id: @options[:request_id])
     end
   end
 
diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb
index 4dfbfc665..eca446243 100644
--- a/app/lib/activitypub/activity/create.rb
+++ b/app/lib/activitypub/activity/create.rb
@@ -108,26 +108,24 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
   def process_status_params
     @status_parser = ActivityPub::Parser::StatusParser.new(@json, followers_collection: @account.followers_url)
 
-    @params = begin
-      {
-        uri: @status_parser.uri,
-        url: @status_parser.url || @status_parser.uri,
-        account: @account,
-        text: converted_object_type? ? converted_text : (@status_parser.text || ''),
-        language: @status_parser.language,
-        spoiler_text: converted_object_type? ? '' : (@status_parser.spoiler_text || ''),
-        created_at: @status_parser.created_at,
-        edited_at: @status_parser.edited_at && @status_parser.edited_at != @status_parser.created_at ? @status_parser.edited_at : nil,
-        override_timestamps: @options[:override_timestamps],
-        reply: @status_parser.reply,
-        sensitive: @account.sensitized? || @status_parser.sensitive || false,
-        visibility: @status_parser.visibility,
-        thread: replied_to_status,
-        conversation: conversation_from_uri(@object['conversation']),
-        media_attachment_ids: process_attachments.take(4).map(&:id),
-        poll: process_poll,
-      }
-    end
+    @params = {
+      uri: @status_parser.uri,
+      url: @status_parser.url || @status_parser.uri,
+      account: @account,
+      text: converted_object_type? ? converted_text : (@status_parser.text || ''),
+      language: @status_parser.language,
+      spoiler_text: converted_object_type? ? '' : (@status_parser.spoiler_text || ''),
+      created_at: @status_parser.created_at,
+      edited_at: @status_parser.edited_at && @status_parser.edited_at != @status_parser.created_at ? @status_parser.edited_at : nil,
+      override_timestamps: @options[:override_timestamps],
+      reply: @status_parser.reply,
+      sensitive: @account.sensitized? || @status_parser.sensitive || false,
+      visibility: @status_parser.visibility,
+      thread: replied_to_status,
+      conversation: conversation_from_uri(@object['conversation']),
+      media_attachment_ids: process_attachments.take(4).map(&:id),
+      poll: process_poll,
+    }
   end
 
   def process_audience
@@ -285,7 +283,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
 
     media_attachments
   rescue Addressable::URI::InvalidURIError => e
-    Rails.logger.debug "Invalid URL in attachment: #{e}"
+    Rails.logger.debug { "Invalid URL in attachment: #{e}" }
     media_attachments
   end
 
@@ -327,18 +325,18 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
   def resolve_thread(status)
     return unless status.reply? && status.thread.nil? && Request.valid_url?(in_reply_to_uri)
 
-    ThreadResolveWorker.perform_async(status.id, in_reply_to_uri)
+    ThreadResolveWorker.perform_async(status.id, in_reply_to_uri, { 'request_id' => @options[:request_id] })
   end
 
   def fetch_replies(status)
     collection = @object['replies']
     return if collection.nil?
 
-    replies = ActivityPub::FetchRepliesService.new.call(status, collection, false)
+    replies = ActivityPub::FetchRepliesService.new.call(status, collection, allow_synchronous_requests: false, request_id: @options[:request_id])
     return unless replies.nil?
 
     uri = value_or_id(collection)
-    ActivityPub::FetchRepliesWorker.perform_async(status.id, uri) unless uri.nil?
+    ActivityPub::FetchRepliesWorker.perform_async(status.id, uri, { 'request_id' => @options[:request_id] }) unless uri.nil?
   end
 
   def conversation_from_uri(uri)
diff --git a/app/lib/activitypub/case_transform.rb b/app/lib/activitypub/case_transform.rb
index 7f716f862..d36e01b8f 100644
--- a/app/lib/activitypub/case_transform.rb
+++ b/app/lib/activitypub/case_transform.rb
@@ -13,7 +13,7 @@ module ActivityPub::CaseTransform
       when Symbol then camel_lower(value.to_s).to_sym
       when String
         camel_lower_cache[value] ||= if value.start_with?('_:')
-                                       '_:' + value.gsub(/\A_:/, '').underscore.camelize(:lower)
+                                       "_:#{value.gsub(/\A_:/, '').underscore.camelize(:lower)}"
                                      else
                                        value.underscore.camelize(:lower)
                                      end
diff --git a/app/lib/activitypub/forwarder.rb b/app/lib/activitypub/forwarder.rb
index 4206b9d82..3a94f9669 100644
--- a/app/lib/activitypub/forwarder.rb
+++ b/app/lib/activitypub/forwarder.rb
@@ -12,7 +12,7 @@ class ActivityPub::Forwarder
   end
 
   def forward!
-    ActivityPub::LowPriorityDeliveryWorker.push_bulk(inboxes) do |inbox_url|
+    ActivityPub::LowPriorityDeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url|
       [payload, signature_account_id, inbox_url]
     end
   end
@@ -28,13 +28,11 @@ class ActivityPub::Forwarder
   end
 
   def signature_account_id
-    @signature_account_id ||= begin
-      if in_reply_to_local?
-        in_reply_to.account_id
-      else
-        reblogged_by_account_ids.first
-      end
-    end
+    @signature_account_id ||= if in_reply_to_local?
+                                in_reply_to.account_id
+                              else
+                                reblogged_by_account_ids.first
+                              end
   end
 
   def inboxes
diff --git a/app/lib/activitypub/linked_data_signature.rb b/app/lib/activitypub/linked_data_signature.rb
index f90adaf6c..ea59879f3 100644
--- a/app/lib/activitypub/linked_data_signature.rb
+++ b/app/lib/activitypub/linked_data_signature.rb
@@ -27,14 +27,12 @@ class ActivityPub::LinkedDataSignature
     document_hash  = hash(@json.without('signature'))
     to_be_verified = options_hash + document_hash
 
-    if creator.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), Base64.decode64(signature), to_be_verified)
-      creator
-    end
+    creator if creator.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), Base64.decode64(signature), to_be_verified)
   end
 
   def sign!(creator, sign_with: nil)
     options = {
-      'type'    => 'RsaSignature2017',
+      'type' => 'RsaSignature2017',
       'creator' => ActivityPub::TagManager.instance.key_uri_for(creator),
       'created' => Time.now.utc.iso8601,
     }
diff --git a/app/lib/activitypub/parser/media_attachment_parser.rb b/app/lib/activitypub/parser/media_attachment_parser.rb
index 656be84b7..56b8b23f8 100644
--- a/app/lib/activitypub/parser/media_attachment_parser.rb
+++ b/app/lib/activitypub/parser/media_attachment_parser.rb
@@ -50,9 +50,7 @@ class ActivityPub::Parser::MediaAttachmentParser
     components = begin
       blurhash = @json['blurhash']
 
-      if blurhash.present? && /^[\w#$%*+,-.:;=?@\[\]^{|}~]+$/.match?(blurhash)
-        Blurhash.components(blurhash)
-      end
+      Blurhash.components(blurhash) if blurhash.present? && /^[\w#$%*+,-.:;=?@\[\]^{|}~]+$/.match?(blurhash)
     end
 
     components.present? && components.none? { |comp| comp > 5 }
diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb
index 3d6b28ef5..a65a9565a 100644
--- a/app/lib/activitypub/tag_manager.rb
+++ b/app/lib/activitypub/tag_manager.rb
@@ -26,6 +26,7 @@ class ActivityPub::TagManager
       target.instance_actor? ? about_more_url(instance_actor: true) : short_account_url(target)
     when :note, :comment, :activity
       return activity_account_status_url(target.account, target) if target.reblog?
+
       short_account_status_url(target.account, target)
     end
   end
@@ -38,6 +39,7 @@ class ActivityPub::TagManager
       target.instance_actor? ? instance_actor_url : account_url(target)
     when :note, :comment, :activity
       return activity_account_status_url(target.account, target) if target.reblog?
+
       account_status_url(target.account, target)
     when :emoji
       emoji_url(target)
diff --git a/app/lib/admin/metrics/dimension/software_versions_dimension.rb b/app/lib/admin/metrics/dimension/software_versions_dimension.rb
index 816615f99..9ab3776c9 100644
--- a/app/lib/admin/metrics/dimension/software_versions_dimension.rb
+++ b/app/lib/admin/metrics/dimension/software_versions_dimension.rb
@@ -58,12 +58,10 @@ class Admin::Metrics::Dimension::SoftwareVersionsDimension < Admin::Metrics::Dim
   end
 
   def redis_info
-    @redis_info ||= begin
-      if redis.is_a?(Redis::Namespace)
-        redis.redis.info
-      else
-        redis.info
-      end
-    end
+    @redis_info ||= if redis.is_a?(Redis::Namespace)
+                      redis.redis.info
+                    else
+                      redis.info
+                    end
   end
 end
diff --git a/app/lib/admin/metrics/dimension/space_usage_dimension.rb b/app/lib/admin/metrics/dimension/space_usage_dimension.rb
index 5867c5bab..cc8560890 100644
--- a/app/lib/admin/metrics/dimension/space_usage_dimension.rb
+++ b/app/lib/admin/metrics/dimension/space_usage_dimension.rb
@@ -59,12 +59,10 @@ class Admin::Metrics::Dimension::SpaceUsageDimension < Admin::Metrics::Dimension
   end
 
   def redis_info
-    @redis_info ||= begin
-      if redis.is_a?(Redis::Namespace)
-        redis.redis.info
-      else
-        redis.info
-      end
-    end
+    @redis_info ||= if redis.is_a?(Redis::Namespace)
+                      redis.redis.info
+                    else
+                      redis.info
+                    end
   end
 end
diff --git a/app/lib/admin/metrics/retention.rb b/app/lib/admin/metrics/retention.rb
index f6135ac1e..9bd47c58e 100644
--- a/app/lib/admin/metrics/retention.rb
+++ b/app/lib/admin/metrics/retention.rb
@@ -42,25 +42,54 @@ class Admin::Metrics::Retention
   end
 
   def perform_query
-    sql = <<-SQL.squish
+    report_rows.each_with_object([]) do |row, arr|
+      current_cohort = arr.last
+
+      if current_cohort.nil? || current_cohort.period != row['cohort_period']
+        current_cohort = Cohort.new(period: row['cohort_period'], frequency: @frequency, data: [])
+        arr << current_cohort
+      end
+
+      value, rate = row['retention_value_and_rate'].delete('{}').split(',')
+
+      current_cohort.data << CohortData.new(
+        date: row['retention_period'],
+        rate: rate.to_f,
+        value: value.to_s
+      )
+    end
+  end
+
+  def report_rows
+    ActiveRecord::Base.connection.select_all(sanitized_sql_string)
+  end
+
+  def sanitized_sql_string
+    ActiveRecord::Base.sanitize_sql_array(
+      [sql_query_string, { start_at: @start_at, end_at: @end_at, frequency: @frequency }]
+    )
+  end
+
+  def sql_query_string
+    <<~SQL.squish
       SELECT axis.*, (
         WITH new_users AS (
           SELECT users.id
           FROM users
-          WHERE date_trunc($3, users.created_at)::date = axis.cohort_period
+          WHERE date_trunc(:frequency, users.created_at)::date = axis.cohort_period
         ),
         retained_users AS (
           SELECT users.id
           FROM users
           INNER JOIN new_users on new_users.id = users.id
-          WHERE date_trunc($3, users.current_sign_in_at) >= axis.retention_period
+          WHERE date_trunc(:frequency, users.current_sign_in_at) >= axis.retention_period
         )
         SELECT ARRAY[count(*), (count(*))::float / (SELECT GREATEST(count(*), 1) FROM new_users)] AS retention_value_and_rate
         FROM retained_users
       )
       FROM (
         WITH cohort_periods AS (
-          SELECT generate_series(date_trunc($3, $1::timestamp)::date, date_trunc($3, $2::timestamp)::date, ('1 ' || $3)::interval) AS cohort_period
+          SELECT generate_series(date_trunc(:frequency, :start_at::timestamp)::date, date_trunc(:frequency, :end_at::timestamp)::date, ('1 ' || :frequency)::interval) AS cohort_period
         ),
         retention_periods AS (
           SELECT cohort_period AS retention_period FROM cohort_periods
@@ -70,24 +99,5 @@ class Admin::Metrics::Retention
         WHERE retention_period >= cohort_period
       ) as axis
     SQL
-
-    rows = ActiveRecord::Base.connection.select_all(sql, nil, [[nil, @start_at], [nil, @end_at], [nil, @frequency]])
-
-    rows.each_with_object([]) do |row, arr|
-      current_cohort = arr.last
-
-      if current_cohort.nil? || current_cohort.period != row['cohort_period']
-        current_cohort = Cohort.new(period: row['cohort_period'], frequency: @frequency, data: [])
-        arr << current_cohort
-      end
-
-      value, rate = row['retention_value_and_rate'].delete('{}').split(',')
-
-      current_cohort.data << CohortData.new(
-        date: row['retention_period'],
-        rate: rate.to_f,
-        value: value.to_s
-      )
-    end
   end
 end
diff --git a/app/lib/admin/system_check.rb b/app/lib/admin/system_check.rb
index f512635ab..89dfcef9f 100644
--- a/app/lib/admin/system_check.rb
+++ b/app/lib/admin/system_check.rb
@@ -2,6 +2,7 @@
 
 class Admin::SystemCheck
   ACTIVE_CHECKS = [
+    Admin::SystemCheck::MediaPrivacyCheck,
     Admin::SystemCheck::DatabaseSchemaCheck,
     Admin::SystemCheck::SidekiqProcessCheck,
     Admin::SystemCheck::RulesCheck,
diff --git a/app/lib/admin/system_check/elasticsearch_check.rb b/app/lib/admin/system_check/elasticsearch_check.rb
index 7f922978f..0b55be350 100644
--- a/app/lib/admin/system_check/elasticsearch_check.rb
+++ b/app/lib/admin/system_check/elasticsearch_check.rb
@@ -30,19 +30,24 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck
 
   def running_version
     @running_version ||= begin
-      Chewy.client.info['version']['minimum_wire_compatibility_version'] ||
-        Chewy.client.info['version']['number']
-    rescue Faraday::ConnectionFailed
+      Chewy.client.info['version']['number']
+    rescue Faraday::ConnectionFailed, Elasticsearch::Transport::Transport::Error
       nil
     end
   end
 
+  def compatible_wire_version
+    Chewy.client.info['version']['minimum_wire_compatibility_version']
+  end
+
   def required_version
     '7.x'
   end
 
   def compatible_version?
     return false if running_version.nil?
-    Gem::Version.new(running_version) >= Gem::Version.new(required_version)
+
+    Gem::Version.new(running_version) >= Gem::Version.new(required_version) ||
+      Gem::Version.new(compatible_wire_version) >= Gem::Version.new(required_version)
   end
 end
diff --git a/app/lib/admin/system_check/media_privacy_check.rb b/app/lib/admin/system_check/media_privacy_check.rb
new file mode 100644
index 000000000..1df05b120
--- /dev/null
+++ b/app/lib/admin/system_check/media_privacy_check.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+class Admin::SystemCheck::MediaPrivacyCheck < Admin::SystemCheck::BaseCheck
+  include RoutingHelper
+
+  def skip?
+    !current_user.can?(:view_devops)
+  end
+
+  def pass?
+    check_media_uploads!
+    @failure_message.nil?
+  end
+
+  def message
+    Admin::SystemCheck::Message.new(@failure_message, @failure_value, @failure_action, true)
+  end
+
+  private
+
+  def check_media_uploads!
+    if Rails.configuration.x.use_s3
+      check_media_listing_inaccessible_s3!
+    else
+      check_media_listing_inaccessible!
+    end
+  end
+
+  def check_media_listing_inaccessible!
+    full_url = full_asset_url(media_attachment.file.url(:original, false))
+
+    # Check if we can list the uploaded file. If true, that's an error
+    directory_url = Addressable::URI.parse(full_url)
+    directory_url.query = nil
+    filename = directory_url.path.gsub(%r{.*/}, '')
+    directory_url.path = directory_url.path.gsub(%r{/[^/]+\Z}, '/')
+    Request.new(:get, directory_url, allow_local: true).perform do |res|
+      if res.truncated_body&.include?(filename)
+        @failure_message = use_storage? ? :upload_check_privacy_error_object_storage : :upload_check_privacy_error
+        @failure_action = 'https://docs.joinmastodon.org/admin/optional/object-storage/#FS'
+      end
+    end
+  rescue
+    nil
+  end
+
+  def check_media_listing_inaccessible_s3!
+    urls_to_check = []
+    paperclip_options = Paperclip::Attachment.default_options
+    s3_protocol = paperclip_options[:s3_protocol]
+    s3_host_alias = paperclip_options[:s3_host_alias]
+    s3_host_name  = paperclip_options[:s3_host_name]
+    bucket_name = paperclip_options.dig(:s3_credentials, :bucket)
+
+    urls_to_check << "#{s3_protocol}://#{s3_host_alias}/" if s3_host_alias.present?
+    urls_to_check << "#{s3_protocol}://#{s3_host_name}/#{bucket_name}/"
+    urls_to_check.uniq.each do |full_url|
+      check_s3_listing!(full_url)
+      break if @failure_message.present?
+    end
+  rescue
+    nil
+  end
+
+  def check_s3_listing!(full_url)
+    bucket_url = Addressable::URI.parse(full_url)
+    bucket_url.path = bucket_url.path.delete_suffix(media_attachment.file.path(:original))
+    bucket_url.query = "max-keys=1&x-random=#{SecureRandom.hex(10)}"
+    Request.new(:get, bucket_url, allow_local: true).perform do |res|
+      if res.truncated_body&.include?('ListBucketResult')
+        @failure_message = :upload_check_privacy_error_object_storage
+        @failure_action  = 'https://docs.joinmastodon.org/admin/optional/object-storage/#S3'
+      end
+    end
+  end
+
+  def media_attachment
+    @media_attachment ||= begin
+      attachment = Account.representative.media_attachments.first
+      if attachment.present?
+        attachment.touch # rubocop:disable Rails/SkipsModelValidations
+        attachment
+      else
+        create_test_attachment!
+      end
+    end
+  end
+
+  def create_test_attachment!
+    Tempfile.create(%w(test-upload .jpg), binmode: true) do |tmp_file|
+      tmp_file.write(
+        Base64.decode64(
+          '/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAA' \
+          'AAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA' \
+          'QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE' \
+          'BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/x' \
+          'ABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAA' \
+          'AAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q=='
+        )
+      )
+      tmp_file.flush
+      Account.representative.media_attachments.create!(file: tmp_file)
+    end
+  end
+end
diff --git a/app/lib/admin/system_check/message.rb b/app/lib/admin/system_check/message.rb
index bfcad3bf3..ad8d4b607 100644
--- a/app/lib/admin/system_check/message.rb
+++ b/app/lib/admin/system_check/message.rb
@@ -1,11 +1,12 @@
 # frozen_string_literal: true
 
 class Admin::SystemCheck::Message
-  attr_reader :key, :value, :action
+  attr_reader :key, :value, :action, :critical
 
-  def initialize(key, value = nil, action = nil)
-    @key    = key
-    @value  = value
-    @action = action
+  def initialize(key, value = nil, action = nil, critical = false)
+    @key      = key
+    @value    = value
+    @action   = action
+    @critical = critical
   end
 end
diff --git a/app/lib/advanced_text_formatter.rb b/app/lib/advanced_text_formatter.rb
index 21e81d4d1..cdf1e2d9c 100644
--- a/app/lib/advanced_text_formatter.rb
+++ b/app/lib/advanced_text_formatter.rb
@@ -15,6 +15,7 @@ class AdvancedTextFormatter < TextFormatter
 
     def autolink(link, link_type)
       return link if link_type == :email
+
       @format_link.call(link)
     end
   end
diff --git a/app/lib/delivery_failure_tracker.rb b/app/lib/delivery_failure_tracker.rb
index 66c1fd8c0..c90716632 100644
--- a/app/lib/delivery_failure_tracker.rb
+++ b/app/lib/delivery_failure_tracker.rb
@@ -6,7 +6,7 @@ class DeliveryFailureTracker
   FAILURE_DAYS_THRESHOLD = 7
 
   def initialize(url_or_host)
-    @host = url_or_host.start_with?('https://') || url_or_host.start_with?('http://') ? Addressable::URI.parse(url_or_host).normalized_host : url_or_host
+    @host = url_or_host.start_with?('https://', 'http://') ? Addressable::URI.parse(url_or_host).normalized_host : url_or_host
   end
 
   def track_failure!
diff --git a/app/lib/extractor.rb b/app/lib/extractor.rb
index aea60dae5..540bbe1a9 100644
--- a/app/lib/extractor.rb
+++ b/app/lib/extractor.rb
@@ -8,17 +8,15 @@ module Extractor
   module_function
 
   def extract_entities_with_indices(text, options = {}, &block)
-    entities = begin
-      extract_urls_with_indices(text, options) +
-        extract_hashtags_with_indices(text, check_url_overlap: false) +
-        extract_mentions_or_lists_with_indices(text) +
-        extract_extra_uris_with_indices(text)
-    end
+    entities = extract_urls_with_indices(text, options) +
+               extract_hashtags_with_indices(text, check_url_overlap: false) +
+               extract_mentions_or_lists_with_indices(text) +
+               extract_extra_uris_with_indices(text)
 
     return [] if entities.empty?
 
     entities = remove_overlapping_entities(entities)
-    entities.each(&block) if block_given?
+    entities.each(&block) if block
     entities
   end
 
@@ -29,7 +27,7 @@ module Extractor
 
     text.scan(Account::MENTION_RE) do |screen_name, _|
       match_data = $LAST_MATCH_INFO
-      after      = $'
+      after      = ::Regexp.last_match.post_match
 
       unless Twitter::TwitterText::Regex[:end_mention_match].match?(after)
         _, domain = screen_name.split('@')
@@ -64,7 +62,7 @@ module Extractor
       match_data     = $LAST_MATCH_INFO
       start_position = match_data.char_begin(1) - 1
       end_position   = match_data.char_end(1)
-      after          = $'
+      after          = ::Regexp.last_match.post_match
 
       if %r{\A://}.match?(after)
         hash_text.match(/(.+)(https?\Z)/) do |matched|
diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb
index 14208e557..15ff6d15f 100644
--- a/app/lib/feed_manager.rb
+++ b/app/lib/feed_manager.rb
@@ -7,7 +7,7 @@ class FeedManager
   include Redisable
 
   # Maximum number of items stored in a single feed
-  MAX_ITEMS = 400
+  MAX_ITEMS = 800
 
   # Number of items in the feed since last reblog of status
   # before the new reblog will be inserted. Must be <= MAX_ITEMS
@@ -306,6 +306,7 @@ class FeedManager
 
       statuses.each do |status|
         next if filter_from_direct?(status, account)
+
         added += 1 if add_to_feed(:direct, account.id, status)
       end
 
@@ -322,27 +323,27 @@ class FeedManager
   def clean_feeds!(type, ids)
     reblogged_id_sets = {}
 
-    redis.pipelined do
+    redis.pipelined do |pipeline|
       ids.each do |feed_id|
-        redis.del(key(type, feed_id))
         reblog_key = key(type, feed_id, 'reblogs')
         # We collect a future for this: we don't block while getting
         # it, but we can iterate over it later.
-        reblogged_id_sets[feed_id] = redis.zrange(reblog_key, 0, -1)
-        redis.del(reblog_key)
+        reblogged_id_sets[feed_id] = pipeline.zrange(reblog_key, 0, -1)
+        pipeline.del(key(type, feed_id), reblog_key)
       end
     end
 
     # Remove all of the reblog tracking keys we just removed the
     # references to.
-    redis.pipelined do
-      reblogged_id_sets.each do |feed_id, future|
-        future.value.each do |reblogged_id|
-          reblog_set_key = key(type, feed_id, "reblogs:#{reblogged_id}")
-          redis.del(reblog_set_key)
-        end
+    keys_to_delete = reblogged_id_sets.flat_map do |feed_id, future|
+      future.value.map do |reblogged_id|
+        key(type, feed_id, "reblogs:#{reblogged_id}")
       end
     end
+
+    redis.del(keys_to_delete) unless keys_to_delete.empty?
+
+    nil
   end
 
   private
@@ -459,6 +460,7 @@ class FeedManager
   # @return [Boolean]
   def filter_from_direct?(status, receiver_id)
     return false if receiver_id == status.account_id
+
     filter_from_mentions?(status, receiver_id)
   end
 
diff --git a/app/lib/html_aware_formatter.rb b/app/lib/html_aware_formatter.rb
index 7a1cd0340..8766c5ee0 100644
--- a/app/lib/html_aware_formatter.rb
+++ b/app/lib/html_aware_formatter.rb
@@ -1,6 +1,8 @@
 # frozen_string_literal: true
 
 class HtmlAwareFormatter
+  STATUS_MIME_TYPES = %w(text/plain text/markdown text/html).freeze
+
   attr_reader :text, :local, :options
 
   alias local? local
diff --git a/app/lib/importer/base_importer.rb b/app/lib/importer/base_importer.rb
index ea522c600..0cd1d3422 100644
--- a/app/lib/importer/base_importer.rb
+++ b/app/lib/importer/base_importer.rb
@@ -45,7 +45,7 @@ class Importer::BaseImporter
   # Remove documents from the index that no longer exist in the database
   def clean_up!
     index.scroll_batches do |documents|
-      ids           = documents.map { |doc| doc['_id'] }
+      ids           = documents.pluck('_id')
       existence_map = index.adapter.target.where(id: ids).pluck(:id).each_with_object({}) { |id, map| map[id.to_s] = true }
       tmp           = ids.reject { |id| existence_map[id] }
 
diff --git a/app/lib/importer/statuses_index_importer.rb b/app/lib/importer/statuses_index_importer.rb
index 5b5153d5c..b0721c2e0 100644
--- a/app/lib/importer/statuses_index_importer.rb
+++ b/app/lib/importer/statuses_index_importer.rb
@@ -24,13 +24,11 @@ class Importer::StatusesIndexImporter < Importer::BaseImporter
           # is called before rendering the data and we need to filter based
           # on the results of the filter, so this filtering happens here instead
           bulk.map! do |entry|
-            new_entry = begin
-              if entry[:index] && entry.dig(:index, :data, 'searchable_by').blank?
-                { delete: entry[:index].except(:data) }
-              else
-                entry
-              end
-            end
+            new_entry = if entry[:index] && entry.dig(:index, :data, 'searchable_by').blank?
+                          { delete: entry[:index].except(:data) }
+                        else
+                          entry
+                        end
 
             if new_entry[:index]
               indexed += 1
diff --git a/app/lib/link_details_extractor.rb b/app/lib/link_details_extractor.rb
index b0c4e4f42..f8a0be636 100644
--- a/app/lib/link_details_extractor.rb
+++ b/app/lib/link_details_extractor.rb
@@ -188,7 +188,7 @@ class LinkDetailsExtractor
   end
 
   def language
-    valid_locale_or_nil(structured_data&.language || opengraph_tag('og:locale') || document.xpath('//html').map { |element| element['lang'] }.first)
+    valid_locale_or_nil(structured_data&.language || opengraph_tag('og:locale') || document.xpath('//html').pick('lang'))
   end
 
   def icon
@@ -220,38 +220,36 @@ class LinkDetailsExtractor
   end
 
   def link_tag(name)
-    document.xpath("//link[@rel=\"#{name}\"]").map { |link| link['href'] }.first
+    document.xpath("//link[@rel=\"#{name}\"]").pick('href')
   end
 
   def opengraph_tag(name)
-    document.xpath("//meta[@property=\"#{name}\" or @name=\"#{name}\"]").map { |meta| meta['content'] }.first
+    document.xpath("//meta[@property=\"#{name}\" or @name=\"#{name}\"]").pick('content')
   end
 
   def meta_tag(name)
-    document.xpath("//meta[@name=\"#{name}\"]").map { |meta| meta['content'] }.first
+    document.xpath("//meta[@name=\"#{name}\"]").pick('content')
   end
 
   def structured_data
-    @structured_data ||= begin
-      # Some publications have more than one JSON-LD definition on the page,
-      # and some of those definitions aren't valid JSON either, so we have
-      # to loop through here until we find something that is the right type
-      # and doesn't break
-      document.xpath('//script[@type="application/ld+json"]').filter_map do |element|
-        json_ld = element.content&.gsub(CDATA_JUNK_PATTERN, '')
+    # Some publications have more than one JSON-LD definition on the page,
+    # and some of those definitions aren't valid JSON either, so we have
+    # to loop through here until we find something that is the right type
+    # and doesn't break
+    @structured_data ||= document.xpath('//script[@type="application/ld+json"]').filter_map do |element|
+      json_ld = element.content&.gsub(CDATA_JUNK_PATTERN, '')
 
-        next if json_ld.blank?
+      next if json_ld.blank?
 
-        structured_data = StructuredData.new(html_entities.decode(json_ld))
+      structured_data = StructuredData.new(html_entities.decode(json_ld))
 
-        next unless structured_data.valid?
+      next unless structured_data.valid?
 
-        structured_data
-      rescue Oj::ParseError, EncodingError
-        Rails.logger.debug("Invalid JSON-LD in #{@original_url}")
-        next
-      end.first
-    end
+      structured_data
+    rescue Oj::ParseError, EncodingError
+      Rails.logger.debug { "Invalid JSON-LD in #{@original_url}" }
+      next
+    end.first
   end
 
   def document
diff --git a/app/lib/ostatus/tag_manager.rb b/app/lib/ostatus/tag_manager.rb
index 4f4501312..7d8131622 100644
--- a/app/lib/ostatus/tag_manager.rb
+++ b/app/lib/ostatus/tag_manager.rb
@@ -5,27 +5,27 @@ class OStatus::TagManager
   include RoutingHelper
 
   VERBS = {
-    post:           'http://activitystrea.ms/schema/1.0/post',
-    share:          'http://activitystrea.ms/schema/1.0/share',
-    favorite:       'http://activitystrea.ms/schema/1.0/favorite',
-    unfavorite:     'http://activitystrea.ms/schema/1.0/unfavorite',
-    delete:         'http://activitystrea.ms/schema/1.0/delete',
-    follow:         'http://activitystrea.ms/schema/1.0/follow',
+    post: 'http://activitystrea.ms/schema/1.0/post',
+    share: 'http://activitystrea.ms/schema/1.0/share',
+    favorite: 'http://activitystrea.ms/schema/1.0/favorite',
+    unfavorite: 'http://activitystrea.ms/schema/1.0/unfavorite',
+    delete: 'http://activitystrea.ms/schema/1.0/delete',
+    follow: 'http://activitystrea.ms/schema/1.0/follow',
     request_friend: 'http://activitystrea.ms/schema/1.0/request-friend',
-    authorize:      'http://activitystrea.ms/schema/1.0/authorize',
-    reject:         'http://activitystrea.ms/schema/1.0/reject',
-    unfollow:       'http://ostatus.org/schema/1.0/unfollow',
-    block:          'http://mastodon.social/schema/1.0/block',
-    unblock:        'http://mastodon.social/schema/1.0/unblock',
+    authorize: 'http://activitystrea.ms/schema/1.0/authorize',
+    reject: 'http://activitystrea.ms/schema/1.0/reject',
+    unfollow: 'http://ostatus.org/schema/1.0/unfollow',
+    block: 'http://mastodon.social/schema/1.0/block',
+    unblock: 'http://mastodon.social/schema/1.0/unblock',
   }.freeze
 
   TYPES = {
-    activity:   'http://activitystrea.ms/schema/1.0/activity',
-    note:       'http://activitystrea.ms/schema/1.0/note',
-    comment:    'http://activitystrea.ms/schema/1.0/comment',
-    person:     'http://activitystrea.ms/schema/1.0/person',
+    activity: 'http://activitystrea.ms/schema/1.0/activity',
+    note: 'http://activitystrea.ms/schema/1.0/note',
+    comment: 'http://activitystrea.ms/schema/1.0/comment',
+    person: 'http://activitystrea.ms/schema/1.0/person',
     collection: 'http://activitystrea.ms/schema/1.0/collection',
-    group:      'http://activitystrea.ms/schema/1.0/group',
+    group: 'http://activitystrea.ms/schema/1.0/group',
   }.freeze
 
   COLLECTIONS = {
diff --git a/app/lib/plain_text_formatter.rb b/app/lib/plain_text_formatter.rb
index 08aa29696..6fa2bc5d2 100644
--- a/app/lib/plain_text_formatter.rb
+++ b/app/lib/plain_text_formatter.rb
@@ -18,7 +18,7 @@ class PlainTextFormatter
     if local?
       text
     else
-      strip_tags(insert_newlines).chomp
+      html_entities.decode(strip_tags(insert_newlines)).chomp
     end
   end
 
@@ -27,4 +27,8 @@ class PlainTextFormatter
   def insert_newlines
     text.gsub(NEWLINE_TAGS_RE) { |match| "#{match}\n" }
   end
+
+  def html_entities
+    HTMLEntities.new
+  end
 end
diff --git a/app/lib/rate_limiter.rb b/app/lib/rate_limiter.rb
index 0e2c9a894..4a0b35b08 100644
--- a/app/lib/rate_limiter.rb
+++ b/app/lib/rate_limiter.rb
@@ -48,7 +48,7 @@ class RateLimiter
     {
       'X-RateLimit-Limit' => @limit.to_s,
       'X-RateLimit-Remaining' => (@limit - (redis.get(key) || 0).to_i).to_s,
-      'X-RateLimit-Reset' => (now + (@period - now.to_i % @period)).iso8601(6),
+      'X-RateLimit-Reset' => (now + (@period - (now.to_i % @period))).iso8601(6),
     }
   end
 
diff --git a/app/lib/request.rb b/app/lib/request.rb
index 0508169dc..4bde6fc91 100644
--- a/app/lib/request.rb
+++ b/app/lib/request.rb
@@ -182,6 +182,7 @@ class Request
 
       contents = truncated_body(limit)
       raise Mastodon::LengthValidationError if contents.bytesize > limit
+
       contents
     end
   end
@@ -215,26 +216,24 @@ class Request
         addr_by_socket = {}
 
         addresses.each do |address|
-          begin
-            check_private_address(address, host)
+          check_private_address(address, host)
 
-            sock     = ::Socket.new(address.is_a?(Resolv::IPv6) ? ::Socket::AF_INET6 : ::Socket::AF_INET, ::Socket::SOCK_STREAM, 0)
-            sockaddr = ::Socket.pack_sockaddr_in(port, address.to_s)
+          sock     = ::Socket.new(address.is_a?(Resolv::IPv6) ? ::Socket::AF_INET6 : ::Socket::AF_INET, ::Socket::SOCK_STREAM, 0)
+          sockaddr = ::Socket.pack_sockaddr_in(port, address.to_s)
 
-            sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
+          sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
 
-            sock.connect_nonblock(sockaddr)
+          sock.connect_nonblock(sockaddr)
 
-            # If that hasn't raised an exception, we somehow managed to connect
-            # immediately, close pending sockets and return immediately
-            socks.each(&:close)
-            return sock
-          rescue IO::WaitWritable
-            socks << sock
-            addr_by_socket[sock] = sockaddr
-          rescue => e
-            outer_e = e
-          end
+          # If that hasn't raised an exception, we somehow managed to connect
+          # immediately, close pending sockets and return immediately
+          socks.each(&:close)
+          return sock
+        rescue IO::WaitWritable
+          socks << sock
+          addr_by_socket[sock] = sockaddr
+        rescue => e
+          outer_e = e
         end
 
         until socks.empty?
@@ -274,14 +273,14 @@ class Request
 
       def check_private_address(address, host)
         addr = IPAddr.new(address.to_s)
-        return if private_address_exceptions.any? { |range| range.include?(addr) }
+
+        return if Rails.env.development? || private_address_exceptions.any? { |range| range.include?(addr) }
+
         raise Mastodon::PrivateNetworkAddressError, host if PrivateAddressCheck.private_address?(addr)
       end
 
       def private_address_exceptions
-        @private_address_exceptions = begin
-          (ENV['ALLOWED_PRIVATE_ADDRESSES'] || '').split(',').map { |addr| IPAddr.new(addr) }
-        end
+        @private_address_exceptions = (ENV['ALLOWED_PRIVATE_ADDRESSES'] || '').split(',').map { |addr| IPAddr.new(addr) }
       end
     end
   end
diff --git a/app/lib/request_pool.rb b/app/lib/request_pool.rb
index e5899a79a..6be172286 100644
--- a/app/lib/request_pool.rb
+++ b/app/lib/request_pool.rb
@@ -64,7 +64,7 @@ class RequestPool
           retries     += 1
           retry
         end
-      rescue StandardError
+      rescue
         # If this connection raises errors of any kind, it's
         # better if it gets reaped as soon as possible
 
diff --git a/app/lib/scope_transformer.rb b/app/lib/scope_transformer.rb
index fdfc6cf13..adcb711f8 100644
--- a/app/lib/scope_transformer.rb
+++ b/app/lib/scope_transformer.rb
@@ -28,7 +28,7 @@ class ScopeTransformer < Parslet::Transform
     def merge!(other_scope)
       raise ArgumentError unless other_scope.namespace == namespace && other_scope.term == term
 
-      @access.concat(other_scope.instance_variable_get('@access'))
+      @access.concat(other_scope.instance_variable_get(:@access))
       @access.uniq!
       @access.sort!
 
diff --git a/app/lib/settings/scoped_settings.rb b/app/lib/settings/scoped_settings.rb
index 796de1113..983865a68 100644
--- a/app/lib/settings/scoped_settings.rb
+++ b/app/lib/settings/scoped_settings.rb
@@ -35,6 +35,7 @@ module Settings
 
       Setting.default_settings.each do |key, default_value|
         next if records.key?(key) || default_value.is_a?(Hash)
+
         records[key] = Setting.new(var: key, value: default_value)
       end
 
@@ -55,6 +56,7 @@ module Settings
         if db_val
           default_value = ScopedSettings.default_settings[key]
           return default_value.with_indifferent_access.merge!(db_val.value) if default_value.is_a?(Hash)
+
           db_val.value
         else
           ScopedSettings.default_settings[key]
diff --git a/app/lib/status_filter.rb b/app/lib/status_filter.rb
index b6c80b801..c0e6f3331 100644
--- a/app/lib/status_filter.rb
+++ b/app/lib/status_filter.rb
@@ -11,6 +11,7 @@ class StatusFilter
 
   def filtered?
     return false if !account.nil? && account.id == status.account_id
+
     blocked_by_policy? || (account_present? && filtered_status?) || silenced_account?
   end
 
diff --git a/app/lib/status_finder.rb b/app/lib/status_finder.rb
index 22ced8bf8..1a7f2fe69 100644
--- a/app/lib/status_finder.rb
+++ b/app/lib/status_finder.rb
@@ -27,8 +27,6 @@ class StatusFinder
   end
 
   def verify_action!
-    unless recognized_params[:action] == 'show'
-      raise ActiveRecord::RecordNotFound
-    end
+    raise ActiveRecord::RecordNotFound unless recognized_params[:action] == 'show'
   end
 end
diff --git a/app/lib/tag_manager.rb b/app/lib/tag_manager.rb
index a1d12a654..7fbf4437d 100644
--- a/app/lib/tag_manager.rb
+++ b/app/lib/tag_manager.rb
@@ -25,6 +25,7 @@ class TagManager
   def local_url?(url)
     uri    = Addressable::URI.parse(url).normalize
     return false unless uri.host
+
     domain = uri.host + (uri.port ? ":#{uri.port}" : '')
 
     TagManager.instance.web_domain?(domain)
diff --git a/app/lib/themes.rb b/app/lib/themes.rb
index 81e016d4a..45ba47780 100644
--- a/app/lib/themes.rb
+++ b/app/lib/themes.rb
@@ -7,24 +7,23 @@ class Themes
   include Singleton
 
   def initialize
-
     core = YAML.load_file(Rails.root.join('app', 'javascript', 'core', 'theme.yml'))
-    core['pack'] = Hash.new unless core['pack']
+    core['pack'] = {} unless core['pack']
 
-    result = Hash.new
-    Dir.glob(Rails.root.join('app', 'javascript', 'flavours', '*', 'theme.yml')) do |path|
-      data = YAML.load_file(path)
+    result = {}
+    Rails.root.glob('app/javascript/flavours/*/theme.yml') do |pathname|
+      data = YAML.load_file(pathname)
       next unless data['pack']
 
-      dir = File.dirname(path)
-      name = File.basename(dir)
+      dir = pathname.dirname
+      name = dir.basename.to_s
       locales = []
       screenshots = []
 
       if data['locales']
         Dir.glob(File.join(dir, data['locales'], '*.{js,json}')) do |locale|
-          localeName = File.basename(locale, File.extname(locale))
-          locales.push(localeName) unless localeName.match(/defaultMessages|whitelist|index/)
+          locale_name = File.basename(locale, File.extname(locale))
+          locales.push(locale_name) unless /defaultMessages|whitelist|index/.match?(locale_name)
         end
       end
 
@@ -43,34 +42,30 @@ class Themes
       result[name] = data
     end
 
-    Dir.glob(Rails.root.join('app', 'javascript', 'skins', '*', '*')) do |path|
-      ext = File.extname(path)
-      skin = File.basename(path)
-      name = File.basename(File.dirname(path))
+    Rails.root.glob('app/javascript/skins/*/*') do |pathname|
+      ext = pathname.extname.to_s
+      skin = pathname.basename.to_s
+      name = pathname.dirname.basename.to_s
       next unless result[name]
 
-      if File.directory?(path)
+      if pathname.directory?
         pack = []
-        Dir.glob(File.join(path, '*.{css,scss}')) do |sheet|
-          pack.push(File.basename(sheet, File.extname(sheet)))
+        pathname.glob('*.{css,scss}') do |sheet|
+          pack.push(sheet.basename(sheet.extname).to_s)
         end
-      elsif ext.match(/^\.s?css$/i)
-        skin = File.basename(path, ext)
+      elsif /^\.s?css$/i.match?(ext)
+        skin = pathname.basename(ext).to_s
         pack = ['common']
       end
 
-      if skin != 'default'
-        result[name]['skin'][skin] = pack
-      end
+      result[name]['skin'][skin] = pack if skin != 'default'
     end
 
     @core = core
     @conf = result
   end
 
-  def core
-    @core
-  end
+  attr_reader :core
 
   def flavour(name)
     @conf[name]
@@ -86,7 +81,7 @@ class Themes
 
   def flavours_and_skins
     flavours.map do |flavour|
-      [flavour, skins_for(flavour).map{ |skin| [flavour, skin] }]
+      [flavour, skins_for(flavour).map { |skin| [flavour, skin] }]
     end
   end
 end
diff --git a/app/lib/toc_generator.rb b/app/lib/toc_generator.rb
deleted file mode 100644
index 0c8f766ca..000000000
--- a/app/lib/toc_generator.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-# frozen_string_literal: true
-
-class TOCGenerator
-  TARGET_ELEMENTS = %w(h1 h2 h3 h4 h5 h6).freeze
-  LISTED_ELEMENTS = %w(h2 h3).freeze
-
-  class Section
-    attr_accessor :depth, :title, :children, :anchor
-
-    def initialize(depth, title, anchor)
-      @depth    = depth
-      @title    = title
-      @children = []
-      @anchor   = anchor
-    end
-
-    delegate :<<, to: :children
-  end
-
-  def initialize(source_html)
-    @source_html = source_html
-    @processed   = false
-    @target_html = ''
-    @headers     = []
-    @slugs       = Hash.new { |h, k| h[k] = 0 }
-  end
-
-  def html
-    parse_and_transform unless @processed
-    @target_html
-  end
-
-  def toc
-    parse_and_transform unless @processed
-    @headers
-  end
-
-  private
-
-  def parse_and_transform
-    return if @source_html.blank?
-
-    parsed_html = Nokogiri::HTML.fragment(@source_html)
-
-    parsed_html.traverse do |node|
-      next unless TARGET_ELEMENTS.include?(node.name)
-
-      anchor = node['id'] || node.text.parameterize.presence || 'sec'
-      @slugs[anchor] += 1
-      anchor = "#{anchor}-#{@slugs[anchor]}" if @slugs[anchor] > 1
-
-      node['id'] = anchor
-
-      next unless LISTED_ELEMENTS.include?(node.name)
-
-      depth          = node.name[1..-1]
-      latest_section = @headers.last
-
-      if latest_section.nil? || latest_section.depth >= depth
-        @headers << Section.new(depth, node.text, anchor)
-      else
-        latest_section << Section.new(depth, node.text, anchor)
-      end
-    end
-
-    @target_html = parsed_html.to_s
-    @processed   = true
-  end
-end
diff --git a/app/lib/translation_service.rb b/app/lib/translation_service.rb
index 285f30939..bfe5de44f 100644
--- a/app/lib/translation_service.rb
+++ b/app/lib/translation_service.rb
@@ -21,6 +21,10 @@ class TranslationService
     ENV['DEEPL_API_KEY'].present? || ENV['LIBRE_TRANSLATE_ENDPOINT'].present?
   end
 
+  def languages
+    {}
+  end
+
   def translate(_text, _source_language, _target_language)
     raise NotImplementedError
   end
diff --git a/app/lib/translation_service/deepl.rb b/app/lib/translation_service/deepl.rb
index 537fd24c0..afcb7ecb2 100644
--- a/app/lib/translation_service/deepl.rb
+++ b/app/lib/translation_service/deepl.rb
@@ -11,33 +11,59 @@ class TranslationService::DeepL < TranslationService
   end
 
   def translate(text, source_language, target_language)
-    request(text, source_language, target_language).perform do |res|
+    form = { text: text, source_lang: source_language&.upcase, target_lang: target_language, tag_handling: 'html' }
+    request(:post, '/v2/translate', form: form) do |res|
+      transform_response(res.body_with_limit)
+    end
+  end
+
+  def languages
+    source_languages = [nil] + fetch_languages('source')
+
+    # In DeepL, EN and PT are deprecated in favor of EN-GB/EN-US and PT-BR/PT-PT, so
+    # they are supported but not returned by the API.
+    target_languages = %w(en pt) + fetch_languages('target')
+
+    source_languages.index_with { |language| target_languages.without(nil, language) }
+  end
+
+  private
+
+  def fetch_languages(type)
+    request(:get, "/v2/languages?type=#{type}") do |res|
+      Oj.load(res.body_with_limit).map { |language| normalize_language(language['language']) }
+    end
+  end
+
+  def normalize_language(language)
+    subtags = language.split(/[_-]/)
+    subtags[0].downcase!
+    subtags[1]&.upcase!
+    subtags.join('-')
+  end
+
+  def request(verb, path, **options)
+    req = Request.new(verb, "#{base_url}#{path}", **options)
+    req.add_headers(Authorization: "DeepL-Auth-Key #{@api_key}")
+    req.perform do |res|
       case res.code
       when 429
         raise TooManyRequestsError
       when 456
         raise QuotaExceededError
       when 200...300
-        transform_response(res.body_with_limit)
+        yield res
       else
         raise UnexpectedResponseError
       end
     end
   end
 
-  private
-
-  def request(text, source_language, target_language)
-    req = Request.new(:post, endpoint_url, form: { text: text, source_lang: source_language&.upcase, target_lang: target_language, tag_handling: 'html' })
-    req.add_headers('Authorization': "DeepL-Auth-Key #{@api_key}")
-    req
-  end
-
-  def endpoint_url
+  def base_url
     if @plan == 'free'
-      'https://api-free.deepl.com/v2/translate'
+      'https://api-free.deepl.com'
     else
-      'https://api.deepl.com/v2/translate'
+      'https://api.deepl.com'
     end
   end
 
diff --git a/app/lib/translation_service/libre_translate.rb b/app/lib/translation_service/libre_translate.rb
index 4ebe21e45..8bb194a9c 100644
--- a/app/lib/translation_service/libre_translate.rb
+++ b/app/lib/translation_service/libre_translate.rb
@@ -9,29 +9,41 @@ class TranslationService::LibreTranslate < TranslationService
   end
 
   def translate(text, source_language, target_language)
-    request(text, source_language, target_language).perform do |res|
+    body = Oj.dump(q: text, source: source_language.presence || 'auto', target: target_language, format: 'html', api_key: @api_key)
+    request(:post, '/translate', body: body) do |res|
+      transform_response(res.body_with_limit, source_language)
+    end
+  end
+
+  def languages
+    request(:get, '/languages') do |res|
+      languages = Oj.load(res.body_with_limit).to_h do |language|
+        [language['code'], language['targets'].without(language['code'])]
+      end
+      languages[nil] = languages.values.flatten.uniq.sort
+      languages
+    end
+  end
+
+  private
+
+  def request(verb, path, **options)
+    req = Request.new(verb, "#{@base_url}#{path}", allow_local: true, **options)
+    req.add_headers('Content-Type': 'application/json')
+    req.perform do |res|
       case res.code
       when 429
         raise TooManyRequestsError
       when 403
         raise QuotaExceededError
       when 200...300
-        transform_response(res.body_with_limit, source_language)
+        yield res
       else
         raise UnexpectedResponseError
       end
     end
   end
 
-  private
-
-  def request(text, source_language, target_language)
-    body = Oj.dump(q: text, source: source_language.presence || 'auto', target: target_language, format: 'html', api_key: @api_key)
-    req = Request.new(:post, "#{@base_url}/translate", body: body, allow_local: true)
-    req.add_headers('Content-Type': 'application/json')
-    req
-  end
-
   def transform_response(str, source_language)
     json = Oj.load(str, mode: :strict)
 
diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb
deleted file mode 100644
index 260077a1c..000000000
--- a/app/lib/user_settings_decorator.rb
+++ /dev/null
@@ -1,180 +0,0 @@
-# frozen_string_literal: true
-
-class UserSettingsDecorator
-  attr_reader :user, :settings
-
-  def initialize(user)
-    @user = user
-  end
-
-  def update(settings)
-    @settings = settings
-    process_update
-  end
-
-  private
-
-  def process_update
-    user.settings['notification_emails'] = merged_notification_emails if change?('notification_emails')
-    user.settings['interactions']        = merged_interactions if change?('interactions')
-    user.settings['default_privacy']     = default_privacy_preference if change?('setting_default_privacy')
-    user.settings['default_sensitive']   = default_sensitive_preference if change?('setting_default_sensitive')
-    user.settings['default_language']    = default_language_preference if change?('setting_default_language')
-    user.settings['unfollow_modal']      = unfollow_modal_preference if change?('setting_unfollow_modal')
-    user.settings['boost_modal']         = boost_modal_preference if change?('setting_boost_modal')
-    user.settings['favourite_modal']     = favourite_modal_preference if change?('setting_favourite_modal')
-    user.settings['delete_modal']        = delete_modal_preference if change?('setting_delete_modal')
-    user.settings['auto_play_gif']       = auto_play_gif_preference if change?('setting_auto_play_gif')
-    user.settings['display_media']       = display_media_preference if change?('setting_display_media')
-    user.settings['expand_spoilers']     = expand_spoilers_preference if change?('setting_expand_spoilers')
-    user.settings['reduce_motion']       = reduce_motion_preference if change?('setting_reduce_motion')
-    user.settings['disable_swiping']     = disable_swiping_preference if change?('setting_disable_swiping')
-    user.settings['system_font_ui']      = system_font_ui_preference if change?('setting_system_font_ui')
-    user.settings['system_emoji_font']   = system_emoji_font_preference if change?('setting_system_emoji_font')
-    user.settings['noindex']             = noindex_preference if change?('setting_noindex')
-    user.settings['hide_followers_count'] = hide_followers_count_preference if change?('setting_hide_followers_count')
-    user.settings['flavour']             = flavour_preference if change?('setting_flavour')
-    user.settings['skin']                = skin_preference if change?('setting_skin')
-    user.settings['aggregate_reblogs']   = aggregate_reblogs_preference if change?('setting_aggregate_reblogs')
-    user.settings['show_application']    = show_application_preference if change?('setting_show_application')
-    user.settings['advanced_layout']     = advanced_layout_preference if change?('setting_advanced_layout')
-    user.settings['default_content_type']= default_content_type_preference if change?('setting_default_content_type')
-    user.settings['use_blurhash']        = use_blurhash_preference if change?('setting_use_blurhash')
-    user.settings['use_pending_items']   = use_pending_items_preference if change?('setting_use_pending_items')
-    user.settings['trends']              = trends_preference if change?('setting_trends')
-    user.settings['crop_images']         = crop_images_preference if change?('setting_crop_images')
-    user.settings['always_send_emails']  = always_send_emails_preference if change?('setting_always_send_emails')
-  end
-
-  def merged_notification_emails
-    user.settings['notification_emails'].merge coerced_settings('notification_emails').to_h
-  end
-
-  def merged_interactions
-    user.settings['interactions'].merge coerced_settings('interactions').to_h
-  end
-
-  def default_privacy_preference
-    settings['setting_default_privacy']
-  end
-
-  def default_sensitive_preference
-    boolean_cast_setting 'setting_default_sensitive'
-  end
-
-  def unfollow_modal_preference
-    boolean_cast_setting 'setting_unfollow_modal'
-  end
-
-  def boost_modal_preference
-    boolean_cast_setting 'setting_boost_modal'
-  end
-
-  def favourite_modal_preference
-    boolean_cast_setting 'setting_favourite_modal'
-  end
-
-  def delete_modal_preference
-    boolean_cast_setting 'setting_delete_modal'
-  end
-
-  def system_font_ui_preference
-    boolean_cast_setting 'setting_system_font_ui'
-  end
-
-  def system_emoji_font_preference
-    boolean_cast_setting 'setting_system_emoji_font'
-  end
-
-  def auto_play_gif_preference
-    boolean_cast_setting 'setting_auto_play_gif'
-  end
-
-  def display_media_preference
-    settings['setting_display_media']
-  end
-
-  def expand_spoilers_preference
-    boolean_cast_setting 'setting_expand_spoilers'
-  end
-
-  def reduce_motion_preference
-    boolean_cast_setting 'setting_reduce_motion'
-  end
-
-  def disable_swiping_preference
-    boolean_cast_setting 'setting_disable_swiping'
-  end
-
-  def noindex_preference
-    boolean_cast_setting 'setting_noindex'
-  end
-
-  def flavour_preference
-    settings['setting_flavour']
-  end
-
-  def skin_preference
-    settings['setting_skin']
-  end
-
-  def hide_followers_count_preference
-    boolean_cast_setting 'setting_hide_followers_count'
-  end
-
-  def show_application_preference
-    boolean_cast_setting 'setting_show_application'
-  end
-
-  def default_language_preference
-    settings['setting_default_language']
-  end
-
-  def aggregate_reblogs_preference
-    boolean_cast_setting 'setting_aggregate_reblogs'
-  end
-
-  def advanced_layout_preference
-    boolean_cast_setting 'setting_advanced_layout'
-  end
-
-  def default_content_type_preference
-    settings['setting_default_content_type']
-  end
-
-  def use_blurhash_preference
-    boolean_cast_setting 'setting_use_blurhash'
-  end
-
-  def use_pending_items_preference
-    boolean_cast_setting 'setting_use_pending_items'
-  end
-
-  def trends_preference
-    boolean_cast_setting 'setting_trends'
-  end
-
-  def crop_images_preference
-    boolean_cast_setting 'setting_crop_images'
-  end
-
-  def always_send_emails_preference
-    boolean_cast_setting 'setting_always_send_emails'
-  end
-
-  def boolean_cast_setting(key)
-    ActiveModel::Type::Boolean.new.cast(settings[key])
-  end
-
-  def coerced_settings(key)
-    coerce_values settings.fetch(key, {})
-  end
-
-  def coerce_values(params_hash)
-    params_hash.transform_values { |x| ActiveModel::Type::Boolean.new.cast(x) }
-  end
-
-  def change?(key)
-    !settings[key].nil?
-  end
-end
diff --git a/app/lib/user_settings_serializer.rb b/app/lib/user_settings_serializer.rb
new file mode 100644
index 000000000..10d1be04d
--- /dev/null
+++ b/app/lib/user_settings_serializer.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class UserSettingsSerializer
+  def self.load(value)
+    json = begin
+      if value.blank?
+        {}
+      else
+        Oj.load(value, symbol_keys: true)
+      end
+    end
+
+    UserSettings.new(json)
+  end
+
+  def self.dump(value)
+    Oj.dump(value.as_json)
+  end
+end
diff --git a/app/lib/vacuum/access_tokens_vacuum.rb b/app/lib/vacuum/access_tokens_vacuum.rb
index 4f3878027..7b91f74a5 100644
--- a/app/lib/vacuum/access_tokens_vacuum.rb
+++ b/app/lib/vacuum/access_tokens_vacuum.rb
@@ -9,10 +9,10 @@ class Vacuum::AccessTokensVacuum
   private
 
   def vacuum_revoked_access_tokens!
-    Doorkeeper::AccessToken.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
+    Doorkeeper::AccessToken.where.not(revoked_at: nil).where('revoked_at < NOW()').delete_all
   end
 
   def vacuum_revoked_access_grants!
-    Doorkeeper::AccessGrant.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
+    Doorkeeper::AccessGrant.where.not(revoked_at: nil).where('revoked_at < NOW()').delete_all
   end
 end
diff --git a/app/lib/validation_error_formatter.rb b/app/lib/validation_error_formatter.rb
index 3f964f739..1d3e8955b 100644
--- a/app/lib/validation_error_formatter.rb
+++ b/app/lib/validation_error_formatter.rb
@@ -19,7 +19,7 @@ class ValidationErrorFormatter
       messages = errors.messages[attribute_name]
 
       h[@aliases[attribute_name] || attribute_name] = attribute_errors.map.with_index do |error, index|
-        { error: 'ERR_' + error[:error].to_s.upcase, description: messages[index] }
+        { error: "ERR_#{error[:error].to_s.upcase}", description: messages[index] }
       end
     end
 
diff --git a/app/lib/webfinger.rb b/app/lib/webfinger.rb
index 7c0c10c33..ae8a3b1ea 100644
--- a/app/lib/webfinger.rb
+++ b/app/lib/webfinger.rb
@@ -57,6 +57,7 @@ class Webfinger
       if res.code == 200
         body = res.body_with_limit
         raise Webfinger::Error, "Request for #{@uri} returned empty response" if body.empty?
+
         body
       elsif res.code == 404 && use_fallback
         body_from_host_meta
@@ -99,7 +100,7 @@ class Webfinger
   end
 
   def standard_url
-    if @domain.end_with? ".onion"
+    if @domain.end_with? '.onion'
       "http://#{@domain}/.well-known/webfinger?resource=#{@uri}"
     else
       "https://#{@domain}/.well-known/webfinger?resource=#{@uri}"
@@ -107,7 +108,7 @@ class Webfinger
   end
 
   def host_meta_url
-    if @domain.end_with? ".onion"
+    if @domain.end_with? '.onion'
       "http://#{@domain}/.well-known/host-meta"
     else
       "https://#{@domain}/.well-known/host-meta"
diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb
index a37682eca..35f0b5fee 100644
--- a/app/mailers/application_mailer.rb
+++ b/app/mailers/application_mailer.rb
@@ -7,11 +7,17 @@ class ApplicationMailer < ActionMailer::Base
   helper :instance
   helper :formatting
 
+  after_action :set_autoreply_headers!
+
   protected
 
-  def locale_for_account(account)
-    I18n.with_locale(account.user_locale || I18n.default_locale) do
-      yield
-    end
+  def locale_for_account(account, &block)
+    I18n.with_locale(account.user_locale || I18n.default_locale, &block)
+  end
+
+  def set_autoreply_headers!
+    headers['Precedence'] = 'list'
+    headers['X-Auto-Response-Suppress'] = 'All'
+    headers['Auto-Submitted'] = 'auto-generated'
   end
 end
diff --git a/app/models/account.rb b/app/models/account.rb
index 64a35aca0..215f8ce09 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: accounts
@@ -52,7 +53,7 @@
 #
 
 class Account < ApplicationRecord
-  self.ignored_columns = %w(
+  self.ignored_columns += %w(
     subscription_expires_at
     secret
     remote_url
@@ -82,14 +83,14 @@ class Account < ApplicationRecord
   MAX_NOTE_LENGTH = (ENV['MAX_BIO_CHARS'] || 500).to_i
   DEFAULT_FIELDS_SIZE = (ENV['MAX_PROFILE_FIELDS'] || 4).to_i
 
-  enum protocol: [:ostatus, :activitypub]
-  enum suspension_origin: [:local, :remote], _prefix: true
+  enum protocol: { ostatus: 0, activitypub: 1 }
+  enum suspension_origin: { local: 0, remote: 1 }, _prefix: true
 
   validates :username, presence: true
   validates_with UniqueUsernameValidator, if: -> { will_save_change_to_username? }
 
-  # Remote user validations
-  validates :username, format: { with: USERNAME_ONLY_RE }, if: -> { !local? && will_save_change_to_username? }
+  # Remote user validations, also applies to internal actors
+  validates :username, format: { with: USERNAME_ONLY_RE }, if: -> { (!local? || actor_type == 'Application') && will_save_change_to_username? }
 
   # Local user validations
   validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? && actor_type != 'Application' }
@@ -111,7 +112,7 @@ class Account < ApplicationRecord
   scope :bots, -> { where(actor_type: %w(Application Service)) }
   scope :groups, -> { where(actor_type: 'Group') }
   scope :alphabetic, -> { order(domain: :asc, username: :asc) }
-  scope :matches_username, ->(value) { where(arel_table[:username].matches("#{value}%")) }
+  scope :matches_username, ->(value) { where('lower((username)::text) LIKE lower(?)', "#{value}%") }
   scope :matches_display_name, ->(value) { where(arel_table[:display_name].matches("#{value}%")) }
   scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
   scope :without_unapproved, -> { left_outer_joins(:user).remote.or(left_outer_joins(:user).merge(User.approved.confirmed)) }
@@ -125,6 +126,8 @@ class Account < ApplicationRecord
   scope :not_excluded_by_account, ->(account) { where.not(id: account.excluded_from_timeline_account_ids) }
   scope :not_domain_blocked_by_account, ->(account) { where(arel_table[:domain].eq(nil).or(arel_table[:domain].not_in(account.excluded_from_timeline_domains))) }
 
+  after_update_commit :trigger_update_webhooks
+
   delegate :email,
            :unconfirmed_email,
            :current_sign_in_at,
@@ -313,9 +316,7 @@ class Account < ApplicationRecord
 
         previous = old_fields.find { |item| item['value'] == attr[:value] }
 
-        if previous && previous['verified_at'].present?
-          attr[:verified_at] = previous['verified_at']
-        end
+        attr[:verified_at] = previous['verified_at'] if previous && previous['verified_at'].present?
 
         fields << attr
       end
@@ -457,13 +458,12 @@ class Account < ApplicationRecord
       return [] if text.blank?
 
       text.scan(MENTION_RE).map { |match| match.first.split('@', 2) }.uniq.filter_map do |(username, domain)|
-        domain = begin
-          if TagManager.instance.local_domain?(domain)
-            nil
-          else
-            TagManager.instance.normalize_domain(domain)
-          end
-        end
+        domain = if TagManager.instance.local_domain?(domain)
+                   nil
+                 else
+                   TagManager.instance.normalize_domain(domain)
+                 end
+
         EntityCache.instance.mention(username, domain)
       end
     end
@@ -538,6 +538,7 @@ class Account < ApplicationRecord
 
   def ensure_keys!
     return unless local? && private_key.blank? && public_key.blank?
+
     generate_keys
     save!
   end
@@ -590,4 +591,9 @@ class Account < ApplicationRecord
 
     CanonicalEmailBlock.where(reference_account: self).delete_all
   end
+
+  # NOTE: the `account.created` webhook is triggered by the `User` model, not `Account`.
+  def trigger_update_webhooks
+    TriggerWebhookWorker.perform_async('account.updated', 'Account', id) if local?
+  end
 end
diff --git a/app/models/account/field.rb b/app/models/account/field.rb
index 4db4cac30..2bada6954 100644
--- a/app/models/account/field.rb
+++ b/app/models/account/field.rb
@@ -14,8 +14,8 @@ class Account::Field < ActiveModelSerializers::Model
     @account        = account
 
     super(
-      name:        sanitize(attributes['name']),
-      value:       sanitize(attributes['value']),
+      name: sanitize(attributes['name']),
+      value: sanitize(attributes['value']),
       verified_at: attributes['verified_at']&.to_datetime,
     )
   end
@@ -25,13 +25,11 @@ class Account::Field < ActiveModelSerializers::Model
   end
 
   def value_for_verification
-    @value_for_verification ||= begin
-      if account.local?
-        value
-      else
-        extract_url_from_html
-      end
-    end
+    @value_for_verification ||= if account.local?
+                                  value
+                                else
+                                  extract_url_from_html
+                                end
   end
 
   def verifiable?
diff --git a/app/models/account_conversation.rb b/app/models/account_conversation.rb
index 45e74bbeb..b3ddc04c1 100644
--- a/app/models/account_conversation.rb
+++ b/app/models/account_conversation.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: account_conversations
@@ -107,6 +108,7 @@ class AccountConversation < ApplicationRecord
 
   def push_to_streaming_api
     return if destroyed? || !subscribed_to_timeline?
+
     PushConversationWorker.perform_async(id)
   end
 
diff --git a/app/models/account_domain_block.rb b/app/models/account_domain_block.rb
index 3aaffde9a..af1e6a68d 100644
--- a/app/models/account_domain_block.rb
+++ b/app/models/account_domain_block.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: account_domain_blocks
diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb
index d27bb46fc..1666ea883 100644
--- a/app/models/account_filter.rb
+++ b/app/models/account_filter.rb
@@ -17,13 +17,13 @@ class AccountFilter
   attr_reader :params
 
   def initialize(params)
-    @params = params
+    @params = params.to_h.symbolize_keys
   end
 
   def results
     scope = Account.includes(:account_stat, user: [:ips, :invite_request]).without_instance_actor.reorder(nil)
 
-    params.each do |key, value|
+    relevant_params.each do |key, value|
       next if key.to_s == 'page'
 
       scope.merge!(scope_for(key, value)) if value.present?
@@ -34,6 +34,16 @@ class AccountFilter
 
   private
 
+  def relevant_params
+    params.tap do |args|
+      args.delete(:origin) if origin_is_remote_and_domain_present?
+    end
+  end
+
+  def origin_is_remote_and_domain_present?
+    params[:origin] == 'remote' && params[:by_domain].present?
+  end
+
   def scope_for(key, value)
     case key.to_s
     when 'origin'
@@ -94,7 +104,15 @@ class AccountFilter
   def order_scope(value)
     case value.to_s
     when 'active'
-      accounts_with_users.left_joins(:account_stat).order(Arel.sql('coalesce(users.current_sign_in_at, account_stats.last_status_at, to_timestamp(0)) desc, accounts.id desc'))
+      accounts_with_users
+        .left_joins(:account_stat)
+        .order(
+          Arel.sql(
+            <<~SQL.squish
+              COALESCE(users.current_sign_in_at, account_stats.last_status_at, to_timestamp(0)) DESC, accounts.id DESC
+            SQL
+          )
+        )
     when 'recent'
       Account.recent
     else
diff --git a/app/models/account_moderation_note.rb b/app/models/account_moderation_note.rb
index 22e312bb2..ff399bab0 100644
--- a/app/models/account_moderation_note.rb
+++ b/app/models/account_moderation_note.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: account_moderation_notes
diff --git a/app/models/account_note.rb b/app/models/account_note.rb
index b338bc92f..9bc704d98 100644
--- a/app/models/account_note.rb
+++ b/app/models/account_note.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: account_notes
diff --git a/app/models/account_pin.rb b/app/models/account_pin.rb
index b51d3d4cd..6c78e8c44 100644
--- a/app/models/account_pin.rb
+++ b/app/models/account_pin.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: account_pins
diff --git a/app/models/account_stat.rb b/app/models/account_stat.rb
index a5d71a5b8..0fea7732e 100644
--- a/app/models/account_stat.rb
+++ b/app/models/account_stat.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: account_stats
@@ -15,7 +16,7 @@
 
 class AccountStat < ApplicationRecord
   self.locking_column = nil
-  self.ignored_columns = %w(lock_version)
+  self.ignored_columns += %w(lock_version)
 
   belongs_to :account, inverse_of: :account_stat
 
diff --git a/app/models/account_statuses_cleanup_policy.rb b/app/models/account_statuses_cleanup_policy.rb
index 49adc6ad0..14ce00abb 100644
--- a/app/models/account_statuses_cleanup_policy.rb
+++ b/app/models/account_statuses_cleanup_policy.rb
@@ -122,9 +122,7 @@ class AccountStatusesCleanupPolicy < ApplicationRecord
       # may need to be deleted, so we'll have to start again.
       redis.del("account_cleanup:#{account.id}")
     end
-    if EXCEPTION_THRESHOLDS.map { |name| attribute_change_to_be_saved(name) }.compact.any? { |old, new| old.present? && (new.nil? || new > old) }
-      redis.del("account_cleanup:#{account.id}")
-    end
+    redis.del("account_cleanup:#{account.id}") if EXCEPTION_THRESHOLDS.map { |name| attribute_change_to_be_saved(name) }.compact.any? { |old, new| old.present? && (new.nil? || new > old) }
   end
 
   def validate_local_account
@@ -141,9 +139,7 @@ class AccountStatusesCleanupPolicy < ApplicationRecord
     # has switched to snowflake IDs significantly over 2 years ago anyway.
     snowflake_id = Mastodon::Snowflake.id_at(min_status_age.seconds.ago, with_random: false)
 
-    if max_id.nil? || snowflake_id < max_id
-      max_id = snowflake_id
-    end
+    max_id = snowflake_id if max_id.nil? || snowflake_id < max_id
 
     Status.where(Status.arel_table[:id].lteq(max_id))
   end
diff --git a/app/models/account_suggestions/setting_source.rb b/app/models/account_suggestions/setting_source.rb
index be9eff233..7b8873e0c 100644
--- a/app/models/account_suggestions/setting_source.rb
+++ b/app/models/account_suggestions/setting_source.rb
@@ -54,7 +54,7 @@ class AccountSuggestions::SettingSource < AccountSuggestions::Source
 
       next if username.blank?
 
-      [username, domain]
+      [username.downcase, domain&.downcase]
     end.compact
   end
 
@@ -63,6 +63,6 @@ class AccountSuggestions::SettingSource < AccountSuggestions::Source
   end
 
   def to_ordered_list_key(account)
-    [account.username, account.domain]
+    [account.username.downcase, account.domain&.downcase]
   end
 end
diff --git a/app/models/account_summary.rb b/app/models/account_summary.rb
index 3a3cebc55..0d8835b83 100644
--- a/app/models/account_summary.rb
+++ b/app/models/account_summary.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: account_summaries
diff --git a/app/models/account_warning.rb b/app/models/account_warning.rb
index 961a078b9..4f8cc5320 100644
--- a/app/models/account_warning.rb
+++ b/app/models/account_warning.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: account_warnings
@@ -17,15 +18,17 @@
 
 class AccountWarning < ApplicationRecord
   enum action: {
-    none:                       0,
-    disable:                    1_000,
+    none: 0,
+    disable: 1_000,
     mark_statuses_as_sensitive: 1_250,
-    delete_statuses:            1_500,
-    sensitive:                  2_000,
-    silence:                    3_000,
-    suspend:                    4_000,
+    delete_statuses: 1_500,
+    sensitive: 2_000,
+    silence: 3_000,
+    suspend: 4_000,
   }, _suffix: :action
 
+  before_validation :before_validate
+
   belongs_to :account, inverse_of: :account_warnings
   belongs_to :target_account, class_name: 'Account', inverse_of: :strikes
   belongs_to :report, optional: true
@@ -47,4 +50,10 @@ class AccountWarning < ApplicationRecord
   def to_log_human_identifier
     target_account.acct
   end
+
+  private
+
+  def before_validate
+    self.text = '' if text.blank?
+  end
 end
diff --git a/app/models/admin/account_action.rb b/app/models/admin/account_action.rb
index bce0d6e17..1ce28f5c8 100644
--- a/app/models/admin/account_action.rb
+++ b/app/models/admin/account_action.rb
@@ -166,13 +166,11 @@ class Admin::AccountAction
   end
 
   def reports
-    @reports ||= begin
-      if type == 'none'
-        with_report? ? [report] : []
-      else
-        Report.where(target_account: target_account).unresolved
-      end
-    end
+    @reports ||= if type == 'none'
+                   with_report? ? [report] : []
+                 else
+                   Report.where(target_account: target_account).unresolved
+                 end
   end
 
   def warning_preset
diff --git a/app/models/admin/action_log.rb b/app/models/admin/action_log.rb
index 4fa8008f5..f2c121d75 100644
--- a/app/models/admin/action_log.rb
+++ b/app/models/admin/action_log.rb
@@ -17,7 +17,7 @@
 #
 
 class Admin::ActionLog < ApplicationRecord
-  self.ignored_columns = %w(
+  self.ignored_columns += %w(
     recorded_changes
   )
 
diff --git a/app/models/admin/import.rb b/app/models/admin/import.rb
index 79c0722d5..0fd4bdb82 100644
--- a/app/models/admin/import.rb
+++ b/app/models/admin/import.rb
@@ -1,5 +1,7 @@
 # frozen_string_literal: true
 
+require 'csv'
+
 # A non-activerecord helper class for csv upload
 class Admin::Import
   include ActiveModel::Model
@@ -15,17 +17,47 @@ class Admin::Import
     data.original_filename
   end
 
+  def csv_rows
+    csv_data.rewind
+
+    csv_data.take(ROWS_PROCESSING_LIMIT + 1)
+  end
+
   private
 
-  def validate_data
-    return if data.blank?
+  def csv_data
+    return @csv_data if defined?(@csv_data)
 
-    csv_data = CSV.read(data.path, encoding: 'UTF-8')
+    csv_converter = lambda do |field, field_info|
+      case field_info.header
+      when '#domain', '#public_comment'
+        field&.strip
+      when '#severity'
+        field&.strip&.to_sym
+      when '#reject_media', '#reject_reports', '#obfuscate'
+        ActiveModel::Type::Boolean.new.cast(field)
+      else
+        field
+      end
+    end
 
-    row_count  = csv_data.size
-    row_count -= 1 if csv_data.first&.first == '#domain'
+    @csv_data = CSV.open(data.path, encoding: 'UTF-8', skip_blanks: true, headers: true, converters: csv_converter)
+    @csv_data.take(1) # Ensure the headers are read
+    @csv_data = CSV.open(data.path, encoding: 'UTF-8', skip_blanks: true, headers: ['#domain'], converters: csv_converter) unless @csv_data.headers&.first == '#domain'
+    @csv_data
+  end
+
+  def csv_row_count
+    return @csv_row_count if defined?(@csv_row_count)
+
+    csv_data.rewind
+    @csv_row_count = csv_data.take(ROWS_PROCESSING_LIMIT + 2).count
+  end
+
+  def validate_data
+    return if data.nil?
 
-    errors.add(:data, I18n.t('imports.errors.over_rows_processing_limit', count: ROWS_PROCESSING_LIMIT)) if row_count > ROWS_PROCESSING_LIMIT
+    errors.add(:data, I18n.t('imports.errors.over_rows_processing_limit', count: ROWS_PROCESSING_LIMIT)) if csv_row_count > ROWS_PROCESSING_LIMIT
   rescue CSV::MalformedCSVError => e
     errors.add(:data, I18n.t('imports.errors.invalid_csv_file', error: e.message))
   end
diff --git a/app/models/admin/status_batch_action.rb b/app/models/admin/status_batch_action.rb
index 39cd7d0eb..b8bdec722 100644
--- a/app/models/admin/status_batch_action.rb
+++ b/app/models/admin/status_batch_action.rb
@@ -6,7 +6,8 @@ class Admin::StatusBatchAction
   include Authorization
 
   attr_accessor :current_account, :type,
-                :status_ids, :report_id
+                :status_ids, :report_id,
+                :text
 
   attr_reader :send_email_notification
 
@@ -57,7 +58,8 @@ class Admin::StatusBatchAction
         action: :delete_statuses,
         account: current_account,
         report: report,
-        status_ids: status_ids
+        status_ids: status_ids,
+        text: text
       )
 
       statuses.each { |status| Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true) } unless target_account.local?
@@ -95,7 +97,8 @@ class Admin::StatusBatchAction
       action: :mark_statuses_as_sensitive,
       account: current_account,
       report: report,
-      status_ids: status_ids
+      status_ids: status_ids,
+      text: text
     )
 
     UserMailer.warning(target_account.user, @warning).deliver_later! if warnable?
diff --git a/app/models/announcement.rb b/app/models/announcement.rb
index 4b2cb4c6d..339f5ae70 100644
--- a/app/models/announcement.rb
+++ b/app/models/announcement.rb
@@ -20,7 +20,7 @@
 class Announcement < ApplicationRecord
   scope :unpublished, -> { where(published: false) }
   scope :published, -> { where(published: true) }
-  scope :without_muted, ->(account) { joins("LEFT OUTER JOIN announcement_mutes ON announcement_mutes.announcement_id = announcements.id AND announcement_mutes.account_id = #{account.id}").where('announcement_mutes.id IS NULL') }
+  scope :without_muted, ->(account) { joins("LEFT OUTER JOIN announcement_mutes ON announcement_mutes.announcement_id = announcements.id AND announcement_mutes.account_id = #{account.id}").where(announcement_mutes: { id: nil }) }
   scope :chronological, -> { order(Arel.sql('COALESCE(announcements.starts_at, announcements.scheduled_at, announcements.published_at, announcements.created_at) ASC')) }
   scope :reverse_chronological, -> { order(Arel.sql('COALESCE(announcements.starts_at, announcements.scheduled_at, announcements.published_at, announcements.created_at) DESC')) }
 
@@ -54,13 +54,11 @@ class Announcement < ApplicationRecord
   end
 
   def statuses
-    @statuses ||= begin
-      if status_ids.nil?
-        []
-      else
-        Status.where(id: status_ids, visibility: [:public, :unlisted])
-      end
-    end
+    @statuses ||= if status_ids.nil?
+                    []
+                  else
+                    Status.where(id: status_ids, visibility: [:public, :unlisted])
+                  end
   end
 
   def tags
diff --git a/app/models/backup.rb b/app/models/backup.rb
index d242fd62c..5feb31d7d 100644
--- a/app/models/backup.rb
+++ b/app/models/backup.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: backups
@@ -17,6 +18,6 @@
 class Backup < ApplicationRecord
   belongs_to :user, inverse_of: :backups
 
-  has_attached_file :dump
-  do_not_validate_attachment_file_type :dump
+  has_attached_file :dump, s3_permissions: ->(*) { ENV['S3_PERMISSION'] == '' ? nil : 'private' }
+  validates_attachment_content_type :dump, content_type: /\Aapplication/
 end
diff --git a/app/models/block.rb b/app/models/block.rb
index bf3e07600..b42c1569b 100644
--- a/app/models/block.rb
+++ b/app/models/block.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: blocks
diff --git a/app/models/bookmark.rb b/app/models/bookmark.rb
index 6334ef0df..04b660372 100644
--- a/app/models/bookmark.rb
+++ b/app/models/bookmark.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: bookmarks
diff --git a/app/models/canonical_email_block.rb b/app/models/canonical_email_block.rb
index 1eb69ac67..d09df6f5e 100644
--- a/app/models/canonical_email_block.rb
+++ b/app/models/canonical_email_block.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: canonical_email_blocks
diff --git a/app/models/concerns/account_finder_concern.rb b/app/models/concerns/account_finder_concern.rb
index e8b804934..37c3b8895 100644
--- a/app/models/concerns/account_finder_concern.rb
+++ b/app/models/concerns/account_finder_concern.rb
@@ -13,9 +13,11 @@ module AccountFinderConcern
     end
 
     def representative
-      Account.find(-99).tap(&:ensure_keys!)
+      actor = Account.find(-99).tap(&:ensure_keys!)
+      actor.update!(username: 'mastodon.internal') if actor.username.include?(':')
+      actor
     rescue ActiveRecord::RecordNotFound
-      Account.create!(id: -99, actor_type: 'Application', locked: true, username: Rails.configuration.x.local_domain)
+      Account.create!(id: -99, actor_type: 'Application', locked: true, username: 'mastodon.internal')
     end
 
     def find_local(username)
diff --git a/app/models/concerns/account_interactions.rb b/app/models/concerns/account_interactions.rb
index de8bf338f..48ab1349d 100644
--- a/app/models/concerns/account_interactions.rb
+++ b/app/models/concerns/account_interactions.rb
@@ -151,9 +151,7 @@ module AccountInteractions
     remove_potential_friendship(other_account)
 
     # When toggling a mute between hiding and allowing notifications, the mute will already exist, so the find_or_create_by! call will return the existing Mute without updating the hide_notifications attribute. Therefore, we check that hide_notifications? is what we want and set it if it isn't.
-    if mute.hide_notifications? != notifications
-      mute.update!(hide_notifications: notifications)
-    end
+    mute.update!(hide_notifications: notifications) if mute.hide_notifications? != notifications
 
     mute
   end
@@ -280,7 +278,7 @@ module AccountInteractions
       followers.where(Account.arel_table[:uri].matches("#{Account.sanitize_sql_like(url_prefix)}/%", false, true)).or(followers.where(uri: url_prefix)).pluck_each(:uri) do |uri|
         Xorcist.xor!(digest, Digest::SHA256.digest(uri))
       end
-      digest.unpack('H*')[0]
+      digest.unpack1('H*')
     end
   end
 
@@ -290,10 +288,25 @@ module AccountInteractions
       followers.where(domain: nil).pluck_each(:username) do |username|
         Xorcist.xor!(digest, Digest::SHA256.digest(ActivityPub::TagManager.instance.uri_for_username(username)))
       end
-      digest.unpack('H*')[0]
+      digest.unpack1('H*')
     end
   end
 
+  def relations_map(account_ids, domains = nil, **options)
+    relations = {
+      blocked_by: Account.blocked_by_map(account_ids, id),
+      following: Account.following_map(account_ids, id),
+    }
+
+    return relations if options[:skip_blocking_and_muting]
+
+    relations.merge!({
+      blocking: Account.blocking_map(account_ids, id),
+      muting: Account.muting_map(account_ids, id),
+      domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, id),
+    })
+  end
+
   private
 
   def remove_potential_friendship(other_account)
diff --git a/app/models/concerns/account_merging.rb b/app/models/concerns/account_merging.rb
index 8161761fb..41071633d 100644
--- a/app/models/concerns/account_merging.rb
+++ b/app/models/concerns/account_merging.rb
@@ -21,11 +21,9 @@ module AccountMerging
 
     owned_classes.each do |klass|
       klass.where(account_id: other_account.id).find_each do |record|
-        begin
-          record.update_attribute(:account_id, id)
-        rescue ActiveRecord::RecordNotUnique
-          next
-        end
+        record.update_attribute(:account_id, id)
+      rescue ActiveRecord::RecordNotUnique
+        next
       end
     end
 
@@ -36,11 +34,9 @@ module AccountMerging
 
     target_classes.each do |klass|
       klass.where(target_account_id: other_account.id).find_each do |record|
-        begin
-          record.update_attribute(:target_account_id, id)
-        rescue ActiveRecord::RecordNotUnique
-          next
-        end
+        record.update_attribute(:target_account_id, id)
+      rescue ActiveRecord::RecordNotUnique
+        next
       end
     end
 
diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb
index 01fae4236..d44c22438 100644
--- a/app/models/concerns/attachmentable.rb
+++ b/app/models/concerns/attachmentable.rb
@@ -5,7 +5,7 @@ require 'mime/types/columnar'
 module Attachmentable
   extend ActiveSupport::Concern
 
-  MAX_MATRIX_LIMIT = 16_777_216 # 4096x4096px or approx. 16MB
+  MAX_MATRIX_LIMIT = 33_177_600 # 7680x4320px or approx. 847MB in RAM
   GIF_MATRIX_LIMIT = 921_600    # 1280x720px
 
   # For some file extensions, there exist different content
diff --git a/app/models/concerns/expireable.rb b/app/models/concerns/expireable.rb
index 4d902abcb..c64fc7d80 100644
--- a/app/models/concerns/expireable.rb
+++ b/app/models/concerns/expireable.rb
@@ -17,7 +17,7 @@ module Expireable
     end
 
     def expires_in=(interval)
-      self.expires_at = interval.present? ? interval.to_i.seconds.from_now : nil 
+      self.expires_at = interval.present? ? interval.to_i.seconds.from_now : nil
       @expires_in     = interval
     end
 
diff --git a/app/models/concerns/has_user_settings.rb b/app/models/concerns/has_user_settings.rb
new file mode 100644
index 000000000..0e9d4e1cd
--- /dev/null
+++ b/app/models/concerns/has_user_settings.rb
@@ -0,0 +1,173 @@
+# frozen_string_literal: true
+
+module HasUserSettings
+  extend ActiveSupport::Concern
+
+  included do
+    serialize :settings, UserSettingsSerializer
+  end
+
+  def settings_attributes=(attributes)
+    settings.update(attributes)
+  end
+
+  def prefers_noindex?
+    settings['noindex']
+  end
+
+  def preferred_posting_language
+    valid_locale_cascade(settings['default_language'], locale, I18n.locale)
+  end
+
+  def setting_auto_play_gif
+    settings['web.auto_play']
+  end
+
+  def setting_default_sensitive
+    settings['default_sensitive']
+  end
+
+  def setting_unfollow_modal
+    settings['web.unfollow_modal']
+  end
+
+  def setting_boost_modal
+    settings['web.reblog_modal']
+  end
+
+  def setting_delete_modal
+    settings['web.delete_modal']
+  end
+
+  def setting_favourite_modal
+    settings['web.favourite_modal']
+  end
+
+  def setting_reduce_motion
+    settings['web.reduce_motion']
+  end
+
+  def setting_system_font_ui
+    settings['web.use_system_font']
+  end
+
+  def setting_system_emoji_font
+    settings['web.use_system_emoji_font']
+  end
+
+  def setting_noindex
+    settings['noindex']
+  end
+
+  def setting_flavour
+    settings['flavour']
+  end
+
+  def setting_skin
+    settings['skin']
+  end
+
+  def setting_display_media
+    settings['web.display_media']
+  end
+
+  def setting_expand_spoilers
+    settings['web.expand_content_warnings']
+  end
+
+  def setting_default_language
+    settings['default_language']
+  end
+
+  def setting_aggregate_reblogs
+    settings['aggregate_reblogs']
+  end
+
+  def setting_show_application
+    settings['show_application']
+  end
+
+  def setting_advanced_layout
+    settings['web.advanced_layout']
+  end
+
+  def setting_use_blurhash
+    settings['web.use_blurhash']
+  end
+
+  def setting_use_pending_items
+    settings['web.use_pending_items']
+  end
+
+  def setting_trends
+    settings['web.trends']
+  end
+
+  def setting_crop_images
+    settings['web.crop_images']
+  end
+
+  def setting_disable_swiping
+    settings['web.disable_swiping']
+  end
+
+  def setting_always_send_emails
+    settings['always_send_emails']
+  end
+
+  def setting_default_privacy
+    settings['default_privacy'] || (account.locked? ? 'private' : 'public')
+  end
+
+  def setting_default_content_type
+    settings['default_content_type']
+  end
+
+  def setting_hide_followers_count
+    settings['hide_followers_count']
+  end
+
+  def allows_report_emails?
+    settings['notification_emails.report']
+  end
+
+  def allows_pending_account_emails?
+    settings['notification_emails.pending_account']
+  end
+
+  def allows_appeal_emails?
+    settings['notification_emails.appeal']
+  end
+
+  def allows_trends_review_emails?
+    settings['notification_emails.trends']
+  end
+
+  def allows_trending_tags_review_emails?
+    settings['notification_emails.trends']
+  end
+
+  def allows_trending_links_review_emails?
+    settings['notification_emails.link_trends']
+  end
+
+  def allows_trending_statuses_review_emails?
+    settings['notification_emails.status_trends']
+  end
+
+  def aggregates_reblogs?
+    settings['aggregate_reblogs']
+  end
+
+  def shows_application?
+    settings['show_application']
+  end
+
+  def show_all_media?
+    settings['web.display_media'] == 'show_all'
+  end
+
+  def hide_all_media?
+    settings['web.display_media'] == 'hide_all'
+  end
+end
diff --git a/app/models/concerns/ldap_authenticable.rb b/app/models/concerns/ldap_authenticable.rb
index dc5abcd5a..775df0817 100644
--- a/app/models/concerns/ldap_authenticable.rb
+++ b/app/models/concerns/ldap_authenticable.rb
@@ -6,7 +6,7 @@ module LdapAuthenticable
   class_methods do
     def authenticate_with_ldap(params = {})
       ldap   = Net::LDAP.new(ldap_options)
-      filter = format(Devise.ldap_search_filter, uid: Devise.ldap_uid, mail: Devise.ldap_mail, email: params[:email])
+      filter = format(Devise.ldap_search_filter, uid: Devise.ldap_uid, mail: Devise.ldap_mail, email: Net::LDAP::Filter.escape(params[:email]))
 
       if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: filter, password: params[:password]))
         ldap_get_user(user_info.first)
diff --git a/app/models/concerns/omniauthable.rb b/app/models/concerns/omniauthable.rb
index feac0a1f5..41eae215b 100644
--- a/app/models/concerns/omniauthable.rb
+++ b/app/models/concerns/omniauthable.rb
@@ -56,14 +56,12 @@ module Omniauthable
       user = User.new(user_params_from_auth(email, auth))
 
       begin
-        if /\A#{URI::DEFAULT_PARSER.make_regexp(%w(http https))}\z/.match?(auth.info.image)
-          user.account.avatar_remote_url = auth.info.image
-        end
+        user.account.avatar_remote_url = auth.info.image if /\A#{URI::DEFAULT_PARSER.make_regexp(%w(http https))}\z/.match?(auth.info.image)
       rescue Mastodon::UnexpectedResponseError
         user.account.avatar_remote_url = nil
       end
 
-      user.skip_confirmation! if email_is_verified
+      user.confirm! if email_is_verified
       user.save!
       user
     end
@@ -97,8 +95,7 @@ module Omniauthable
     def ensure_valid_username(starting_username)
       starting_username = starting_username.split('@')[0]
       temp_username = starting_username.gsub(/[^a-z0-9_]+/i, '')
-      validated_username = temp_username.truncate(30, omission: '')
-      validated_username
+      temp_username.truncate(30, omission: '')
     end
   end
 end
diff --git a/app/models/concerns/paginable.rb b/app/models/concerns/paginable.rb
index 62e39f671..b76e78c1e 100644
--- a/app/models/concerns/paginable.rb
+++ b/app/models/concerns/paginable.rb
@@ -4,7 +4,7 @@ module Paginable
   extend ActiveSupport::Concern
 
   included do
-    scope :paginate_by_max_id, ->(limit, max_id = nil, since_id = nil) {
+    scope :paginate_by_max_id, lambda { |limit, max_id = nil, since_id = nil|
       query = order(arel_table[:id].desc).limit(limit)
       query = query.where(arel_table[:id].lt(max_id)) if max_id.present?
       query = query.where(arel_table[:id].gt(since_id)) if since_id.present?
@@ -14,7 +14,7 @@ module Paginable
     # Differs from :paginate_by_max_id in that it gives the results immediately following min_id,
     # whereas since_id gives the items with largest id, but with since_id as a cutoff.
     # Results will be in ascending order by id.
-    scope :paginate_by_min_id, ->(limit, min_id = nil, max_id = nil) {
+    scope :paginate_by_min_id, lambda { |limit, min_id = nil, max_id = nil|
       query = reorder(arel_table[:id]).limit(limit)
       query = query.where(arel_table[:id].gt(min_id)) if min_id.present?
       query = query.where(arel_table[:id].lt(max_id)) if max_id.present?
diff --git a/app/models/concerns/pam_authenticable.rb b/app/models/concerns/pam_authenticable.rb
index 6169d4dfa..f97f986a4 100644
--- a/app/models/concerns/pam_authenticable.rb
+++ b/app/models/concerns/pam_authenticable.rb
@@ -42,13 +42,11 @@ module PamAuthenticable
     def self.pam_get_user(attributes = {})
       return nil unless attributes[:email]
 
-      resource = begin
-        if Devise.check_at_sign && !attributes[:email].index('@')
-          joins(:account).find_by(accounts: { username: attributes[:email] })
-        else
-          find_by(email: attributes[:email])
-        end
-      end
+      resource = if Devise.check_at_sign && !attributes[:email].index('@')
+                   joins(:account).find_by(accounts: { username: attributes[:email] })
+                 else
+                   find_by(email: attributes[:email])
+                 end
 
       if resource.nil?
         resource = new(email: attributes[:email], agreement: true)
diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb
index ffe8a7565..cb8f46e68 100644
--- a/app/models/concerns/remotable.rb
+++ b/app/models/concerns/remotable.rb
@@ -27,11 +27,11 @@ module Remotable
             public_send("#{attachment_name}=", ResponseWithLimit.new(response, limit))
           end
         rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError => e
-          Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
+          Rails.logger.debug { "Error fetching remote #{attachment_name}: #{e}" }
           public_send("#{attachment_name}=", nil) if public_send("#{attachment_name}_file_name").present?
           raise e unless suppress_errors
         rescue Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Paperclip::Error, Mastodon::DimensionsValidationError, Mastodon::StreamValidationError => e
-          Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
+          Rails.logger.debug { "Error fetching remote #{attachment_name}: #{e}" }
           public_send("#{attachment_name}=", nil) if public_send("#{attachment_name}_file_name").present?
         end
 
diff --git a/app/models/concerns/status_threading_concern.rb b/app/models/concerns/status_threading_concern.rb
index 8b628beea..2ca3b66c2 100644
--- a/app/models/concerns/status_threading_concern.rb
+++ b/app/models/concerns/status_threading_concern.rb
@@ -79,7 +79,7 @@ module StatusThreadingConcern
     statuses    = Status.with_accounts(ids).to_a
     account_ids = statuses.map(&:account_id).uniq
     domains     = statuses.filter_map(&:account_domain).uniq
-    relations   = relations_map_for_account(account, account_ids, domains)
+    relations   = account&.relations_map(account_ids, domains) || {}
 
     statuses.reject! { |status| StatusFilter.new(status, account, relations).filtered? }
 
@@ -108,16 +108,4 @@ module StatusThreadingConcern
 
     arr
   end
-
-  def relations_map_for_account(account, account_ids, domains)
-    return {} if account.nil?
-
-    {
-      blocking: Account.blocking_map(account_ids, account.id),
-      blocked_by: Account.blocked_by_map(account_ids, account.id),
-      muting: Account.muting_map(account_ids, account.id),
-      following: Account.following_map(account_ids, account.id),
-      domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
-    }
-  end
 end
diff --git a/app/models/conversation.rb b/app/models/conversation.rb
index 4dfaea889..5de259962 100644
--- a/app/models/conversation.rb
+++ b/app/models/conversation.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: conversations
diff --git a/app/models/conversation_mute.rb b/app/models/conversation_mute.rb
index 52c1a33e0..31f8e1966 100644
--- a/app/models/conversation_mute.rb
+++ b/app/models/conversation_mute.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: conversation_mutes
diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb
index fc8d3aed3..b5a07a5a0 100644
--- a/app/models/custom_emoji.rb
+++ b/app/models/custom_emoji.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: custom_emojis
diff --git a/app/models/custom_filter.rb b/app/models/custom_filter.rb
index 5a4a974be..0f4fd78cb 100644
--- a/app/models/custom_filter.rb
+++ b/app/models/custom_filter.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: custom_filters
@@ -14,7 +15,7 @@
 #
 
 class CustomFilter < ApplicationRecord
-  self.ignored_columns = %w(whole_word irreversible)
+  self.ignored_columns += %w(whole_word irreversible)
 
   alias_attribute :title, :phrase
   alias_attribute :filter_action, :action
@@ -30,11 +31,11 @@ class CustomFilter < ApplicationRecord
   include Expireable
   include Redisable
 
-  enum action: [:warn, :hide], _suffix: :action
+  enum action: { warn: 0, hide: 1 }, _suffix: :action
 
   belongs_to :account
-  has_many :keywords, class_name: 'CustomFilterKeyword', foreign_key: :custom_filter_id, inverse_of: :custom_filter, dependent: :destroy
-  has_many :statuses, class_name: 'CustomFilterStatus', foreign_key: :custom_filter_id, inverse_of: :custom_filter, dependent: :destroy
+  has_many :keywords, class_name: 'CustomFilterKeyword', inverse_of: :custom_filter, dependent: :destroy
+  has_many :statuses, class_name: 'CustomFilterStatus', inverse_of: :custom_filter, dependent: :destroy
   accepts_nested_attributes_for :keywords, reject_if: :all_blank, allow_destroy: true
 
   validates :title, :context, presence: true
@@ -101,6 +102,7 @@ class CustomFilter < ApplicationRecord
       status_matches = [status.id, status.reblog_of_id].compact & rules[:status_ids] if rules[:status_ids].present?
 
       next if keyword_matches.blank? && status_matches.blank?
+
       FilterResultPresenter.new(filter: filter, keyword_matches: keyword_matches, status_matches: status_matches)
     end
   end
@@ -111,6 +113,7 @@ class CustomFilter < ApplicationRecord
 
   def invalidate_cache!
     return unless @should_invalidate_cache
+
     @should_invalidate_cache = false
 
     Rails.cache.delete("filters:v3:#{account_id}")
diff --git a/app/models/custom_filter_keyword.rb b/app/models/custom_filter_keyword.rb
index e0d0289ae..3158b3b79 100644
--- a/app/models/custom_filter_keyword.rb
+++ b/app/models/custom_filter_keyword.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: custom_filter_keywords
diff --git a/app/models/custom_filter_status.rb b/app/models/custom_filter_status.rb
index e748d6963..0a5650204 100644
--- a/app/models/custom_filter_status.rb
+++ b/app/models/custom_filter_status.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: custom_filter_statuses
diff --git a/app/models/device.rb b/app/models/device.rb
index 97d0d2774..5dc6cf1e6 100644
--- a/app/models/device.rb
+++ b/app/models/device.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: devices
diff --git a/app/models/direct_feed.rb b/app/models/direct_feed.rb
index 1f2448070..689a735b3 100644
--- a/app/models/direct_feed.rb
+++ b/app/models/direct_feed.rb
@@ -4,9 +4,8 @@ class DirectFeed < Feed
   include Redisable
 
   def initialize(account)
-    @type    = :direct
-    @id      = account.id
     @account = account
+    super(:direct, account.id)
   end
 
   def get(limit, max_id = nil, since_id = nil, min_id = nil)
@@ -19,10 +18,12 @@ class DirectFeed < Feed
 
   private
 
-  def from_database(limit, max_id, since_id, min_id)
+  # TODO: _min_id is not actually handled by `as_direct_timeline`
+  def from_database(limit, max_id, since_id, _min_id)
     loop do
-      statuses = Status.as_direct_timeline(@account, limit, max_id, since_id, min_id)
+      statuses = Status.as_direct_timeline(@account, limit, max_id, since_id)
       return statuses if statuses.empty?
+
       max_id = statuses.last.id
       statuses = statuses.reject { |status| FeedManager.instance.filter?(:direct, status, @account) }
       return statuses unless statuses.empty?
diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb
index 8e298ac9d..fbb045416 100644
--- a/app/models/domain_block.rb
+++ b/app/models/domain_block.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: domain_blocks
@@ -20,7 +21,7 @@ class DomainBlock < ApplicationRecord
   include DomainNormalizable
   include DomainMaterializable
 
-  enum severity: [:silence, :suspend, :noop]
+  enum severity: { silence: 0, suspend: 1, noop: 2 }
 
   validates :domain, presence: true, uniqueness: true, domain: true
 
diff --git a/app/models/email_domain_block.rb b/app/models/email_domain_block.rb
index 10a0e5102..3c9be51ca 100644
--- a/app/models/email_domain_block.rb
+++ b/app/models/email_domain_block.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: email_domain_blocks
@@ -11,7 +12,7 @@
 #
 
 class EmailDomainBlock < ApplicationRecord
-  self.ignored_columns = %w(
+  self.ignored_columns += %w(
     ips
     last_refresh_at
   )
@@ -69,13 +70,11 @@ class EmailDomainBlock < ApplicationRecord
 
     def extract_uris(domain_or_domains)
       Array(domain_or_domains).map do |str|
-        domain = begin
-          if str.include?('@')
-            str.split('@', 2).last
-          else
-            str
-          end
-        end
+        domain = if str.include?('@')
+                   str.split('@', 2).last
+                 else
+                   str
+                 end
 
         Addressable::URI.new.tap { |u| u.host = domain.strip } if domain.present?
       rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
diff --git a/app/models/encrypted_message.rb b/app/models/encrypted_message.rb
index 7b4e32283..3e7e95594 100644
--- a/app/models/encrypted_message.rb
+++ b/app/models/encrypted_message.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: encrypted_messages
diff --git a/app/models/favourite.rb b/app/models/favourite.rb
index 2f355739a..042f72bea 100644
--- a/app/models/favourite.rb
+++ b/app/models/favourite.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: favourites
@@ -38,6 +39,7 @@ class Favourite < ApplicationRecord
 
   def decrement_cache_counters
     return if association(:status).loaded? && status.marked_for_destruction?
+
     status&.decrement_count!(:favourites_count)
   end
 
diff --git a/app/models/featured_tag.rb b/app/models/featured_tag.rb
index 70f949b6a..587dcf991 100644
--- a/app/models/featured_tag.rb
+++ b/app/models/featured_tag.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: featured_tags
diff --git a/app/models/follow.rb b/app/models/follow.rb
index e5cecbbc1..108f5c5d5 100644
--- a/app/models/follow.rb
+++ b/app/models/follow.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: follows
diff --git a/app/models/follow_recommendation.rb b/app/models/follow_recommendation.rb
index e552b5a88..602d32985 100644
--- a/app/models/follow_recommendation.rb
+++ b/app/models/follow_recommendation.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: follow_recommendations
@@ -12,7 +13,7 @@ class FollowRecommendation < ApplicationRecord
   self.primary_key = :account_id
 
   belongs_to :account_summary, foreign_key: :account_id
-  belongs_to :account, foreign_key: :account_id
+  belongs_to :account
 
   scope :localized, ->(locale) { joins(:account_summary).merge(AccountSummary.localized(locale)) }
 
diff --git a/app/models/follow_recommendation_suppression.rb b/app/models/follow_recommendation_suppression.rb
index 170506b85..e261a2fe3 100644
--- a/app/models/follow_recommendation_suppression.rb
+++ b/app/models/follow_recommendation_suppression.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: follow_recommendation_suppressions
@@ -19,9 +20,9 @@ class FollowRecommendationSuppression < ApplicationRecord
   private
 
   def remove_follow_recommendations
-    redis.pipelined do
+    redis.pipelined do |pipeline|
       I18n.available_locales.each do |locale|
-        redis.zrem("follow_recommendations:#{locale}", account_id)
+        pipeline.zrem("follow_recommendations:#{locale}", account_id)
       end
     end
   end
diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb
index 9034250c0..78f79c18f 100644
--- a/app/models/follow_request.rb
+++ b/app/models/follow_request.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: follow_requests
diff --git a/app/models/form/account_batch.rb b/app/models/form/account_batch.rb
index 473622edf..6a05f8163 100644
--- a/app/models/form/account_batch.rb
+++ b/app/models/form/account_batch.rb
@@ -17,8 +17,8 @@ class Form::AccountBatch
       unfollow!
     when 'remove_from_followers'
       remove_from_followers!
-    when 'block_domains'
-      block_domains!
+    when 'remove_domains_from_followers'
+      remove_domains_from_followers!
     when 'approve'
       approve!
     when 'reject'
@@ -35,9 +35,15 @@ class Form::AccountBatch
   private
 
   def follow!
+    error = nil
+
     accounts.each do |target_account|
       FollowService.new.call(current_account, target_account)
+    rescue Mastodon::NotPermittedError, ActiveRecord::RecordNotFound => e
+      error ||= e
     end
+
+    raise error if error.present?
   end
 
   def unfollow!
@@ -50,10 +56,8 @@ class Form::AccountBatch
     RemoveFromFollowersService.new.call(current_account, account_ids)
   end
 
-  def block_domains!
-    AfterAccountDomainBlockWorker.push_bulk(account_domains) do |domain|
-      [current_account.id, domain]
-    end
+  def remove_domains_from_followers!
+    RemoveDomainsFromFollowersService.new.call(current_account, account_domains)
   end
 
   def account_domains
diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb
index b595529f8..eaee142fa 100644
--- a/app/models/form/admin_settings.rb
+++ b/app/models/form/admin_settings.rb
@@ -28,6 +28,7 @@ class Form::AdminSettings
     show_reblogs_in_public_timelines
     show_replies_in_public_timelines
     trends
+    trends_as_landing_page
     trendable_by_default
     trending_status_cw
     show_domain_blocks
@@ -39,6 +40,7 @@ class Form::AdminSettings
     media_cache_retention_period
     content_cache_retention_period
     backups_retention_period
+    status_page_url
   ).freeze
 
   INTEGER_KEYS = %i(
@@ -57,6 +59,7 @@ class Form::AdminSettings
     show_reblogs_in_public_timelines
     show_replies_in_public_timelines
     trends
+    trends_as_landing_page
     trendable_by_default
     trending_status_cw
     noindex
@@ -83,19 +86,18 @@ class Form::AdminSettings
   validates :show_domain_blocks_rationale, inclusion: { in: %w(disabled users all) }, if: -> { defined?(@show_domain_blocks_rationale) }
   validates :media_cache_retention_period, :content_cache_retention_period, :backups_retention_period, numericality: { only_integer: true }, allow_blank: true, if: -> { defined?(@media_cache_retention_period) || defined?(@content_cache_retention_period) || defined?(@backups_retention_period) }
   validates :site_short_description, length: { maximum: 200 }, if: -> { defined?(@site_short_description) }
+  validates :status_page_url, url: true, allow_blank: true
   validate :validate_site_uploads
 
   KEYS.each do |key|
     define_method(key) do
       return instance_variable_get("@#{key}") if instance_variable_defined?("@#{key}")
 
-      stored_value = begin
-        if UPLOAD_KEYS.include?(key)
-          SiteUpload.where(var: key).first_or_initialize(var: key)
-        else
-          Setting.public_send(key)
-        end
-      end
+      stored_value = if UPLOAD_KEYS.include?(key)
+                       SiteUpload.where(var: key).first_or_initialize(var: key)
+                     else
+                       Setting.public_send(key)
+                     end
 
       instance_variable_set("@#{key}", stored_value)
     end
@@ -151,6 +153,7 @@ class Form::AdminSettings
   def validate_site_uploads
     UPLOAD_KEYS.each do |key|
       next unless instance_variable_defined?("@#{key}")
+
       upload = instance_variable_get("@#{key}")
       next if upload.valid?
 
diff --git a/app/models/form/custom_emoji_batch.rb b/app/models/form/custom_emoji_batch.rb
index f4fa84c10..484415f90 100644
--- a/app/models/form/custom_emoji_batch.rb
+++ b/app/models/form/custom_emoji_batch.rb
@@ -36,13 +36,11 @@ class Form::CustomEmojiBatch
   def update!
     custom_emojis.each { |custom_emoji| authorize(custom_emoji, :update?) }
 
-    category = begin
-      if category_id.present?
-        CustomEmojiCategory.find(category_id)
-      elsif category_name.present?
-        CustomEmojiCategory.find_or_create_by!(name: category_name)
-      end
-    end
+    category = if category_id.present?
+                 CustomEmojiCategory.find(category_id)
+               elsif category_name.present?
+                 CustomEmojiCategory.find_or_create_by!(name: category_name)
+               end
 
     custom_emojis.each do |custom_emoji|
       custom_emoji.update(category_id: category&.id)
diff --git a/app/models/identity.rb b/app/models/identity.rb
index 8cc65aef4..6f10fed4d 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: identities
diff --git a/app/models/import.rb b/app/models/import.rb
index 00a54892e..21634005e 100644
--- a/app/models/import.rb
+++ b/app/models/import.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: imports
@@ -24,7 +25,7 @@ class Import < ApplicationRecord
 
   belongs_to :account
 
-  enum type: [:following, :blocking, :muting, :domain_blocking, :bookmarks]
+  enum type: { following: 0, blocking: 1, muting: 2, domain_blocking: 3, bookmarks: 4 }
 
   validates :type, presence: true
   validates_with ImportValidator, on: :create
diff --git a/app/models/instance.rb b/app/models/instance.rb
index edbf02a6d..1f96d3728 100644
--- a/app/models/instance.rb
+++ b/app/models/instance.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: instances
diff --git a/app/models/invite.rb b/app/models/invite.rb
index 7ea4e2f98..8e816cef0 100644
--- a/app/models/invite.rb
+++ b/app/models/invite.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: invites
diff --git a/app/models/ip_block.rb b/app/models/ip_block.rb
index 31343f0e1..99783050b 100644
--- a/app/models/ip_block.rb
+++ b/app/models/ip_block.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: ip_blocks
diff --git a/app/models/list.rb b/app/models/list.rb
index cdc6ebdb3..bd1bdbd24 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: lists
@@ -16,7 +17,7 @@ class List < ApplicationRecord
 
   PER_ACCOUNT_LIMIT = 50
 
-  enum replies_policy: [:list, :followed, :none], _prefix: :show
+  enum replies_policy: { list: 0, followed: 1, none: 2 }, _prefix: :show
 
   belongs_to :account, optional: true
 
diff --git a/app/models/list_account.rb b/app/models/list_account.rb
index 785923c4c..a5767d3d8 100644
--- a/app/models/list_account.rb
+++ b/app/models/list_account.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: list_accounts
diff --git a/app/models/login_activity.rb b/app/models/login_activity.rb
index 52a0fd01d..2b7b37f8e 100644
--- a/app/models/login_activity.rb
+++ b/app/models/login_activity.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: login_activities
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb
index 4dd3042ab..0367b4af7 100644
--- a/app/models/media_attachment.rb
+++ b/app/models/media_attachment.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: media_attachments
@@ -33,16 +34,16 @@ class MediaAttachment < ApplicationRecord
 
   include Attachmentable
 
-  enum type: [:image, :gifv, :video, :unknown, :audio]
-  enum processing: [:queued, :in_progress, :complete, :failed], _prefix: true
+  enum type: { :image => 0, :gifv => 1, :video => 2, :unknown => 3, :audio => 4 }
+  enum processing: { :queued => 0, :in_progress => 1, :complete => 2, :failed => 3 }, _prefix: true
 
   MAX_DESCRIPTION_LENGTH = 1_500
 
-  IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 10.megabytes).to_i
-  VIDEO_LIMIT = (ENV['MAX_VIDEO_SIZE'] || 40.megabytes).to_i
+  IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 16.megabytes).to_i
+  VIDEO_LIMIT = (ENV['MAX_VIDEO_SIZE'] || 99.megabytes).to_i
 
-  MAX_VIDEO_MATRIX_LIMIT = 2_304_000 # 1920x1200px
-  MAX_VIDEO_FRAME_RATE   = 60
+  MAX_VIDEO_MATRIX_LIMIT = 8_294_400 # 3840x2160px
+  MAX_VIDEO_FRAME_RATE   = 120
 
   IMAGE_FILE_EXTENSIONS = %w(.jpg .jpeg .png .gif .webp .heic .heif .avif).freeze
   VIDEO_FILE_EXTENSIONS = %w(.webm .mp4 .m4v .mov).freeze
@@ -68,7 +69,7 @@ class MediaAttachment < ApplicationRecord
 
   IMAGE_STYLES = {
     original: {
-      pixels: 2_073_600, # 1920x1080px
+      pixels: 8_294_400, # 3840x2160px
       file_geometry_parser: FastGeometryParser,
     }.freeze,
 
@@ -372,7 +373,7 @@ class MediaAttachment < ApplicationRecord
     return {} if width.nil?
 
     {
-      width:  width,
+      width: width,
       height: height,
       size: "#{width}x#{height}",
       aspect: width.to_f / height,
diff --git a/app/models/mention.rb b/app/models/mention.rb
index d01a88e32..2348b2905 100644
--- a/app/models/mention.rb
+++ b/app/models/mention.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: mentions
diff --git a/app/models/mute.rb b/app/models/mute.rb
index 578345ef6..8fc542262 100644
--- a/app/models/mute.rb
+++ b/app/models/mute.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: mutes
diff --git a/app/models/notification.rb b/app/models/notification.rb
index bbc63c1c0..3eaf557b0 100644
--- a/app/models/notification.rb
+++ b/app/models/notification.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: notifications
@@ -19,12 +20,12 @@ class Notification < ApplicationRecord
   include Paginable
 
   LEGACY_TYPE_CLASS_MAP = {
-    'Mention'       => :mention,
-    'Status'        => :reblog,
-    'Follow'        => :follow,
+    'Mention' => :mention,
+    'Status' => :reblog,
+    'Follow' => :follow,
     'FollowRequest' => :follow_request,
-    'Favourite'     => :favourite,
-    'Poll'          => :poll,
+    'Favourite' => :favourite,
+    'Poll' => :poll,
   }.freeze
 
   TYPES = %i(
@@ -87,13 +88,11 @@ class Notification < ApplicationRecord
 
   class << self
     def browserable(types: [], exclude_types: [], from_account_id: nil)
-      requested_types = begin
-        if types.empty?
-          TYPES
-        else
-          types.map(&:to_sym) & TYPES
-        end
-      end
+      requested_types = if types.empty?
+                          TYPES
+                        else
+                          types.map(&:to_sym) & TYPES
+                        end
 
       requested_types -= exclude_types.map(&:to_sym)
 
diff --git a/app/models/one_time_key.rb b/app/models/one_time_key.rb
index 8ada34824..23604e2f7 100644
--- a/app/models/one_time_key.rb
+++ b/app/models/one_time_key.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: one_time_keys
diff --git a/app/models/poll.rb b/app/models/poll.rb
index af3b09315..dd35e953b 100644
--- a/app/models/poll.rb
+++ b/app/models/poll.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: polls
@@ -74,9 +75,9 @@ class Poll < ApplicationRecord
 
     def initialize(poll, id, title, votes_count)
       super(
-        poll:        poll,
-        id:          id,
-        title:       title,
+        poll: poll,
+        id: id,
+        title: title,
         votes_count: votes_count,
       )
     end
@@ -105,6 +106,7 @@ class Poll < ApplicationRecord
 
   def reset_parent_cache
     return if status_id.nil?
+
     Rails.cache.delete("statuses/#{status_id}")
   end
 
diff --git a/app/models/poll_vote.rb b/app/models/poll_vote.rb
index ad24eb691..92d74a6db 100644
--- a/app/models/poll_vote.rb
+++ b/app/models/poll_vote.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: poll_votes
@@ -22,6 +23,7 @@ class PollVote < ApplicationRecord
   after_create_commit :increment_counter_cache
 
   delegate :local?, to: :account
+  delegate :multiple?, :expired?, to: :poll, prefix: true
 
   def object_type
     :vote
diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb
index 56ca62d5e..a738940be 100644
--- a/app/models/preview_card.rb
+++ b/app/models/preview_card.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: preview_cards
@@ -35,7 +36,7 @@ class PreviewCard < ApplicationRecord
   include Attachmentable
 
   IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze
-  LIMIT = 1.megabytes
+  LIMIT = 2.megabytes
 
   BLURHASH_OPTIONS = {
     x_comp: 4,
@@ -44,8 +45,8 @@ class PreviewCard < ApplicationRecord
 
   self.inheritance_column = false
 
-  enum type: [:link, :photo, :video, :rich]
-  enum link_type: [:unknown, :article]
+  enum type: { link: 0, photo: 1, video: 2, rich: 3 }
+  enum link_type: { unknown: 0, article: 1 }
 
   has_and_belongs_to_many :statuses
   has_one :trend, class_name: 'PreviewCardTrend', inverse_of: :preview_card, dependent: :destroy
@@ -120,7 +121,7 @@ class PreviewCard < ApplicationRecord
     def image_styles(file)
       styles = {
         original: {
-          geometry: '400x400>',
+          pixels: 230_400, # 640x360px
           file_geometry_parser: FastGeometryParser,
           convert_options: '-coalesce',
           blurhash: BLURHASH_OPTIONS,
diff --git a/app/models/preview_card_provider.rb b/app/models/preview_card_provider.rb
index d61fe6020..1dd95fc91 100644
--- a/app/models/preview_card_provider.rb
+++ b/app/models/preview_card_provider.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: preview_card_providers
diff --git a/app/models/relay.rb b/app/models/relay.rb
index c66bfe4ff..a5fa03a99 100644
--- a/app/models/relay.rb
+++ b/app/models/relay.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: relays
@@ -14,7 +15,7 @@
 class Relay < ApplicationRecord
   validates :inbox_url, presence: true, uniqueness: true, url: true, if: :will_save_change_to_inbox_url?
 
-  enum state: [:idle, :pending, :accepted, :rejected]
+  enum state: { idle: 0, pending: 1, accepted: 2, rejected: 3 }
 
   scope :enabled, -> { accepted }
 
diff --git a/app/models/remote_follow.rb b/app/models/remote_follow.rb
index 911c06713..10715ac97 100644
--- a/app/models/remote_follow.rb
+++ b/app/models/remote_follow.rb
@@ -36,13 +36,11 @@ class RemoteFollow
 
     username, domain = value.strip.gsub(/\A@/, '').split('@')
 
-    domain = begin
-      if TagManager.instance.local_domain?(domain)
-        nil
-      else
-        TagManager.instance.normalize_domain(domain)
-      end
-    end
+    domain = if TagManager.instance.local_domain?(domain)
+               nil
+             else
+               TagManager.instance.normalize_domain(domain)
+             end
 
     [username, domain].compact.join('@')
   rescue Addressable::URI::InvalidURIError
diff --git a/app/models/report.rb b/app/models/report.rb
index 525d22ad5..c3a0c4c8b 100644
--- a/app/models/report.rb
+++ b/app/models/report.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: reports
@@ -20,7 +21,7 @@
 #
 
 class Report < ApplicationRecord
-  self.ignored_columns = %w(action_taken)
+  self.ignored_columns += %w(action_taken)
 
   include Paginable
   include RateLimitable
@@ -32,7 +33,7 @@ class Report < ApplicationRecord
   belongs_to :action_taken_by_account, class_name: 'Account', optional: true
   belongs_to :assigned_account, class_name: 'Account', optional: true
 
-  has_many :notes, class_name: 'ReportNote', foreign_key: :report_id, inverse_of: :report, dependent: :destroy
+  has_many :notes, class_name: 'ReportNote', inverse_of: :report, dependent: :destroy
   has_many :notifications, as: :activity, dependent: :destroy
 
   scope :unresolved, -> { where(action_taken_at: nil) }
diff --git a/app/models/report_note.rb b/app/models/report_note.rb
index 6f39df623..8307badd6 100644
--- a/app/models/report_note.rb
+++ b/app/models/report_note.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: report_notes
diff --git a/app/models/session_activation.rb b/app/models/session_activation.rb
index 3a59bad93..10c3a6c25 100644
--- a/app/models/session_activation.rb
+++ b/app/models/session_activation.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: session_activations
@@ -51,6 +52,7 @@ class SessionActivation < ApplicationRecord
 
     def deactivate(id)
       return unless id
+
       where(session_id: id).destroy_all
     end
 
@@ -59,7 +61,7 @@ class SessionActivation < ApplicationRecord
     end
 
     def exclusive(id)
-      where('session_id != ?', id).destroy_all
+      where.not(session_id: id).destroy_all
     end
   end
 
diff --git a/app/models/setting.rb b/app/models/setting.rb
index 4bcaa060f..3bdc6ffb4 100644
--- a/app/models/setting.rb
+++ b/app/models/setting.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: settings
@@ -23,19 +24,19 @@ class Setting < RailsSettings::Base
     def [](key)
       return super(key) unless rails_initialized?
 
-      val = Rails.cache.fetch(cache_key(key, nil)) do
+      Rails.cache.fetch(cache_key(key, nil)) do
         db_val = object(key)
 
         if db_val
           default_value = default_settings[key]
 
           return default_value.with_indifferent_access.merge!(db_val.value) if default_value.is_a?(Hash)
+
           db_val.value
         else
           default_settings[key]
         end
       end
-      val
     end
 
     def all_as_records
@@ -44,6 +45,7 @@ class Setting < RailsSettings::Base
 
       default_settings.each do |key, default_value|
         next if records.key?(key) || default_value.is_a?(Hash)
+
         records[key] = Setting.new(var: key, value: default_value)
       end
 
@@ -52,6 +54,7 @@ class Setting < RailsSettings::Base
 
     def default_settings
       return {} unless RailsSettings::Default.enabled?
+
       RailsSettings::Default.instance
     end
   end
diff --git a/app/models/site_upload.rb b/app/models/site_upload.rb
index 167131fdd..e17668110 100644
--- a/app/models/site_upload.rb
+++ b/app/models/site_upload.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: site_uploads
diff --git a/app/models/status.rb b/app/models/status.rb
index 14b7a39fe..8a58e5d68 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: statuses
@@ -50,12 +51,12 @@ class Status < ApplicationRecord
 
   update_index('statuses', :proper)
 
-  enum visibility: [:public, :unlisted, :private, :direct, :limited], _suffix: :visibility
+  enum visibility: { public: 0, unlisted: 1, private: 2, direct: 3, limited: 4 }, _suffix: :visibility
 
   belongs_to :application, class_name: 'Doorkeeper::Application', optional: true
 
   belongs_to :account, inverse_of: :statuses
-  belongs_to :in_reply_to_account, foreign_key: 'in_reply_to_account_id', class_name: 'Account', optional: true
+  belongs_to :in_reply_to_account, class_name: 'Account', optional: true
   belongs_to :conversation, optional: true
   belongs_to :preloadable_poll, class_name: 'Poll', foreign_key: 'poll_id', optional: true
 
@@ -97,24 +98,27 @@ class Status < ApplicationRecord
   scope :local,  -> { where(local: true).or(where(uri: nil)) }
   scope :with_accounts, ->(ids) { where(id: ids).includes(:account) }
   scope :without_replies, -> { where('statuses.reply = FALSE OR statuses.in_reply_to_account_id = statuses.account_id') }
-  scope :without_reblogs, -> { where('statuses.reblog_of_id IS NULL') }
+  scope :without_reblogs, -> { where(statuses: { reblog_of_id: nil }) }
   scope :with_public_visibility, -> { where(visibility: :public) }
   scope :tagged_with, ->(tag_ids) { joins(:statuses_tags).where(statuses_tags: { tag_id: tag_ids }) }
   scope :excluding_silenced_accounts, -> { left_outer_joins(:account).where(accounts: { silenced_at: nil }) }
   scope :including_silenced_accounts, -> { left_outer_joins(:account).where.not(accounts: { silenced_at: nil }) }
   scope :not_excluded_by_account, ->(account) { where.not(account_id: account.excluded_from_timeline_account_ids) }
   scope :not_domain_blocked_by_account, ->(account) { account.excluded_from_timeline_domains.blank? ? left_outer_joins(:account) : left_outer_joins(:account).where('accounts.domain IS NULL OR accounts.domain NOT IN (?)', account.excluded_from_timeline_domains) }
-  scope :tagged_with_all, ->(tag_ids) {
+  scope :tagged_with_all, lambda { |tag_ids|
     Array(tag_ids).map(&:to_i).reduce(self) do |result, id|
       result.joins("INNER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}")
     end
   }
-  scope :tagged_with_none, ->(tag_ids) {
+  scope :tagged_with_none, lambda { |tag_ids|
     where('NOT EXISTS (SELECT * FROM statuses_tags forbidden WHERE forbidden.status_id = statuses.id AND forbidden.tag_id IN (?))', tag_ids)
   }
 
   scope :not_local_only, -> { where(local_only: [false, nil]) }
 
+  after_create_commit :trigger_create_webhooks
+  after_update_commit :trigger_update_webhooks
+
   cache_associated :application,
                    :media_attachments,
                    :conversation,
@@ -122,7 +126,7 @@ class Status < ApplicationRecord
                    :tags,
                    :preview_cards,
                    :preloadable_poll,
-                   account: [:account_stat, :user],
+                   account: [:account_stat, user: :role],
                    active_mentions: { account: :account_stat },
                    reblog: [
                      :application,
@@ -132,7 +136,7 @@ class Status < ApplicationRecord
                      :conversation,
                      :status_stat,
                      :preloadable_poll,
-                     account: [:account_stat, :user],
+                     account: [:account_stat, user: :role],
                      active_mentions: { account: :account_stat },
                    ],
                    thread: { account: :account_stat }
@@ -141,6 +145,10 @@ class Status < ApplicationRecord
 
   REAL_TIME_WINDOW = 6.hours
 
+  def cache_key
+    "v2:#{super}"
+  end
+
   def searchable_by(preloaded = nil)
     ids = []
 
@@ -330,7 +338,7 @@ class Status < ApplicationRecord
       visibilities.keys - %w(direct limited)
     end
 
-    def as_direct_timeline(account, limit = 20, max_id = nil, since_id = nil, cache_ids = false)
+    def as_direct_timeline(account, limit = 20, max_id = nil, since_id = nil)
       # direct timeline is mix of direct message from_me and to_me.
       # 2 queries are executed with pagination.
       # constant expression using arel_table is required for partial index
@@ -361,14 +369,9 @@ class Status < ApplicationRecord
         query_to_me = query_to_me.where('mentions.status_id > ?', since_id)
       end
 
-      if cache_ids
-        # returns array of cache_ids object that have id and updated_at
-        (query_from_me.cache_ids.to_a + query_to_me.cache_ids.to_a).uniq(&:id).sort_by(&:id).reverse.take(limit)
-      else
-        # returns ActiveRecord.Relation
-        items = (query_from_me.select(:id).to_a + query_to_me.select(:id).to_a).uniq(&:id).sort_by(&:id).reverse.take(limit)
-        Status.where(id: items.map(&:id))
-      end
+      # returns ActiveRecord.Relation
+      items = (query_from_me.select(:id).to_a + query_to_me.select(:id).to_a).uniq(&:id).sort_by(&:id).reverse.take(limit)
+      Status.where(id: items.map(&:id))
     end
 
     def favourites_map(status_ids, account_id)
@@ -415,13 +418,12 @@ class Status < ApplicationRecord
       return [] if text.blank?
 
       text.scan(FetchLinkCardService::URL_PATTERN).map(&:second).uniq.filter_map do |url|
-        status = begin
-          if TagManager.instance.local_url?(url)
-            ActivityPub::TagManager.instance.uri_to_resource(url, Status)
-          else
-            EntityCache.instance.status(url)
-          end
-        end
+        status = if TagManager.instance.local_url?(url)
+                   ActivityPub::TagManager.instance.uri_to_resource(url, Status)
+                 else
+                   EntityCache.instance.status(url)
+                 end
+
         status&.distributable? ? status : nil
       end
     end
@@ -546,9 +548,9 @@ class Status < ApplicationRecord
   end
 
   def set_locality
-    if account.domain.nil? && !attribute_changed?(:local_only)
-      self.local_only = marked_local_only?
-    end
+    return unless account.domain.nil? && !attribute_changed?(:local_only)
+
+    self.local_only = marked_local_only?
   end
 
   def set_conversation
@@ -597,4 +599,12 @@ class Status < ApplicationRecord
     reblog&.decrement_count!(:reblogs_count) if reblog?
     thread&.decrement_count!(:replies_count) if in_reply_to_id.present? && distributable?
   end
+
+  def trigger_create_webhooks
+    TriggerWebhookWorker.perform_async('status.created', 'Status', id) if local?
+  end
+
+  def trigger_update_webhooks
+    TriggerWebhookWorker.perform_async('status.updated', 'Status', id) if local?
+  end
 end
diff --git a/app/models/status_edit.rb b/app/models/status_edit.rb
index c2330c04f..fa35e38ac 100644
--- a/app/models/status_edit.rb
+++ b/app/models/status_edit.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: status_edits
@@ -20,7 +21,7 @@
 class StatusEdit < ApplicationRecord
   include RateLimitable
 
-  self.ignored_columns = %w(
+  self.ignored_columns += %w(
     media_attachments_changed
   )
 
@@ -46,20 +47,19 @@ class StatusEdit < ApplicationRecord
 
   def emojis
     return @emojis if defined?(@emojis)
+
     @emojis = CustomEmoji.from_text([spoiler_text, text].join(' '), status.account.domain)
   end
 
   def ordered_media_attachments
     return @ordered_media_attachments if defined?(@ordered_media_attachments)
 
-    @ordered_media_attachments = begin
-      if ordered_media_attachment_ids.nil?
-        []
-      else
-        map = status.media_attachments.index_by(&:id)
-        ordered_media_attachment_ids.map.with_index { |media_attachment_id, index| PreservedMediaAttachment.new(media_attachment: map[media_attachment_id], description: media_descriptions[index]) }
-      end
-    end
+    @ordered_media_attachments = if ordered_media_attachment_ids.nil?
+                                   []
+                                 else
+                                   map = status.media_attachments.index_by(&:id)
+                                   ordered_media_attachment_ids.map.with_index { |media_attachment_id, index| PreservedMediaAttachment.new(media_attachment: map[media_attachment_id], description: media_descriptions[index]) }
+                                 end
   end
 
   def proper
diff --git a/app/models/status_pin.rb b/app/models/status_pin.rb
index 93a0ea1c0..dae4a5b4e 100644
--- a/app/models/status_pin.rb
+++ b/app/models/status_pin.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: status_pins
diff --git a/app/models/status_stat.rb b/app/models/status_stat.rb
index 437861d1c..d101cc178 100644
--- a/app/models/status_stat.rb
+++ b/app/models/status_stat.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: status_stats
diff --git a/app/models/system_key.rb b/app/models/system_key.rb
index f17db7c2d..1be399dd6 100644
--- a/app/models/system_key.rb
+++ b/app/models/system_key.rb
@@ -14,7 +14,7 @@ class SystemKey < ApplicationRecord
 
   before_validation :set_key
 
-  scope :expired, ->(now = Time.now.utc) { where(arel_table[:created_at].lt(now - ROTATION_PERIOD * 3)) }
+  scope :expired, ->(now = Time.now.utc) { where(arel_table[:created_at].lt(now - (ROTATION_PERIOD * 3))) }
 
   class << self
     def current_key
diff --git a/app/models/tag.rb b/app/models/tag.rb
index 47a05d00a..554a92d90 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: tags
@@ -49,7 +50,7 @@ class Tag < ApplicationRecord
   scope :listable, -> { where(listable: [true, nil]) }
   scope :trendable, -> { Setting.trendable_by_default ? where(trendable: [true, nil]) : where(trendable: true) }
   scope :not_trendable, -> { where(trendable: false) }
-  scope :recently_used, ->(account) {
+  scope :recently_used, lambda { |account|
                           joins(:statuses)
                             .where(statuses: { id: account.statuses.select(:id).limit(1000) })
                             .group(:id).order(Arel.sql('count(*) desc'))
diff --git a/app/models/trends/history.rb b/app/models/trends/history.rb
index 74723e35c..83532e7bc 100644
--- a/app/models/trends/history.rb
+++ b/app/models/trends/history.rb
@@ -87,8 +87,8 @@ class Trends::History
   end
 
   def each(&block)
-    if block_given?
-      (0...7).map { |i| block.call(get(i.days.ago)) }
+    if block
+      (0...7).map { |i| yield(get(i.days.ago)) }
     else
       to_enum(:each)
     end
diff --git a/app/models/trends/links.rb b/app/models/trends/links.rb
index 8808b3ab6..c94f7c023 100644
--- a/app/models/trends/links.rb
+++ b/app/models/trends/links.rb
@@ -113,13 +113,11 @@ class Trends::Links < Trends::Base
       max_score = preview_card.max_score
       max_score = 0 if max_time.nil? || max_time < (at_time - options[:max_score_cooldown])
 
-      score = begin
-        if expected > observed || observed < options[:threshold]
-          0
-        else
-          ((observed - expected)**2) / expected
-        end
-      end
+      score = if expected > observed || observed < options[:threshold]
+                0
+              else
+                ((observed - expected)**2) / expected
+              end
 
       if score > max_score
         max_score = score
@@ -129,13 +127,11 @@ class Trends::Links < Trends::Base
         preview_card.update_columns(max_score: max_score, max_score_at: max_time)
       end
 
-      decaying_score = begin
-        if max_score.zero? || !valid_locale?(preview_card.language)
-          0
-        else
-          max_score * (0.5**((at_time.to_f - max_time.to_f) / options[:max_score_halflife].to_f))
-        end
-      end
+      decaying_score = if max_score.zero? || !valid_locale?(preview_card.language)
+                         0
+                       else
+                         max_score * (0.5**((at_time.to_f - max_time.to_f) / options[:max_score_halflife].to_f))
+                       end
 
       [decaying_score, preview_card]
     end
diff --git a/app/models/trends/statuses.rb b/app/models/trends/statuses.rb
index 14a05e6d8..d4d5b1c24 100644
--- a/app/models/trends/statuses.rb
+++ b/app/models/trends/statuses.rb
@@ -99,21 +99,17 @@ class Trends::Statuses < Trends::Base
       expected  = 1.0
       observed  = (status.reblogs_count + status.favourites_count).to_f
 
-      score = begin
-        if expected > observed || observed < options[:threshold]
-          0
-        else
-          ((observed - expected)**2) / expected
-        end
-      end
-
-      decaying_score = begin
-        if score.zero? || !eligible?(status)
-          0
-        else
-          score * (0.5**((at_time.to_f - status.created_at.to_f) / options[:score_halflife].to_f))
-        end
-      end
+      score = if expected > observed || observed < options[:threshold]
+                0
+              else
+                ((observed - expected)**2) / expected
+              end
+
+      decaying_score = if score.zero? || !eligible?(status)
+                         0
+                       else
+                         score * (0.5**((at_time.to_f - status.created_at.to_f) / options[:score_halflife].to_f))
+                       end
 
       [decaying_score, status]
     end
diff --git a/app/models/trends/tag_filter.rb b/app/models/trends/tag_filter.rb
index 3b142efc4..46b747819 100644
--- a/app/models/trends/tag_filter.rb
+++ b/app/models/trends/tag_filter.rb
@@ -13,13 +13,11 @@ class Trends::TagFilter
   end
 
   def results
-    scope = begin
-      if params[:status] == 'pending_review'
-        Tag.unscoped
-      else
-        trending_scope
-      end
-    end
+    scope = if params[:status] == 'pending_review'
+              Tag.unscoped
+            else
+              trending_scope
+            end
 
     params.each do |key, value|
       next if key.to_s == 'page'
diff --git a/app/models/trends/tags.rb b/app/models/trends/tags.rb
index 19ade52ba..931532990 100644
--- a/app/models/trends/tags.rb
+++ b/app/models/trends/tags.rb
@@ -63,13 +63,11 @@ class Trends::Tags < Trends::Base
       max_score = tag.max_score
       max_score = 0 if max_time.nil? || max_time < (at_time - options[:max_score_cooldown])
 
-      score = begin
-        if expected > observed || observed < options[:threshold]
-          0
-        else
-          ((observed - expected)**2) / expected
-        end
-      end
+      score = if expected > observed || observed < options[:threshold]
+                0
+              else
+                ((observed - expected)**2) / expected
+              end
 
       if score > max_score
         max_score = score
diff --git a/app/models/unavailable_domain.rb b/app/models/unavailable_domain.rb
index dfc0ef14e..c3f2f20e9 100644
--- a/app/models/unavailable_domain.rb
+++ b/app/models/unavailable_domain.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: unavailable_domains
diff --git a/app/models/user.rb b/app/models/user.rb
index 2e3c067ec..daf8768e8 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: users
@@ -38,10 +39,11 @@
 #  webauthn_id               :string
 #  sign_up_ip                :inet
 #  role_id                   :bigint(8)
+#  settings                  :text
 #
 
 class User < ApplicationRecord
-  self.ignored_columns = %w(
+  self.ignored_columns += %w(
     remember_created_at
     remember_token
     current_sign_in_ip
@@ -50,9 +52,9 @@ class User < ApplicationRecord
     filtered_languages
   )
 
-  include Settings::Extend
   include Redisable
   include LanguagesHelper
+  include HasUserSettings
 
   # The home and list feeds will be stored in Redis for this amount
   # of time, and status fan-out to followers will include only people
@@ -131,13 +133,6 @@ class User < ApplicationRecord
 
   has_many :session_activations, dependent: :destroy
 
-  delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :favourite_modal, :delete_modal,
-           :reduce_motion, :system_font_ui, :noindex, :flavour, :skin, :display_media, :hide_followers_count,
-           :expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
-           :advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
-           :disable_swiping, :always_send_emails, :default_content_type, :system_emoji_font,
-           to: :settings, prefix: :setting, allow_nil: false
-
   delegate :can?, to: :role
 
   attr_reader :invite_code
@@ -195,10 +190,16 @@ class User < ApplicationRecord
 
     super
 
-    if new_user && approved?
-      prepare_new_user!
-    elsif new_user
-      notify_staff_about_pending_account!
+    if new_user
+      # Avoid extremely unlikely race condition when approving and confirming
+      # the user at the same time
+      reload unless approved?
+
+      if approved?
+        prepare_new_user!
+      else
+        notify_staff_about_pending_account!
+      end
     end
   end
 
@@ -209,7 +210,13 @@ class User < ApplicationRecord
     skip_confirmation!
     save!
 
-    prepare_new_user! if new_user && approved?
+    if new_user
+      # Avoid extremely unlikely race condition when approving and confirming
+      # the user at the same time
+      reload unless approved?
+
+      prepare_new_user! if approved?
+    end
   end
 
   def update_sign_in!(new_sign_in: false)
@@ -237,7 +244,6 @@ class User < ApplicationRecord
   end
 
   def functional?
-
     functional_or_moved?
   end
 
@@ -254,14 +260,18 @@ class User < ApplicationRecord
   end
 
   def inactive_message
-    !approved? ? :pending : super
+    approved? ? super : :pending
   end
 
   def approve!
     return if approved?
 
     update!(approved: true)
-    prepare_new_user!
+
+    # Avoid extremely unlikely race condition when approving and confirming
+    # the user at the same time
+    reload unless confirmed?
+    prepare_new_user! if confirmed?
   end
 
   def otp_enabled?
@@ -286,50 +296,6 @@ class User < ApplicationRecord
     save!
   end
 
-  def prefers_noindex?
-    setting_noindex
-  end
-
-  def preferred_posting_language
-    valid_locale_cascade(settings.default_language, locale, I18n.locale)
-  end
-
-  def setting_default_privacy
-    settings.default_privacy || (account.locked? ? 'private' : 'public')
-  end
-
-  def allows_report_emails?
-    settings.notification_emails['report']
-  end
-
-  def allows_pending_account_emails?
-    settings.notification_emails['pending_account']
-  end
-
-  def allows_appeal_emails?
-    settings.notification_emails['appeal']
-  end
-
-  def allows_trending_tags_review_emails?
-    settings.notification_emails['trending_tag']
-  end
-
-  def allows_trending_links_review_emails?
-    settings.notification_emails['trending_link']
-  end
-
-  def allows_trending_statuses_review_emails?
-    settings.notification_emails['trending_status']
-  end
-
-  def aggregates_reblogs?
-    @aggregates_reblogs ||= settings.aggregate_reblogs
-  end
-
-  def shows_application?
-    @shows_application ||= settings.show_application
-  end
-
   def token_for_app(app)
     return nil if app.nil? || app.owner != self
 
@@ -409,14 +375,6 @@ class User < ApplicationRecord
     send_reset_password_instructions
   end
 
-  def show_all_media?
-    setting_display_media == 'show_all'
-  end
-
-  def hide_all_media?
-    setting_display_media == 'hide_all'
-  end
-
   protected
 
   def send_devise_notification(notification, *args, **kwargs)
@@ -485,23 +443,29 @@ class User < ApplicationRecord
 
   def sanitize_languages
     return if chosen_languages.nil?
-    chosen_languages.reject!(&:blank?)
+
+    chosen_languages.compact_blank!
+
     self.chosen_languages = nil if chosen_languages.empty?
   end
 
   def sanitize_role
     return if role.nil?
+
     self.role = nil if role.everyone?
   end
 
   def prepare_new_user!
     BootstrapTimelineWorker.perform_async(account_id)
     ActivityTracker.increment('activity:accounts:local')
+    ActivityTracker.record('activity:logins', id)
     UserMailer.welcome(self).deliver_later
     TriggerWebhookWorker.perform_async('account.approved', 'Account', account_id)
   end
 
   def prepare_returning_user!
+    return unless confirmed?
+
     ActivityTracker.record('activity:logins', id)
     regenerate_feed! if needs_feed_update?
   end
@@ -509,6 +473,7 @@ class User < ApplicationRecord
   def notify_staff_about_pending_account!
     User.those_who_can(:manage_users).includes(:account).find_each do |u|
       next unless u.allows_pending_account_emails?
+
       AdminMailer.new_pending_account(u.account, self).deliver_later
     end
   end
diff --git a/app/models/user_ip.rb b/app/models/user_ip.rb
index a8e802e13..38287c2a6 100644
--- a/app/models/user_ip.rb
+++ b/app/models/user_ip.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: user_ips
@@ -11,7 +12,7 @@
 class UserIp < ApplicationRecord
   self.primary_key = :user_id
 
-  belongs_to :user, foreign_key: :user_id
+  belongs_to :user
 
   def readonly?
     true
diff --git a/app/models/user_role.rb b/app/models/user_role.rb
index 74dfdc220..a1b91dc0f 100644
--- a/app/models/user_role.rb
+++ b/app/models/user_role.rb
@@ -163,6 +163,7 @@ class UserRole < ApplicationRecord
 
   def in_permissions?(privilege)
     raise ArgumentError, "Unknown privilege: #{privilege}" unless FLAGS.key?(privilege)
+
     computed_permissions & FLAGS[privilege] == FLAGS[privilege]
   end
 
@@ -172,6 +173,7 @@ class UserRole < ApplicationRecord
 
   def validate_own_role_edition
     return unless defined?(@current_account) && @current_account.user_role.id == id
+
     errors.add(:permissions_as_keys, :own_role) if permissions_changed?
     errors.add(:position, :own_role) if position_changed?
   end
diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb
new file mode 100644
index 000000000..0be8c5fbc
--- /dev/null
+++ b/app/models/user_settings.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+class UserSettings
+  class Error < StandardError; end
+  class KeyError < Error; end
+
+  include UserSettings::DSL
+  include UserSettings::Glue
+
+  setting :always_send_emails, default: false
+  setting :aggregate_reblogs, default: true
+  setting :flavour, default: -> { ::Setting.flavour }
+  setting :skin, default: -> { ::Setting.skin }
+  setting :noindex, default: -> { ::Setting.noindex }
+  setting :show_application, default: true
+  setting :default_language, default: nil
+  setting :default_sensitive, default: false
+  setting :default_privacy, default: nil
+  setting :default_content_type, default: 'text/plain'
+  setting :hide_followers_count, default: false
+
+  namespace :web do
+    setting :crop_images, default: true
+    setting :advanced_layout, default: false
+    setting :trends, default: true
+    setting :use_blurhash, default: true
+    setting :use_pending_items, default: false
+    setting :use_system_font, default: false
+    setting :disable_swiping, default: false
+    setting :delete_modal, default: true
+    setting :reblog_modal, default: false
+    setting :unfollow_modal, default: true
+    setting :favourite_modal, default: false
+    setting :reduce_motion, default: false
+    setting :expand_content_warnings, default: false
+    setting :display_media, default: 'default', in: %w(default show_all hide_all)
+    setting :auto_play, default: false
+    setting :use_system_emoji_font, default: false
+  end
+
+  namespace :notification_emails do
+    setting :follow, default: true
+    setting :reblog, default: false
+    setting :favourite, default: false
+    setting :mention, default: true
+    setting :follow_request, default: true
+    setting :report, default: true
+    setting :pending_account, default: true
+    setting :trends, default: true
+    setting :link_trends, default: false
+    setting :status_trends, default: false
+    setting :appeal, default: true
+  end
+
+  namespace :interactions do
+    setting :must_be_follower, default: false
+    setting :must_be_following, default: false
+    setting :must_be_following_dm, default: false
+  end
+
+  def initialize(original_hash)
+    @original_hash = original_hash || {}
+  end
+
+  def [](key)
+    key = key.to_sym
+
+    raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key)
+
+    if @original_hash.key?(key)
+      @original_hash[key]
+    else
+      self.class.definition_for(key).default_value
+    end
+  end
+
+  def []=(key, value)
+    key = key.to_sym
+
+    raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key)
+
+    typecast_value = self.class.definition_for(key).type_cast(value)
+
+    if typecast_value.nil?
+      @original_hash.delete(key)
+    else
+      @original_hash[key] = typecast_value
+    end
+  end
+
+  def update(params)
+    params.each do |k, v|
+      self[k] = v unless v.nil?
+    end
+  end
+
+  keys.each do |key|
+    define_method(key) do
+      self[key]
+    end
+  end
+
+  def as_json
+    @original_hash
+  end
+end
diff --git a/app/models/user_settings/dsl.rb b/app/models/user_settings/dsl.rb
new file mode 100644
index 000000000..26238bbbe
--- /dev/null
+++ b/app/models/user_settings/dsl.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module UserSettings::DSL
+  module ClassMethods
+    def setting(key, options = {})
+      @definitions ||= {}
+
+      UserSettings::Setting.new(key, options).tap do |s|
+        @definitions[s.key] = s
+      end
+    end
+
+    def namespace(key, &block)
+      @definitions ||= {}
+
+      UserSettings::Namespace.new(key).configure(&block).tap do |n|
+        @definitions.merge!(n.definitions)
+      end
+    end
+
+    def keys
+      @definitions.keys
+    end
+
+    def definition_for(key)
+      @definitions[key.to_sym]
+    end
+
+    def definition_for?(key)
+      @definitions.key?(key.to_sym)
+    end
+  end
+
+  def self.included(base)
+    base.extend ClassMethods
+  end
+end
diff --git a/app/models/user_settings/glue.rb b/app/models/user_settings/glue.rb
new file mode 100644
index 000000000..02066a411
--- /dev/null
+++ b/app/models/user_settings/glue.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module UserSettings::Glue
+  def to_model
+    self
+  end
+
+  def to_key
+    ''
+  end
+
+  def persisted?
+    false
+  end
+
+  def type_for_attribute(key)
+    self.class.definition_for(key)&.type
+  end
+
+  def has_attribute?(key) # rubocop:disable Naming/PredicateName
+    self.class.definition_for?(key)
+  end
+end
diff --git a/app/models/user_settings/namespace.rb b/app/models/user_settings/namespace.rb
new file mode 100644
index 000000000..b8f7e092e
--- /dev/null
+++ b/app/models/user_settings/namespace.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class UserSettings::Namespace
+  attr_reader :name, :definitions
+
+  def initialize(name)
+    @name        = name.to_sym
+    @definitions = {}
+  end
+
+  def configure(&block)
+    instance_eval(&block)
+    self
+  end
+
+  def setting(key, options = {})
+    UserSettings::Setting.new(key, options.merge(namespace: name)).tap do |s|
+      @definitions[s.key] = s
+    end
+  end
+end
diff --git a/app/models/user_settings/setting.rb b/app/models/user_settings/setting.rb
new file mode 100644
index 000000000..5f5504254
--- /dev/null
+++ b/app/models/user_settings/setting.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+class UserSettings::Setting
+  attr_reader :name, :namespace, :in
+
+  def initialize(name, options = {})
+    @name          = name.to_sym
+    @default_value = options[:default]
+    @namespace     = options[:namespace]
+    @in            = options[:in]
+  end
+
+  def default_value
+    if @default_value.respond_to?(:call)
+      @default_value.call
+    else
+      @default_value
+    end
+  end
+
+  def type
+    case default_value
+    when TrueClass, FalseClass
+      ActiveModel::Type::Boolean.new
+    else
+      ActiveModel::Type::String.new
+    end
+  end
+
+  def type_cast(value)
+    if type.respond_to?(:cast)
+      type.cast(value)
+    else
+      value
+    end
+  end
+
+  def to_a
+    [key, default_value]
+  end
+
+  def key
+    if namespace
+      "#{namespace}.#{name}".to_sym
+    else
+      name
+    end
+  end
+end
diff --git a/app/models/web/push_subscription.rb b/app/models/web/push_subscription.rb
index 6e46573ae..0ffbe068e 100644
--- a/app/models/web/push_subscription.rb
+++ b/app/models/web/push_subscription.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: web_push_subscriptions
@@ -53,25 +54,21 @@ class Web::PushSubscription < ApplicationRecord
   def associated_user
     return @associated_user if defined?(@associated_user)
 
-    @associated_user = begin
-      if user_id.nil?
-        session_activation.user
-      else
-        user
-      end
-    end
+    @associated_user = if user_id.nil?
+                         session_activation.user
+                       else
+                         user
+                       end
   end
 
   def associated_access_token
     return @associated_access_token if defined?(@associated_access_token)
 
-    @associated_access_token = begin
-      if access_token_id.nil?
-        find_or_create_access_token.token
-      else
-        access_token.token
-      end
-    end
+    @associated_access_token = if access_token_id.nil?
+                                 find_or_create_access_token.token
+                               else
+                                 access_token.token
+                               end
   end
 
   class << self
diff --git a/app/models/web/setting.rb b/app/models/web/setting.rb
index 99588d26c..3d5efe664 100644
--- a/app/models/web/setting.rb
+++ b/app/models/web/setting.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: web_settings
diff --git a/app/models/webauthn_credential.rb b/app/models/webauthn_credential.rb
index 7d423e38d..4fa31ece5 100644
--- a/app/models/webauthn_credential.rb
+++ b/app/models/webauthn_credential.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 # == Schema Information
 #
 # Table name: webauthn_credentials
@@ -18,5 +19,5 @@ class WebauthnCredential < ApplicationRecord
   validates :external_id, uniqueness: true
   validates :nickname, uniqueness: { scope: :user_id }
   validates :sign_count,
-            numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: 2**63 - 1 }
+            numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than_or_equal_to: (2**63) - 1 }
 end
diff --git a/app/models/webhook.rb b/app/models/webhook.rb
index 4aafb1257..9a056a386 100644
--- a/app/models/webhook.rb
+++ b/app/models/webhook.rb
@@ -17,7 +17,10 @@ class Webhook < ApplicationRecord
   EVENTS = %w(
     account.approved
     account.created
+    account.updated
     report.created
+    status.created
+    status.updated
   ).freeze
 
   scope :enabled, -> { where(enabled: true) }
diff --git a/app/presenters/account_relationships_presenter.rb b/app/presenters/account_relationships_presenter.rb
index ab8bac412..5d2b5435d 100644
--- a/app/presenters/account_relationships_presenter.rb
+++ b/app/presenters/account_relationships_presenter.rb
@@ -70,16 +70,16 @@ class AccountRelationshipsPresenter
   def cache_uncached!
     @uncached_account_ids.each do |account_id|
       maps_for_account = {
-        following:       { account_id => following[account_id] },
-        followed_by:     { account_id => followed_by[account_id] },
-        blocking:        { account_id => blocking[account_id] },
-        blocked_by:      { account_id => blocked_by[account_id] },
-        muting:          { account_id => muting[account_id] },
-        requested:       { account_id => requested[account_id] },
-        requested_by:    { account_id => requested_by[account_id] },
+        following: { account_id => following[account_id] },
+        followed_by: { account_id => followed_by[account_id] },
+        blocking: { account_id => blocking[account_id] },
+        blocked_by: { account_id => blocked_by[account_id] },
+        muting: { account_id => muting[account_id] },
+        requested: { account_id => requested[account_id] },
+        requested_by: { account_id => requested_by[account_id] },
         domain_blocking: { account_id => domain_blocking[account_id] },
-        endorsed:        { account_id => endorsed[account_id] },
-        account_note:    { account_id => account_note[account_id] },
+        endorsed: { account_id => endorsed[account_id] },
+        account_note: { account_id => account_note[account_id] },
       }
 
       Rails.cache.write("relationship:#{@current_account_id}:#{account_id}", maps_for_account, expires_in: 1.day)
diff --git a/app/presenters/instance_presenter.rb b/app/presenters/instance_presenter.rb
index fba3cc734..25df4d85a 100644
--- a/app/presenters/instance_presenter.rb
+++ b/app/presenters/instance_presenter.rb
@@ -22,10 +22,6 @@ class InstancePresenter < ActiveModelSerializers::Model
     ContactPresenter.new
   end
 
-  def closed_registrations_message
-    Setting.closed_registrations_message
-  end
-
   def description
     Setting.site_short_description
   end
@@ -34,8 +30,8 @@ class InstancePresenter < ActiveModelSerializers::Model
     Setting.site_extended_description
   end
 
-  def privacy_policy
-    Setting.site_terms
+  def status_page_url
+    Setting.status_page_url
   end
 
   def domain
@@ -70,10 +66,6 @@ class InstancePresenter < ActiveModelSerializers::Model
     Rails.cache.fetch('distinct_domain_count') { Instance.count }
   end
 
-  def sample_accounts
-    Rails.cache.fetch('sample_accounts', expires_in: 12.hours) { Account.local.discoverable.popular.limit(3) }
-  end
-
   def version
     Mastodon::Version.to_s
   end
diff --git a/app/presenters/tag_relationships_presenter.rb b/app/presenters/tag_relationships_presenter.rb
index c3bdbaf07..52e24314b 100644
--- a/app/presenters/tag_relationships_presenter.rb
+++ b/app/presenters/tag_relationships_presenter.rb
@@ -4,12 +4,10 @@ class TagRelationshipsPresenter
   attr_reader :following_map
 
   def initialize(tags, current_account_id = nil, **options)
-    @following_map = begin
-      if current_account_id.nil?
-        {}
-      else
-        TagFollow.select(:tag_id).where(tag_id: tags.map(&:id), account_id: current_account_id).each_with_object({}) { |f, h| h[f.tag_id] = true }.merge(options[:following_map] || {})
-      end
-    end
+    @following_map = if current_account_id.nil?
+                       {}
+                     else
+                       TagFollow.select(:tag_id).where(tag_id: tags.map(&:id), account_id: current_account_id).each_with_object({}) { |f, h| h[f.tag_id] = true }.merge(options[:following_map] || {})
+                     end
   end
 end
diff --git a/app/serializers/activitypub/note_serializer.rb b/app/serializers/activitypub/note_serializer.rb
index ca067ed9b..52ffaf717 100644
--- a/app/serializers/activitypub/note_serializer.rb
+++ b/app/serializers/activitypub/note_serializer.rb
@@ -32,6 +32,7 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer
 
   def id
     raise Mastodon::NotPermittedError, 'Local-only statuses should not be serialized' if object.local_only? && !instance_options[:allow_local_only]
+
     ActivityPub::TagManager.instance.uri_for(object)
   end
 
diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb
index d753fa51a..45ee06e12 100644
--- a/app/serializers/initial_state_serializer.rb
+++ b/app/serializers/initial_state_serializer.rb
@@ -24,7 +24,6 @@ class InitialStateSerializer < ActiveModel::Serializer
     }
   end
 
-  # rubocop:disable Metrics/AbcSize
   def meta
     store = {
       streaming_api_base_url: Rails.configuration.x.streaming_api_base_url,
@@ -45,7 +44,8 @@ class InitialStateSerializer < ActiveModel::Serializer
       timeline_preview: Setting.timeline_preview,
       activity_api_enabled: Setting.activity_api_enabled,
       single_user_mode: Rails.configuration.x.single_user_mode,
-      translation_enabled: TranslationService.configured?,
+      trends_as_landing_page: Setting.trends_as_landing_page,
+      status_page_url: Setting.status_page_url,
     }
 
     if object.current_account
@@ -77,13 +77,10 @@ class InitialStateSerializer < ActiveModel::Serializer
     store[:disabled_account_id] = object.disabled_account.id.to_s if object.disabled_account
     store[:moved_to_account_id] = object.moved_to_account.id.to_s if object.moved_to_account
 
-    if Rails.configuration.x.single_user_mode
-      store[:owner] = object.owner&.id&.to_s
-    end
+    store[:owner] = object.owner&.id&.to_s if Rails.configuration.x.single_user_mode
 
     store
   end
-  # rubocop:enable Metrics/AbcSize
 
   def compose
     store = {}
diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb
index 9a3ca75dc..d4e7ac974 100644
--- a/app/serializers/rest/account_serializer.rb
+++ b/app/serializers/rest/account_serializer.rb
@@ -16,6 +16,28 @@ class REST::AccountSerializer < ActiveModel::Serializer
   attribute :silenced, key: :limited, if: :silenced?
   attribute :noindex, if: :local?
 
+  attribute :memorial, if: :memorial?
+
+  class AccountDecorator < SimpleDelegator
+    def self.model_name
+      Account.model_name
+    end
+
+    def moved?
+      false
+    end
+  end
+
+  class RoleSerializer < ActiveModel::Serializer
+    attributes :id, :name, :color
+
+    def id
+      object.id.to_s
+    end
+  end
+
+  has_many :roles, serializer: RoleSerializer, if: :local?
+
   class FieldSerializer < ActiveModel::Serializer
     include FormattingHelper
 
@@ -69,7 +91,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
   end
 
   def followers_count
-    (Setting.hide_followers_count || object.user&.setting_hide_followers_count) ? -1 : object.followers_count
+    Setting.hide_followers_count || object.user&.setting_hide_followers_count ? -1 : object.followers_count
   end
 
   def display_name
@@ -89,7 +111,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
   end
 
   def moved_to_account
-    object.suspended? ? nil : object.moved_to_account
+    object.suspended? ? nil : AccountDecorator.new(object.moved_to_account)
   end
 
   def emojis
@@ -108,13 +130,25 @@ class REST::AccountSerializer < ActiveModel::Serializer
     object.silenced?
   end
 
+  def memorial
+    object.memorial?
+  end
+
+  def roles
+    if object.suspended? || object.user.nil?
+      []
+    else
+      [object.user.role].compact.filter(&:highlighted?)
+    end
+  end
+
   def noindex
     object.user_prefers_noindex?
   end
 
-  delegate :suspended?, :silenced?, :local?, to: :object
+  delegate :suspended?, :silenced?, :local?, :memorial?, to: :object
 
   def moved_and_not_nested?
-    object.moved? && object.moved_to_account.moved_to_account_id.nil?
+    object.moved?
   end
 end
diff --git a/app/serializers/rest/admin/account_serializer.rb b/app/serializers/rest/admin/account_serializer.rb
index ad98a53e8..959884c55 100644
--- a/app/serializers/rest/admin/account_serializer.rb
+++ b/app/serializers/rest/admin/account_serializer.rb
@@ -2,7 +2,7 @@
 
 class REST::Admin::AccountSerializer < ActiveModel::Serializer
   attributes :id, :username, :domain, :created_at,
-             :email, :ip, :role, :confirmed, :suspended,
+             :email, :ip, :confirmed, :suspended,
              :silenced, :sensitized, :disabled, :approved, :locale,
              :invite_request
 
@@ -11,6 +11,7 @@ class REST::Admin::AccountSerializer < ActiveModel::Serializer
 
   has_many :ips, serializer: REST::Admin::IpSerializer
   has_one :account, serializer: REST::AccountSerializer
+  has_one :role, serializer: REST::RoleSerializer
 
   def id
     object.id.to_s
diff --git a/app/serializers/rest/admin/webhook_event_serializer.rb b/app/serializers/rest/admin/webhook_event_serializer.rb
index fe0ac23f9..b6d2616e5 100644
--- a/app/serializers/rest/admin/webhook_event_serializer.rb
+++ b/app/serializers/rest/admin/webhook_event_serializer.rb
@@ -7,6 +7,8 @@ class REST::Admin::WebhookEventSerializer < ActiveModel::Serializer
       REST::Admin::AccountSerializer
     when 'Report'
       REST::Admin::ReportSerializer
+    when 'Status'
+      REST::StatusSerializer
     else
       super
     end
diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb
index 5ae1099d0..41adaed80 100644
--- a/app/serializers/rest/instance_serializer.rb
+++ b/app/serializers/rest/instance_serializer.rb
@@ -45,6 +45,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
     {
       urls: {
         streaming: Rails.configuration.x.streaming_api_base_url,
+        status: object.status_page_url,
       },
 
       accounts: {
@@ -55,6 +56,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
         max_characters: StatusLengthValidator::MAX_CHARS,
         max_media_attachments: 4,
         characters_reserved_per_url: StatusLengthValidator::URL_PLACEHOLDER_CHARS,
+        supported_mime_types: HtmlAwareFormatter::STATUS_MIME_TYPES,
       },
 
       media_attachments: {
@@ -94,11 +96,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer
   end
 
   def registrations_message
-    if Setting.closed_registrations_message.present?
-      markdown.render(Setting.closed_registrations_message)
-    else
-      nil
-    end
+    markdown.render(Setting.closed_registrations_message) if Setting.closed_registrations_message.present?
   end
 
   def markdown
diff --git a/app/serializers/rest/mute_serializer.rb b/app/serializers/rest/mute_serializer.rb
index 043a2f059..c9b55ff16 100644
--- a/app/serializers/rest/mute_serializer.rb
+++ b/app/serializers/rest/mute_serializer.rb
@@ -2,7 +2,7 @@
 
 class REST::MuteSerializer < ActiveModel::Serializer
   include RoutingHelper
-  
+
   attributes :id, :account, :target_account, :created_at, :hide_notifications
 
   def account
@@ -12,4 +12,4 @@ class REST::MuteSerializer < ActiveModel::Serializer
   def target_account
     REST::AccountSerializer.new(object.target_account)
   end
-end
\ No newline at end of file
+end
diff --git a/app/serializers/rest/preview_card_serializer.rb b/app/serializers/rest/preview_card_serializer.rb
index 66ff47d22..8413b23d8 100644
--- a/app/serializers/rest/preview_card_serializer.rb
+++ b/app/serializers/rest/preview_card_serializer.rb
@@ -3,7 +3,7 @@
 class REST::PreviewCardSerializer < ActiveModel::Serializer
   include RoutingHelper
 
-  attributes :url, :title, :description, :type,
+  attributes :url, :title, :description, :language, :type,
              :author_name, :author_url, :provider_name,
              :provider_url, :html, :width, :height,
              :image, :embed_url, :blurhash
diff --git a/app/serializers/rest/privacy_policy_serializer.rb b/app/serializers/rest/privacy_policy_serializer.rb
index f0572e714..57a67abf3 100644
--- a/app/serializers/rest/privacy_policy_serializer.rb
+++ b/app/serializers/rest/privacy_policy_serializer.rb
@@ -8,7 +8,7 @@ class REST::PrivacyPolicySerializer < ActiveModel::Serializer
   end
 
   def content
-    markdown.render(object.text % { domain: Rails.configuration.x.local_domain })
+    markdown.render(format(object.text, domain: Rails.configuration.x.local_domain))
   end
 
   private
diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb
index 659c45b83..eb5f3c3ea 100644
--- a/app/serializers/rest/status_serializer.rb
+++ b/app/serializers/rest/status_serializer.rb
@@ -13,7 +13,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
   attribute :muted, if: :current_user?
   attribute :bookmarked, if: :current_user?
   attribute :pinned, if: :pinnable?
-  attribute :local_only if :local?
+  attribute :local_only, if: :local?
   has_many :filtered, serializer: REST::FilterResultSerializer, if: :current_user?
 
   attribute :content, unless: :source_requested?
@@ -32,6 +32,8 @@ class REST::StatusSerializer < ActiveModel::Serializer
   has_one :preview_card, key: :card, serializer: REST::PreviewCardSerializer
   has_one :preloadable_poll, key: :poll, serializer: REST::PollSerializer
 
+  delegate :local?, to: :object
+
   def id
     object.id.to_s
   end
diff --git a/app/serializers/rest/v1/instance_serializer.rb b/app/serializers/rest/v1/instance_serializer.rb
index 389ec7dff..0c2101404 100644
--- a/app/serializers/rest/v1/instance_serializer.rb
+++ b/app/serializers/rest/v1/instance_serializer.rb
@@ -79,6 +79,7 @@ class REST::V1::InstanceSerializer < ActiveModel::Serializer
         max_characters: StatusLengthValidator::MAX_CHARS,
         max_media_attachments: 4,
         characters_reserved_per_url: StatusLengthValidator::URL_PLACEHOLDER_CHARS,
+        supported_mime_types: HtmlAwareFormatter::STATUS_MIME_TYPES,
       },
 
       media_attachments: {
diff --git a/app/serializers/rest/web_push_subscription_serializer.rb b/app/serializers/rest/web_push_subscription_serializer.rb
index 194cc0a8c..674a2d5a8 100644
--- a/app/serializers/rest/web_push_subscription_serializer.rb
+++ b/app/serializers/rest/web_push_subscription_serializer.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class REST::WebPushSubscriptionSerializer < ActiveModel::Serializer
-  attributes :id, :endpoint, :alerts, :server_key
+  attributes :id, :endpoint, :alerts, :server_key, :policy
 
   def alerts
     (object.data&.dig('alerts') || {}).each_with_object({}) { |(k, v), h| h[k] = ActiveModel::Type::Boolean.new.cast(v) }
@@ -10,4 +10,8 @@ class REST::WebPushSubscriptionSerializer < ActiveModel::Serializer
   def server_key
     Rails.configuration.x.vapid_public_key
   end
+
+  def policy
+    object.data&.dig('policy') || 'all'
+  end
 end
diff --git a/app/services/account_search_service.rb b/app/services/account_search_service.rb
index 85538870b..dfc3a45f8 100644
--- a/app/services/account_search_service.rb
+++ b/app/services/account_search_service.rb
@@ -32,15 +32,13 @@ class AccountSearchService < BaseService
 
     return @exact_match if defined?(@exact_match)
 
-    match = begin
-      if options[:resolve]
-        ResolveAccountService.new.call(query)
-      elsif domain_is_local?
-        Account.find_local(query_username)
-      else
-        Account.find_remote(query_username, query_domain)
-      end
-    end
+    match = if options[:resolve]
+              ResolveAccountService.new.call(query)
+            elsif domain_is_local?
+              Account.find_local(query_username)
+            else
+              Account.find_remote(query_username, query_domain)
+            end
 
     match = nil if !match.nil? && !account.nil? && options[:following] && !account.following?(match)
 
diff --git a/app/services/activitypub/fetch_featured_collection_service.rb b/app/services/activitypub/fetch_featured_collection_service.rb
index a746ef4d6..1208820df 100644
--- a/app/services/activitypub/fetch_featured_collection_service.rb
+++ b/app/services/activitypub/fetch_featured_collection_service.rb
@@ -53,7 +53,7 @@ class ActivityPub::FetchFeaturedCollectionService < BaseService
 
       status.id
     rescue ActiveRecord::RecordInvalid => e
-      Rails.logger.debug "Invalid pinned status #{uri}: #{e.message}"
+      Rails.logger.debug { "Invalid pinned status #{uri}: #{e.message}" }
       nil
     end
 
diff --git a/app/services/activitypub/fetch_featured_tags_collection_service.rb b/app/services/activitypub/fetch_featured_tags_collection_service.rb
index ab047a0f8..ff1a88aa1 100644
--- a/app/services/activitypub/fetch_featured_tags_collection_service.rb
+++ b/app/services/activitypub/fetch_featured_tags_collection_service.rb
@@ -22,14 +22,12 @@ class ActivityPub::FetchFeaturedTagsCollectionService < BaseService
     collection = fetch_collection(collection['first']) if collection['first'].present?
 
     while collection.is_a?(Hash)
-      items = begin
-        case collection['type']
-        when 'Collection', 'CollectionPage'
-          collection['items']
-        when 'OrderedCollection', 'OrderedCollectionPage'
-          collection['orderedItems']
-        end
-      end
+      items = case collection['type']
+              when 'Collection', 'CollectionPage'
+                collection['items']
+              when 'OrderedCollection', 'OrderedCollectionPage'
+                collection['orderedItems']
+              end
 
       break if items.blank?
 
diff --git a/app/services/activitypub/fetch_remote_account_service.rb b/app/services/activitypub/fetch_remote_account_service.rb
index 7aba8269e..567dd8a14 100644
--- a/app/services/activitypub/fetch_remote_account_service.rb
+++ b/app/services/activitypub/fetch_remote_account_service.rb
@@ -6,7 +6,7 @@ class ActivityPub::FetchRemoteAccountService < ActivityPub::FetchRemoteActorServ
     actor = super
     return actor if actor.nil? || actor.is_a?(Account)
 
-    Rails.logger.debug "Fetching account #{uri} failed: Expected Account, got #{actor.class.name}"
+    Rails.logger.debug { "Fetching account #{uri} failed: Expected Account, got #{actor.class.name}" }
     raise Error, "Expected Account, got #{actor.class.name}" unless suppress_errors
   end
 end
diff --git a/app/services/activitypub/fetch_remote_actor_service.rb b/app/services/activitypub/fetch_remote_actor_service.rb
index a25fa54c4..c29570086 100644
--- a/app/services/activitypub/fetch_remote_actor_service.rb
+++ b/app/services/activitypub/fetch_remote_actor_service.rb
@@ -28,6 +28,7 @@ class ActivityPub::FetchRemoteActorService < BaseService
     raise Error, "Unsupported JSON-LD context for document #{uri}" unless supported_context?
     raise Error, "Unexpected object type for actor #{uri} (expected any of: #{SUPPORTED_TYPES})" unless expected_type?
     raise Error, "Actor #{uri} has moved to #{@json['movedTo']}" if break_on_redirect && @json['movedTo'].present?
+    raise Error, "Actor #{uri} has no 'preferredUsername', which is a requirement for Mastodon compatibility" if @json['preferredUsername'].blank?
 
     @uri      = @json['id']
     @username = @json['preferredUsername']
@@ -37,7 +38,7 @@ class ActivityPub::FetchRemoteActorService < BaseService
 
     ActivityPub::ProcessAccountService.new.call(@username, @domain, @json, only_key: only_key, verified_webfinger: !only_key, request_id: request_id)
   rescue Error => e
-    Rails.logger.debug "Fetching actor #{uri} failed: #{e.message}"
+    Rails.logger.debug { "Fetching actor #{uri} failed: #{e.message}" }
     raise unless suppress_errors
   end
 
@@ -49,15 +50,14 @@ class ActivityPub::FetchRemoteActorService < BaseService
 
     if @username.casecmp(confirmed_username).zero? && @domain.casecmp(confirmed_domain).zero?
       raise Error, "Webfinger response for #{@username}@#{@domain} does not loop back to #{@uri}" if webfinger.link('self', 'href') != @uri
+
       return
     end
 
     webfinger                            = webfinger!("acct:#{confirmed_username}@#{confirmed_domain}")
     @username, @domain                   = split_acct(webfinger.subject)
 
-    unless confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?
-      raise Webfinger::RedirectError, "Too many webfinger redirects for URI #{@uri} (stopped at #{@username}@#{@domain})"
-    end
+    raise Webfinger::RedirectError, "Too many webfinger redirects for URI #{@uri} (stopped at #{@username}@#{@domain})" unless confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?
 
     raise Error, "Webfinger response for #{@username}@#{@domain} does not loop back to #{@uri}" if webfinger.link('self', 'href') != @uri
   rescue Webfinger::RedirectError => e
diff --git a/app/services/activitypub/fetch_remote_key_service.rb b/app/services/activitypub/fetch_remote_key_service.rb
index 32e82b47a..8eb97c1e6 100644
--- a/app/services/activitypub/fetch_remote_key_service.rb
+++ b/app/services/activitypub/fetch_remote_key_service.rb
@@ -38,7 +38,7 @@ class ActivityPub::FetchRemoteKeyService < BaseService
 
     find_actor(owner_uri, @owner, suppress_errors)
   rescue Error => e
-    Rails.logger.debug "Fetching key #{uri} failed: #{e.message}"
+    Rails.logger.debug { "Fetching key #{uri} failed: #{e.message}" }
     raise unless suppress_errors
   end
 
diff --git a/app/services/activitypub/fetch_remote_status_service.rb b/app/services/activitypub/fetch_remote_status_service.rb
index 21b9242f8..ab0acf7f0 100644
--- a/app/services/activitypub/fetch_remote_status_service.rb
+++ b/app/services/activitypub/fetch_remote_status_service.rb
@@ -2,17 +2,18 @@
 
 class ActivityPub::FetchRemoteStatusService < BaseService
   include JsonLdHelper
+  include Redisable
+
+  DISCOVERIES_PER_REQUEST = 1000
 
   # Should be called when uri has already been checked for locality
   def call(uri, id: true, prefetched_body: nil, on_behalf_of: nil, expected_actor_uri: nil, request_id: nil)
-    @request_id = request_id
-    @json = begin
-      if prefetched_body.nil?
-        fetch_resource(uri, id, on_behalf_of)
-      else
-        body_to_json(prefetched_body, compare_id: id ? uri : nil)
-      end
-    end
+    @request_id = request_id || "#{Time.now.utc.to_i}-status-#{uri}"
+    @json = if prefetched_body.nil?
+              fetch_resource(uri, id, on_behalf_of)
+            else
+              body_to_json(prefetched_body, compare_id: id ? uri : nil)
+            end
 
     return unless supported_context?
 
@@ -42,13 +43,20 @@ class ActivityPub::FetchRemoteStatusService < BaseService
     # activity as an update rather than create
     activity_json['type'] = 'Update' if equals_or_includes_any?(activity_json['type'], %w(Create)) && Status.where(uri: object_uri, account_id: actor.id).exists?
 
-    ActivityPub::Activity.factory(activity_json, actor, request_id: request_id).perform
+    with_redis do |redis|
+      discoveries = redis.incr("status_discovery_per_request:#{@request_id}")
+      redis.expire("status_discovery_per_request:#{@request_id}", 5.minutes.seconds)
+      return nil if discoveries > DISCOVERIES_PER_REQUEST
+    end
+
+    ActivityPub::Activity.factory(activity_json, actor, request_id: @request_id).perform
   end
 
   private
 
   def trustworthy_attribution?(uri, attributed_to)
     return false if uri.nil? || attributed_to.nil?
+
     Addressable::URI.parse(uri).normalized_host.casecmp(Addressable::URI.parse(attributed_to).normalized_host).zero?
   end
 
diff --git a/app/services/activitypub/fetch_replies_service.rb b/app/services/activitypub/fetch_replies_service.rb
index 8cb309e52..3fe150ba2 100644
--- a/app/services/activitypub/fetch_replies_service.rb
+++ b/app/services/activitypub/fetch_replies_service.rb
@@ -3,14 +3,14 @@
 class ActivityPub::FetchRepliesService < BaseService
   include JsonLdHelper
 
-  def call(parent_status, collection_or_uri, allow_synchronous_requests = true)
+  def call(parent_status, collection_or_uri, allow_synchronous_requests: true, request_id: nil)
     @account = parent_status.account
     @allow_synchronous_requests = allow_synchronous_requests
 
     @items = collection_items(collection_or_uri)
     return if @items.nil?
 
-    FetchReplyWorker.push_bulk(filtered_replies)
+    FetchReplyWorker.push_bulk(filtered_replies) { |reply_uri| [reply_uri, { 'request_id' => request_id }] }
 
     @items
   end
@@ -36,6 +36,7 @@ class ActivityPub::FetchRepliesService < BaseService
     return collection_or_uri if collection_or_uri.is_a?(Hash)
     return unless @allow_synchronous_requests
     return if invalid_origin?(collection_or_uri)
+
     fetch_resource_without_id_validation(collection_or_uri, nil, true)
   end
 
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index 2da9096c7..603e4cf48 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -226,6 +226,7 @@ class ActivityPub::ProcessAccountService < BaseService
 
   def property_values
     return unless @json['attachment'].is_a?(Array)
+
     as_array(@json['attachment']).select { |attachment| attachment['type'] == 'PropertyValue' }.map { |attachment| attachment.slice('name', 'value') }
   end
 
@@ -289,6 +290,7 @@ class ActivityPub::ProcessAccountService < BaseService
 
   def domain_block
     return @domain_block if defined?(@domain_block)
+
     @domain_block = DomainBlock.rule_for(@domain)
   end
 
diff --git a/app/services/activitypub/process_collection_service.rb b/app/services/activitypub/process_collection_service.rb
index fffe30195..52f48bd49 100644
--- a/app/services/activitypub/process_collection_service.rb
+++ b/app/services/activitypub/process_collection_service.rb
@@ -11,7 +11,7 @@ class ActivityPub::ProcessCollectionService < BaseService
     begin
       @json = compact(@json) if @json['signature'].is_a?(Hash)
     rescue JSON::LD::JsonLdError => e
-      Rails.logger.debug "Error when compacting JSON-LD document for #{value_or_id(@json['actor'])}: #{e.message}"
+      Rails.logger.debug { "Error when compacting JSON-LD document for #{value_or_id(@json['actor'])}: #{e.message}" }
       @json = original_json.without('signature')
     end
 
@@ -71,8 +71,8 @@ class ActivityPub::ProcessCollectionService < BaseService
     @account = ActivityPub::LinkedDataSignature.new(@json).verify_actor!
     @account = nil unless @account.is_a?(Account)
     @account
-  rescue JSON::LD::JsonLdError => e
-    Rails.logger.debug "Could not verify LD-Signature for #{value_or_id(@json['actor'])}: #{e.message}"
+  rescue JSON::LD::JsonLdError, RDF::WriterError => e
+    Rails.logger.debug { "Could not verify LD-Signature for #{value_or_id(@json['actor'])}: #{e.message}" }
     nil
   end
 end
diff --git a/app/services/activitypub/process_status_update_service.rb b/app/services/activitypub/process_status_update_service.rb
index 11b38ab92..ac7372f74 100644
--- a/app/services/activitypub/process_status_update_service.rb
+++ b/app/services/activitypub/process_status_update_service.rb
@@ -80,9 +80,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
 
         # If a previously existing media attachment was significantly updated, mark
         # media attachments as changed even if none were added or removed
-        if media_attachment_parser.significantly_changes?(media_attachment)
-          @media_attachments_changed = true
-        end
+        @media_attachments_changed = true if media_attachment_parser.significantly_changes?(media_attachment)
 
         media_attachment.description          = media_attachment_parser.description
         media_attachment.focus                = media_attachment_parser.focus
@@ -94,7 +92,7 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
 
         @next_media_attachments << media_attachment
       rescue Addressable::URI::InvalidURIError => e
-        Rails.logger.debug "Invalid URL in attachment: #{e}"
+        Rails.logger.debug { "Invalid URL in attachment: #{e}" }
       end
     end
 
diff --git a/app/services/backup_service.rb b/app/services/backup_service.rb
index f07f407d8..c5e7a8e58 100644
--- a/app/services/backup_service.rb
+++ b/app/services/backup_service.rb
@@ -23,7 +23,7 @@ class BackupService < BaseService
     account.statuses.with_includes.reorder(nil).find_in_batches do |statuses|
       statuses.each do |status|
         item = serialize_payload(ActivityPub::ActivityPresenter.from_status(status), ActivityPub::ActivitySerializer, signer: @account, allow_local_only: true)
-        item.delete(:'@context')
+        item.delete(:@context)
 
         unless item[:type] == 'Announce' || item[:object][:attachment].blank?
           item[:object][:attachment].each do |attachment|
@@ -53,7 +53,7 @@ class BackupService < BaseService
       end
     end
 
-    archive_filename = ['archive', Time.now.utc.strftime('%Y%m%d%H%M%S'), SecureRandom.hex(16)].join('-') + '.tar.gz'
+    archive_filename = "#{['archive', Time.now.utc.strftime('%Y%m%d%H%M%S'), SecureRandom.hex(16)].join('-')}.tar.gz"
 
     @backup.dump      = ActionDispatch::Http::UploadedFile.new(tempfile: tmp_file, filename: archive_filename)
     @backup.processed = true
@@ -86,14 +86,14 @@ class BackupService < BaseService
   def dump_actor!(tar)
     actor = serialize(account, ActivityPub::ActorSerializer)
 
-    actor[:icon][:url]  = 'avatar' + File.extname(actor[:icon][:url])  if actor[:icon]
-    actor[:image][:url] = 'header' + File.extname(actor[:image][:url]) if actor[:image]
+    actor[:icon][:url]  = "avatar#{File.extname(actor[:icon][:url])}"  if actor[:icon]
+    actor[:image][:url] = "header#{File.extname(actor[:image][:url])}" if actor[:image]
     actor[:outbox]      = 'outbox.json'
     actor[:likes]       = 'likes.json'
     actor[:bookmarks]   = 'bookmarks.json'
 
-    download_to_tar(tar, account.avatar, 'avatar' + File.extname(account.avatar.path)) if account.avatar.exists?
-    download_to_tar(tar, account.header, 'header' + File.extname(account.header.path)) if account.header.exists?
+    download_to_tar(tar, account.avatar, "avatar#{File.extname(account.avatar.path)}") if account.avatar.exists?
+    download_to_tar(tar, account.header, "header#{File.extname(account.header.path)}") if account.header.exists?
 
     json = Oj.dump(actor)
 
@@ -154,7 +154,7 @@ class BackupService < BaseService
       object,
       serializer: serializer,
       adapter: ActivityPub::Adapter,
-      allow_local_only: true,
+      allow_local_only: true
     ).as_json
   end
 
diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb
index e2c370057..a48386ba2 100644
--- a/app/services/batched_remove_status_service.rb
+++ b/app/services/batched_remove_status_service.rb
@@ -48,9 +48,9 @@ class BatchedRemoveStatusService < BaseService
 
     # Cannot be batched
     @status_id_cutoff = Mastodon::Snowflake.id_at(2.weeks.ago)
-    redis.pipelined do
+    redis.pipelined do |pipeline|
       statuses.each do |status|
-        unpush_from_public_timelines(status)
+        unpush_from_public_timelines(status, pipeline)
       end
     end
   end
@@ -73,22 +73,22 @@ class BatchedRemoveStatusService < BaseService
     end
   end
 
-  def unpush_from_public_timelines(status)
+  def unpush_from_public_timelines(status, pipeline)
     return unless status.public_visibility? && status.id > @status_id_cutoff
 
     payload = Oj.dump(event: :delete, payload: status.id.to_s)
 
-    redis.publish('timeline:public', payload)
-    redis.publish(status.local? ? 'timeline:public:local' : 'timeline:public:remote', payload)
+    pipeline.publish('timeline:public', payload)
+    pipeline.publish(status.local? ? 'timeline:public:local' : 'timeline:public:remote', payload)
 
     if status.media_attachments.any?
-      redis.publish('timeline:public:media', payload)
-      redis.publish(status.local? ? 'timeline:public:local:media' : 'timeline:public:remote:media', payload)
+      pipeline.publish('timeline:public:media', payload)
+      pipeline.publish(status.local? ? 'timeline:public:local:media' : 'timeline:public:remote:media', payload)
     end
 
     status.tags.map { |tag| tag.name.mb_chars.downcase }.each do |hashtag|
-      redis.publish("timeline:hashtag:#{hashtag}", payload)
-      redis.publish("timeline:hashtag:#{hashtag}:local", payload) if status.local?
+      pipeline.publish("timeline:hashtag:#{hashtag}", payload)
+      pipeline.publish("timeline:hashtag:#{hashtag}:local", payload) if status.local?
     end
   end
 
diff --git a/app/services/delete_account_service.rb b/app/services/delete_account_service.rb
index 3a082c7c8..190a72e5c 100644
--- a/app/services/delete_account_service.rb
+++ b/app/services/delete_account_service.rb
@@ -257,17 +257,17 @@ class DeleteAccountService < BaseService
   end
 
   def delete_actor!
-    ActivityPub::DeliveryWorker.push_bulk(delivery_inboxes) do |inbox_url|
+    ActivityPub::DeliveryWorker.push_bulk(delivery_inboxes, limit: 1_000) do |inbox_url|
       [delete_actor_json, @account.id, inbox_url]
     end
 
-    ActivityPub::LowPriorityDeliveryWorker.push_bulk(low_priority_delivery_inboxes) do |inbox_url|
+    ActivityPub::LowPriorityDeliveryWorker.push_bulk(low_priority_delivery_inboxes, limit: 1_000) do |inbox_url|
       [delete_actor_json, @account.id, inbox_url]
     end
   end
 
   def delete_actor_json
-    @delete_actor_json ||= Oj.dump(serialize_payload(@account, ActivityPub::DeleteActorSerializer))
+    @delete_actor_json ||= Oj.dump(serialize_payload(@account, ActivityPub::DeleteActorSerializer, signer: @account, always_sign: true))
   end
 
   def delivery_inboxes
diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb
index 8e74e152e..3b14a6748 100644
--- a/app/services/fan_out_on_write_service.rb
+++ b/app/services/fan_out_on_write_service.rb
@@ -116,7 +116,7 @@ class FanOutOnWriteService < BaseService
   end
 
   def deliver_to_direct_timelines!
-    FeedInsertWorker.push_bulk(@status.mentions.includes(:account).map(&:account).select { |mentioned_account| mentioned_account.local? }) do |account|
+    FeedInsertWorker.push_bulk(@status.mentions.includes(:account).map(&:account).select(&:local?)) do |account|
       [@status.id, account.id, 'direct', { 'update' => update? }]
     end
   end
diff --git a/app/services/favourite_service.rb b/app/services/favourite_service.rb
index dc7fe8855..6fdc92a17 100644
--- a/app/services/favourite_service.rb
+++ b/app/services/favourite_service.rb
@@ -40,6 +40,7 @@ class FavouriteService < BaseService
   def bump_potential_friendship(account, status)
     ActivityTracker.increment('activity:interactions')
     return if account.following?(status.account_id)
+
     PotentialFriendshipTracker.record(account.id, status.account_id, :favourite)
   end
 
diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb
index e5b5b730e..8d07958b7 100644
--- a/app/services/fetch_link_card_service.rb
+++ b/app/services/fetch_link_card_service.rb
@@ -30,7 +30,7 @@ class FetchLinkCardService < BaseService
 
     attach_card if @card&.persisted?
   rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
-    Rails.logger.debug "Error fetching link #{@original_url}: #{e}"
+    Rails.logger.debug { "Error fetching link #{@original_url}: #{e}" }
     nil
   end
 
@@ -45,7 +45,7 @@ class FetchLinkCardService < BaseService
   def html
     return @html if defined?(@html)
 
-    Request.new(:get, @url).add_headers('Accept' => 'text/html', 'User-Agent' => Mastodon::Version.user_agent + ' Bot').perform do |res|
+    Request.new(:get, @url).add_headers('Accept' => 'text/html', 'User-Agent' => "#{Mastodon::Version.user_agent} Bot").perform do |res|
       # We follow redirects, and ideally we want to save the preview card for
       # the destination URL and not any link shortener in-between, so here
       # we set the URL to the one of the last response in the redirect chain
@@ -69,16 +69,14 @@ class FetchLinkCardService < BaseService
   end
 
   def parse_urls
-    urls = begin
-      if @status.local?
-        @status.text.scan(URL_PATTERN).map { |array| Addressable::URI.parse(array[1]).normalize }
-      else
-        document = Nokogiri::HTML(@status.text)
-        links    = document.css('a')
-
-        links.filter_map { |a| Addressable::URI.parse(a['href']) unless skip_link?(a) }.filter_map(&:normalize)
-      end
-    end
+    urls = if @status.local?
+             @status.text.scan(URL_PATTERN).map { |array| Addressable::URI.parse(array[1]).normalize }
+           else
+             document = Nokogiri::HTML(@status.text)
+             links = document.css('a')
+
+             links.filter_map { |a| Addressable::URI.parse(a['href']) unless skip_link?(a) }.filter_map(&:normalize)
+           end
 
     urls.reject { |uri| bad_url?(uri) }.first
   end
diff --git a/app/services/fetch_oembed_service.rb b/app/services/fetch_oembed_service.rb
index 7d0879c79..9851ac098 100644
--- a/app/services/fetch_oembed_service.rb
+++ b/app/services/fetch_oembed_service.rb
@@ -82,7 +82,7 @@ class FetchOEmbedService
     return if @endpoint_url.blank?
 
     body = Request.new(:get, @endpoint_url).perform do |res|
-      res.code != 200 ? nil : res.body_with_limit
+      res.code == 200 ? res.body_with_limit : nil
     end
 
     validate(parse_for_format(body)) if body.present?
diff --git a/app/services/fetch_remote_status_service.rb b/app/services/fetch_remote_status_service.rb
index eafde4d4a..08c2d24ba 100644
--- a/app/services/fetch_remote_status_service.rb
+++ b/app/services/fetch_remote_status_service.rb
@@ -1,7 +1,7 @@
 # frozen_string_literal: true
 
 class FetchRemoteStatusService < BaseService
-  def call(url, prefetched_body = nil)
+  def call(url, prefetched_body: nil, request_id: nil)
     if prefetched_body.nil?
       resource_url, resource_options = FetchResourceService.new.call(url)
     else
@@ -9,6 +9,6 @@ class FetchRemoteStatusService < BaseService
       resource_options = { prefetched_body: prefetched_body }
     end
 
-    ActivityPub::FetchRemoteStatusService.new.call(resource_url, **resource_options) unless resource_url.nil?
+    ActivityPub::FetchRemoteStatusService.new.call(resource_url, **resource_options.merge(request_id: request_id)) unless resource_url.nil?
   end
 end
diff --git a/app/services/fetch_resource_service.rb b/app/services/fetch_resource_service.rb
index 73204e55d..4470fca01 100644
--- a/app/services/fetch_resource_service.rb
+++ b/app/services/fetch_resource_service.rb
@@ -12,7 +12,7 @@ class FetchResourceService < BaseService
 
     process(url)
   rescue HTTP::Error, OpenSSL::SSL::SSLError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
-    Rails.logger.debug "Error fetching resource #{@url}: #{e}"
+    Rails.logger.debug { "Error fetching resource #{@url}: #{e}" }
     nil
   end
 
diff --git a/app/services/follow_migration_service.rb b/app/services/follow_migration_service.rb
new file mode 100644
index 000000000..cfe9093cb
--- /dev/null
+++ b/app/services/follow_migration_service.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+class FollowMigrationService < FollowService
+  # Follow an account with the same settings as another account, and unfollow the old account once the request is sent
+  # @param [Account] source_account From which to follow
+  # @param [Account] target_account Account to follow
+  # @param [Account] old_target_account Account to unfollow once the follow request has been sent to the new one
+  # @option [Boolean] bypass_locked Whether to immediately follow the new account even if it is locked
+  def call(source_account, target_account, old_target_account, bypass_locked: false)
+    @old_target_account = old_target_account
+
+    follow    = source_account.active_relationships.find_by(target_account: old_target_account)
+    reblogs   = follow&.show_reblogs?
+    notify    = follow&.notify?
+    languages = follow&.languages
+
+    super(source_account, target_account, reblogs: reblogs, notify: notify, languages: languages, bypass_locked: bypass_locked, bypass_limit: true)
+  end
+
+  private
+
+  def request_follow!
+    follow_request = @source_account.request_follow!(@target_account, **follow_options.merge(rate_limit: @options[:with_rate_limit], bypass_limit: @options[:bypass_limit]))
+
+    if @target_account.local?
+      LocalNotificationWorker.perform_async(@target_account.id, follow_request.id, follow_request.class.name, 'follow_request')
+      UnfollowService.new.call(@source_account, @old_target_account, skip_unmerge: true)
+    elsif @target_account.activitypub?
+      ActivityPub::MigratedFollowDeliveryWorker.perform_async(build_json(follow_request), @source_account.id, @target_account.inbox_url, @old_target_account.id)
+    end
+
+    follow_request
+  end
+
+  def direct_follow!
+    follow = super
+    UnfollowService.new.call(@source_account, @old_target_account, skip_unmerge: true)
+    follow
+  end
+end
diff --git a/app/services/import_service.rb b/app/services/import_service.rb
index 2f48abc36..56f191c1f 100644
--- a/app/services/import_service.rb
+++ b/app/services/import_service.rb
@@ -67,7 +67,7 @@ class ImportService < BaseService
 
   def import_relationships!(action, undo_action, overwrite_scope, limit, extra_fields = {})
     local_domain_suffix = "@#{Rails.configuration.x.local_domain}"
-    items = @data.take(limit).map { |row| [row['Account address']&.strip&.delete_suffix(local_domain_suffix), Hash[extra_fields.map { |key, field_settings| [key, row[field_settings[:header]]&.strip || field_settings[:default]] }]] }.reject { |(id, _)| id.blank? }
+    items = @data.take(limit).map { |row| [row['Account address']&.strip&.delete_suffix(local_domain_suffix), extra_fields.to_h { |key, field_settings| [key, row[field_settings[:header]]&.strip || field_settings[:default]] }] }.reject { |(id, _)| id.blank? }
 
     if @import.overwrite?
       presence_hash = items.each_with_object({}) { |(id, extra), mapping| mapping[id] = [true, extra] }
@@ -114,13 +114,13 @@ class ImportService < BaseService
       status || ActivityPub::FetchRemoteStatusService.new.call(uri)
     rescue HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::UnexpectedResponseError
       nil
-    rescue StandardError => e
+    rescue => e
       Rails.logger.warn "Unexpected error when importing bookmark: #{e}"
       nil
     end
 
     account_ids         = statuses.map(&:account_id)
-    preloaded_relations = relations_map_for_account(@account, account_ids)
+    preloaded_relations = @account.relations_map(account_ids, skip_blocking_and_muting: true)
 
     statuses.keep_if { |status| StatusPolicy.new(@account, status, preloaded_relations).show? }
 
@@ -138,14 +138,4 @@ class ImportService < BaseService
   def import_data
     Paperclip.io_adapters.for(@import.data).read.force_encoding(Encoding::UTF_8)
   end
-
-  def relations_map_for_account(account, account_ids)
-    {
-      blocking: {},
-      blocked_by: Account.blocked_by_map(account_ids, account.id),
-      muting: {},
-      following: Account.following_map(account_ids, account.id),
-      domain_blocking_by_domain: {},
-    }
-  end
 end
diff --git a/app/services/keys/claim_service.rb b/app/services/keys/claim_service.rb
index ae9e24a24..ebce9cce7 100644
--- a/app/services/keys/claim_service.rb
+++ b/app/services/keys/claim_service.rb
@@ -9,10 +9,10 @@ class Keys::ClaimService < BaseService
 
     def initialize(account, device_id, key_attributes = {})
       super(
-        account:   account,
+        account: account,
         device_id: device_id,
-        key_id:    key_attributes[:key_id],
-        key:       key_attributes[:key],
+        key_id: key_attributes[:key_id],
+        key: key_attributes[:key],
         signature: key_attributes[:signature],
       )
     end
@@ -58,7 +58,7 @@ class Keys::ClaimService < BaseService
 
     @result = Result.new(@target_account, @device_id, key_id: json['id'], key: json['publicKeyBase64'], signature: json.dig('signature', 'signatureValue'))
   rescue HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error => e
-    Rails.logger.debug "Claiming one-time key for #{@target_account.acct}:#{@device_id} failed: #{e}"
+    Rails.logger.debug { "Claiming one-time key for #{@target_account.acct}:#{@device_id} failed: #{e}" }
     nil
   end
 
diff --git a/app/services/keys/query_service.rb b/app/services/keys/query_service.rb
index ac3388bdc..14c9d9205 100644
--- a/app/services/keys/query_service.rb
+++ b/app/services/keys/query_service.rb
@@ -23,9 +23,9 @@ class Keys::QueryService < BaseService
 
     def initialize(attributes = {})
       super(
-        device_id:       attributes[:device_id],
-        name:            attributes[:name],
-        identity_key:    attributes[:identity_key],
+        device_id: attributes[:device_id],
+        name: attributes[:name],
+        identity_key: attributes[:identity_key],
         fingerprint_key: attributes[:fingerprint_key],
       )
       @claim_url = attributes[:claim_url]
@@ -73,7 +73,7 @@ class Keys::QueryService < BaseService
       Device.new(device_id: device['id'], name: device['name'], identity_key: device.dig('identityKey', 'publicKeyBase64'), fingerprint_key: device.dig('fingerprintKey', 'publicKeyBase64'), claim_url: device['claim'])
     end
   rescue HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::Error => e
-    Rails.logger.debug "Querying devices for #{@account.acct} failed: #{e}"
+    Rails.logger.debug { "Querying devices for #{@account.acct} failed: #{e}" }
     nil
   end
 end
diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb
index c7454fc60..069f370cf 100644
--- a/app/services/notify_service.rb
+++ b/app/services/notify_service.rb
@@ -3,6 +3,12 @@
 class NotifyService < BaseService
   include Redisable
 
+  NON_EMAIL_TYPES = %i(
+    admin.report
+    admin.sign_up
+    update
+  ).freeze
+
   def call(recipient, type, activity)
     @recipient    = recipient
     @activity     = activity
@@ -31,15 +37,16 @@ class NotifyService < BaseService
 
   def following_sender?
     return @following_sender if defined?(@following_sender)
+
     @following_sender = @recipient.following?(@notification.from_account) || @recipient.requested?(@notification.from_account)
   end
 
   def optional_non_follower?
-    @recipient.user.settings.interactions['must_be_follower']  && !@notification.from_account.following?(@recipient)
+    @recipient.user.settings['interactions.must_be_follower']  && !@notification.from_account.following?(@recipient)
   end
 
   def optional_non_following?
-    @recipient.user.settings.interactions['must_be_following'] && !following_sender?
+    @recipient.user.settings['interactions.must_be_following'] && !following_sender?
   end
 
   def message?
@@ -81,7 +88,7 @@ class NotifyService < BaseService
 
   def optional_non_following_and_direct?
     direct_message? &&
-      @recipient.user.settings.interactions['must_be_following_dm'] &&
+      @recipient.user.settings['interactions.must_be_following_dm'] &&
       !following_sender? &&
       !response_to_recipient?
   end
@@ -170,6 +177,6 @@ class NotifyService < BaseService
   end
 
   def send_email_for_notification_type?
-    @recipient.user.settings.notification_emails[@notification.type.to_s]
+    NON_EMAIL_TYPES.exclude?(@notification.type) && @recipient.user.settings["notification_emails.#{@notification.type}"]
   end
 end
diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb
index bcda001f5..74ec47a33 100644
--- a/app/services/post_status_service.rb
+++ b/app/services/post_status_service.rb
@@ -6,6 +6,15 @@ class PostStatusService < BaseService
 
   MIN_SCHEDULE_OFFSET = 5.minutes.freeze
 
+  class UnexpectedMentionsError < StandardError
+    attr_reader :accounts
+
+    def initialize(message, accounts)
+      super(message)
+      @accounts = accounts
+    end
+  end
+
   # Post a text status update, fetch and notify remote users mentioned
   # @param [Account] account Account from which to post
   # @param [Hash] options
@@ -21,6 +30,7 @@ class PostStatusService < BaseService
   # @option [Doorkeeper::Application] :application
   # @option [String] :idempotency Optional idempotency key
   # @option [Boolean] :with_rate_limit
+  # @option [Enumerable] :allowed_mentions Optional array of expected mentioned account IDs, raises `UnexpectedMentionsError` if unexpected accounts end up in mentions
   # @return [Status]
   def call(account, options = {})
     @account     = account
@@ -51,17 +61,22 @@ class PostStatusService < BaseService
 
   private
 
-  def preprocess_attributes!
-    if @text.blank? && @options[:spoiler_text].present?
-     @text = '.'
-     if @media&.find(&:video?) || @media&.find(&:gifv?)
-       @text = '📹'
-     elsif @media&.find(&:audio?)
-       @text = '🎵'
-     elsif @media&.find(&:image?)
-       @text = '🖼'
-     end
+  def fill_blank_text!
+    return unless @text.blank? && @options[:spoiler_text].present?
+
+    if @media&.any?(&:video?) || @media&.any?(&:gifv?)
+      @text = '📹'
+    elsif @media&.any?(&:audio?)
+      @text = '🎵'
+    elsif @media&.any?(&:image?)
+      @text = '🖼'
+    else
+      @text = '.'
     end
+  end
+
+  def preprocess_attributes!
+    fill_blank_text!
     @sensitive    = (@options[:sensitive].nil? ? @account.user&.setting_default_sensitive : @options[:sensitive]) || @options[:spoiler_text].present?
     @visibility   = @options[:visibility] || @account.user&.setting_default_privacy
     @visibility   = :unlisted if @visibility&.to_sym == :public && @account.silenced?
@@ -72,14 +87,28 @@ class PostStatusService < BaseService
   end
 
   def process_status!
+    @status = @account.statuses.new(status_attributes)
+    process_mentions_service.call(@status, save_records: false)
+    safeguard_mentions!(@status)
+
     # The following transaction block is needed to wrap the UPDATEs to
     # the media attachments when the status is created
-
     ApplicationRecord.transaction do
-      @status = @account.statuses.create!(status_attributes)
+      @status.save!
     end
   end
 
+  def safeguard_mentions!(status)
+    return if @options[:allowed_mentions].nil?
+
+    expected_account_ids = @options[:allowed_mentions].map(&:to_i)
+
+    unexpected_accounts = status.mentions.map(&:account).to_a.reject { |mentioned_account| expected_account_ids.include?(mentioned_account.id) }
+    return if unexpected_accounts.empty?
+
+    raise UnexpectedMentionsError.new('Post would be sent to unexpected accounts', unexpected_accounts)
+  end
+
   def schedule_status!
     status_for_validation = @account.statuses.build(status_attributes)
 
@@ -102,7 +131,6 @@ class PostStatusService < BaseService
 
   def postprocess_status!
     process_hashtags_service.call(@status)
-    process_mentions_service.call(@status)
     Trends.tags.register(@status)
     LinkCrawlWorker.perform_async(@status.id)
     DistributionWorker.perform_async(@status.id)
@@ -162,8 +190,10 @@ class PostStatusService < BaseService
 
   def bump_potential_friendship!
     return if !@status.reply? || @account.id == @status.in_reply_to_account_id
+
     ActivityTracker.increment('activity:interactions')
     return if @account.following?(@status.in_reply_to_account_id)
+
     PotentialFriendshipTracker.record(@account.id, @status.in_reply_to_account_id, :reply)
   end
 
diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb
index b117db8c2..b3b279147 100644
--- a/app/services/process_mentions_service.rb
+++ b/app/services/process_mentions_service.rb
@@ -3,12 +3,13 @@
 class ProcessMentionsService < BaseService
   include Payloadable
 
-  # Scan status for mentions and fetch remote mentioned users, create
-  # local mention pointers, send Salmon notifications to mentioned
-  # remote users
+  # Scan status for mentions and fetch remote mentioned users,
+  # and create local mention pointers
   # @param [Status] status
-  def call(status)
+  # @param [Boolean] save_records Whether to save records in database
+  def call(status, save_records: true)
     @status = status
+    @save_records = save_records
 
     return unless @status.local?
 
@@ -27,13 +28,11 @@ class ProcessMentionsService < BaseService
     @status.text = @status.text.gsub(Account::MENTION_RE) do |match|
       username, domain = Regexp.last_match(1).split('@')
 
-      domain = begin
-        if TagManager.instance.local_domain?(domain)
-          nil
-        else
-          TagManager.instance.normalize_domain(domain)
-        end
-      end
+      domain = if TagManager.instance.local_domain?(domain)
+                 nil
+               else
+                 TagManager.instance.normalize_domain(domain)
+               end
 
       mentioned_account = Account.find_remote(username, domain)
 
@@ -55,14 +54,15 @@ class ProcessMentionsService < BaseService
       next match if mention_undeliverable?(mentioned_account) || mentioned_account&.suspended?
 
       mention   = @previous_mentions.find { |x| x.account_id == mentioned_account.id }
-      mention ||= mentioned_account.mentions.new(status: @status)
+      mention ||= @current_mentions.find  { |x| x.account_id == mentioned_account.id }
+      mention ||= @status.mentions.new(account: mentioned_account)
 
       @current_mentions << mention
 
       "@#{mentioned_account.acct}"
     end
 
-    @status.save!
+    @status.save! if @save_records
   end
 
   def assign_mentions!
@@ -73,11 +73,12 @@ class ProcessMentionsService < BaseService
       mentioned_account_ids = @current_mentions.map(&:account_id)
       blocked_account_ids = Set.new(@status.account.block_relationships.where(target_account_id: mentioned_account_ids).pluck(:target_account_id))
 
-      @current_mentions.select! { |mention| !(blocked_account_ids.include?(mention.account_id) || blocked_domains.include?(mention.account.domain)) }
+      dropped_mentions, @current_mentions = @current_mentions.partition { |mention| blocked_account_ids.include?(mention.account_id) || blocked_domains.include?(mention.account.domain) }
+      dropped_mentions.each(&:destroy)
     end
 
     @current_mentions.each do |mention|
-      mention.save if mention.new_record?
+      mention.save if mention.new_record? && @save_records
     end
 
     # If previous mentions are no longer contained in the text, convert them
diff --git a/app/services/reblog_service.rb b/app/services/reblog_service.rb
index 97f9f8e92..b73669f9d 100644
--- a/app/services/reblog_service.rb
+++ b/app/services/reblog_service.rb
@@ -20,13 +20,11 @@ class ReblogService < BaseService
 
     return reblog unless reblog.nil?
 
-    visibility = begin
-      if reblogged_status.hidden?
-        reblogged_status.visibility
-      else
-        options[:visibility] || account.user&.setting_default_privacy
-      end
-    end
+    visibility = if reblogged_status.hidden?
+                   reblogged_status.visibility
+                 else
+                   options[:visibility] || account.user&.setting_default_privacy
+                 end
 
     reblog = account.statuses.create!(reblog: reblogged_status, text: '', visibility: visibility, rate_limit: options[:with_rate_limit])
 
diff --git a/app/services/remove_domains_from_followers_service.rb b/app/services/remove_domains_from_followers_service.rb
new file mode 100644
index 000000000..d76763409
--- /dev/null
+++ b/app/services/remove_domains_from_followers_service.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class RemoveDomainsFromFollowersService < BaseService
+  include Payloadable
+
+  def call(source_account, target_domains)
+    source_account.passive_relationships.where(account_id: Account.where(domain: target_domains)).find_each do |follow|
+      follow.destroy
+
+      create_notification(follow) if source_account.local? && !follow.account.local? && follow.account.activitypub?
+    end
+  end
+
+  private
+
+  def create_notification(follow)
+    ActivityPub::DeliveryWorker.perform_async(build_json(follow), follow.target_account_id, follow.account.inbox_url)
+  end
+
+  def build_json(follow)
+    Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer))
+  end
+end
diff --git a/app/services/remove_from_followers_service.rb b/app/services/remove_from_followers_service.rb
index 3dac5467f..007d5b1fd 100644
--- a/app/services/remove_from_followers_service.rb
+++ b/app/services/remove_from_followers_service.rb
@@ -7,9 +7,7 @@ class RemoveFromFollowersService < BaseService
     source_account.passive_relationships.where(account_id: target_accounts).find_each do |follow|
       follow.destroy
 
-      if source_account.local? && !follow.account.local? && follow.account.activitypub?
-        create_notification(follow)
-      end
+      create_notification(follow) if source_account.local? && !follow.account.local? && follow.account.activitypub?
     end
   end
 
diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb
index 58bf1dcb7..4f98ccea7 100644
--- a/app/services/remove_status_service.rb
+++ b/app/services/remove_status_service.rb
@@ -90,7 +90,7 @@ class RemoveStatusService < BaseService
 
     status_reach_finder = StatusReachFinder.new(@status, unsafe: true)
 
-    ActivityPub::DeliveryWorker.push_bulk(status_reach_finder.inboxes) do |inbox_url|
+    ActivityPub::DeliveryWorker.push_bulk(status_reach_finder.inboxes, limit: 1_000) do |inbox_url|
       [signed_activity_json, @account.id, inbox_url]
     end
   end
diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb
index d8b81a7b9..abe1534a5 100644
--- a/app/services/resolve_account_service.rb
+++ b/app/services/resolve_account_service.rb
@@ -54,7 +54,7 @@ class ResolveAccountService < BaseService
 
     fetch_account!
   rescue Webfinger::Error => e
-    Rails.logger.debug "Webfinger query for #{@uri} failed: #{e}"
+    Rails.logger.debug { "Webfinger query for #{@uri} failed: #{e}" }
     raise unless @options[:suppress_errors]
   end
 
@@ -71,13 +71,11 @@ class ResolveAccountService < BaseService
       @username, @domain = uri.strip.gsub(/\A@/, '').split('@')
     end
 
-    @domain = begin
-      if TagManager.instance.local_domain?(@domain)
-        nil
-      else
-        TagManager.instance.normalize_domain(@domain)
-      end
-    end
+    @domain = if TagManager.instance.local_domain?(@domain)
+                nil
+              else
+                TagManager.instance.normalize_domain(@domain)
+              end
 
     @uri = [@username, @domain].compact.join('@')
   end
@@ -96,9 +94,7 @@ class ResolveAccountService < BaseService
     @webfinger         = webfinger!("acct:#{confirmed_username}@#{confirmed_domain}")
     @username, @domain = split_acct(@webfinger.subject)
 
-    unless confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?
-      raise Webfinger::RedirectError, "Too many webfinger redirects for URI #{uri} (stopped at #{@username}@#{@domain})"
-    end
+    raise Webfinger::RedirectError, "Too many webfinger redirects for URI #{uri} (stopped at #{@username}@#{@domain})" unless confirmed_username.casecmp(@username).zero? && confirmed_domain.casecmp(@domain).zero?
   rescue Webfinger::GoneError
     @gone = true
   end
diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb
index 52f35daf3..d8e795f3b 100644
--- a/app/services/resolve_url_service.rb
+++ b/app/services/resolve_url_service.rb
@@ -25,7 +25,7 @@ class ResolveURLService < BaseService
     if equals_or_includes_any?(type, ActivityPub::FetchRemoteActorService::SUPPORTED_TYPES)
       ActivityPub::FetchRemoteActorService.new.call(resource_url, prefetched_body: body)
     elsif equals_or_includes_any?(type, ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES)
-      status = FetchRemoteStatusService.new.call(resource_url, body)
+      status = FetchRemoteStatusService.new.call(resource_url, prefetched_body: body)
       authorize_with @on_behalf_of, status, :show? unless status.nil?
       status
     end
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
index 1a76cbb38..b1ce5453f 100644
--- a/app/services/search_service.rb
+++ b/app/services/search_service.rb
@@ -37,9 +37,7 @@ class SearchService < BaseService
   def perform_statuses_search!
     definition = parsed_query.apply(StatusesIndex.filter(term: { searchable_by: @account.id }))
 
-    if @options[:account_id].present?
-      definition = definition.filter(term: { account_id: @options[:account_id] })
-    end
+    definition = definition.filter(term: { account_id: @options[:account_id] }) if @options[:account_id].present?
 
     if @options[:min_id].present? || @options[:max_id].present?
       range      = {}
@@ -51,7 +49,7 @@ class SearchService < BaseService
     results             = definition.limit(@limit).offset(@offset).objects.compact
     account_ids         = results.map(&:account_id)
     account_domains     = results.map(&:account_domain)
-    preloaded_relations = relations_map_for_account(@account, account_ids, account_domains)
+    preloaded_relations = @account.relations_map(account_ids, account_domains)
 
     results.reject { |status| StatusFilter.new(status, @account, preloaded_relations).filtered? }
   rescue Faraday::ConnectionFailed, Parslet::ParseFailed
@@ -113,16 +111,6 @@ class SearchService < BaseService
     @options[:type].blank? || @options[:type] == 'statuses'
   end
 
-  def relations_map_for_account(account, account_ids, domains)
-    {
-      blocking: Account.blocking_map(account_ids, account.id),
-      blocked_by: Account.blocked_by_map(account_ids, account.id),
-      muting: Account.muting_map(account_ids, account.id),
-      following: Account.following_map(account_ids, account.id),
-      domain_blocking_by_domain: Account.domain_blocking_map_by_domain(domains, account.id),
-    }
-  end
-
   def parsed_query
     SearchQueryTransformer.new.apply(SearchQueryParser.new.parse(@query))
   end
diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb
index 211544fea..cfb3eb583 100644
--- a/app/services/suspend_account_service.rb
+++ b/app/services/suspend_account_service.rb
@@ -31,13 +31,13 @@ class SuspendAccountService < BaseService
     # counterpart to this operation, i.e. you can't then force a remote
     # account to re-follow you, so this part is not reversible.
 
-    follows = Follow.where(account: @account).to_a
+    Follow.where(account: @account).find_in_batches do |follows|
+      ActivityPub::DeliveryWorker.push_bulk(follows) do |follow|
+        [Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), follow.target_account_id, @account.inbox_url]
+      end
 
-    ActivityPub::DeliveryWorker.push_bulk(follows) do |follow|
-      [Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), follow.target_account_id, @account.inbox_url]
+      follows.each(&:destroy)
     end
-
-    follows.each(&:destroy)
   end
 
   def distribute_update_actor!
@@ -45,7 +45,7 @@ class SuspendAccountService < BaseService
 
     account_reach_finder = AccountReachFinder.new(@account)
 
-    ActivityPub::DeliveryWorker.push_bulk(account_reach_finder.inboxes) do |inbox_url|
+    ActivityPub::DeliveryWorker.push_bulk(account_reach_finder.inboxes, limit: 1_000) do |inbox_url|
       [signed_activity_json, @account.id, inbox_url]
     end
   end
diff --git a/app/services/translate_status_service.rb b/app/services/translate_status_service.rb
index 539a0d9db..796f13a0d 100644
--- a/app/services/translate_status_service.rb
+++ b/app/services/translate_status_service.rb
@@ -6,19 +6,29 @@ class TranslateStatusService < BaseService
   include FormattingHelper
 
   def call(status, target_language)
-    raise Mastodon::NotPermittedError unless status.public_visibility? || status.unlisted_visibility?
-
     @status = status
     @content = status_content_format(@status)
     @target_language = target_language
 
+    raise Mastodon::NotPermittedError unless permitted?
+
     Rails.cache.fetch("translations/#{@status.language}/#{@target_language}/#{content_hash}", expires_in: CACHE_TTL) { translation_backend.translate(@content, @status.language, @target_language) }
   end
 
   private
 
   def translation_backend
-    TranslationService.configured
+    @translation_backend ||= TranslationService.configured
+  end
+
+  def permitted?
+    return false unless @status.distributable? && @status.content.present? && TranslationService.configured?
+
+    languages[@status.language]&.include?(@target_language)
+  end
+
+  def languages
+    Rails.cache.fetch('translation_service/languages', expires_in: 7.days, race_condition_ttl: 1.hour) { TranslationService.configured.languages }
   end
 
   def content_hash
diff --git a/app/services/unsuspend_account_service.rb b/app/services/unsuspend_account_service.rb
index 70667308e..d851a0f70 100644
--- a/app/services/unsuspend_account_service.rb
+++ b/app/services/unsuspend_account_service.rb
@@ -41,7 +41,7 @@ class UnsuspendAccountService < BaseService
 
     account_reach_finder = AccountReachFinder.new(@account)
 
-    ActivityPub::DeliveryWorker.push_bulk(account_reach_finder.inboxes) do |inbox_url|
+    ActivityPub::DeliveryWorker.push_bulk(account_reach_finder.inboxes, limit: 1_000) do |inbox_url|
       [signed_activity_json, @account.id, inbox_url]
     end
   end
diff --git a/app/services/update_account_service.rb b/app/services/update_account_service.rb
index 71976ab00..4604d71b2 100644
--- a/app/services/update_account_service.rb
+++ b/app/services/update_account_service.rb
@@ -22,7 +22,7 @@ class UpdateAccountService < BaseService
   def authorize_all_follow_requests(account)
     follow_requests = FollowRequest.where(target_account: account)
     follow_requests = follow_requests.preload(:account).select { |req| !req.account.silenced? }
-    AuthorizeFollowWorker.push_bulk(follow_requests) do |req|
+    AuthorizeFollowWorker.push_bulk(follow_requests, limit: 1_000) do |req|
       [req.account_id, req.target_account_id]
     end
   end
diff --git a/app/services/update_status_service.rb b/app/services/update_status_service.rb
index cc4ec670d..de6f1e6d1 100644
--- a/app/services/update_status_service.rb
+++ b/app/services/update_status_service.rb
@@ -10,6 +10,7 @@ class UpdateStatusService < BaseService
   # @param [Integer] account_id
   # @param [Hash] options
   # @option options [Array<Integer>] :media_ids
+  # @option options [Array<Hash>] :media_attributes
   # @option options [Hash] :poll
   # @option options [String] :text
   # @option options [String] :spoiler_text
@@ -51,10 +52,18 @@ class UpdateStatusService < BaseService
     next_media_attachments     = validate_media!
     added_media_attachments    = next_media_attachments - previous_media_attachments
 
+    (@options[:media_attributes] || []).each do |attributes|
+      media = next_media_attachments.find { |attachment| attachment.id == attributes[:id].to_i }
+      next if media.nil?
+
+      media.update!(attributes.slice(:thumbnail, :description, :focus))
+      @media_attachments_changed ||= media.significantly_changed?
+    end
+
     MediaAttachment.where(id: added_media_attachments.map(&:id)).update_all(status_id: @status.id)
 
     @status.ordered_media_attachment_ids = (@options[:media_ids] || []).map(&:to_i) & next_media_attachments.map(&:id)
-    @media_attachments_changed = previous_media_attachments.map(&:id) != @status.ordered_media_attachment_ids
+    @media_attachments_changed ||= previous_media_attachments.map(&:id) != @status.ordered_media_attachment_ids
     @status.media_attachments.reload
   end
 
@@ -134,9 +143,9 @@ class UpdateStatusService < BaseService
     poll = @status.preloadable_poll
 
     # If the poll had no expiration date set but now has, or now has a sooner
-    # expiration date, and people have voted, schedule a notification
+    # expiration date, schedule a notification
 
-    return unless poll.present? && poll.expires_at.present? && poll.votes.exists?
+    return unless poll.present? && poll.expires_at.present?
 
     PollExpirationNotifyWorker.remove_from_scheduled(poll.id) if @previous_expires_at.present? && @previous_expires_at > poll.expires_at
     PollExpirationNotifyWorker.perform_at(poll.expires_at + 5.minutes, poll.id)
diff --git a/app/services/verify_link_service.rb b/app/services/verify_link_service.rb
index d049b52d1..707aeb4e0 100644
--- a/app/services/verify_link_service.rb
+++ b/app/services/verify_link_service.rb
@@ -10,8 +10,8 @@ class VerifyLinkService < BaseService
     return unless link_back_present?
 
     field.mark_verified!
-  rescue OpenSSL::SSL::SSLError, HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e
-    Rails.logger.debug "Error fetching link #{@url}: #{e}"
+  rescue OpenSSL::SSL::SSLError, HTTP::Error, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, IPAddr::AddressFamilyError => e
+    Rails.logger.debug { "Error fetching link #{@url}: #{e}" }
     nil
   end
 
@@ -19,7 +19,7 @@ class VerifyLinkService < BaseService
 
   def perform_request!
     @body = Request.new(:get, @url).add_headers('Accept' => 'text/html').perform do |res|
-      res.code != 200 ? nil : res.body_with_limit
+      res.code == 200 ? res.body_with_limit : nil
     end
   end
 
diff --git a/app/services/vote_service.rb b/app/services/vote_service.rb
index 114ec285c..9ebf5a98d 100644
--- a/app/services/vote_service.rb
+++ b/app/services/vote_service.rb
@@ -44,11 +44,13 @@ class VoteService < BaseService
 
   def distribute_poll!
     return if @poll.hide_totals?
+
     ActivityPub::DistributePollUpdateWorker.perform_in(3.minutes, @poll.status.id)
   end
 
   def queue_final_poll_check!
     return unless @poll.expires?
+
     PollExpirationNotifyWorker.perform_at(@poll.expires_at + 5.minutes, @poll.id)
   end
 
diff --git a/app/validators/domain_validator.rb b/app/validators/domain_validator.rb
index 6e4a854ff..3a951f9a7 100644
--- a/app/validators/domain_validator.rb
+++ b/app/validators/domain_validator.rb
@@ -4,13 +4,11 @@ class DomainValidator < ActiveModel::EachValidator
   def validate_each(record, attribute, value)
     return if value.blank?
 
-    domain = begin
-      if options[:acct]
-        value.split('@').last
-      else
-        value
-      end
-    end
+    domain = if options[:acct]
+               value.split('@').last
+             else
+               value
+             end
 
     record.errors.add(attribute, I18n.t('domain_validator.invalid_domain')) unless compliant?(domain)
   end
diff --git a/app/validators/ed25519_key_validator.rb b/app/validators/ed25519_key_validator.rb
index 00a448d5a..adf49296b 100644
--- a/app/validators/ed25519_key_validator.rb
+++ b/app/validators/ed25519_key_validator.rb
@@ -6,7 +6,7 @@ class Ed25519KeyValidator < ActiveModel::EachValidator
 
     key = Base64.decode64(value)
 
-    record.errors[attribute] << I18n.t('crypto.errors.invalid_key') unless verified?(key)
+    record.errors.add(attribute, I18n.t('crypto.errors.invalid_key')) unless verified?(key)
   end
 
   private
diff --git a/app/validators/ed25519_signature_validator.rb b/app/validators/ed25519_signature_validator.rb
index 77a21b837..0e74c231e 100644
--- a/app/validators/ed25519_signature_validator.rb
+++ b/app/validators/ed25519_signature_validator.rb
@@ -8,7 +8,7 @@ class Ed25519SignatureValidator < ActiveModel::EachValidator
     signature  = Base64.decode64(value)
     message    = option_to_value(record, :message)
 
-    record.errors[attribute] << I18n.t('crypto.errors.invalid_signature') unless verified?(verify_key, signature, message)
+    record.errors.add(attribute, I18n.t('crypto.errors.invalid_signature')) unless verified?(verify_key, signature, message)
   end
 
   private
diff --git a/app/validators/email_mx_validator.rb b/app/validators/email_mx_validator.rb
index 20f2fd37c..19c57bdf6 100644
--- a/app/validators/email_mx_validator.rb
+++ b/app/validators/email_mx_validator.rb
@@ -10,6 +10,8 @@ class EmailMxValidator < ActiveModel::Validator
 
     if domain.blank?
       user.errors.add(:email, :invalid)
+    elsif domain.include?('..')
+      user.errors.add(:email, :invalid)
     elsif !on_allowlist?(domain)
       resolved_ips, resolved_domains = resolve_mx(domain)
 
diff --git a/app/validators/existing_username_validator.rb b/app/validators/existing_username_validator.rb
index 1c5596821..45de4f4a4 100644
--- a/app/validators/existing_username_validator.rb
+++ b/app/validators/existing_username_validator.rb
@@ -4,16 +4,14 @@ class ExistingUsernameValidator < ActiveModel::EachValidator
   def validate_each(record, attribute, value)
     return if value.blank?
 
-    usernames_and_domains = begin
-      value.split(',').map do |str|
-        username, domain = str.strip.gsub(/\A@/, '').split('@', 2)
-        domain = nil if TagManager.instance.local_domain?(domain)
+    usernames_and_domains = value.split(',').map do |str|
+      username, domain = str.strip.gsub(/\A@/, '').split('@', 2)
+      domain = nil if TagManager.instance.local_domain?(domain)
 
-        next if username.blank?
+      next if username.blank?
 
-        [str, username, domain]
-      end.compact
-    end
+      [str, username, domain]
+    end.compact
 
     usernames_with_no_accounts = usernames_and_domains.filter_map do |(str, username, domain)|
       str unless Account.find_remote(username, domain)
diff --git a/app/validators/follow_limit_validator.rb b/app/validators/follow_limit_validator.rb
index 409bf0176..c619cb9a3 100644
--- a/app/validators/follow_limit_validator.rb
+++ b/app/validators/follow_limit_validator.rb
@@ -6,6 +6,7 @@ class FollowLimitValidator < ActiveModel::Validator
 
   def validate(follow)
     return if follow.account.nil? || !follow.account.local?
+
     follow.errors.add(:base, I18n.t('users.follow_limit_reached', limit: self.class.limit_for_account(follow.account))) if limit_reached?(follow.account)
   end
 
diff --git a/app/validators/html_validator.rb b/app/validators/html_validator.rb
deleted file mode 100644
index b85b9769f..000000000
--- a/app/validators/html_validator.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-# frozen_string_literal: true
-
-class HtmlValidator < ActiveModel::EachValidator
-  ERROR_RE = /Opening and ending tag mismatch|Unexpected end tag/
-
-  def validate_each(record, attribute, value)
-    return if value.blank?
-
-    errors = html_errors(value)
-
-    record.errors.add(attribute, I18n.t('html_validator.invalid_markup', error: errors.first.to_s)) unless errors.empty?
-  end
-
-  private
-
-  def html_errors(str)
-    fragment = Nokogiri::HTML.fragment(options[:wrap_with] ? "<#{options[:wrap_with]}>#{str}</#{options[:wrap_with]}>" : str)
-    fragment.errors.select { |error| ERROR_RE.match?(error.message) }
-  end
-end
diff --git a/app/validators/import_validator.rb b/app/validators/import_validator.rb
index cbad56df6..782baf5d6 100644
--- a/app/validators/import_validator.rb
+++ b/app/validators/import_validator.rb
@@ -35,13 +35,11 @@ class ImportValidator < ActiveModel::Validator
   def validate_following_import(import, row_count)
     base_limit = FollowLimitValidator.limit_for_account(import.account)
 
-    limit = begin
-      if import.overwrite?
-        base_limit
-      else
-        base_limit - import.account.following_count
-      end
-    end
+    limit = if import.overwrite?
+              base_limit
+            else
+              base_limit - import.account.following_count
+            end
 
     import.errors.add(:data, I18n.t('users.follow_limit_reached', limit: base_limit)) if row_count > limit
   end
diff --git a/app/validators/status_pin_validator.rb b/app/validators/status_pin_validator.rb
index 9466a81fe..4af7bd295 100644
--- a/app/validators/status_pin_validator.rb
+++ b/app/validators/status_pin_validator.rb
@@ -7,6 +7,6 @@ class StatusPinValidator < ActiveModel::Validator
     pin.errors.add(:base, I18n.t('statuses.pin_errors.reblog')) if pin.status.reblog?
     pin.errors.add(:base, I18n.t('statuses.pin_errors.ownership')) if pin.account_id != pin.status.account_id
     pin.errors.add(:base, I18n.t('statuses.pin_errors.direct')) if pin.status.direct_visibility?
-    pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count >= MAX_PINNED  && pin.account.local?
+    pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count >= MAX_PINNED && pin.account.local?
   end
 end
diff --git a/app/validators/unreserved_username_validator.rb b/app/validators/unreserved_username_validator.rb
index 974f3ba62..f82f4b91d 100644
--- a/app/validators/unreserved_username_validator.rb
+++ b/app/validators/unreserved_username_validator.rb
@@ -13,12 +13,14 @@ class UnreservedUsernameValidator < ActiveModel::Validator
 
   def pam_controlled?
     return false unless Devise.pam_authentication && Devise.pam_controlled_service
+
     Rpam2.account(Devise.pam_controlled_service, @username).present?
   end
 
   def reserved_username?
     return true if pam_controlled?
     return false unless Setting.reserved_usernames
+
     Setting.reserved_usernames.include?(@username.downcase)
   end
 end
diff --git a/app/validators/vote_validator.rb b/app/validators/vote_validator.rb
index b1692562d..9c55f9ab6 100644
--- a/app/validators/vote_validator.rb
+++ b/app/validators/vote_validator.rb
@@ -2,13 +2,13 @@
 
 class VoteValidator < ActiveModel::Validator
   def validate(vote)
-    vote.errors.add(:base, I18n.t('polls.errors.expired')) if vote.poll.expired?
+    vote.errors.add(:base, I18n.t('polls.errors.expired')) if vote.poll_expired?
 
     vote.errors.add(:base, I18n.t('polls.errors.invalid_choice')) if invalid_choice?(vote)
 
-    if vote.poll.multiple? && vote.poll.votes.where(account: vote.account, choice: vote.choice).exists?
+    if vote.poll_multiple? && already_voted_for_same_choice_on_multiple_poll?(vote)
       vote.errors.add(:base, I18n.t('polls.errors.already_voted'))
-    elsif !vote.poll.multiple? && vote.poll.votes.where(account: vote.account).exists?
+    elsif !vote.poll_multiple? && already_voted_on_non_multiple_poll?(vote)
       vote.errors.add(:base, I18n.t('polls.errors.already_voted'))
     end
   end
@@ -18,4 +18,24 @@ class VoteValidator < ActiveModel::Validator
   def invalid_choice?(vote)
     vote.choice.negative? || vote.choice >= vote.poll.options.size
   end
+
+  def already_voted_for_same_choice_on_multiple_poll?(vote)
+    if vote.persisted?
+      account_votes_on_same_poll(vote).where(choice: vote.choice).where.not(poll_votes: { id: vote }).exists?
+    else
+      account_votes_on_same_poll(vote).where(choice: vote.choice).exists?
+    end
+  end
+
+  def already_voted_on_non_multiple_poll?(vote)
+    if vote.persisted?
+      account_votes_on_same_poll(vote).where.not(poll_votes: { id: vote }).exists?
+    else
+      account_votes_on_same_poll(vote).exists?
+    end
+  end
+
+  def account_votes_on_same_poll(vote)
+    vote.poll.votes.where(account: vote.account)
+  end
 end
diff --git a/app/views/accounts/show.rss.ruby b/app/views/accounts/show.rss.ruby
index 34e29d483..7a77511ce 100644
--- a/app/views/accounts/show.rss.ruby
+++ b/app/views/accounts/show.rss.ruby
@@ -5,7 +5,7 @@ RSS::Builder.build do |doc|
   doc.image(full_asset_url(@account.avatar.url(:original)), display_name(@account), params[:tag].present? ? short_account_tag_url(@account, params[:tag]) : short_account_url(@account))
   doc.last_build_date(@statuses.first.created_at) if @statuses.any?
   doc.icon(full_asset_url(@account.avatar.url(:original)))
-  doc.generator("Mastodon v#{Mastodon::Version.to_s}")
+  doc.generator("Mastodon v#{Mastodon::Version}")
 
   @statuses.each do |status|
     doc.item do |item|
@@ -18,12 +18,12 @@ RSS::Builder.build do |doc|
         item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size)
       end
 
-      status.ordered_media_attachments.each do |media|
-        item.media_content(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size) do |media_content|
-          media_content.medium(media.gifv? ? 'image' : media.type.to_s)
+      status.ordered_media_attachments.each do |media_attachment|
+        item.media_content(full_asset_url(media_attachment.file.url(:original, false)), media_attachment.file.content_type, media_attachment.file.size) do |media_content|
+          media_content.medium(media_attachment.gifv? ? 'image' : media_attachment.type.to_s)
           media_content.rating(status.sensitive? ? 'adult' : 'nonadult')
-          media_content.description(media.description) if media.description.present?
-          media_content.thumbnail(media.thumbnail.url(:original, false)) if media.thumbnail?
+          media_content.description(media_attachment.description) if media_attachment.description.present?
+          media_content.thumbnail(media_attachment.thumbnail.url(:original, false)) if media_attachment.thumbnail?
         end
       end
 
diff --git a/app/views/admin/account_actions/new.html.haml b/app/views/admin/account_actions/new.html.haml
index c7bb618df..2a0cae15a 100644
--- a/app/views/admin/account_actions/new.html.haml
+++ b/app/views/admin/account_actions/new.html.haml
@@ -5,7 +5,7 @@
   = f.input :report_id, as: :hidden
 
   .fields-group
-    = f.input :type, as: :radio_buttons, collection: Admin::AccountAction.types_for_account(@account), include_blank: false, wrapper: :with_block_label, label_method: ->(type) { safe_join([I18n.t("simple_form.labels.admin_account_action.types.#{type}"), content_tag(:span, I18n.t("simple_form.hints.admin_account_action.types.#{type}"), class: 'hint')])}, hint: t('simple_form.hints.admin_account_action.type_html', acct: @account.pretty_acct)
+    = f.input :type, as: :radio_buttons, collection: Admin::AccountAction.types_for_account(@account), include_blank: false, wrapper: :with_block_label, label_method: ->(type) { safe_join([I18n.t("simple_form.labels.admin_account_action.types.#{type}"), content_tag(:span, I18n.t("simple_form.hints.admin_account_action.types.#{type}"), class: 'hint')]) }, hint: t('simple_form.hints.admin_account_action.type_html', acct: @account.pretty_acct)
 
   - if @account.local?
     %hr.spacer/
diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml
index db5c255c9..44867d0a2 100644
--- a/app/views/admin/accounts/show.html.haml
+++ b/app/views/admin/accounts/show.html.haml
@@ -37,7 +37,7 @@
       .dashboard__counters__num= number_to_human_size @account.media_attachments.sum('file_file_size')
       .dashboard__counters__label= t 'admin.accounts.media_attachments'
   %div
-    = link_to admin_account_relationships_path(@account.id, location: 'local', relationship: 'followed_by') do
+    = link_to admin_account_relationships_path(@account.id, location: @account.local? ? nil : 'local', relationship: 'followed_by') do
       .dashboard__counters__num= number_with_delimiter @account.local_followers_count
       .dashboard__counters__label= t 'admin.accounts.followers'
   %div
@@ -187,7 +187,7 @@
             %th= t('admin.accounts.shared_inbox_url')
             %td
               = @account.shared_inbox_url
-              = fa_icon DeliveryFailureTracker.available?(@account.shared_inbox_url) ? 'check': 'times'
+              = fa_icon DeliveryFailureTracker.available?(@account.shared_inbox_url) ? 'check' : 'times'
             %td
               - if @domain_block.nil?
                 = table_link_to 'ban', t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @account.domain)
@@ -206,7 +206,7 @@
     - if @deletion_request.present?
       = link_to t('admin.accounts.delete'), admin_account_path(@account.id), method: :delete, class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure') } if can?(:destroy, @account)
   - else
-    %div.action-buttons
+    .action-buttons
       %div
         - if @account.local? && @account.user_approved?
           = link_to t('admin.accounts.warn'), new_admin_account_action_path(@account.id, type: 'none'), class: 'button' if can?(:warn, @account)
@@ -276,9 +276,9 @@
   %hr.spacer/
 
   - if @account.user&.invite_request&.text&.present?
-    %div.speech-bubble
-      %div.speech-bubble__bubble
+    .speech-bubble
+      .speech-bubble__bubble
         = @account.user&.invite_request&.text
-      %div.speech-bubble__owner
+      .speech-bubble__owner
         = admin_account_link_to @account
         = t('admin.accounts.invite_request_text')
diff --git a/app/views/admin/action_logs/index.html.haml b/app/views/admin/action_logs/index.html.haml
index 7869570e6..c4929cc42 100644
--- a/app/views/admin/action_logs/index.html.haml
+++ b/app/views/admin/action_logs/index.html.haml
@@ -13,10 +13,10 @@
     .filter-subset.filter-subset--with-select
       %strong= t('admin.action_logs.filter_by_action')
       .input.select.optional
-        = select_tag :action_type, options_for_select(Admin::ActionLogFilter::ACTION_TYPE_MAP.keys.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key]}, params[:action_type]), prompt: I18n.t('admin.accounts.moderation.all')
+        = select_tag :action_type, options_for_select(Admin::ActionLogFilter::ACTION_TYPE_MAP.keys.map { |key| [I18n.t("admin.action_logs.action_types.#{key}"), key] }, params[:action_type]), prompt: I18n.t('admin.accounts.moderation.all')
 
 - if @action_logs.empty?
-  %div.muted-hint.center-text
+  .muted-hint.center-text
     = t 'admin.action_logs.empty'
 - else
   .report-notes
diff --git a/app/views/admin/announcements/edit.html.haml b/app/views/admin/announcements/edit.html.haml
index fa69c6fab..df1ac455f 100644
--- a/app/views/admin/announcements/edit.html.haml
+++ b/app/views/admin/announcements/edit.html.haml
@@ -16,7 +16,7 @@
 
   - unless @announcement.published?
     .fields-group
-      = f.input :scheduled_at, include_blank: true, wrapper: :with_block_label
+      = f.input :scheduled_at, include_blank: true, wrapper: :with_block_label, html5: true, input_html: { pattern: '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}(:[0-9]{2}){1,2}', placeholder: Time.now.strftime('%FT%R') }
 
   .actions
     = f.button :button, t('generic.save_changes'), type: :submit
diff --git a/app/views/admin/announcements/index.html.haml b/app/views/admin/announcements/index.html.haml
index 40f02b914..ce520f59d 100644
--- a/app/views/admin/announcements/index.html.haml
+++ b/app/views/admin/announcements/index.html.haml
@@ -12,7 +12,7 @@
       %li= filter_link_to safe_join([t('admin.announcements.live'), "(#{number_with_delimiter(Announcement.published.count)})"], ' '), published: '1', unpublished: nil
 
 - if @announcements.empty?
-  %div.muted-hint.center-text
+  .muted-hint.center-text
     = t 'admin.announcements.empty'
 - else
   .announcements-list
diff --git a/app/views/admin/change_emails/show.html.haml b/app/views/admin/change_emails/show.html.haml
index bc00d6114..8536a18d2 100644
--- a/app/views/admin/change_emails/show.html.haml
+++ b/app/views/admin/change_emails/show.html.haml
@@ -9,4 +9,4 @@
     = 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')
+    = f.button :submit, class: 'button', value: t('admin.accounts.change_email.submit')
diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml
index b6cf7ba64..89eb653e3 100644
--- a/app/views/admin/custom_emojis/index.html.haml
+++ b/app/views/admin/custom_emojis/index.html.haml
@@ -12,12 +12,12 @@
       %li= filter_link_to t('admin.accounts.location.all'), local: nil, remote: nil
       %li
         - if selected? local: '1', remote: nil
-          = filter_link_to t('admin.accounts.location.local'), {local: nil, remote: nil}, {local: '1', remote: nil}
+          = filter_link_to t('admin.accounts.location.local'), { local: nil, remote: nil }, { local: '1', remote: nil }
         - else
           = filter_link_to t('admin.accounts.location.local'), local: '1', remote: nil
       %li
         - if selected? remote: '1', local: nil
-          = filter_link_to t('admin.accounts.location.remote'), {remote: nil, local: nil}, {remote: '1', local: nil}
+          = filter_link_to t('admin.accounts.location.remote'), { remote: nil, local: nil }, { remote: '1', local: nil }
         - else
           = filter_link_to t('admin.accounts.location.remote'), remote: '1', local: nil
 
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 66e0c0251..3597152e0 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -9,7 +9,7 @@
 - unless @system_checks.empty?
   .flash-message-stack
     - @system_checks.each do |message|
-      .flash-message.warning
+      .flash-message{ class: message.critical ? 'alert' : 'warning' }
         = t("admin.system_checks.#{message.key}.message_html", value: message.value ? content_tag(:strong, message.value) : nil)
         - if message.action
           = link_to t("admin.system_checks.#{message.key}.action"), message.action
@@ -56,7 +56,7 @@
     = react_admin_component :dimension, dimension: 'servers', start_at: @time_period.first, end_at: @time_period.last, limit: 8, label: t('admin.dashboard.top_servers')
 
   .dashboard__item.dashboard__item--span-double-column
-    = react_admin_component :retention, start_at: @time_period.last - 6.months,   end_at: @time_period.last, frequency: 'month'
+    = react_admin_component :retention, start_at: @time_period.last - 6.months, end_at: @time_period.last, frequency: 'month'
 
   .dashboard__item.dashboard__item--span-double-row
     = react_admin_component :trends, limit: 7
diff --git a/app/views/admin/disputes/appeals/index.html.haml b/app/views/admin/disputes/appeals/index.html.haml
index 42e9c4b1d..7f04dd40f 100644
--- a/app/views/admin/disputes/appeals/index.html.haml
+++ b/app/views/admin/disputes/appeals/index.html.haml
@@ -10,7 +10,7 @@
       %li= filter_link_to t('admin.trends.rejected'), status: 'rejected'
 
 - if @appeals.empty?
-  %div.muted-hint.center-text
+  .muted-hint.center-text
     = t 'admin.disputes.appeals.empty'
 - else
   .announcements-list
diff --git a/app/views/admin/instances/index.html.haml b/app/views/admin/instances/index.html.haml
index abb2d8c0e..3e70a51ee 100644
--- a/app/views/admin/instances/index.html.haml
+++ b/app/views/admin/instances/index.html.haml
@@ -44,7 +44,7 @@
 %hr.spacer/
 
 - if @instances.empty?
-  %div.muted-hint.center-text
+  .muted-hint.center-text
     = t 'admin.instances.empty'
 - else
   = render partial: 'instance', collection: @instances
diff --git a/app/views/admin/report_notes/_report_note.html.haml b/app/views/admin/report_notes/_report_note.html.haml
index 54c252ee8..64628989a 100644
--- a/app/views/admin/report_notes/_report_note.html.haml
+++ b/app/views/admin/report_notes/_report_note.html.haml
@@ -8,7 +8,7 @@
       = l report_note.created_at.to_date
 
   .report-notes__item__content
-    = simple_format(h(report_note.content))
+    = linkify(report_note.content)
 
   - if can?(:destroy, report_note)
     .report-notes__item__actions
diff --git a/app/views/admin/reports/_actions.html.haml b/app/views/admin/reports/_actions.html.haml
index 486eb486c..aad441625 100644
--- a/app/views/admin/reports/_actions.html.haml
+++ b/app/views/admin/reports/_actions.html.haml
@@ -1,4 +1,4 @@
-= form_tag admin_report_actions_path(@report), method: :post do
+= form_tag preview_admin_report_actions_path(@report), method: :post do
   .report-actions
     .report-actions__item
       .report-actions__item__button
diff --git a/app/views/admin/reports/actions/preview.html.haml b/app/views/admin/reports/actions/preview.html.haml
new file mode 100644
index 000000000..70edb48d8
--- /dev/null
+++ b/app/views/admin/reports/actions/preview.html.haml
@@ -0,0 +1,78 @@
+- target_acct = @report.target_account.acct
+- warning_action = { 'delete' => 'delete_statuses', 'mark_as_sensitive' => 'mark_statuses_as_sensitive' }.fetch(@moderation_action, @moderation_action)
+
+- content_for :page_title do
+  = t('admin.reports.confirm_action', acct: target_acct)
+
+= form_tag admin_report_actions_path(@report), class: 'simple_form', method: :post do
+  = hidden_field_tag :moderation_action, @moderation_action
+
+  %p.hint= t("admin.reports.summary.action_preambles.#{@moderation_action}_html", acct: target_acct)
+  %ul.hint
+    %li.warning-hint= t("admin.reports.summary.actions.#{@moderation_action}_html", acct: target_acct)
+    - if @moderation_action == 'suspend'
+      %li.warning-hint= t('admin.reports.summary.delete_data_html', acct: target_acct)
+    - if %w(silence suspend).include?(@moderation_action)
+      %li.warning-hint= t('admin.reports.summary.close_reports_html', acct: target_acct)
+    - else
+      %li= t('admin.reports.summary.close_report', id: @report.id)
+    %li= t('admin.reports.summary.record_strike_html', acct: target_acct)
+    - if @report.target_account.local? && !@report.spam?
+      %li= t('admin.reports.summary.send_email_html', acct: target_acct)
+
+  %hr.spacer/
+
+  - if @report.target_account.local?
+    %p.hint= t('admin.reports.summary.preview_preamble_html', acct: target_acct)
+
+    .strike-card
+      - unless warning_action == 'none'
+        %p= t "user_mailer.warning.explanation.#{warning_action}", instance: Rails.configuration.x.local_domain
+
+      .fields-group
+        = text_area_tag :text, nil, placeholder: t('admin.reports.summary.warning_placeholder')
+
+      - if !@report.other?
+        %p
+          %strong= t('user_mailer.warning.reason')
+          = t("user_mailer.warning.categories.#{@report.category}")
+
+        - if @report.violation? && @report.rule_ids.present?
+          %ul.strike-card__rules
+            - @report.rules.each do |rule|
+              %li
+                %span.strike-card__rules__text= rule.text
+
+      - if @report.status_ids.present? && !@report.status_ids.empty?
+        %p
+          %strong= t('user_mailer.warning.statuses')
+
+        .strike-card__statuses-list
+          - status_map = @report.statuses.includes(:application, :media_attachments).index_by(&:id)
+
+          - @report.status_ids.each do |status_id|
+            .strike-card__statuses-list__item
+              - if (status = status_map[status_id.to_i])
+                .one-liner
+                  .emojify= one_line_preview(status)
+
+                  - status.ordered_media_attachments.each do |media_attachment|
+                    %abbr{ title: media_attachment.description }
+                      = fa_icon 'link'
+                      = media_attachment.file_file_name
+                .strike-card__statuses-list__item__meta
+                  = link_to ActivityPub::TagManager.instance.url_for(status), target: '_blank' do
+                    %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
+                  - unless status.application.nil?
+                    ·
+                    = status.application.name
+              - else
+                .one-liner= t('disputes.strikes.status', id: status_id)
+                .strike-card__statuses-list__item__meta
+                  = t('disputes.strikes.status_removed')
+
+    %hr.spacer/
+
+  .actions
+    = link_to t('admin.reports.cancel'), admin_report_path(@report), class: 'button button-tertiary'
+    = button_tag t('admin.reports.confirm'), name: :confirm, class: 'button', type: :submit
diff --git a/app/views/admin/rules/index.html.haml b/app/views/admin/rules/index.html.haml
index 4fb993ad0..aa6a4c1b6 100644
--- a/app/views/admin/rules/index.html.haml
+++ b/app/views/admin/rules/index.html.haml
@@ -18,7 +18,7 @@
   %hr.spacer/
 
 - if @rules.empty?
-  %div.muted-hint.center-text
+  .muted-hint.center-text
     = t 'admin.rules.empty'
 - else
   .announcements-list
diff --git a/app/views/admin/settings/about/show.html.haml b/app/views/admin/settings/about/show.html.haml
index 6ee719e36..cbba20faa 100644
--- a/app/views/admin/settings/about/show.html.haml
+++ b/app/views/admin/settings/about/show.html.haml
@@ -24,6 +24,9 @@
       = f.input :show_domain_blocks_rationale, wrapper: :with_label, collection: %i(disabled users all), label_method: lambda { |value| t("admin.settings.domain_blocks.#{value}") }, include_blank: false, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li'
 
   .fields-group
+    = f.input :status_page_url, wrapper: :with_block_label, input_html: { placeholder: "https://status.#{Rails.configuration.x.local_domain}" }
+
+  .fields-group
     = f.input :site_terms, wrapper: :with_block_label, as: :text, input_html: { rows: 8 }
 
   .actions
diff --git a/app/views/admin/settings/discovery/show.html.haml b/app/views/admin/settings/discovery/show.html.haml
index 01e3124cf..460bb5709 100644
--- a/app/views/admin/settings/discovery/show.html.haml
+++ b/app/views/admin/settings/discovery/show.html.haml
@@ -16,6 +16,9 @@
     = f.input :trends, as: :boolean, wrapper: :with_label
 
   .fields-group
+    = f.input :trends_as_landing_page, as: :boolean, wrapper: :with_label
+
+  .fields-group
     = f.input :trendable_by_default, as: :boolean, wrapper: :with_label, recommended: :not_recommended
 
   .fields-group
diff --git a/app/views/admin/statuses/show.html.haml b/app/views/admin/statuses/show.html.haml
index 1e1e63f37..e070e5872 100644
--- a/app/views/admin/statuses/show.html.haml
+++ b/app/views/admin/statuses/show.html.haml
@@ -31,7 +31,7 @@
           %td
             - if @status.trend.allowed?
               %abbr{ title: t('admin.trends.tags.current_score', score: @status.trend.score) }= t('admin.trends.tags.trending_rank', rank: @status.trend.rank)
-            - elsif @status.trend.requires_review?
+            - elsif @status.requires_review?
               = t('admin.trends.pending_review')
             - else
               = t('admin.trends.not_allowed_to_trend')
diff --git a/app/views/admin/warning_presets/index.html.haml b/app/views/admin/warning_presets/index.html.haml
index dbc23fa30..b26a13d96 100644
--- a/app/views/admin/warning_presets/index.html.haml
+++ b/app/views/admin/warning_presets/index.html.haml
@@ -17,7 +17,7 @@
   %hr.spacer/
 
 - if @warning_presets.empty?
-  %div.muted-hint.center-text
+  .muted-hint.center-text
     = t 'admin.warning_presets.empty'
 - else
   .announcements-list
diff --git a/app/views/admin/webhooks/index.html.haml b/app/views/admin/webhooks/index.html.haml
index e4499e078..603d0edd2 100644
--- a/app/views/admin/webhooks/index.html.haml
+++ b/app/views/admin/webhooks/index.html.haml
@@ -9,7 +9,7 @@
 %hr.spacer/
 
 - if @webhooks.empty?
-  %div.muted-hint.center-text
+  .muted-hint.center-text
     = t 'admin.webhooks.empty'
 - else
   .applications-list
diff --git a/app/views/application/_card.html.haml b/app/views/application/_card.html.haml
index 3d0e6b1da..719856d49 100644
--- a/app/views/application/_card.html.haml
+++ b/app/views/application/_card.html.haml
@@ -13,4 +13,4 @@
           %strong.emojify.p-name= display_name(account, custom_emojify: true)
         %span
           = acct(account)
-          = fa_icon('lock', { data: ({hidden: true} unless account.locked?)})
+          = fa_icon('lock', { data: ({ hidden: true } unless account.locked?) })
diff --git a/app/views/application/_sidebar.html.haml b/app/views/application/_sidebar.html.haml
deleted file mode 100644
index 6d18668b0..000000000
--- a/app/views/application/_sidebar.html.haml
+++ /dev/null
@@ -1,16 +0,0 @@
-.hero-widget
-  .hero-widget__img
-    = image_tag @instance_presenter.thumbnail&.file&.url(:'@1x') || asset_pack_path('media/images/preview.png'), alt: @instance_presenter.title
-
-  .hero-widget__text
-    %p= @instance_presenter.description.html_safe.presence || t('about.about_mastodon_html')
-
-- if Setting.trends && !(user_signed_in? && !current_user.setting_trends)
-  - trends = Trends.tags.query.allowed.limit(3)
-
-  - unless trends.empty?
-    .endorsements-widget.trends-widget
-      %h4.emojify= t('footer.trending_now')
-
-      - trends.each do |tag|
-        = react_component :hashtag, hashtag: ActiveModelSerializers::SerializableResource.new(tag, serializer: REST::TagSerializer, scope: current_user, scope_name: :current_user).as_json
diff --git a/app/views/auth/confirmations/captcha.html.haml b/app/views/auth/confirmations/captcha.html.haml
index 0fae367db..642f19062 100644
--- a/app/views/auth/confirmations/captcha.html.haml
+++ b/app/views/auth/confirmations/captcha.html.haml
@@ -11,4 +11,4 @@
     = render_captcha
 
   .actions
-    %button.button= t('challenge.continue')
+    %button.button= t('challenge.confirm')
diff --git a/app/views/auth/confirmations/new.html.haml b/app/views/auth/confirmations/new.html.haml
index a294d3cb5..a98257873 100644
--- a/app/views/auth/confirmations/new.html.haml
+++ b/app/views/auth/confirmations/new.html.haml
@@ -5,7 +5,7 @@
   = render 'shared/error_messages', object: resource
 
   .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
+    = 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') }, readonly: current_user.present?, hint: current_user.present? && t('auth.confirmations.wrong_email_hint')
 
   .actions
     = f.button :button, t('auth.resend_confirmation'), type: :submit
diff --git a/app/views/auth/registrations/_sessions.html.haml b/app/views/auth/registrations/_sessions.html.haml
index c094dfd25..55d753c18 100644
--- a/app/views/auth/registrations/_sessions.html.haml
+++ b/app/views/auth/registrations/_sessions.html.haml
@@ -20,7 +20,7 @@
             %span{ title: session.user_agent }<
               = fa_icon "#{session_device_icon(session)} fw", 'aria-label': session_device_icon(session)
               = ' '
-              = t 'sessions.description', browser: t("sessions.browsers.#{session.browser}", default: "#{session.browser}"), platform: t("sessions.platforms.#{session.platform}", default: "#{session.platform}")
+              = t 'sessions.description', browser: t("sessions.browsers.#{session.browser}", default: session.browser.to_s), platform: t("sessions.platforms.#{session.platform}", default: session.platform.to_s)
           %td
             %samp= session.ip
           %td
diff --git a/app/views/auth/registrations/edit.html.haml b/app/views/auth/registrations/edit.html.haml
index 60fd1635e..27d3f331e 100644
--- a/app/views/auth/registrations/edit.html.haml
+++ b/app/views/auth/registrations/edit.html.haml
@@ -8,7 +8,7 @@
 = simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: 'auth_edit', novalidate: false }) do |f|
   = render 'shared/error_messages', object: resource
 
-  - if !use_seamless_external_login? || resource.encrypted_password.present?
+  - if (!use_seamless_external_login? || resource.encrypted_password.present?) && !omniauth_only?
     .fields-row
       .fields-row__column.fields-group.fields-row__column-6
         = f.input :email, wrapper: :with_label, input_html: { 'aria-label': t('simple_form.labels.defaults.email') }, required: true, disabled: current_account.suspended?
@@ -23,6 +23,8 @@
 
     .actions
       = f.button :button, t('generic.save_changes'), type: :submit, class: 'button', disabled: current_account.suspended?
+  - elsif omniauth_only? && sso_account_settings.present?
+    = link_to t('users.go_to_sso_account_settings'), sso_account_settings
   - else
     %p.hint= t('users.seamless_external_login')
 
diff --git a/app/views/auth/sessions/new.html.haml b/app/views/auth/sessions/new.html.haml
index e98c1ff3d..7914e0157 100644
--- a/app/views/auth/sessions/new.html.haml
+++ b/app/views/auth/sessions/new.html.haml
@@ -19,7 +19,7 @@
     .actions
       = f.button :button, t('auth.login'), type: :submit
 
-- if devise_mapping.omniauthable? and resource_class.omniauth_providers.any?
+- if devise_mapping.omniauthable? && resource_class.omniauth_providers.any?
   .simple_form.alternative-login
     %h4= omniauth_only? ? t('auth.log_in_with') : t('auth.or_log_in_with')
 
diff --git a/app/views/disputes/strikes/show.html.haml b/app/views/disputes/strikes/show.html.haml
index 7797348dd..ce52e470d 100644
--- a/app/views/disputes/strikes/show.html.haml
+++ b/app/views/disputes/strikes/show.html.haml
@@ -50,15 +50,15 @@
             .strike-card__statuses-list__item
               - if (status = status_map[status_id.to_i])
                 .one-liner
-                  = link_to short_account_status_url(@strike.target_account, status_id), class: 'emojify' do
-                    = one_line_preview(status)
+                  .emojify= one_line_preview(status)
 
-                    - status.ordered_media_attachments.each do |media_attachment|
-                      %abbr{ title: media_attachment.description }
-                        = fa_icon 'link'
-                        = media_attachment.file_file_name
+                  - status.ordered_media_attachments.each do |media_attachment|
+                    %abbr{ title: media_attachment.description }
+                      = fa_icon 'link'
+                      = media_attachment.file_file_name
                 .strike-card__statuses-list__item__meta
-                  %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
+                  = link_to ActivityPub::TagManager.instance.url_for(status), target: '_blank' do
+                    %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
                   - unless status.application.nil?
                     ·
                     = status.application.name
diff --git a/app/views/filters/_filter_fields.html.haml b/app/views/filters/_filter_fields.html.haml
index c58978f5a..a554b55ff 100644
--- a/app/views/filters/_filter_fields.html.haml
+++ b/app/views/filters/_filter_fields.html.haml
@@ -35,6 +35,6 @@
         = render 'keyword_fields', f: keyword
     %tfoot
       %tr
-        %td{ colspan: 3}
+        %td{ colspan: 3 }
           = link_to_add_association f, :keywords, class: 'table-action-link', partial: 'keyword_fields', 'data-association-insertion-node': '.keywords-table tbody', 'data-association-insertion-method': 'append' do
             = safe_join([fa_icon('plus'), t('filters.edit.add_keyword')])
diff --git a/app/views/filters/index.html.haml b/app/views/filters/index.html.haml
index 0227526a4..9c84f796f 100644
--- a/app/views/filters/index.html.haml
+++ b/app/views/filters/index.html.haml
@@ -5,7 +5,7 @@
   = link_to t('filters.new.title'), new_filter_path, class: 'button'
 
 - if @filters.empty?
-  %div.muted-hint.center-text= t 'filters.index.empty'
+  .muted-hint.center-text= t 'filters.index.empty'
 - else
   .applications-list
     = render partial: 'filter', collection: @filters
diff --git a/app/views/kaminari/_gap.html.haml b/app/views/kaminari/_gap.html.haml
new file mode 100644
index 000000000..0f9cff8fe
--- /dev/null
+++ b/app/views/kaminari/_gap.html.haml
@@ -0,0 +1,9 @@
+-#
+  Non-link tag that stands for skipped pages...
+  available local variables
+    current_page:  a page object for the currently displayed page
+    total_pages:   total number of pages
+    per_page:      number of items to fetch per page
+    remote:        data-remote
+%span.page.gap
+  != t('pagination.truncate')
diff --git a/app/views/kaminari/_next_page.html.haml b/app/views/kaminari/_next_page.html.haml
index 30a3643d6..c44aea1f1 100644
--- a/app/views/kaminari/_next_page.html.haml
+++ b/app/views/kaminari/_next_page.html.haml
@@ -1,9 +1,11 @@
--#  Link to the "Next" page
--#  available local variables
--#    url:           url to the next page
--#    current_page:  a page object for the currently displayed page
--#    total_pages:   total number of pages
--#    per_page:      number of items to fetch per page
--#    remote:        data-remote
+-#
+  Link to the "Next" page
+  available local variables
+    url:           url to the next page
+    current_page:  a page object for the currently displayed page
+    total_pages:   total number of pages
+    per_page:      number of items to fetch per page
+    remote:        data-remote
+
 %span.next
   = link_to_unless current_page.last?, safe_join([t('pagination.next'), fa_icon('chevron-right')], ' '), url, rel: 'next', remote: remote
diff --git a/app/views/kaminari/_paginator.html.haml b/app/views/kaminari/_paginator.html.haml
index b1da236d5..4778f6279 100644
--- a/app/views/kaminari/_paginator.html.haml
+++ b/app/views/kaminari/_paginator.html.haml
@@ -1,10 +1,11 @@
--#  The container tag
--#  available local variables
--#    current_page:  a page object for the currently displayed page
--#    total_pages:   total number of pages
--#    per_page:      number of items to fetch per page
--#    remote:        data-remote
--#    paginator:     the paginator that renders the pagination tags inside
+-#
+  The container tag
+  available local variables
+    current_page:  a page object for the currently displayed page
+    total_pages:   total number of pages
+    per_page:      number of items to fetch per page
+    remote:        data-remote
+    paginator:     the paginator that renders the pagination tags inside
 = paginator.render do
   %nav.pagination
     = prev_page_tag unless current_page.first?
diff --git a/app/views/kaminari/_prev_page.html.haml b/app/views/kaminari/_prev_page.html.haml
index 1089e3566..284d6223b 100644
--- a/app/views/kaminari/_prev_page.html.haml
+++ b/app/views/kaminari/_prev_page.html.haml
@@ -1,9 +1,10 @@
--#  Link to the "Previous" page
--#  available local variables
--#    url:           url to the previous page
--#    current_page:  a page object for the currently displayed page
--#    total_pages:   total number of pages
--#    per_page:      number of items to fetch per page
--#    remote:        data-remote
+-#
+  Link to the "Previous" page
+  available local variables
+    url:           url to the previous page
+    current_page:  a page object for the currently displayed page
+    total_pages:   total number of pages
+    per_page:      number of items to fetch per page
+    remote:        data-remote
 %span.prev
   = link_to_unless current_page.first?, safe_join([fa_icon('chevron-left'), t('pagination.prev')], ' '), url, rel: 'prev', remote: remote
diff --git a/app/views/layouts/_theme.html.haml b/app/views/layouts/_theme.html.haml
index 5dba77621..71e661de6 100644
--- a/app/views/layouts/_theme.html.haml
+++ b/app/views/layouts/_theme.html.haml
@@ -9,6 +9,5 @@
         = stylesheet_pack_tag pack_path, media: 'all', crossorigin: 'anonymous'
       - else
         = stylesheet_pack_tag "skins/#{theme[:flavour]}/#{theme[:skin]}/#{theme[:pack]}", media: 'all', crossorigin: 'anonymous'
-    - if theme[:preload]
-      - theme[:preload].each do |link|
-        %link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
+    - theme[:preload]&.each do |link|
+      %link{ href: asset_pack_path("#{link}.js"), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index d19ea1390..f4f8744e9 100755
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -39,7 +39,7 @@
 
     = yield :header_tags
 
-    -#  These must come after :header_tags to ensure our initial state has been defined.
+    -# These must come after :header_tags to ensure our initial state has been defined.
     = render partial: 'layouts/theme', object: @core
     = render partial: 'layouts/theme', object: @theme
 
diff --git a/app/views/layouts/embedded.html.haml b/app/views/layouts/embedded.html.haml
index b8817d566..210ac101d 100644
--- a/app/views/layouts/embedded.html.haml
+++ b/app/views/layouts/embedded.html.haml
@@ -12,7 +12,7 @@
       %link{ rel: 'dns-prefetch', href: storage_host }/
 
     = render_initial_state
-    = javascript_pack_tag "locales", crossorigin: 'anonymous'
+    = javascript_pack_tag 'locales', crossorigin: 'anonymous'
     - if @theme
       - if @theme[:supported_locales].include? I18n.locale.to_s
         = javascript_pack_tag "locales/#{@theme[:flavour]}/#{I18n.locale}", crossorigin: 'anonymous'
diff --git a/app/views/layouts/mailer.html.haml b/app/views/layouts/mailer.html.haml
index 1d0840dc1..288c473d2 100644
--- a/app/views/layouts/mailer.html.haml
+++ b/app/views/layouts/mailer.html.haml
@@ -35,7 +35,7 @@
                 %tbody
                   %tr
                     %td.content-cell.content-end
-                      != "&nbsp; "
+                      != '&nbsp; '
                   %tr
                     %td.blank-cell.footer
                       .email-row
diff --git a/app/views/layouts/modal.html.haml b/app/views/layouts/modal.html.haml
index cf608766b..5d08d7848 100644
--- a/app/views/layouts/modal.html.haml
+++ b/app/views/layouts/modal.html.haml
@@ -12,6 +12,6 @@
 
   .modal-layout__mastodon
     %div
-      %img{alt: '', draggable: 'false', src: mascot_url }
+      %img{ alt: '', draggable: 'false', src: mascot_url }
 
 = render template: 'layouts/application'
diff --git a/app/views/media/player.html.haml b/app/views/media/player.html.haml
index c1d630a63..c907d5c60 100644
--- a/app/views/media/player.html.haml
+++ b/app/views/media/player.html.haml
@@ -1,6 +1,6 @@
 - content_for :header_tags do
   = render_initial_state
-  = javascript_pack_tag "locales", crossorigin: 'anonymous'
+  = javascript_pack_tag 'locales', crossorigin: 'anonymous'
   - if @theme
     - if @theme[:supported_locales].include? I18n.locale.to_s
       = javascript_pack_tag "locales/#{@theme[:flavour]}/#{I18n.locale}", crossorigin: 'anonymous'
diff --git a/app/views/notification_mailer/_status.html.haml b/app/views/notification_mailer/_status.html.haml
index e7cd5ba3e..fd65039ae 100644
--- a/app/views/notification_mailer/_status.html.haml
+++ b/app/views/notification_mailer/_status.html.haml
@@ -20,17 +20,17 @@
                                 %tbody
                                   %tr
                                     %td{ align: 'left', width: 48 }
-                                      = image_tag full_asset_url(status.account.avatar.url), alt:''
+                                      = image_tag full_asset_url(status.account.avatar.url), alt: ''
                                     %td{ align: 'left' }
                                       %bdi= display_name(status.account)
                                       = "@#{status.account.pretty_acct}"
 
                               - if status.spoiler_text?
-                                %div.auto-dir
+                                .auto-dir
                                   %p
                                     = status.spoiler_text
 
-                              %div.auto-dir
+                              .auto-dir
                                 = status_content_format(status)
 
                                 - if status.ordered_media_attachments.size > 0
diff --git a/app/views/notification_mailer/favourite.html.haml b/app/views/notification_mailer/favourite.html.haml
index 5d9be3f57..4ec89172d 100644
--- a/app/views/notification_mailer/favourite.html.haml
+++ b/app/views/notification_mailer/favourite.html.haml
@@ -17,7 +17,7 @@
                                 %tbody
                                   %tr
                                     %td
-                                      = image_tag full_pack_url('media/images/mailer/icon_grade.png'), alt:''
+                                      = image_tag full_pack_url('media/images/mailer/icon_grade.png'), alt: ''
 
                               %h1= t 'notification_mailer.favourite.title'
                               %p.lead= t('notification_mailer.favourite.body', name: @account.pretty_acct)
diff --git a/app/views/notification_mailer/follow_request.html.haml b/app/views/notification_mailer/follow_request.html.haml
index 4c32c831e..3885a411d 100644
--- a/app/views/notification_mailer/follow_request.html.haml
+++ b/app/views/notification_mailer/follow_request.html.haml
@@ -39,5 +39,5 @@
                             %tbody
                               %tr
                                 %td.button-primary
-                                  = link_to web_url("follow_requests") do
+                                  = link_to web_url('follow_requests') do
                                     %span= t 'notification_mailer.follow_request.action'
diff --git a/app/views/oauth/authorizations/show.html.haml b/app/views/oauth/authorizations/show.html.haml
index c3c9960d8..a5122a87f 100644
--- a/app/views/oauth/authorizations/show.html.haml
+++ b/app/views/oauth/authorizations/show.html.haml
@@ -3,5 +3,5 @@
     %p= t('doorkeeper.authorizations.show.title')
     .input-copy
       .input-copy__wrapper
-        %input{ type: 'text', class: 'oauth-code', spellcheck: 'false', readonly: true, value: params[:code] }
+        %input.oauth-code{ type: 'text', spellcheck: 'false', readonly: true, value: params[:code] }
       %button{ type: :button }= t('generic.copy')
diff --git a/app/views/relationships/show.html.haml b/app/views/relationships/show.html.haml
index e1ead6945..fcda6317e 100644
--- a/app/views/relationships/show.html.haml
+++ b/app/views/relationships/show.html.haml
@@ -45,7 +45,7 @@
 
         = f.button safe_join([fa_icon('trash'), t('relationships.remove_selected_followers')]), name: :remove_from_followers, class: 'table-action-link', type: :submit, data: { confirm: t('relationships.confirm_remove_selected_followers') } unless following_relationship?
 
-        = f.button safe_join([fa_icon('trash'), t('relationships.remove_selected_domains')]), name: :block_domains, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } if followed_by_relationship?
+        = f.button safe_join([fa_icon('trash'), t('relationships.remove_selected_domains')]), name: :remove_domains_from_followers, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } if followed_by_relationship?
     .batch-table__body
       - if @accounts.empty?
         = nothing_here 'nothing-here--under-tabs'
diff --git a/app/views/settings/applications/index.html.haml b/app/views/settings/applications/index.html.haml
index a1f904a3a..5c31d56bc 100644
--- a/app/views/settings/applications/index.html.haml
+++ b/app/views/settings/applications/index.html.haml
@@ -5,7 +5,7 @@
   = link_to t('doorkeeper.applications.index.new'), new_settings_application_path, class: 'button'
 
 - if @applications.empty?
-  %div.muted-hint.center-text=t 'doorkeeper.applications.index.empty'
+  .muted-hint.center-text= t 'doorkeeper.applications.index.empty'
 - else
   .table-wrapper
     %table.table
diff --git a/app/views/settings/applications/show.html.haml b/app/views/settings/applications/show.html.haml
index 390682d6f..466a8ba34 100644
--- a/app/views/settings/applications/show.html.haml
+++ b/app/views/settings/applications/show.html.haml
@@ -6,7 +6,7 @@
 .table-wrapper
   %table.table
     %tbody
-      %tr  
+      %tr
         %th= t('doorkeeper.applications.show.application_id')
         %td
           %code= @application.uid
@@ -15,7 +15,7 @@
         %td
           %code= @application.secret
       %tr
-        %th{ rowspan: 2}= t('applications.your_token')
+        %th{ rowspan: 2 }= t('applications.your_token')
         %td
           %code= current_user.token_for_app(@application).token
       %tr
diff --git a/app/views/settings/exports/show.html.haml b/app/views/settings/exports/show.html.haml
index c49613fdc..d7b59af27 100644
--- a/app/views/settings/exports/show.html.haml
+++ b/app/views/settings/exports/show.html.haml
@@ -64,6 +64,6 @@
             %td= l backup.created_at
             - if backup.processed?
               %td= number_to_human_size backup.dump_file_size
-              %td= table_link_to 'download', t('exports.archive_takeout.download'), backup.dump.url
+              %td= table_link_to 'download', t('exports.archive_takeout.download'), download_backup_url(backup)
             - else
               %td{ colspan: 2 }= t('exports.archive_takeout.in_progress')
diff --git a/app/views/settings/featured_tags/index.html.haml b/app/views/settings/featured_tags/index.html.haml
index 078abd788..3ead9d2e2 100644
--- a/app/views/settings/featured_tags/index.html.haml
+++ b/app/views/settings/featured_tags/index.html.haml
@@ -17,7 +17,7 @@
 %hr.spacer/
 
 - @featured_tags.each do |featured_tag|
-  .directory__tag{ class: params[:tag] == featured_tag.name ? 'active' : nil }
+  .directory__tag
     %div
       %h4
         = fa_icon 'hashtag'
diff --git a/app/views/settings/flavours/show.html.haml b/app/views/settings/flavours/show.html.haml
index c3f785aa0..ea2540858 100644
--- a/app/views/settings/flavours/show.html.haml
+++ b/app/views/settings/flavours/show.html.haml
@@ -5,7 +5,7 @@
   = render 'shared/error_messages', object: current_user
 
   - Themes.instance.flavour(@selected)['screenshot'].each do |screen|
-    %img.flavour-screen{ src: full_pack_url("media/#{screen}") }
+    %img.flavour-screen{ src: full_pack_url("media/#{screen}"), alt: '' }
 
   .flavour-description
     = t "flavours.#{@selected}.description", default: ''
@@ -14,7 +14,7 @@
 
   - if Themes.instance.skins_for(@selected).length > 1
     .fields-group
-      = f.input :setting_skin, collection: Themes.instance.skins_for(@selected), label_method: lambda { |skin| I18n.t("skins.#{@selected}.#{skin}", default: skin) }, wrapper: :with_label, include_blank: false
+      = f.input :setting_skin, collection: Themes.instance.skins_for(@selected), label_method: ->(skin) { I18n.t("skins.#{@selected}.#{skin}", default: skin) }, wrapper: :with_label, include_blank: false
 
   .actions
     = f.button :button, t('generic.use_this'), type: :submit
diff --git a/app/views/settings/login_activities/_login_activity.html.haml b/app/views/settings/login_activities/_login_activity.html.haml
index 0c2c7087d..94ed60312 100644
--- a/app/views/settings/login_activities/_login_activity.html.haml
+++ b/app/views/settings/login_activities/_login_activity.html.haml
@@ -1,6 +1,6 @@
 - method_str = content_tag(:span, login_activity.omniauth? ? t(login_activity.provider, scope: 'auth.providers') : t(login_activity.authentication_method, scope: 'login_activities.authentication_methods'), class: 'target')
 - ip_str = content_tag(:span, login_activity.ip, class: 'target')
-- browser_str = content_tag(:span, t('sessions.description', browser: t("sessions.browsers.#{login_activity.browser}", default: "#{login_activity.browser}"), platform: t("sessions.platforms.#{login_activity.platform}", default: "#{login_activity.platform}")), class: 'target', title: login_activity.user_agent)
+- browser_str = content_tag(:span, t('sessions.description', browser: t("sessions.browsers.#{login_activity.browser}", default: login_activity.browser.to_s), platform: t("sessions.platforms.#{login_activity.platform}", default: login_activity.platform.to_s)), class: 'target', title: login_activity.user_agent)
 
 .log-entry
   .log-entry__header
diff --git a/app/views/settings/login_activities/index.html.haml b/app/views/settings/login_activities/index.html.haml
index ce524fbef..6fb1bc34c 100644
--- a/app/views/settings/login_activities/index.html.haml
+++ b/app/views/settings/login_activities/index.html.haml
@@ -6,7 +6,7 @@
 %hr.spacer/
 
 - if @login_activities.empty?
-  %div.muted-hint.center-text
+  .muted-hint.center-text
     = t 'login_activities.empty'
 - else
   .announcements-list
diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml
index 0eec44534..b3752ed89 100644
--- a/app/views/settings/preferences/appearance/show.html.haml
+++ b/app/views/settings/preferences/appearance/show.html.haml
@@ -5,62 +5,63 @@
   = button_tag t('generic.save_changes'), class: 'button', form: 'edit_user'
 
 = simple_form_for current_user, url: settings_preferences_appearance_path, html: { method: :put, id: 'edit_user' } do |f|
-  .fields-group
-    = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| native_locale_name(locale) }, selected: I18n.locale, hint: false
+  .fields-row
+    .fields-group
+      = f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| native_locale_name(locale) }, selected: I18n.locale, hint: false
 
   - unless I18n.locale == :en
     .flash-message.translation-prompt
-      #{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: "_blank", rel: "noopener")}
+      #{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: '_blank', rel: 'noopener')}
       = link_to t('appearance.localization.glitch_guide_link'), target: '_blank', rel: 'noopener noreferrer' do
         = t('appearance.localization.glitch_guide_link_text')
 
-  %h4= t 'appearance.advanced_web_interface'
+  = f.simple_fields_for :settings, current_user.settings do |ff|
+    %h4= t 'appearance.advanced_web_interface'
 
-  %p.hint= t 'appearance.advanced_web_interface_hint'
+    %p.hint= t 'appearance.advanced_web_interface_hint'
 
-  .fields-group
-    = f.input :setting_advanced_layout, as: :boolean, wrapper: :with_label, hint: false
+    .fields-group
+      = ff.input :'web.advanced_layout', wrapper: :with_label, hint: false, label: I18n.t('simple_form.labels.defaults.setting_advanced_layout')
+    %h4= t 'appearance.animations_and_accessibility'
 
-  %h4= t 'appearance.animations_and_accessibility'
+    .fields-group
+      = ff.input :'web.use_pending_items', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_use_pending_items'), hint: I18n.t('simple_form.hints.defaults.setting_use_pending_items')
 
-  .fields-group
-    = f.input :setting_use_pending_items, as: :boolean, wrapper: :with_label
+    .fields-group
+      = ff.input :'web.auto_play', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_auto_play_gif')
+      = ff.input :'web.reduce_motion', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_reduce_motion')
+      = ff.input :'web.disable_swiping', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_swiping')
+      = ff.input :'web.use_system_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_font_ui')
+      = ff.input :'web.use_system_emoji_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_emoji_font'), glitch_only: true
 
-  .fields-group
-    = f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label
-    = f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label
-    = f.input :setting_disable_swiping, as: :boolean, wrapper: :with_label
-    = f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label
-    = f.input :setting_system_emoji_font, as: :boolean, wrapper: :with_label, glitch_only: true
+    %h4= t 'appearance.toot_layout'
 
-  %h4= t 'appearance.toot_layout'
+    .fields-group
+      = ff.input :'web.crop_images', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_crop_images')
 
-  .fields-group
-    = f.input :setting_crop_images, as: :boolean, wrapper: :with_label
+    %h4= t 'appearance.discovery'
 
-  %h4= t 'appearance.discovery'
+    .fields-group
+      = ff.input :'web.trends', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_trends')
 
-  .fields-group
-    = f.input :setting_trends, as: :boolean, wrapper: :with_label
+    %h4= t 'appearance.confirmation_dialogs'
 
-  %h4= t 'appearance.confirmation_dialogs'
+    .fields-group
+      = ff.input :'web.unfollow_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_unfollow_modal')
+      = ff.input :'web.reblog_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_boost_modal')
+      = ff.input :'web.favourite_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_favourite_modal'), glitch_only: true
+      = ff.input :'web.delete_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_delete_modal')
 
-  .fields-group
-    = f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label
-    = f.input :setting_boost_modal, as: :boolean, wrapper: :with_label
-    = f.input :setting_favourite_modal, as: :boolean, wrapper: :with_label, glitch_only: true
-    = f.input :setting_delete_modal, as: :boolean, wrapper: :with_label
+    %h4= t 'appearance.sensitive_content'
 
-  %h4= t 'appearance.sensitive_content'
+    .fields-group
+      = ff.input :'web.display_media', collection: ['default', 'show_all', 'hide_all'],label_method: lambda { |item| t("simple_form.hints.defaults.setting_display_media_#{item}") }, hint: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label, label: I18n.t('simple_form.labels.defaults.setting_display_media')
 
-  .fields-group
-    = f.input :setting_display_media, collection: ['default', 'show_all', 'hide_all'],label_method: lambda { |item| t("simple_form.hints.defaults.setting_display_media_#{item}") }, hint: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label
+    .fields-group
+      = ff.input :'web.use_blurhash', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_use_blurhash'), hint: I18n.t('simple_form.hints.defaults.setting_use_blurhash')
 
-  .fields-group
-    = f.input :setting_use_blurhash, as: :boolean, wrapper: :with_label
-
-  .fields-group
-    = f.input :setting_expand_spoilers, as: :boolean, wrapper: :with_label
+    .fields-group
+      = ff.input :'web.expand_content_warnings', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_expand_spoilers')
 
   .actions
     = f.button :button, t('generic.save_changes'), type: :submit
diff --git a/app/views/settings/preferences/notifications/show.html.haml b/app/views/settings/preferences/notifications/show.html.haml
index a03faa145..cfc468eef 100644
--- a/app/views/settings/preferences/notifications/show.html.haml
+++ b/app/views/settings/preferences/notifications/show.html.haml
@@ -11,27 +11,27 @@
 
   %p.hint= t 'notifications.email_events_hint'
 
-  .fields-group
-    = f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff|
-      = ff.input :follow, as: :boolean, wrapper: :with_label
-      = ff.input :follow_request, as: :boolean, wrapper: :with_label
-      = ff.input :reblog, as: :boolean, wrapper: :with_label
-      = ff.input :favourite, as: :boolean, wrapper: :with_label
-      = ff.input :mention, as: :boolean, wrapper: :with_label
-      = ff.input :report, as: :boolean, wrapper: :with_label if current_user.can?(:manage_reports)
-      = ff.input :appeal, as: :boolean, wrapper: :with_label if current_user.can?(:manage_appeals)
-      = ff.input :pending_account, as: :boolean, wrapper: :with_label if current_user.can?(:manage_users)
-      = ff.input :trending_tag, as: :boolean, wrapper: :with_label if current_user.can?(:manage_taxonomies)
-      = ff.input :trending_link, as: :boolean, wrapper: :with_label if current_user.can?(:manage_taxonomies)
-      = ff.input :trending_status, as: :boolean, wrapper: :with_label if current_user.can?(:manage_taxonomies)
-
-  .fields-group
-    = f.input :setting_always_send_emails, as: :boolean, wrapper: :with_label
+  = f.simple_fields_for :settings, current_user.settings do |ff|
+    .fields-group
+      = ff.input :'notification_emails.follow', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.follow')
+      = ff.input :'notification_emails.follow_request', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.follow_request')
+      = ff.input :'notification_emails.reblog', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.reblog')
+      = ff.input :'notification_emails.favourite', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.favourite')
+      = ff.input :'notification_emails.mention', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.mention')
+      = ff.input :'notification_emails.report', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.report') if current_user.can?(:manage_reports)
+      = ff.input :'notification_emails.appeal', as: :boolean, wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.appeal') if current_user.can?(:manage_appeals)
+      = ff.input :'notification_emails.pending_account', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.pending_account') if current_user.can?(:manage_users)
+      = ff.input :'notification_emails.trends', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.trending_tag') if current_user.can?(:manage_taxonomies)
+      = ff.input :'notification_emails.link_trends', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.trending_link') if current_user.can?(:manage_taxonomies)
+      = ff.input :'notification_emails.status_trends', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.trending_status') if current_user.can?(:manage_taxonomies)
+
+    .fields-group
+      = ff.input :always_send_emails, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_always_send_emails'), hint: I18n.t('simple_form.hints.defaults.setting_always_send_emails')
 
   %h4= t 'notifications.other_settings'
 
   .fields-group
-    = f.simple_fields_for :interactions, hash_to_object(current_user.settings.interactions) do |ff|
-      = ff.input :must_be_follower, as: :boolean, wrapper: :with_label
-      = ff.input :must_be_following, as: :boolean, wrapper: :with_label
-      = ff.input :must_be_following_dm, as: :boolean, wrapper: :with_label
+    = f.simple_fields_for :settings, current_user.settings do |ff|
+      = ff.input :'interactions.must_be_follower', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_follower')
+      = ff.input :'interactions.must_be_following', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_following')
+      = ff.input :'interactions.must_be_following_dm', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_following_dm')
diff --git a/app/views/settings/preferences/other/show.html.haml b/app/views/settings/preferences/other/show.html.haml
index f12ff3975..42765d0cb 100644
--- a/app/views/settings/preferences/other/show.html.haml
+++ b/app/views/settings/preferences/other/show.html.haml
@@ -7,33 +7,34 @@
 = simple_form_for current_user, url: settings_preferences_other_path, html: { method: :put, id: 'edit_preferences' } do |f|
   = render 'shared/error_messages', object: current_user
 
-  .fields-group
-    = f.input :setting_noindex, as: :boolean, wrapper: :with_label
-
-  .fields-group
-    = f.input :setting_aggregate_reblogs, as: :boolean, wrapper: :with_label
+  = f.simple_fields_for :settings, current_user.settings do |ff|
+    .fields-group
+      = ff.input :noindex, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_noindex'), hint: I18n.t('simple_form.hints.defaults.setting_noindex')
 
-  - unless Setting.hide_followers_count
     .fields-group
-      = f.input :setting_hide_followers_count, as: :boolean, wrapper: :with_label, glitch_only: true
+      = ff.input :aggregate_reblogs, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_aggregate_reblogs'), hint: I18n.t('simple_form.hints.defaults.setting_aggregate_reblogs')
 
-  %h4= t 'preferences.posting_defaults'
+    - unless Setting.hide_followers_count
+      .fields-group
+        = ff.input :hide_followers_count, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_hide_followers_count'), glitch_only: true
 
-  .fields-row
-    .fields-group.fields-row__column.fields-row__column-6
-      = f.input :setting_default_privacy, collection: Status.selectable_visibilities, wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), I18n.t("statuses.visibilities.#{visibility}_long")], ' - ') }, required: false, hint: false
+    %h4= t 'preferences.posting_defaults'
 
-    .fields-group.fields-row__column.fields-row__column-6
-      = f.input :setting_default_language, collection: [nil] + filterable_languages, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.default_language') : native_locale_name(locale) }, required: false, include_blank: false, hint: false
+    .fields-row
+      .fields-group.fields-row__column.fields-row__column-6
+        = ff.input :default_privacy, collection: Status.selectable_visibilities, wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), I18n.t("statuses.visibilities.#{visibility}_long")], ' - ') }, required: false, hint: false, label: I18n.t('simple_form.labels.defaults.setting_default_privacy')
 
-  .fields-group
-    = f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label
+      .fields-group.fields-row__column.fields-row__column-6
+        = ff.input :default_language, collection: [nil] + filterable_languages, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.default_language') : native_locale_name(locale) }, required: false, include_blank: false, hint: false, label: I18n.t('simple_form.labels.defaults.setting_default_language')
 
-  .fields-group
-    = f.input :setting_show_application, as: :boolean, wrapper: :with_label
+    .fields-group
+      = ff.input :default_sensitive, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_default_sensitive'), hint: I18n.t('simple_form.hints.defaults.setting_default_sensitive')
 
-  .fields-group
-    = f.input :setting_default_content_type, collection: ['text/plain', 'text/markdown', 'text/html'], wrapper: :with_label, include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.defaults.setting_default_content_type_#{item.split('/')[1]}"), content_tag(:span, t("simple_form.hints.defaults.setting_default_content_type_#{item.split('/')[1]}"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', glitch_only: true
+    .fields-group
+      = ff.input :show_application, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_show_application'), hint: I18n.t('simple_form.hints.defaults.setting_show_application')
+
+    .fields-group
+      = ff.input :default_content_type, collection: ['text/plain', 'text/markdown', 'text/html'], wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_default_content_type'), include_blank: false, label_method: lambda { |item| safe_join([t("simple_form.labels.defaults.setting_default_content_type_#{item.split('/')[1]}"), content_tag(:span, t("simple_form.hints.defaults.setting_default_content_type_#{item.split('/')[1]}"), class: 'hint')]) }, required: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', glitch_only: true
 
   %h4= t 'preferences.public_timelines'
 
diff --git a/app/views/shared/_og.html.haml b/app/views/shared/_og.html.haml
index 2941b566e..a5d99ae33 100644
--- a/app/views/shared/_og.html.haml
+++ b/app/views/shared/_og.html.haml
@@ -1,5 +1,5 @@
 - thumbnail     = @instance_presenter.thumbnail
-- description ||= strip_tags(@instance_presenter.description.presence || t('about.about_mastodon_html'))
+- description ||= @instance_presenter.description.presence || strip_tags(t('about.about_mastodon_html'))
 
 %meta{ name: 'description', content: description }/
 
diff --git a/app/views/statuses/_detailed_status.html.haml b/app/views/statuses/_detailed_status.html.haml
index bf498e33d..70cfbd6b8 100644
--- a/app/views/statuses/_detailed_status.html.haml
+++ b/app/views/statuses/_detailed_status.html.haml
@@ -62,18 +62,18 @@
       - else
         = fa_icon('reply-all')
       %span.detailed-status__reblogs>= friendly_number_to_human status.replies_count
-      = " "
+      = ' '
     ·
     - if status.public_visibility? || status.unlisted_visibility?
       %span.detailed-status__link
         = fa_icon('retweet')
         %span.detailed-status__reblogs>= friendly_number_to_human status.reblogs_count
-        = " "
+        = ' '
       ·
     %span.detailed-status__link
       = fa_icon('star')
       %span.detailed-status__favorites>= friendly_number_to_human status.favourites_count
-      = " "
+      = ' '
 
     - if user_signed_in?
       ·
diff --git a/app/views/statuses/_og_image.html.haml b/app/views/statuses/_og_image.html.haml
index 5a647531a..1ae97adff 100644
--- a/app/views/statuses/_og_image.html.haml
+++ b/app/views/statuses/_og_image.html.haml
@@ -31,7 +31,7 @@
       - player_card = true
       = opengraph 'og:image', full_asset_url(account.avatar.url(:original))
       = opengraph 'og:image:width', '400'
-      = opengraph 'og:image:height','400'
+      = opengraph 'og:image:height', '400'
       = opengraph 'og:audio', full_asset_url(media.file.url(:original))
       = opengraph 'og:audio:secure_url', full_asset_url(media.file.url(:original))
       = opengraph 'og:audio:type', media.file_content_type
@@ -45,7 +45,4 @@
   - else
     = opengraph 'twitter:card', 'summary_large_image'
 - else
-  = opengraph 'og:image', full_asset_url(account.avatar.url(:original))
-  = opengraph 'og:image:width', '400'
-  = opengraph 'og:image:height','400'
   = opengraph 'twitter:card', 'summary'
diff --git a/app/views/statuses/_poll.html.haml b/app/views/statuses/_poll.html.haml
index d0f264095..248c6058c 100644
--- a/app/views/statuses/_poll.html.haml
+++ b/app/views/statuses/_poll.html.haml
@@ -21,7 +21,7 @@
             %span.poll__chart
         - else
           %label.poll__option><
-            %span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}><
+            %span.poll__input{ class: poll.multiple? ? 'checkbox' : nil }><
             %span.poll__option__text
               = prerender_custom_emojis(h(option.title), status.emojis)
   .poll__footer
diff --git a/app/views/statuses/_status.html.haml b/app/views/statuses/_status.html.haml
index 3b7152753..afeb55faf 100644
--- a/app/views/statuses/_status.html.haml
+++ b/app/views/statuses/_status.html.haml
@@ -11,7 +11,7 @@
   h_class           = microformats_h_class(status, is_predecessor, is_successor, include_threads)
   style_classes     = style_classes(status, is_predecessor, is_successor, include_threads)
   mf_classes        = microformats_classes(status, is_direct_parent, is_direct_child)
-  entry_classes     = h_class + ' ' + mf_classes + ' ' + style_classes
+  entry_classes     = "#{h_class} #{mf_classes} #{style_classes}"
 
 - if status.reply? && include_threads
   - if @next_ancestor
diff --git a/app/views/tags/show.rss.ruby b/app/views/tags/show.rss.ruby
index 8e0c2327b..bbda1ad4b 100644
--- a/app/views/tags/show.rss.ruby
+++ b/app/views/tags/show.rss.ruby
@@ -3,7 +3,7 @@ RSS::Builder.build do |doc|
   doc.description(I18n.t('rss.descriptions.tag', hashtag: @tag.display_name))
   doc.link(tag_url(@tag))
   doc.last_build_date(@statuses.first.created_at) if @statuses.any?
-  doc.generator("Mastodon v#{Mastodon::Version.to_s}")
+  doc.generator("Mastodon v#{Mastodon::Version}")
 
   @statuses.each do |status|
     doc.item do |item|
@@ -16,12 +16,12 @@ RSS::Builder.build do |doc|
         item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size)
       end
 
-      status.ordered_media_attachments.each do |media|
-        item.media_content(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size) do |media_content|
-          media_content.medium(media.gifv? ? 'image' : media.type.to_s)
+      status.ordered_media_attachments.each do |media_attachment|
+        item.media_content(full_asset_url(media_attachment.file.url(:original, false)), media_attachment.file.content_type, media_attachment.file.size) do |media_content|
+          media_content.medium(media_attachment.gifv? ? 'image' : media_attachment.type.to_s)
           media_content.rating(status.sensitive? ? 'adult' : 'nonadult')
-          media_content.description(media.description) if media.description.present?
-          media_content.thumbnail(media.thumbnail.url(:original, false)) if media.thumbnail?
+          media_content.description(media_attachment.description) if media_attachment.description.present?
+          media_content.thumbnail(media_attachment.thumbnail.url(:original, false)) if media_attachment.thumbnail?
         end
       end
 
diff --git a/app/views/user_mailer/appeal_rejected.html.haml b/app/views/user_mailer/appeal_rejected.html.haml
index 75cd9d023..c316a73fb 100644
--- a/app/views/user_mailer/appeal_rejected.html.haml
+++ b/app/views/user_mailer/appeal_rejected.html.haml
@@ -17,7 +17,7 @@
                                 %tbody
                                   %tr
                                     %td
-                                      = image_tag full_pack_url('media/images/mailer/icon_warning.png'), alt: ''
+                                      = image_tag full_pack_url('media/images/mailer/icon_flag.png'), alt: ''
 
                               %h1= t 'user_mailer.appeal_rejected.title'
 
diff --git a/app/views/user_mailer/backup_ready.html.haml b/app/views/user_mailer/backup_ready.html.haml
index 85140b08b..465ead2c8 100644
--- a/app/views/user_mailer/backup_ready.html.haml
+++ b/app/views/user_mailer/backup_ready.html.haml
@@ -55,5 +55,5 @@
                             %tbody
                               %tr
                                 %td.button-primary
-                                  = link_to full_asset_url(@backup.dump.url) do
+                                  = link_to download_backup_url(@backup) do
                                     %span= t 'exports.archive_takeout.download'
diff --git a/app/views/user_mailer/backup_ready.text.erb b/app/views/user_mailer/backup_ready.text.erb
index eb89e7d74..8ebbaae85 100644
--- a/app/views/user_mailer/backup_ready.text.erb
+++ b/app/views/user_mailer/backup_ready.text.erb
@@ -4,4 +4,4 @@
 
 <%= t 'user_mailer.backup_ready.explanation' %>
 
-=> <%= full_asset_url(@backup.dump.url) %>
+=> <%= download_backup_url(@backup) %>
diff --git a/app/views/user_mailer/suspicious_sign_in.html.haml b/app/views/user_mailer/suspicious_sign_in.html.haml
index 856f9fb7c..e4ad500c3 100644
--- a/app/views/user_mailer/suspicious_sign_in.html.haml
+++ b/app/views/user_mailer/suspicious_sign_in.html.haml
@@ -45,7 +45,7 @@
                                 = @remote_ip
                                 %br/
                                 %strong= "#{t('sessions.browser')}:"
-                                %span{ title: @user_agent }= t 'sessions.description', browser: t("sessions.browsers.#{@detection.id}", default: "#{@detection.id}"), platform: t("sessions.platforms.#{@detection.platform.id}", default: "#{@detection.platform.id}")
+                                %span{ title: @user_agent }= t 'sessions.description', browser: t("sessions.browsers.#{@detection.id}", default: @detection.id.to_s), platform: t("sessions.platforms.#{@detection.platform.id}", default: @detection.platform.id.to_s)
                                 %br/
                                 = l(@timestamp)
 
diff --git a/app/views/user_mailer/webauthn_credential_added.html.haml b/app/views/user_mailer/webauthn_credential_added.html.haml
index 81de84b56..c91c96d6f 100644
--- a/app/views/user_mailer/webauthn_credential_added.html.haml
+++ b/app/views/user_mailer/webauthn_credential_added.html.haml
@@ -20,7 +20,7 @@
                                       = image_tag full_pack_url('media/images/mailer/icon_lock_open.png'), alt: ''
 
                               %h1= t 'devise.mailer.webauthn_credential.added.title'
-                              %p.lead= "#{t 'devise.mailer.webauthn_credential.added.explanation' }:"
+                              %p.lead= "#{t('devise.mailer.webauthn_credential.added.explanation')}:"
                               %p.lead= @webauthn_credential.nickname
 
 %table.email-table{ cellspacing: 0, cellpadding: 0 }
diff --git a/app/views/user_mailer/webauthn_credential_deleted.html.haml b/app/views/user_mailer/webauthn_credential_deleted.html.haml
index 7b47f0c88..578a08022 100644
--- a/app/views/user_mailer/webauthn_credential_deleted.html.haml
+++ b/app/views/user_mailer/webauthn_credential_deleted.html.haml
@@ -20,7 +20,7 @@
                                       = image_tag full_pack_url('media/images/mailer/icon_lock_open.png'), alt: ''
 
                               %h1= t 'devise.mailer.webauthn_credential.deleted.title'
-                              %p.lead= "#{t 'devise.mailer.webauthn_credential.deleted.explanation' }:"
+                              %p.lead= "#{t('devise.mailer.webauthn_credential.deleted.explanation')}:"
                               %p.lead= @webauthn_credential.nickname
 
 %table.email-table{ cellspacing: 0, cellpadding: 0 }
diff --git a/app/views/well_known/host_meta/show.xml.ruby b/app/views/well_known/host_meta/show.xml.ruby
index b4e867c5f..bb5a01a1b 100644
--- a/app/views/well_known/host_meta/show.xml.ruby
+++ b/app/views/well_known/host_meta/show.xml.ruby
@@ -9,4 +9,4 @@ doc << Ox::Element.new('XRD').tap do |xrd|
   end
 end
 
-('<?xml version="1.0" encoding="UTF-8"?>' + Ox.dump(doc, effort: :tolerant)).force_encoding('UTF-8')
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>#{Ox.dump(doc, effort: :tolerant)}".force_encoding('UTF-8')
diff --git a/app/workers/activitypub/delivery_worker.rb b/app/workers/activitypub/delivery_worker.rb
index d9153132b..7c1c14766 100644
--- a/app/workers/activitypub/delivery_worker.rb
+++ b/app/workers/activitypub/delivery_worker.rb
@@ -10,6 +10,16 @@ class ActivityPub::DeliveryWorker
 
   sidekiq_options queue: 'push', retry: 16, dead: false
 
+  # Unfortunately, we cannot control Sidekiq's jitter, so add our own
+  sidekiq_retry_in do |count|
+    # This is Sidekiq's default delay
+    delay  = (count**4) + 15
+    # Our custom jitter, that will be added to Sidekiq's built-in one.
+    # Sidekiq's built-in jitter is `rand(10) * (count + 1)`
+    jitter = rand(0.5 * (count**4))
+    delay + jitter
+  end
+
   HEADERS = { 'Content-Type' => 'application/activity+json' }.freeze
 
   def perform(json, source_account_id, inbox_url, options = {})
diff --git a/app/workers/activitypub/distribute_poll_update_worker.rb b/app/workers/activitypub/distribute_poll_update_worker.rb
index 601075ea6..ebdb78bb3 100644
--- a/app/workers/activitypub/distribute_poll_update_worker.rb
+++ b/app/workers/activitypub/distribute_poll_update_worker.rb
@@ -12,7 +12,7 @@ class ActivityPub::DistributePollUpdateWorker
 
     return if @status.preloadable_poll.nil? || @status.local_only?
 
-    ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
+    ActivityPub::DeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url|
       [payload, @account.id, inbox_url]
     end
 
diff --git a/app/workers/activitypub/fetch_replies_worker.rb b/app/workers/activitypub/fetch_replies_worker.rb
index 54d98f228..d72bad745 100644
--- a/app/workers/activitypub/fetch_replies_worker.rb
+++ b/app/workers/activitypub/fetch_replies_worker.rb
@@ -6,8 +6,8 @@ class ActivityPub::FetchRepliesWorker
 
   sidekiq_options queue: 'pull', retry: 3
 
-  def perform(parent_status_id, replies_uri)
-    ActivityPub::FetchRepliesService.new.call(Status.find(parent_status_id), replies_uri)
+  def perform(parent_status_id, replies_uri, options = {})
+    ActivityPub::FetchRepliesService.new.call(Status.find(parent_status_id), replies_uri, **options.deep_symbolize_keys)
   rescue ActiveRecord::RecordNotFound
     true
   end
diff --git a/app/workers/activitypub/migrated_follow_delivery_worker.rb b/app/workers/activitypub/migrated_follow_delivery_worker.rb
new file mode 100644
index 000000000..daf30e0ae
--- /dev/null
+++ b/app/workers/activitypub/migrated_follow_delivery_worker.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class ActivityPub::MigratedFollowDeliveryWorker < ActivityPub::DeliveryWorker
+  def perform(json, source_account_id, inbox_url, old_target_account_id, options = {})
+    super(json, source_account_id, inbox_url, options)
+    unfollow_old_account!(old_target_account_id)
+  end
+
+  private
+
+  def unfollow_old_account!(old_target_account_id)
+    old_target_account = Account.find(old_target_account_id)
+    UnfollowService.new.call(@source_account, old_target_account, skip_unmerge: true)
+  rescue
+    true
+  end
+end
diff --git a/app/workers/activitypub/move_distribution_worker.rb b/app/workers/activitypub/move_distribution_worker.rb
index 65c5c0d1c..1680fcc76 100644
--- a/app/workers/activitypub/move_distribution_worker.rb
+++ b/app/workers/activitypub/move_distribution_worker.rb
@@ -10,7 +10,7 @@ class ActivityPub::MoveDistributionWorker
     @migration = AccountMigration.find(migration_id)
     @account   = @migration.account
 
-    ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
+    ActivityPub::DeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url|
       [signed_payload, @account.id, inbox_url]
     end
 
diff --git a/app/workers/activitypub/processing_worker.rb b/app/workers/activitypub/processing_worker.rb
index 5e36fab51..1bb94b7f2 100644
--- a/app/workers/activitypub/processing_worker.rb
+++ b/app/workers/activitypub/processing_worker.rb
@@ -15,6 +15,6 @@ class ActivityPub::ProcessingWorker
 
     ActivityPub::ProcessCollectionService.new.call(body, actor, override_timestamps: true, delivered_to_account_id: delivered_to_account_id, delivery: true)
   rescue ActiveRecord::RecordInvalid => e
-    Rails.logger.debug "Error processing incoming ActivityPub object: #{e}"
+    Rails.logger.debug { "Error processing incoming ActivityPub object: #{e}" }
   end
 end
diff --git a/app/workers/activitypub/raw_distribution_worker.rb b/app/workers/activitypub/raw_distribution_worker.rb
index 8ecc17db9..c77821e0f 100644
--- a/app/workers/activitypub/raw_distribution_worker.rb
+++ b/app/workers/activitypub/raw_distribution_worker.rb
@@ -25,7 +25,7 @@ class ActivityPub::RawDistributionWorker
   def distribute!
     return if inboxes.empty?
 
-    ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
+    ActivityPub::DeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url|
       [payload, source_account_id, inbox_url, options]
     end
   end
diff --git a/app/workers/backup_worker.rb b/app/workers/backup_worker.rb
index 7b0b52844..df933142a 100644
--- a/app/workers/backup_worker.rb
+++ b/app/workers/backup_worker.rb
@@ -9,12 +9,10 @@ class BackupWorker
     backup_id = msg['args'].first
 
     ActiveRecord::Base.connection_pool.with_connection do
-      begin
-        backup = Backup.find(backup_id)
-        backup.destroy
-      rescue ActiveRecord::RecordNotFound
-        true
-      end
+      backup = Backup.find(backup_id)
+      backup.destroy
+    rescue ActiveRecord::RecordNotFound
+      true
     end
   end
 
diff --git a/app/workers/concerns/exponential_backoff.rb b/app/workers/concerns/exponential_backoff.rb
index f2b931e33..7626b2151 100644
--- a/app/workers/concerns/exponential_backoff.rb
+++ b/app/workers/concerns/exponential_backoff.rb
@@ -5,7 +5,7 @@ module ExponentialBackoff
 
   included do
     sidekiq_retry_in do |count|
-      15 + 10 * (count**4) + rand(10 * (count**4))
+      15 + (10 * (count**4)) + rand(10 * (count**4))
     end
   end
 end
diff --git a/app/workers/fetch_reply_worker.rb b/app/workers/fetch_reply_worker.rb
index f7aa25e81..68a7414be 100644
--- a/app/workers/fetch_reply_worker.rb
+++ b/app/workers/fetch_reply_worker.rb
@@ -6,7 +6,7 @@ class FetchReplyWorker
 
   sidekiq_options queue: 'pull', retry: 3
 
-  def perform(child_url)
-    FetchRemoteStatusService.new.call(child_url)
+  def perform(child_url, options = {})
+    FetchRemoteStatusService.new.call(child_url, **options.deep_symbolize_keys)
   end
 end
diff --git a/app/workers/import/relationship_worker.rb b/app/workers/import/relationship_worker.rb
index 6791b15c3..c2728c196 100644
--- a/app/workers/import/relationship_worker.rb
+++ b/app/workers/import/relationship_worker.rb
@@ -49,7 +49,7 @@ class Import::RelationshipWorker
         .with_error_handler { |error, handle| error.is_a?(HTTP::Error) || error.is_a?(OpenSSL::SSL::SSLError) ? handle.call(error) : raise(error) }
         .run
     else
-      block.call
+      yield
     end
   end
 end
diff --git a/app/workers/poll_expiration_notify_worker.rb b/app/workers/poll_expiration_notify_worker.rb
index 0e29a5f60..b7a60fab8 100644
--- a/app/workers/poll_expiration_notify_worker.rb
+++ b/app/workers/poll_expiration_notify_worker.rb
@@ -3,7 +3,7 @@
 class PollExpirationNotifyWorker
   include Sidekiq::Worker
 
-  sidekiq_options lock: :until_executed
+  sidekiq_options lock: :until_executing
 
   def perform(poll_id)
     @poll = Poll.find(poll_id)
diff --git a/app/workers/post_process_media_worker.rb b/app/workers/post_process_media_worker.rb
index 24201101c..996d5def9 100644
--- a/app/workers/post_process_media_worker.rb
+++ b/app/workers/post_process_media_worker.rb
@@ -9,13 +9,11 @@ class PostProcessMediaWorker
     media_attachment_id = msg['args'].first
 
     ActiveRecord::Base.connection_pool.with_connection do
-      begin
-        media_attachment = MediaAttachment.find(media_attachment_id)
-        media_attachment.processing = :failed
-        media_attachment.save
-      rescue ActiveRecord::RecordNotFound
-        true
-      end
+      media_attachment = MediaAttachment.find(media_attachment_id)
+      media_attachment.processing = :failed
+      media_attachment.save
+    rescue ActiveRecord::RecordNotFound
+      true
     end
 
     Sidekiq.logger.error("Processing media attachment #{media_attachment_id} failed with #{msg['error_message']}")
diff --git a/app/workers/scheduler/accounts_statuses_cleanup_scheduler.rb b/app/workers/scheduler/accounts_statuses_cleanup_scheduler.rb
index bd92fe32c..f237f1dc9 100644
--- a/app/workers/scheduler/accounts_statuses_cleanup_scheduler.rb
+++ b/app/workers/scheduler/accounts_statuses_cleanup_scheduler.rb
@@ -7,7 +7,7 @@ class Scheduler::AccountsStatusesCleanupScheduler
   # This limit is mostly to be nice to the fediverse at large and not
   # generate too much traffic.
   # This also helps limiting the running time of the scheduler itself.
-  MAX_BUDGET         = 50
+  MAX_BUDGET         = 150
 
   # This is an attempt to spread the load across instances, as various
   # accounts are likely to have various followers.
@@ -15,28 +15,22 @@ class Scheduler::AccountsStatusesCleanupScheduler
 
   # This is an attempt to limit the workload generated by status removal
   # jobs to something the particular instance can handle.
-  PER_THREAD_BUDGET  = 5
+  PER_THREAD_BUDGET  = 6
 
   # Those avoid loading an instance that is already under load
-  MAX_DEFAULT_SIZE    = 2
+  MAX_DEFAULT_SIZE    = 200
   MAX_DEFAULT_LATENCY = 5
-  MAX_PUSH_SIZE       = 5
+  MAX_PUSH_SIZE       = 500
   MAX_PUSH_LATENCY    = 10
+
   # 'pull' queue has lower priority jobs, and it's unlikely that pushing
   # deletes would cause much issues with this queue if it didn't cause issues
   # with default and push. Yet, do not enqueue deletes if the instance is
   # lagging behind too much.
-  MAX_PULL_SIZE       = 500
-  MAX_PULL_LATENCY    = 300
-
-  # This is less of an issue in general, but deleting old statuses is likely
-  # to cause delivery errors, and thus increase the number of jobs to be retried.
-  # This doesn't directly translate to load, but connection errors and a high
-  # number of dead instances may lead to this spiraling out of control if
-  # unchecked.
-  MAX_RETRY_SIZE = 50_000
+  MAX_PULL_SIZE       = 10_000
+  MAX_PULL_LATENCY    = 5.minutes.to_i
 
-  sidekiq_options retry: 0, lock: :until_executed
+  sidekiq_options retry: 0, lock: :until_executed, lock_ttl: 1.day.to_i
 
   def perform
     return if under_load?
@@ -62,17 +56,17 @@ class Scheduler::AccountsStatusesCleanupScheduler
       # The idea here is to loop through all policies at least once until the budget is exhausted
       # and start back after the last processed account otherwise
       break if budget.zero? || (num_processed_accounts.zero? && first_policy_id.nil?)
+
       first_policy_id = nil
     end
   end
 
   def compute_budget
-    threads = Sidekiq::ProcessSet.new.select { |x| x['queues'].include?('push') }.map { |x| x['concurrency'] }.sum
+    threads = Sidekiq::ProcessSet.new.select { |x| x['queues'].include?('push') }.pluck('concurrency').sum
     [PER_THREAD_BUDGET * threads, MAX_BUDGET].min
   end
 
   def under_load?
-    return true if Sidekiq::Stats.new.retry_size > MAX_RETRY_SIZE
     queue_under_load?('default', MAX_DEFAULT_SIZE, MAX_DEFAULT_LATENCY) || queue_under_load?('push', MAX_PUSH_SIZE, MAX_PUSH_LATENCY) || queue_under_load?('pull', MAX_PULL_SIZE, MAX_PULL_LATENCY)
   end
 
diff --git a/app/workers/scheduler/follow_recommendations_scheduler.rb b/app/workers/scheduler/follow_recommendations_scheduler.rb
index 57f78170e..17cf3f2cc 100644
--- a/app/workers/scheduler/follow_recommendations_scheduler.rb
+++ b/app/workers/scheduler/follow_recommendations_scheduler.rb
@@ -19,13 +19,11 @@ class Scheduler::FollowRecommendationsScheduler
     fallback_recommendations = FollowRecommendation.order(rank: :desc).limit(SET_SIZE)
 
     Trends.available_locales.each do |locale|
-      recommendations = begin
-        if AccountSummary.safe.filtered.localized(locale).exists? # We can skip the work if no accounts with that language exist
-          FollowRecommendation.localized(locale).order(rank: :desc).limit(SET_SIZE).map { |recommendation| [recommendation.account_id, recommendation.rank] }
-        else
-          []
-        end
-      end
+      recommendations = if AccountSummary.safe.filtered.localized(locale).exists? # We can skip the work if no accounts with that language exist
+                          FollowRecommendation.localized(locale).order(rank: :desc).limit(SET_SIZE).map { |recommendation| [recommendation.rank, recommendation.account_id] }
+                        else
+                          []
+                        end
 
       # Use language-agnostic results if there are not enough language-specific ones
       missing = SET_SIZE - recommendations.size
@@ -35,14 +33,14 @@ class Scheduler::FollowRecommendationsScheduler
 
         # Language-specific results should be above language-agnostic ones,
         # otherwise language-agnostic ones will always overshadow them
-        recommendations.map! { |(account_id, rank)| [account_id, rank + max_fallback_rank] }
+        recommendations.map! { |(rank, account_id)| [rank + max_fallback_rank, account_id] }
 
         added = 0
 
         fallback_recommendations.each do |recommendation|
-          next if recommendations.any? { |(account_id, _)| account_id == recommendation.account_id }
+          next if recommendations.any? { |(_, account_id)| account_id == recommendation.account_id }
 
-          recommendations << [recommendation.account_id, recommendation.rank]
+          recommendations << [recommendation.rank, recommendation.account_id]
           added += 1
 
           break if added >= missing
@@ -51,10 +49,7 @@ class Scheduler::FollowRecommendationsScheduler
 
       redis.multi do |multi|
         multi.del(key(locale))
-
-        recommendations.each do |(account_id, rank)|
-          multi.zadd(key(locale), rank, account_id)
-        end
+        multi.zadd(key(locale), recommendations)
       end
     end
   end
diff --git a/app/workers/scheduler/indexing_scheduler.rb b/app/workers/scheduler/indexing_scheduler.rb
index c42396629..d622f5586 100644
--- a/app/workers/scheduler/indexing_scheduler.rb
+++ b/app/workers/scheduler/indexing_scheduler.rb
@@ -6,17 +6,19 @@ class Scheduler::IndexingScheduler
 
   sidekiq_options retry: 0
 
+  IMPORT_BATCH_SIZE = 1000
+  SCAN_BATCH_SIZE = 10 * IMPORT_BATCH_SIZE
+
   def perform
     return unless Chewy.enabled?
 
     indexes.each do |type|
       with_redis do |redis|
-        ids = redis.smembers("chewy:queue:#{type.name}")
-
-        type.import!(ids)
-
-        redis.pipelined do |pipeline|
-          ids.each { |id| pipeline.srem("chewy:queue:#{type.name}", id) }
+        redis.sscan_each("chewy:queue:#{type.name}", count: SCAN_BATCH_SIZE).each_slice(IMPORT_BATCH_SIZE) do |ids|
+          type.import!(ids)
+          redis.pipelined do |pipeline|
+            pipeline.srem("chewy:queue:#{type.name}", ids)
+          end
         end
       end
     end
diff --git a/app/workers/scheduler/user_cleanup_scheduler.rb b/app/workers/scheduler/user_cleanup_scheduler.rb
index 63f9ed78c..45cfbc62e 100644
--- a/app/workers/scheduler/user_cleanup_scheduler.rb
+++ b/app/workers/scheduler/user_cleanup_scheduler.rb
@@ -15,7 +15,7 @@ class Scheduler::UserCleanupScheduler
   def clean_unconfirmed_accounts!
     User.where('confirmed_at is NULL AND confirmation_sent_at <= ?', 2.days.ago).reorder(nil).find_in_batches do |batch|
       # We have to do it separately because of missing database constraints
-      AccountModerationNote.where(account_id: batch.map(&:account_id)).delete_all
+      AccountModerationNote.where(target_account_id: batch.map(&:account_id)).delete_all
       Account.where(id: batch.map(&:account_id)).delete_all
       User.where(id: batch.map(&:id)).delete_all
     end
diff --git a/app/workers/thread_resolve_worker.rb b/app/workers/thread_resolve_worker.rb
index 1b77dfdd9..3206c45f6 100644
--- a/app/workers/thread_resolve_worker.rb
+++ b/app/workers/thread_resolve_worker.rb
@@ -6,9 +6,9 @@ class ThreadResolveWorker
 
   sidekiq_options queue: 'pull', retry: 3
 
-  def perform(child_status_id, parent_url)
+  def perform(child_status_id, parent_url, options = {})
     child_status  = Status.find(child_status_id)
-    parent_status = FetchRemoteStatusService.new.call(parent_url)
+    parent_status = FetchRemoteStatusService.new.call(parent_url, **options.deep_symbolize_keys)
 
     return if parent_status.nil?
 
diff --git a/app/workers/unfollow_follow_worker.rb b/app/workers/unfollow_follow_worker.rb
index 7203b4888..a4d57839d 100644
--- a/app/workers/unfollow_follow_worker.rb
+++ b/app/workers/unfollow_follow_worker.rb
@@ -10,13 +10,7 @@ class UnfollowFollowWorker
     old_target_account = Account.find(old_target_account_id)
     new_target_account = Account.find(new_target_account_id)
 
-    follow    = follower_account.active_relationships.find_by(target_account: old_target_account)
-    reblogs   = follow&.show_reblogs?
-    notify    = follow&.notify?
-    languages = follow&.languages
-
-    FollowService.new.call(follower_account, new_target_account, reblogs: reblogs, notify: notify, languages: languages, bypass_locked: bypass_locked, bypass_limit: true)
-    UnfollowService.new.call(follower_account, old_target_account, skip_unmerge: true)
+    FollowMigrationService.new.call(follower_account, new_target_account, old_target_account, bypass_locked: bypass_locked)
   rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
     true
   end
diff --git a/app/workers/web/push_notification_worker.rb b/app/workers/web/push_notification_worker.rb
index 1ed5bb9e0..7e9691aab 100644
--- a/app/workers/web/push_notification_worker.rb
+++ b/app/workers/web/push_notification_worker.rb
@@ -22,13 +22,13 @@ class Web::PushNotificationWorker
       request = Request.new(:post, @subscription.endpoint, body: payload.fetch(:ciphertext), http_client: http_client)
 
       request.add_headers(
-        'Content-Type'     => 'application/octet-stream',
-        'Ttl'              => TTL,
-        'Urgency'          => URGENCY,
+        'Content-Type' => 'application/octet-stream',
+        'Ttl' => TTL,
+        'Urgency' => URGENCY,
         'Content-Encoding' => 'aesgcm',
-        'Encryption'       => "salt=#{Webpush.encode64(payload.fetch(:salt)).delete('=')}",
-        'Crypto-Key'       => "dh=#{Webpush.encode64(payload.fetch(:server_public_key)).delete('=')};#{@subscription.crypto_key_header}",
-        'Authorization'    => @subscription.authorization_header
+        'Encryption' => "salt=#{Webpush.encode64(payload.fetch(:salt)).delete('=')}",
+        'Crypto-Key' => "dh=#{Webpush.encode64(payload.fetch(:server_public_key)).delete('=')};#{@subscription.crypto_key_header}",
+        'Authorization' => @subscription.authorization_header
       )
 
       request.perform do |response|
diff --git a/app/workers/webhooks/delivery_worker.rb b/app/workers/webhooks/delivery_worker.rb
index b1e345c5e..f8ed669fb 100644
--- a/app/workers/webhooks/delivery_worker.rb
+++ b/app/workers/webhooks/delivery_worker.rb
@@ -19,7 +19,7 @@ class Webhooks::DeliveryWorker
   private
 
   def perform_request
-    request = Request.new(:post, @webhook.url, body: @body)
+    request = Request.new(:post, @webhook.url, body: @body, allow_local: true)
 
     request.add_headers(
       'Content-Type' => 'application/json',
diff --git a/babel.config.js b/babel.config.js
index b040cc159..9ed074099 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -13,13 +13,14 @@ module.exports = (api) => {
 
   const config = {
     presets: [
+      '@babel/preset-typescript',
       ['@babel/react', reactOptions],
       ['@babel/env', envOptions],
     ],
     plugins: [
-      ['@babel/proposal-decorators', { legacy: true }],
       ['react-intl', { messagesDir: './build/messages' }],
       'preval',
+      '@babel/plugin-proposal-nullish-coalescing-operator',
     ],
     overrides: [
       {
@@ -67,4 +68,3 @@ module.exports = (api) => {
 
   return config;
 };
-
diff --git a/bin/tootctl b/bin/tootctl
index a9ebb22c6..9c7ae8b87 100755
--- a/bin/tootctl
+++ b/bin/tootctl
@@ -5,7 +5,9 @@ require_relative '../config/boot'
 require_relative '../lib/cli'
 
 begin
-  Mastodon::CLI.start(ARGV)
+  Chewy.strategy(:mastodon) do
+    Mastodon::CLI.start(ARGV)
+  end
 rescue Interrupt
   exit(130)
 end
diff --git a/config.ru b/config.ru
index 3476455ef..afd13e211 100644
--- a/config.ru
+++ b/config.ru
@@ -1,5 +1,6 @@
 # frozen_string_literal: true
+
 # This file is used by Rack-based servers to start the application.
 
-require ::File.expand_path('../config/environment', __FILE__)
+require File.expand_path('config/environment', __dir__)
 run Rails.application
diff --git a/config/application.rb b/config/application.rb
index 47988d9d0..f72cc8e11 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -35,9 +35,11 @@ require_relative '../lib/terrapin/multi_pipe_extensions'
 require_relative '../lib/mastodon/snowflake'
 require_relative '../lib/mastodon/version'
 require_relative '../lib/mastodon/rack_middleware'
+require_relative '../lib/public_file_server_middleware'
 require_relative '../lib/devise/two_factor_ldap_authenticatable'
 require_relative '../lib/devise/two_factor_pam_authenticatable'
 require_relative '../lib/chewy/strategy/mastodon'
+require_relative '../lib/chewy/strategy/bypass_with_warning'
 require_relative '../lib/webpacker/manifest_extensions'
 require_relative '../lib/webpacker/helper_extensions'
 require_relative '../lib/rails/engine_extensions'
@@ -69,12 +71,14 @@ module Mastodon
     # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
     config.i18n.available_locales = [
       :af,
+      :an,
       :ar,
       :ast,
       :be,
       :bg,
       :bn,
       :br,
+      :bs,
       :ca,
       :ckb,
       :co,
@@ -85,6 +89,7 @@ module Mastodon
       :el,
       :en,
       :'en-cafe',
+      :'en-GB',
       :eo,
       :es,
       :'es-AR',
@@ -93,7 +98,9 @@ module Mastodon
       :eu,
       :fa,
       :fi,
+      :fo,
       :fr,
+      :'fr-QC',
       :fy,
       :ga,
       :gd,
@@ -104,6 +111,7 @@ module Mastodon
       :hu,
       :hy,
       :id,
+      :ig,
       :io,
       :is,
       :it,
@@ -114,16 +122,20 @@ module Mastodon
       :kn,
       :ko,
       :ku,
+      :kw,
+      :la,
       :lt,
       :lv,
       :mk,
       :ml,
       :mr,
       :ms,
+      :my,
       :nl,
       :nn,
       :no,
       :oc,
+      :pa,
       :pl,
       :'pt-BR',
       :'pt-PT',
@@ -131,6 +143,7 @@ module Mastodon
       :ru,
       :sa,
       :sc,
+      :sco,
       :si,
       :sk,
       :sl,
@@ -138,10 +151,14 @@ module Mastodon
       :sr,
       :'sr-Latn',
       :sv,
+      :szl,
       :ta,
+      :tai,
       :te,
       :th,
       :tr,
+      :tt,
+      :ug,
       :uk,
       :ur,
       :vi,
@@ -170,6 +187,10 @@ module Mastodon
     config.active_job.queue_adapter = :sidekiq
     config.action_mailer.deliver_later_queue_name = 'mailers'
 
+    # We use our own middleware for this
+    config.public_file_server.enabled = false
+
+    config.middleware.use PublicFileServerMiddleware if Rails.env.development? || ENV['RAILS_SERVE_STATIC_FILES'] == 'true'
     config.middleware.use Rack::Attack
     config.middleware.use Mastodon::RackMiddleware
 
diff --git a/config/database.yml b/config/database.yml
index bfb53f21b..34acf2f19 100644
--- a/config/database.yml
+++ b/config/database.yml
@@ -5,6 +5,7 @@ default: &default
   connect_timeout: 15
   encoding: unicode
   sslmode: <%= ENV['DB_SSLMODE'] || "prefer" %>
+  application_name: ''
 
 development:
   <<: *default
diff --git a/config/environments/development.rb b/config/environments/development.rb
index de8762ff7..a633dfce5 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -16,15 +16,9 @@ Rails.application.configure do
   # Run rails dev:cache to toggle caching.
   if Rails.root.join('tmp/caching-dev.txt').exist?
     config.action_controller.perform_caching = true
-
     config.cache_store = :redis_cache_store, REDIS_CACHE_PARAMS
-
-    config.public_file_server.headers = {
-      'Cache-Control' => "public, max-age=#{2.days.to_i}",
-    }
   else
     config.action_controller.perform_caching = false
-
     config.cache_store = :null_store
   end
 
@@ -34,9 +28,10 @@ Rails.application.configure do
   end
 
   # Generate random VAPID keys
-  vapid_key = Webpush.generate_key
-  config.x.vapid_private_key = vapid_key.private_key
-  config.x.vapid_public_key = vapid_key.public_key
+  Webpush.generate_key.tap do |vapid_key|
+    config.x.vapid_private_key = vapid_key.private_key
+    config.x.vapid_public_key = vapid_key.public_key
+  end
 
   # Don't care if the mailer can't send.
   config.action_mailer.raise_delivery_errors = false
@@ -76,21 +71,9 @@ Rails.application.configure do
   # Otherwise, use letter_opener, which launches a browser window to view sent mail.
   config.action_mailer.delivery_method = (ENV['HEROKU'] || ENV['VAGRANT'] || ENV['REMOTE_DEV']) ? :letter_opener_web : :letter_opener
 
-  config.after_initialize do
-    Bullet.enable        = true
-    Bullet.bullet_logger = true
-    Bullet.rails_logger  = false
-
-    Bullet.add_safelist type: :n_plus_one_query, class_name: 'User', association: :account
-  end
-
+  # We provide a default secret for the development environment here.
+  # This value should not be used in production environments!
   config.x.otp_secret = ENV.fetch('OTP_SECRET', '1fc2b87989afa6351912abeebe31ffc5c476ead9bf8b3d74cbc4a302c7b69a45b40b1bbef3506ddad73e942e15ed5ca4b402bf9a66423626051104f4b5f05109')
 end
 
-ActiveRecordQueryTrace.enabled = ENV['QUERY_TRACE_ENABLED'] == 'true'
-
-module PrivateAddressCheck
-  def self.private_address?(*)
-    false
-  end
-end
+Redis.raise_deprecations = true
diff --git a/config/environments/production.rb b/config/environments/production.rb
index a7540c12e..aec3b80a1 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -19,27 +19,16 @@ Rails.application.configure do
   # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
   # config.require_master_key = true
 
-  # Disable serving static files from the `/public` folder by default since
-  # Apache or NGINX already handles this.
-  config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
-
   ActiveSupport::Logger.new(STDOUT).tap do |logger|
     logger.formatter = config.log_formatter
     config.logger = ActiveSupport::TaggedLogging.new(logger)
   end
 
-  # Compress JavaScripts and CSS.
-  # config.assets.js_compressor = Uglifier.new(mangle: false)
-  # config.assets.css_compressor = :sass
-
   # Do not fallback to assets pipeline if a precompiled asset is missed.
   config.assets.compile = false
 
-  # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
-
   # Specifies the header that your server uses for sending files.
-  # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
-  config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
+  config.action_dispatch.x_sendfile_header = ENV['SENDFILE_HEADER'] if ENV['SENDFILE_HEADER'].present?
 
   # Allow to specify public IP of reverse proxy if it's needed
   config.action_dispatch.trusted_proxies = ENV['TRUSTED_PROXY_IP'].split(/(?:\s*,\s*|\s+)/).map { |item| IPAddr.new(item) } if ENV['TRUSTED_PROXY_IP'].present?
@@ -65,6 +54,10 @@ Rails.application.configure do
   # Set this to true and configure the email server for immediate delivery to raise delivery errors.
   # config.action_mailer.raise_delivery_errors = false
 
+  # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+  # English when a translation cannot be found).
+  config.i18n.fallbacks = true
+
   # Send deprecation notices to registered listeners.
   config.active_support.deprecation = :notify
 
@@ -124,6 +117,7 @@ Rails.application.configure do
     enable_starttls_auto: enable_starttls_auto,
     tls: ENV['SMTP_TLS'].presence && ENV['SMTP_TLS'] == 'true',
     ssl: ENV['SMTP_SSL'].presence && ENV['SMTP_SSL'] == 'true',
+    read_timeout: 20,
   }
 
   config.action_mailer.delivery_method = ENV.fetch('SMTP_DELIVERY_METHOD', 'smtp').to_sym
diff --git a/config/environments/test.rb b/config/environments/test.rb
index ef3cb2e48..493b041eb 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -12,11 +12,6 @@ Rails.application.configure do
   # preloads Rails for running tests, you may have to set it to true.
   config.eager_load = false
 
-  # Configure public file server for tests with Cache-Control for performance.
-  config.public_file_server.enabled = true
-  config.public_file_server.headers = {
-    'Cache-Control' => "public, max-age=#{1.hour.to_i}"
-  }
   config.assets.digest = false
 
   # Show full error reports and disable caching.
@@ -56,6 +51,12 @@ Rails.application.configure do
 
   config.i18n.default_locale = :en
   config.i18n.fallbacks = true
+
+  config.to_prepare do
+    # Force Status to always be SHAPE_TOO_COMPLEX
+    # Ref: https://github.com/mastodon/mastodon/issues/23644
+    10.times { |i| Status.allocate.instance_variable_set(:"@ivar_#{i}", nil) }
+  end
 end
 
 Paperclip::Attachment.default_options[:path] = "#{Rails.root}/spec/test_files/:class/:id_partition/:style.:extension"
@@ -73,3 +74,5 @@ end
 
 # Catch serialization warnings early
 Sidekiq.strict_args!
+
+Redis.raise_deprecations = true
diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml
index 72f9e4eb7..6b34f6011 100644
--- a/config/i18n-tasks.yml
+++ b/config/i18n-tasks.yml
@@ -65,9 +65,11 @@ ignore_unused:
   - 'errors.429'
   - 'admin.accounts.roles.*'
   - 'admin.action_logs.actions.*'
-  - 'themes.*'
+  - 'admin.reports.summary.action_preambles.*'
+  - 'admin.reports.summary.actions.*'
   - 'admin_mailer.new_appeal.actions.*'
   - 'statuses.attached.*'
+  - 'themes.*'
   - 'move_handler.carry_{mutes,blocks}_over_text'
   - 'notification_mailer.*'
 
diff --git a/config/initializers/0_duplicate_migrations.rb b/config/initializers/0_duplicate_migrations.rb
index 6c45e4bd2..1b8b59025 100644
--- a/config/initializers/0_duplicate_migrations.rb
+++ b/config/initializers/0_duplicate_migrations.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # Some migrations have been present in glitch-soc for a long time and have then
 # been merged in upstream Mastodon, under a different version number.
 #
@@ -12,24 +14,26 @@
 # we decided monkey-patching Rails' Migrator to completely ignore the duplicate,
 # keeping only the one that has run, or an arbitrary one.
 
-ALLOWED_DUPLICATES = [20180410220657, 20180831171112].freeze
+ALLOWED_DUPLICATES = [2018_04_10_220657, 2018_08_31_171112].freeze
 
 module ActiveRecord
   class Migrator
     def self.new(direction, migrations, schema_migration, target_version = nil)
       migrated = Set.new(Base.connection.migration_context.get_all_versions)
 
-      migrations.group_by(&:name).each do |name, duplicates|
-        if duplicates.length > 1 && duplicates.all? { |m| ALLOWED_DUPLICATES.include?(m.version) }
-          # We have a set of allowed duplicates. Keep the migrated one, if any.
-          non_migrated = duplicates.reject { |m| migrated.include?(m.version.to_i) }
+      migrations.group_by(&:name).each do |_name, duplicates|
+        next unless duplicates.length > 1 && duplicates.all? { |m| ALLOWED_DUPLICATES.include?(m.version) }
+
+        # We have a set of allowed duplicates. Keep the migrated one, if any.
+        non_migrated = duplicates.reject { |m| migrated.include?(m.version.to_i) }
 
-          if duplicates.length == non_migrated.length || non_migrated.length == 0
+        migrations = begin
+          if duplicates.length == non_migrated.length || non_migrated.empty?
             # There weren't any migrated one, so we have to pick one “canonical” migration
-            migrations = migrations - duplicates[1..-1]
+            migrations - duplicates[1..]
           else
             # Just reject every duplicate which hasn't been migrated yet
-            migrations = migrations - non_migrated
+            migrations - non_migrated
           end
         end
       end
@@ -43,10 +47,10 @@ module ActiveRecord
       # A set of duplicated migrations is considered migrated if at least one of
       # them is migrated.
       migrated = get_all_versions
-      migrations.group_by(&:name).each do |name, duplicates|
+      migrations.group_by(&:name).each do |_name, duplicates|
         return true unless duplicates.any? { |m| migrated.include?(m.version.to_i) }
       end
-      return false
+      false
     end
   end
 end
diff --git a/config/initializers/chewy.rb b/config/initializers/chewy.rb
index 752fc3c6d..daf4a5f32 100644
--- a/config/initializers/chewy.rb
+++ b/config/initializers/chewy.rb
@@ -19,7 +19,7 @@ Chewy.settings = {
 # cycle, which takes care of checking if Elasticsearch is enabled
 # or not. However, mind that for the Rails console, the :urgent
 # strategy is set automatically with no way to override it.
-Chewy.root_strategy              = :mastodon
+Chewy.root_strategy              = :bypass_with_warning if Rails.env.production?
 Chewy.request_strategy           = :mastodon
 Chewy.use_after_commit_callbacks = false
 
diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb
index a361cb0ec..95f0b5788 100644
--- a/config/initializers/inflections.rb
+++ b/config/initializers/inflections.rb
@@ -26,6 +26,7 @@ ActiveSupport::Inflector.inflections(:en) do |inflect|
   inflect.acronym 'URL'
   inflect.acronym 'ASCII'
   inflect.acronym 'DeepL'
+  inflect.acronym 'DSL'
 
   inflect.singular 'data', 'data'
 end
diff --git a/config/initializers/paperclip.rb b/config/initializers/paperclip.rb
index a2285427c..bd37f6709 100644
--- a/config/initializers/paperclip.rb
+++ b/config/initializers/paperclip.rb
@@ -90,6 +90,12 @@ if ENV['S3_ENABLED'] == 'true'
     )
   end
 
+  if ENV.has_key?('S3_STORAGE_CLASS')
+    Paperclip::Attachment.default_options[:s3_headers].merge!(
+      'X-Amz-Storage-Class' => ENV['S3_STORAGE_CLASS']
+    )
+  end
+
   # Some S3-compatible providers might not actually be compatible with some APIs
   # used by kt-paperclip, see https://github.com/mastodon/mastodon/issues/16822
   if ENV['S3_FORCE_SINGLE_REQUEST'] == 'true'
@@ -124,6 +130,7 @@ elsif ENV['SWIFT_ENABLED'] == 'true'
       openstack_domain_name: ENV.fetch('SWIFT_DOMAIN_NAME') { 'default' },
       openstack_region: ENV['SWIFT_REGION'],
       openstack_cache_ttl: ENV.fetch('SWIFT_CACHE_TTL') { 60 },
+      openstack_temp_url_key: ENV['SWIFT_TEMP_URL_KEY'],
     },
     
     fog_file: { 'Cache-Control' => 'public, max-age=315576000, immutable' },
diff --git a/config/initializers/rack_attack.rb b/config/initializers/rack_attack.rb
index 72ef7ba80..3857e3055 100644
--- a/config/initializers/rack_attack.rb
+++ b/config/initializers/rack_attack.rb
@@ -33,6 +33,10 @@ class Rack::Attack
       authenticated_token&.resource_owner_id
     end
 
+    def authenticated_token_id
+      authenticated_token&.id
+    end
+
     def unauthenticated?
       !authenticated_user_id
     end
@@ -62,10 +66,14 @@ class Rack::Attack
     IpBlock.blocked?(req.remote_ip)
   end
 
-  throttle('throttle_authenticated_api', limit: 300, period: 5.minutes) do |req|
+  throttle('throttle_authenticated_api', limit: 1_500, period: 5.minutes) do |req|
     req.authenticated_user_id if req.api_request?
   end
 
+  throttle('throttle_per_token_api', limit: 300, period: 5.minutes) do |req|
+    req.authenticated_token_id if req.api_request?
+  end
+
   throttle('throttle_unauthenticated_api', limit: 300, period: 5.minutes) do |req|
     req.throttleable_remote_ip if req.api_request? && req.unauthenticated?
   end
diff --git a/config/initializers/redis.rb b/config/initializers/redis.rb
new file mode 100644
index 000000000..f2bbd1e45
--- /dev/null
+++ b/config/initializers/redis.rb
@@ -0,0 +1 @@
+Redis.sadd_returns_boolean = false
diff --git a/config/initializers/simple_form.rb b/config/initializers/simple_form.rb
index 5e4763b7f..2de22a945 100644
--- a/config/initializers/simple_form.rb
+++ b/config/initializers/simple_form.rb
@@ -11,6 +11,7 @@ end
 module GlitchOnlyComponent
   def glitch_only(_wrapper_options = nil)
     return unless options[:glitch_only]
+
     options[:label_text] = ->(raw_label_text, _required_label_text, _label_present) { safe_join([raw_label_text, ' ', content_tag(:span, I18n.t('simple_form.glitch_only'), class: 'glitch_only')]) }
     nil
   end
diff --git a/config/locales-glitch/ckb.yml b/config/locales-glitch/ckb.yml
index cc251e86a..77d538af7 100644
--- a/config/locales-glitch/ckb.yml
+++ b/config/locales-glitch/ckb.yml
@@ -1 +1 @@
-ckb-IR:
+ckb:
diff --git a/config/locales-glitch/es-AR.yml b/config/locales-glitch/es-AR.yml
index fd38e165e..93e3d0540 100644
--- a/config/locales-glitch/es-AR.yml
+++ b/config/locales-glitch/es-AR.yml
@@ -1,10 +1,21 @@
 ---
 es-AR:
   admin:
+    custom_emojis:
+      batch_copy_error: Se produjo un error cuando se copian algunos emojis seleccionados %{message}
+      batch_error: Ocurrió un error %{message}
     settings:
+      captcha_enabled:
+        desc_html: Esto depende de scripts externos de hCaptcha, que pueden ser una preocupación de seguridad y privacidad. Además, <strong>esto puede hacer el proceso de registro significativamente menos accesible para algunas personas (especialmente minusválidos)</strong>. Por estas razones, por favor considera medidas alternativas como el registro basado en la aprobación o la invitación.<br>Los usuarios que han sido invitados a través de una invitación de uso limitado no necesitarán resolver un CAPTCHA
+        title: Pedir a los usuarios nuevos resolver un CAPTCHA para confirmar su cuenta
+      flavour_and_skin:
+        title: Sabor y apariencia
       hide_followers_count:
         desc_html: No mostrar el conteo de seguidorxs en perfiles de usuarix
         title: Ocultar conteo de seguidorxs
+      other:
+        preamble: Varias configuraciones de glitch-soc que no encajan en otras categorías.
+        title: Otro
       outgoing_spoilers:
         desc_html: Cuando los toots federen, agrega esta etiqueta de contenido a los toots que no tengan. Es útil si tu servidor se especializa en contenido que otros servidores desearían tener con una advertencia de contenido. Los medios también se marcarán como sensibles.
         title: Advertencia de contenido para los toots salientes
@@ -14,6 +25,17 @@ es-AR:
       show_replies_in_public_timelines:
         desc_html: Además de auto-respuestas públicas (hilos), mostrar respuestas públicas en las línea de tiempo local y pública.
         title: Mostrar respuestas en líneas de tiempo públicas
+      trending_status_cw:
+        desc_html: Cuando las publicaciones en tendencia están habilitadas, permitir que la que contienen Advertencias de Contenido sean elegibles. Los cambios en esta configuración no son retroactivos.
+        title: Permitir que publicaciones con advertencias de contenido sean tendencia
+  appearance:
+    localization:
+      glitch_guide_link: https://crowdin.com/project/glitch-soc
+      glitch_guide_link_text: Igual para glitch-soc!
+  auth:
+    captcha_confirmation:
+      hint_html: ¡Solo un paso más! Para confirmar tu cuenta, este servidor requiere que resuelvas un CAPTCHA. Puedes contactar <a href="/about/more"> con el administrador del servidor</a> si tienes preguntas o necesitas ayuda para confirmar tu cuenta.
+      title: Verificación de usuario
   generic:
     use_this: Usar
   settings:
diff --git a/config/locales-glitch/es-MX.yml b/config/locales-glitch/es-MX.yml
index 48e24be76..1319dd3e3 100644
--- a/config/locales-glitch/es-MX.yml
+++ b/config/locales-glitch/es-MX.yml
@@ -1,10 +1,21 @@
 ---
 es-MX:
   admin:
+    custom_emojis:
+      batch_copy_error: Se produjo un error cuando se copian algunos emojis seleccionados %{message}
+      batch_error: Ocurrió un error %{message}
     settings:
+      captcha_enabled:
+        desc_html: Esto depende de scripts externos de hCaptcha, que pueden ser una preocupación de seguridad y privacidad. Además, <strong>esto puede hacer el proceso de registro significativamente menos accesible para algunas personas (especialmente minusválidos)</strong>. Por estas razones, por favor considera medidas alternativas como el registro basado en la aprobación o la invitación.<br>Los usuarios que han sido invitados a través de una invitación de uso limitado no necesitarán resolver un CAPTCHA
+        title: Pedir a los usuarios nuevos resolver un CAPTCHA para confirmar su cuenta
+      flavour_and_skin:
+        title: Sabor y apariencia
       hide_followers_count:
         desc_html: No mostrar el conteo de seguidorxs en perfiles de usuarix
         title: Ocultar conteo de seguidorxs
+      other:
+        preamble: Varias configuraciones de glitch-soc que no encajan en otras categorías.
+        title: Otro
       outgoing_spoilers:
         desc_html: Cuando los toots federen, agrega esta etiqueta de contenido a los toots que no tengan. Es útil si tu servidor se especializa en contenido que otros servidores desearían tener con una advertencia de contenido. Los medios también se marcarán como sensibles.
         title: Advertencia de contenido para los toots salientes
@@ -14,6 +25,17 @@ es-MX:
       show_replies_in_public_timelines:
         desc_html: Además de auto-respuestas públicas (hilos), mostrar respuestas públicas en las línea de tiempo local y pública.
         title: Mostrar respuestas en líneas de tiempo públicas
+      trending_status_cw:
+        desc_html: Cuando las publicaciones en tendencia están habilitadas, permitir que la que contienen Advertencias de Contenido sean elegibles. Los cambios en esta configuración no son retroactivos.
+        title: Permitir que publicaciones con advertencias de contenido sean tendencia
+  appearance:
+    localization:
+      glitch_guide_link: https://crowdin.com/project/glitch-soc
+      glitch_guide_link_text: Igual para glitch-soc!
+  auth:
+    captcha_confirmation:
+      hint_html: ¡Solo un paso más! Para confirmar tu cuenta, este servidor requiere que resuelvas un CAPTCHA. Puedes contactar <a href="/about/more"> con el administrador del servidor</a> si tienes preguntas o necesitas ayuda para confirmar tu cuenta.
+      title: Verificación de usuario
   generic:
     use_this: Usar
   settings:
diff --git a/config/locales-glitch/es.yml b/config/locales-glitch/es.yml
index d842deb67..4e054b056 100644
--- a/config/locales-glitch/es.yml
+++ b/config/locales-glitch/es.yml
@@ -1,19 +1,41 @@
 ---
 es:
   admin:
+    custom_emojis:
+      batch_copy_error: Se produjo un error cuando se copian algunos emojis seleccionados %{message}
+      batch_error: Ocurrió un error %{message}
     settings:
+      captcha_enabled:
+        desc_html: Esto depende de scripts externos de hCaptcha, que pueden ser una preocupación de seguridad y privacidad. Además, <strong>esto puede hacer el proceso de registro significativamente menos accesible para algunas personas (especialmente minusválidos)</strong>. Por estas razones, por favor considera medidas alternativas como el registro basado en la aprobación o la invitación.<br>Los usuarios que han sido invitados a través de una invitación de uso limitado no necesitarán resolver un CAPTCHA
+        title: Pedir a los usuarios nuevos resolver un CAPTCHA para confirmar su cuenta
+      flavour_and_skin:
+        title: Sabor y apariencia
       hide_followers_count:
-        desc_html: No mostrar el conteo de seguidorxs en perfiles de usuarix
+        desc_html: No mostrar el conteo de seguidores en los perfiles de usuario
         title: Ocultar conteo de seguidorxs
+      other:
+        preamble: Varias configuraciones de glitch-soc que no encajan en otras categorías.
+        title: Otro
       outgoing_spoilers:
         desc_html: Cuando los toots federen, agrega esta etiqueta de contenido a los toots que no tengan. Es útil si tu servidor se especializa en contenido que otros servidores desearían tener con una advertencia de contenido. Los medios también se marcarán como sensibles.
-        title: Advertencia de contenido para los toots salientes
+        title: Advertencia de contenido para publicaciones salientes
       show_reblogs_in_public_timelines:
-        desc_html: Mostrar retoots públicos en las línea de tiempo local y pública.
-        title: Mostrar retoots en líneas de tiempo públicas
+        desc_html: Mostrar impulsos públicos en las líneas de tiempo local y pública.
+        title: Mostrar impulsos en líneas de tiempo públicas
       show_replies_in_public_timelines:
-        desc_html: Además de auto-respuestas públicas (hilos), mostrar respuestas públicas en las línea de tiempo local y pública.
+        desc_html: Además de auto-respuestas públicas (hilos), mostrar respuestas públicas en las líneas de tiempo local y pública.
         title: Mostrar respuestas en líneas de tiempo públicas
+      trending_status_cw:
+        desc_html: Cuando las publicaciones en tendencia están habilitadas, permitir que la que contienen Advertencias de Contenido sean elegibles. Los cambios en esta configuración no son retroactivos.
+        title: Permitir que publicaciones con advertencias de contenido sean tendencia
+  appearance:
+    localization:
+      glitch_guide_link: https://crowdin.com/project/glitch-soc
+      glitch_guide_link_text: Igual para glitch-soc!
+  auth:
+    captcha_confirmation:
+      hint_html: ¡Solo un paso más! Para confirmar tu cuenta, este servidor requiere que resuelvas un CAPTCHA. Puedes contactar <a href="/about/more"> con el administrador del servidor</a> si tienes preguntas o necesitas ayuda para confirmar tu cuenta.
+      title: Verificación de usuario
   generic:
     use_this: Usar
   settings:
diff --git a/config/locales-glitch/fr-QC.yml b/config/locales-glitch/fr-QC.yml
index 6fa399b65..0cba194f5 100644
--- a/config/locales-glitch/fr-QC.yml
+++ b/config/locales-glitch/fr-QC.yml
@@ -34,7 +34,7 @@ fr-QC:
       glitch_guide_link_text: Et c'est pareil avec glitch-soc !
   auth:
     captcha_confirmation:
-      hint_html: Plus qu'une étape ! Pour vérifier votre compte sur ce serveur, vous devez résoudre un CAPTCHA. Vous pouvez <a href="/about/more>contacter l'administrateur du serveur</a> si vous avez des questions ou besoin d'assistance dans la vérification de votre compte.
+      hint_html: Plus qu'une étape ! Pour vérifier votre compte sur ce serveur, vous devez résoudre un CAPTCHA. Vous pouvez <a href="/about/more">contacter l'administrateur·ice du serveur</a> si vous avez des questions ou besoin d'assistance dans la vérification de votre compte.
       title: Vérification de l'utilisateur
   generic:
     use_this: Utiliser ceci
diff --git a/config/locales-glitch/fr.yml b/config/locales-glitch/fr.yml
index 44e032e66..15c3f8ce5 100644
--- a/config/locales-glitch/fr.yml
+++ b/config/locales-glitch/fr.yml
@@ -34,7 +34,7 @@ fr:
       glitch_guide_link_text: Et c'est pareil avec glitch-soc !
   auth:
     captcha_confirmation:
-      hint_html: Plus qu'une étape ! Pour vérifier votre compte sur ce serveur, vous devez résoudre un CAPTCHA. Vous pouvez <a href="/about/more>contacter l'administrateur du serveur</a> si vous avez des questions ou besoin d'assistance dans la vérification de votre compte.
+      hint_html: Plus qu'une étape ! Pour vérifier votre compte sur ce serveur, vous devez résoudre un CAPTCHA. Vous pouvez <a href="/about/more">contacter l'administrateur·ice du serveur</a> si vous avez des questions ou besoin d'assistance dans la vérification de votre compte.
       title: Vérification de l'utilisateur
   generic:
     use_this: Utiliser ceci
diff --git a/config/locales-glitch/ku.yml b/config/locales-glitch/ku.yml
index aa87618e4..b36f7c988 100644
--- a/config/locales-glitch/ku.yml
+++ b/config/locales-glitch/ku.yml
@@ -1 +1 @@
-kmr-TR:
+ku:
diff --git a/config/locales-glitch/no.yml b/config/locales-glitch/no.yml
index d2a4697e5..1dcec34b6 100644
--- a/config/locales-glitch/no.yml
+++ b/config/locales-glitch/no.yml
@@ -1 +1,2 @@
-no:
+---
+'no':
diff --git a/config/locales-glitch/pl.yml b/config/locales-glitch/pl.yml
index 3fcdedcf3..33f947b89 100644
--- a/config/locales-glitch/pl.yml
+++ b/config/locales-glitch/pl.yml
@@ -1,5 +1,41 @@
 ---
 pl:
+  admin:
+    custom_emojis:
+      batch_copy_error: 'Podczas kopiowania niektórych zaznaczonych emotikon wystąpił następujący problem: %{message}'
+      batch_error: 'Wystąpił problem: %{message}'
+    settings:
+      captcha_enabled:
+        desc_html: Wymaga użycia zewnętrznych skryptów hCaptcha, co może negatywnie wpływać na bezpieczeństwo i prywatność. <strong>Może również przyczynić się do znaczącego utrudnienia procesu rejestracji niektórym, np. niepełnosprawnym, osobom.</strong> Dlatego sugeruje się używanie zaproszeń bądź ręcznie potwierdzanie kont.<br>Użytkownicy rejestrujący się za pomocą limitowanych zaproszeń nie będą musieli rozwiązywać zadania CHAPTCHA
+        title: W celu potwierdzenia ich kont wymagaj rozwiązania zadania CAPTCHA przez nowych użytkowników
+      flavour_and_skin:
+        title: Odmiana i motyw
+      hide_followers_count:
+        desc_html: Nie pokazuj liczby obserwujących w profilach użytkowników.
+        title: Ukryj liczbę obserwujących
+      other:
+        preamble: Ustawienia glitch-soc niepasujące do innych kategorii.
+        title: Inne
+      outgoing_spoilers:
+        desc_html: Podczas wysyłania wpisów do innych serwerów ukryj ich treść za tym ostrzeżeniem a załączniki oznacz jako wrażliwe. Ta opcja jest dedykowana serwerom specjalizującym się w tematyce, która przez innych może traktowana jako wymagająca ostrzeżenia.
+        title: Ostrzeżenie o zawartości dla wpisów wysyłanych do innych serwerów
+      show_reblogs_in_public_timelines:
+        desc_html: Pokaż publiczne podbicia publicznych wpisów w lokalnych i publicznych osiach czasu.
+        title: Pokaż podbicia w publicznych osiach czasu
+      show_replies_in_public_timelines:
+        desc_html: Oprócz odpowiedzi tworzących wątki pokazuj publiczne odpowiedzi w lokalnych i publicznych osiach czasu.
+        title: Pokaż odpowiedzi w publicznych osiach czasu
+      trending_status_cw:
+        desc_html: Pozwól ukrytym za ostrzeżeniem wpisom na włączenie do sekcji „Popularne teraz”, jeżeli jest ona włączona. Zmiana tego ustawienia nie wpłynie na już opublikowane wpisy.
+        title: Pozwól ukrytym za ostrzeżeniem wpisom na włączenie do sekcji „Popularne teraz”
+  appearance:
+    localization:
+      glitch_guide_link: https://crowdin.com/project/glitch-soc
+      glitch_guide_link_text: Również w wypadku glitch-soc.
+  auth:
+    captcha_confirmation:
+      hint_html: Aby potwierdzić Twoje konto, ten serwer wymaga rozwiązania przez Ciebie zadania CAPTCHA. Możesz <a href="/about/more">skontaktować się z administratorem</a>, jeśli masz jakieś pytania lub potrzebujesz pomocy.
+      title: Weryfikacja użytkownika
   generic:
     use_this: Użyj tego
   settings:
diff --git a/config/locales-glitch/pt-PT.yml b/config/locales-glitch/pt-PT.yml
index a1a2dc36b..18e41a056 100644
--- a/config/locales-glitch/pt-PT.yml
+++ b/config/locales-glitch/pt-PT.yml
@@ -1 +1,42 @@
+---
 pt-PT:
+  admin:
+    custom_emojis:
+      batch_copy_error: 'Houve um erro ao copiar alguns dos emoji selecionados: %{message}'
+      batch_error: 'Houve um erro: %{message}'
+    settings:
+      captcha_enabled:
+        desc_html: Isto depende de scripts externos da hCaptcha, o que pode ser uma preocupação de segurança e privacidade. Além disso, <strong>isto pode tornar o processo de registo menos acessível para algumas pessoas (especialmente as com limitações físicas)</strong>. Por isto, considera medidas alternativas tais como registo mediante aprovação ou sob convite.<br>Pessoas que se registam com um convite não precisam de resolver um CAPTCHA
+        title: Exigir que novas contas resolvam um CAPTCHA para validar o registo
+      flavour_and_skin:
+        title: Sabor e tema
+      hide_followers_count:
+        desc_html: Não mostrar o número de seguidores nos perfis
+        title: Esconder o número de seguidores
+      other:
+        preamble: Várias opções do glitch-soc que não cabem noutras categorias.
+        title: Outras opções
+      outgoing_spoilers:
+        desc_html: Ao federar toots, juntar este aviso de conteúdo aos toots que não têm aviso. Isto é útil se o teu servidor for especializado em conteúdos que outros servidores podem querer ter sob um Aviso de Conteúdo. Os media também vão ser marcados como sensíveis.
+        title: Aviso de conteúdo para toots enviados
+      show_reblogs_in_public_timelines:
+        desc_html: Mostrar boosts públicos de toots públicos nas linhas de tempo locais e públicas.
+        title: Mostrar boosts nas timelines públicas
+      show_replies_in_public_timelines:
+        desc_html: Além de auto-respostas públicas (fios), mostrar as respostas públicas em linhas de tempo locais e públicas.
+        title: Mostrar respostas nas linhas de tempo públicas
+      trending_status_cw:
+        desc_html: Quando os posts em tendência estão ativados, permitir que posts com Avisos de Conteúdo também possam aparecer. As alterações nesta opção não são retroativas.
+        title: Permitir que posts com Avisos de Conteúdo possam aparecer nos posts em tendência
+  appearance:
+    localization:
+      glitch_guide_link: https://crowdin.com/project/glitch-soc
+      glitch_guide_link_text: E também para o glitch-soc!
+  auth:
+    captcha_confirmation:
+      hint_html: Só mais um passo! Para confirmares a tua conta, este servidor exige que resolvas um CAPTCHA. Podes <a href="/about/more">entrar em contacto com o administrador do servidor</a> se tiveres dúvidas ou precisares de ajuda.
+      title: Verificação
+  generic:
+    use_this: Usar isto
+  settings:
+    flavours: Sabores
diff --git a/config/locales-glitch/simple_form.ckb.yml b/config/locales-glitch/simple_form.ckb.yml
index cc251e86a..77d538af7 100644
--- a/config/locales-glitch/simple_form.ckb.yml
+++ b/config/locales-glitch/simple_form.ckb.yml
@@ -1 +1 @@
-ckb-IR:
+ckb:
diff --git a/config/locales-glitch/simple_form.es-AR.yml b/config/locales-glitch/simple_form.es-AR.yml
index affb4eae3..76529e4e3 100644
--- a/config/locales-glitch/simple_form.es-AR.yml
+++ b/config/locales-glitch/simple_form.es-AR.yml
@@ -1,18 +1,27 @@
 ---
 es-AR:
   simple_form:
+    glitch_only: glitch-soc
     hints:
       defaults:
+        fields: Puedes tener hasta %{count} elementos mostrados como una tabla en tu perfil
         setting_default_content_type_html: Al escribir toots, asume que estás escritos en HTML, a menos que se especifique lo contrario
         setting_default_content_type_markdown: Al escribir toots, asume que estás usando Markdown para dar formato de texto enriquecido, a menos que se especifique lo contrario
         setting_default_content_type_plain: Al escribir toots, asume que estás usando texto sin formato, a menos que se especifique lo contrario (predeterminado de Mastodon)
         setting_default_language: El idioma de tus toots se puede detectar automáticamente, pero no siempre es correcto
+        setting_hide_followers_count: Ocultar el conteo de seguidores de todos, incluyendo tú. Algunas aplicaciones pueden mostrar un conteo de seguidores negativos.
         setting_skin: Cambia el diseño de la edición seleccionada de Mastodon
     labels:
       defaults:
         setting_default_content_type: Formato predeterminado de tus toots
+        setting_default_content_type_html: HTML
+        setting_default_content_type_markdown: Markdown
         setting_default_content_type_plain: Sin formato
         setting_favourite_modal: Mostrar diálogo de confirmación antes de marcar como favorito (sólo aplica a la edición Glich)
         setting_hide_followers_count: Ocultar tu conteo de seguidorxs
         setting_skin: Diseño
         setting_system_emoji_font: Usar la fuente predeterminada del sistema para emojis (sólo aplica a la edición Glitch)
+      notification_emails:
+        trending_link: Un vínculo en tendencia requiere revisión
+        trending_status: Una publicación en tendencia requiere revisión
+        trending_tag: Una etiqueta en tendencia requiere revisión
diff --git a/config/locales-glitch/simple_form.es-MX.yml b/config/locales-glitch/simple_form.es-MX.yml
index 561ca3b28..377f20a6e 100644
--- a/config/locales-glitch/simple_form.es-MX.yml
+++ b/config/locales-glitch/simple_form.es-MX.yml
@@ -1,18 +1,27 @@
 ---
 es-MX:
   simple_form:
+    glitch_only: glitch-soc
     hints:
       defaults:
+        fields: Puedes tener hasta %{count} elementos mostrados como una tabla en tu perfil
         setting_default_content_type_html: Al escribir toots, asume que estás escritos en HTML, a menos que se especifique lo contrario
         setting_default_content_type_markdown: Al escribir toots, asume que estás usando Markdown para dar formato de texto enriquecido, a menos que se especifique lo contrario
         setting_default_content_type_plain: Al escribir toots, asume que estás usando texto sin formato, a menos que se especifique lo contrario (predeterminado de Mastodon)
         setting_default_language: El idioma de tus toots se puede detectar automáticamente, pero no siempre es correcto
+        setting_hide_followers_count: Ocultar el conteo de seguidores de todos, incluyendo tú. Algunas aplicaciones pueden mostrar un conteo de seguidores negativos.
         setting_skin: Cambia el diseño de la edición seleccionada de Mastodon
     labels:
       defaults:
         setting_default_content_type: Formato predeterminado de tus toots
+        setting_default_content_type_html: HTML
+        setting_default_content_type_markdown: Markdown
         setting_default_content_type_plain: Sin formato
         setting_favourite_modal: Mostrar diálogo de confirmación antes de marcar como favorito (sólo aplica a la edición Glich)
         setting_hide_followers_count: Ocultar tu conteo de seguidorxs
         setting_skin: Diseño
         setting_system_emoji_font: Usar la fuente predeterminada del sistema para emojis (sólo aplica a la edición Glitch)
+      notification_emails:
+        trending_link: Un vínculo en tendencia requiere revisión
+        trending_status: Una publicación en tendencia requiere revisión
+        trending_tag: Una etiqueta en tendencia requiere revisión
diff --git a/config/locales-glitch/simple_form.es.yml b/config/locales-glitch/simple_form.es.yml
index 22277d30d..6c16642c1 100644
--- a/config/locales-glitch/simple_form.es.yml
+++ b/config/locales-glitch/simple_form.es.yml
@@ -1,18 +1,27 @@
 ---
 es:
   simple_form:
+    glitch_only: glitch-soc
     hints:
       defaults:
+        fields: Puedes tener hasta %{count} elementos mostrados como una tabla en tu perfil
         setting_default_content_type_html: Al escribir toots, asume que estás escritos en HTML, a menos que se especifique lo contrario
         setting_default_content_type_markdown: Al escribir toots, asume que estás usando Markdown para dar formato de texto enriquecido, a menos que se especifique lo contrario
         setting_default_content_type_plain: Al escribir toots, asume que estás usando texto sin formato, a menos que se especifique lo contrario (predeterminado de Mastodon)
         setting_default_language: El idioma de tus toots se puede detectar automáticamente, pero no siempre es correcto
+        setting_hide_followers_count: Ocultar el conteo de seguidores de todos, incluyendo tú. Algunas aplicaciones pueden mostrar un conteo de seguidores negativos.
         setting_skin: Cambia el diseño de la edición seleccionada de Mastodon
     labels:
       defaults:
         setting_default_content_type: Formato predeterminado de tus toots
+        setting_default_content_type_html: HTML
+        setting_default_content_type_markdown: Markdown
         setting_default_content_type_plain: Sin formato
         setting_favourite_modal: Mostrar diálogo de confirmación antes de marcar como favorito (sólo aplica a la edición Glich)
         setting_hide_followers_count: Ocultar tu conteo de seguidorxs
         setting_skin: Diseño
         setting_system_emoji_font: Usar la fuente predeterminada del sistema para emojis (sólo aplica a la edición Glitch)
+      notification_emails:
+        trending_link: Un vínculo en tendencia requiere revisión
+        trending_status: Una publicación en tendencia requiere revisión
+        trending_tag: Una etiqueta en tendencia requiere revisión
diff --git a/config/locales-glitch/simple_form.ku.yml b/config/locales-glitch/simple_form.ku.yml
index aa87618e4..b36f7c988 100644
--- a/config/locales-glitch/simple_form.ku.yml
+++ b/config/locales-glitch/simple_form.ku.yml
@@ -1 +1 @@
-kmr-TR:
+ku:
diff --git a/config/locales-glitch/simple_form.no.yml b/config/locales-glitch/simple_form.no.yml
index d2a4697e5..1dcec34b6 100644
--- a/config/locales-glitch/simple_form.no.yml
+++ b/config/locales-glitch/simple_form.no.yml
@@ -1 +1,2 @@
-no:
+---
+'no':
diff --git a/config/locales-glitch/simple_form.pl.yml b/config/locales-glitch/simple_form.pl.yml
index 264494c2d..081587b14 100644
--- a/config/locales-glitch/simple_form.pl.yml
+++ b/config/locales-glitch/simple_form.pl.yml
@@ -1,10 +1,27 @@
 ---
 pl:
   simple_form:
+    glitch_only: glitch-soc
     hints:
       defaults:
+        fields: Możesz ustawić maksymalnie %{count} niestandardowe pola wyświetlane jako tabela w Twoim profilu
+        setting_default_content_type_html: Jeżeli nie powiedziano inaczej, załóż, że wpisy są pisane w HTML
+        setting_default_content_type_markdown: Jeżeli nie powiedziano inaczej, załóż, że wpisy są pisane w Markdown
+        setting_default_content_type_plain: Jeżeli nie powiedziano inaczej, załóż, że wpisy zawierają jedynie czysty tekst (domyślne zachowane Mastodonu)
+        setting_default_language: Język Twoich wpisów może zostać wykryty automatycznie, aczkolwiek poprawność działania tej fukcji nie jest gwarantowana
+        setting_hide_followers_count: Ukryj ilość Twoich obserwujących przed wszystkimi (równiej przed Tobą). Może to spowodować wyświetlanie ujemnej liczby obserwujących przez niektóre aplikacje.
         setting_skin: Zmienia wygląd używanej odmiany Mastodona
     labels:
       defaults:
+        setting_default_content_type: Domyślny format wpisów
+        setting_default_content_type_html: HTML
+        setting_default_content_type_markdown: Markdown
+        setting_default_content_type_plain: Czysty tekst
         setting_favourite_modal: Pytaj o potwierdzenie przed dodaniem do ulubionych
+        setting_hide_followers_count: Ukryj liczbę Twoich obserwujących
         setting_skin: Motyw
+        setting_system_emoji_font: Użyj systemowej czcionki emotikon (tylko w wypadku odmiany „Glitch”)
+      notification_emails:
+        trending_link: Nowy, popularny link wymaga przeglądu
+        trending_status: Nowy, popularny wpis wymaga przeglądu
+        trending_tag: Nowy, popularny tag wymaga przeglądu
diff --git a/config/locales-glitch/simple_form.sr-Latn.yml b/config/locales-glitch/simple_form.sr-Latn.yml
index 9e26af819..c482b5e44 100644
--- a/config/locales-glitch/simple_form.sr-Latn.yml
+++ b/config/locales-glitch/simple_form.sr-Latn.yml
@@ -1 +1 @@
-sr:
+sr-Latn:
diff --git a/config/locales-glitch/simple_form.zh-CN.yml b/config/locales-glitch/simple_form.zh-CN.yml
index 63b9e9222..c162ba721 100644
--- a/config/locales-glitch/simple_form.zh-CN.yml
+++ b/config/locales-glitch/simple_form.zh-CN.yml
@@ -1,18 +1,27 @@
 ---
 zh-CN:
   simple_form:
+    glitch_only: glitch-soc
     hints:
       defaults:
+        fields: 您可以在您的个人资料中最多显示 %{count} 个项目
         setting_default_content_type_html: 在撰写嘟文时,除非另有指定,假定它们使用原始 HTML 语言撰写
         setting_default_content_type_markdown: 在撰写嘟文时,除非另有指定,假定它们使用 Markdown 进行富文本格式化
         setting_default_content_type_plain: 在撰写嘟文时,除非另有指定,假定它们是没有特殊格式的纯文本(默认的 Mastodon 行为)
         setting_default_language: 你的嘟文语言可以自动检测,但不一定准确
-        setting_skin: 更换为所选择的 Mastodon 风味
+        setting_hide_followers_count: 对所有人隐藏您的粉丝数量,包括您在内。一些应用程序可能显示负粉丝数。
+        setting_skin: 更换为所选择的 Mastodon 风格
     labels:
       defaults:
         setting_default_content_type: 嘟文的默认格式
+        setting_default_content_type_html: HTML
+        setting_default_content_type_markdown: Markdown
         setting_default_content_type_plain: 纯文本
-        setting_favourite_modal: 在喜欢嘟文前询问我 (仅限于 Glitch 风味)
+        setting_favourite_modal: 在喜欢嘟文前询问我 (仅限于 Glitch 风格)
         setting_hide_followers_count: 隐藏你的关注者人数
         setting_skin: 皮肤
-        setting_system_emoji_font: 表情符号使用系统默认字体 (仅限于 Glitch 风味)
+        setting_system_emoji_font: 表情符号使用系统默认字体 (仅限于 Glitch 风格)
+      notification_emails:
+        trending_link: 新热门链接需要审核
+        trending_status: 新热门贴文需要审核
+        trending_tag: 新热门话题标签需要审核
diff --git a/config/locales-glitch/simple_form.zh-HK.yml b/config/locales-glitch/simple_form.zh-HK.yml
index 35a3adbaf..8e51e5648 100644
--- a/config/locales-glitch/simple_form.zh-HK.yml
+++ b/config/locales-glitch/simple_form.zh-HK.yml
@@ -1 +1 @@
-zh:
+zh-HK:
diff --git a/config/locales-glitch/sr-Latn.yml b/config/locales-glitch/sr-Latn.yml
index 9e26af819..c482b5e44 100644
--- a/config/locales-glitch/sr-Latn.yml
+++ b/config/locales-glitch/sr-Latn.yml
@@ -1 +1 @@
-sr:
+sr-Latn:
diff --git a/config/locales-glitch/zh-CN.yml b/config/locales-glitch/zh-CN.yml
index 40a09c1c9..d449f83b2 100644
--- a/config/locales-glitch/zh-CN.yml
+++ b/config/locales-glitch/zh-CN.yml
@@ -1,10 +1,21 @@
 ---
 zh-CN:
   admin:
+    custom_emojis:
+      batch_copy_error: '复制部分所选表情时发生错误: %{message}'
+      batch_error: 发生了一个错误:%{message}
     settings:
+      captcha_enabled:
+        desc_html: 这依赖于来自hCaptcha的外部脚本,这可能是一个安全和隐私问题。 此外, <strong>这可能使得某些人(尤其是残疾人)</strong>的注册简单程度大幅减少。 出于这些原因,请考虑采取其他措施,例如基于审核或邀请的注册。<br>通过限定邀请链接注册的用户将不需要解决验证码问题
+        title: 要求新用户解决验证码以确认他们的帐户
+      flavour_and_skin:
+        title: 风格与皮肤
       hide_followers_count:
         desc_html: 不要在用户资料中显示关注者人数
         title: 隐藏关注者人数
+      other:
+        preamble: 各种不适合归类到其他类别的glitch-soc设置。
+        title: 其它
       outgoing_spoilers:
         desc_html: 在联邦化嘟文的时候,将这个内容警告添加到没有内容警告的嘟文中。如果你的服务器专用于其他服务器可能希望有内容警告的内容,它会很有用。媒体也将被标记为敏感。
         title: 对外嘟文的内容警告
@@ -14,7 +25,18 @@ zh-CN:
       show_replies_in_public_timelines:
         desc_html: 除了公开的自我回复(线程模式),在本地和跨站时间轴中显示公开回复。
         title: 在公共时间轴中显示回复
+      trending_status_cw:
+        desc_html: 当热门帖子被启用时,允许带有内容警告的帖子获得资格。此设置的更改不具有追溯性。
+        title: 允许带有内容警告的帖子进入热门
+  appearance:
+    localization:
+      glitch_guide_link: https://crowdin.com/project/glitch-soc
+      glitch_guide_link_text: Glitch-soc也是如此!
+  auth:
+    captcha_confirmation:
+      hint_html: 只需最后一步!为了确认您的账户,本服务器需要您解决一个验证码。如果您有疑问或需要帮助确认您的账户,请<a href="/about/more">联系服务器管理员</a>。
+      title: 用户验证
   generic:
     use_this: 使用这个
   settings:
-    flavours: 风味
+    flavours: 风格
diff --git a/config/locales-glitch/zh-HK.yml b/config/locales-glitch/zh-HK.yml
index 35a3adbaf..8e51e5648 100644
--- a/config/locales-glitch/zh-HK.yml
+++ b/config/locales-glitch/zh-HK.yml
@@ -1 +1 @@
-zh:
+zh-HK:
diff --git a/config/locales/activerecord.bg.yml b/config/locales/activerecord.bg.yml
index ddebd8f08..8b1e44ee4 100644
--- a/config/locales/activerecord.bg.yml
+++ b/config/locales/activerecord.bg.yml
@@ -40,8 +40,8 @@ bg:
         user:
           attributes:
             email:
-              blocked: използва се непозволен доставчик на услуга за е-поща
-              unreachable: изглежда, че не съществува
+              blocked: използва се забранен доставчик на услуга за е-поща
+              unreachable: изглежда не съществува
             role_id:
               elevated: не може да е по-висока от текущата ви роля
         user_role:
diff --git a/config/locales/activerecord.ca.yml b/config/locales/activerecord.ca.yml
index 607dcd1ea..17d0720d5 100644
--- a/config/locales/activerecord.ca.yml
+++ b/config/locales/activerecord.ca.yml
@@ -8,7 +8,7 @@ ca:
       user:
         agreement: Acord de servei
         email: Adreça de correu electrònic
-        locale: Idioma
+        locale: Llengua
         password: Contrasenya
       user/account:
         username: Nom d'usuari
diff --git a/config/locales/activerecord.csb.yml b/config/locales/activerecord.csb.yml
new file mode 100644
index 000000000..0de706e41
--- /dev/null
+++ b/config/locales/activerecord.csb.yml
@@ -0,0 +1 @@
+csb:
diff --git a/config/locales/activerecord.de.yml b/config/locales/activerecord.de.yml
index 7e49ed1e1..c45d3ae8c 100644
--- a/config/locales/activerecord.de.yml
+++ b/config/locales/activerecord.de.yml
@@ -4,14 +4,14 @@ de:
     attributes:
       poll:
         expires_at: Abstimmungsende
-        options: Auswahlmöglichkeiten
+        options: Auswahlfelder
       user:
         agreement: Service-Vereinbarung
         email: E-Mail-Adresse
         locale: Sprache
         password: Passwort
       user/account:
-        username: Benutzername
+        username: Profilname
       user/invite_request:
         text: Begründung
     errors:
@@ -28,7 +28,7 @@ de:
         doorkeeper/application:
           attributes:
             website:
-              invalid: ist keine gültige Adresse
+              invalid: ist keine gültige URL
         import:
           attributes:
             data:
@@ -43,13 +43,13 @@ de:
               blocked: verwendet einen unerlaubten E-Mail-Anbieter
               unreachable: scheint nicht zu existieren
             role_id:
-              elevated: kann nicht höher als Ihre aktuelle Rolle sein
+              elevated: kann nicht höher als deine derzeitige Rolle sein
         user_role:
           attributes:
             permissions_as_keys:
-              dangerous: enthält Berechtigungen, welche nicht sicher sind für die Basisrolle
-              elevated: kann nicht Berechtigungen beinhalten, die deine aktuelle Rolle nicht besitzt
+              dangerous: enthält Berechtigungen, die für die Basisrolle nicht sicher sind
+              elevated: kann keine Berechtigungen enthalten, die deine aktuelle Rolle nicht besitzt
               own_role: kann nicht mit deiner aktuellen Rolle geändert werden
             position:
-              elevated: kann nicht höher sein als deine aktuelle Rolle
-              own_role: kann nicht mit deiner aktuellen Rolle geändert werden
+              elevated: darf nicht höher als deine derzeitige Rolle sein
+              own_role: darf nicht mit deiner aktuellen Rolle geändert werden
diff --git a/config/locales/activerecord.el.yml b/config/locales/activerecord.el.yml
index b285e457a..4eae3b6a0 100644
--- a/config/locales/activerecord.el.yml
+++ b/config/locales/activerecord.el.yml
@@ -21,6 +21,14 @@ el:
             username:
               invalid: μόνο γράμματα, αριθμοί και κάτω παύλες
               reserved: είναι δεσμευμένο
+        admin/webhook:
+          attributes:
+            url:
+              invalid: δεν είναι έγκυρο URL
+        doorkeeper/application:
+          attributes:
+            website:
+              invalid: δεν είναι έγκυρο URL
         import:
           attributes:
             data:
@@ -34,9 +42,14 @@ el:
             email:
               blocked: χρησιμοποιεί μη επιτρεπόμενο πάροχο e-mail
               unreachable: δεν φαίνεται να υπάρχει
+            role_id:
+              elevated: δεν μπορεί να είναι μεγαλύτερο από τον τρέχοντα ρόλο σας
         user_role:
           attributes:
             permissions_as_keys:
+              dangerous: προσθήκη δικαιωμάτων που δεν είναι ασφαλή για τον βασικό ρόλο
+              elevated: δεν είναι δυνατή η προσθήκη δικαιωμάτων που ο τρέχων ρόλος σας δεν κατέχει
               own_role: δεν μπορεί να αλλάξει με τον τρέχοντα ρόλο σας
             position:
+              elevated: δεν μπορεί να είναι μεγαλύτερο από τον τρέχοντα ρόλο σας
               own_role: δεν μπορεί να αλλάξει με τον τρέχοντα ρόλο σας
diff --git a/config/locales/activerecord.eo.yml b/config/locales/activerecord.eo.yml
index 9ae8bab42..64af882b4 100644
--- a/config/locales/activerecord.eo.yml
+++ b/config/locales/activerecord.eo.yml
@@ -6,7 +6,7 @@ eo:
         expires_at: Limdato
         options: Elektebloj
       user:
-        agreement: Servo-interkonsento
+        agreement: Interkonsento pri servoj
         email: Retpoŝtadreso
         locale: Lokaĵaro
         password: Pasvorto
diff --git a/config/locales/activerecord.fi.yml b/config/locales/activerecord.fi.yml
index c1dbaff0f..2cf1c823d 100644
--- a/config/locales/activerecord.fi.yml
+++ b/config/locales/activerecord.fi.yml
@@ -24,11 +24,11 @@ fi:
         admin/webhook:
           attributes:
             url:
-              invalid: ei ole kelvollinen URL
+              invalid: ei ole kelvollinen verkko-osoite
         doorkeeper/application:
           attributes:
             website:
-              invalid: ei ole kelvollinen URL
+              invalid: ei ole kelvollinen verkko-osoite
         import:
           attributes:
             data:
diff --git a/config/locales/activerecord.fr.yml b/config/locales/activerecord.fr.yml
index e2d950d1f..bd34fcbcf 100644
--- a/config/locales/activerecord.fr.yml
+++ b/config/locales/activerecord.fr.yml
@@ -19,7 +19,7 @@ fr:
         account:
           attributes:
             username:
-              invalid: seulement des lettres, des nombres et des tirets bas
+              invalid: seulement des lettres, des chiffres et des tirets bas
               reserved: est réservé
         admin/webhook:
           attributes:
diff --git a/config/locales/activerecord.fy.yml b/config/locales/activerecord.fy.yml
index cc10d817d..22f0c0d15 100644
--- a/config/locales/activerecord.fy.yml
+++ b/config/locales/activerecord.fy.yml
@@ -43,13 +43,13 @@ fy:
               blocked: brûkt in net tastiene e-mailprovider
               unreachable: liket net te bestean
             role_id:
-              elevated: kin net heger wêze as dyn aktuele rol
+              elevated: kin net heger wêze as jo aktuele rol
         user_role:
           attributes:
             permissions_as_keys:
               dangerous: rjochten tafoegje dy’t net feilich binne foar de basisrol
-              elevated: kin gjin rjochten tafoegje dy’t dyn aktuele rol net besit
-              own_role: kin net mei dyn aktuele rol wizige wurde
+              elevated: kin gjin rjochten tafoegje dy’t jo aktuele rol net besit
+              own_role: kin net mei jo aktuele rol wizige wurde
             position:
-              elevated: kin net heger wêze as dyn aktuele rol
-              own_role: kin net mei dyn aktuele rol wizige wurde
+              elevated: kin net heger wêze as jo aktuele rol
+              own_role: kin net mei jo aktuele rol wizige wurde
diff --git a/config/locales/activerecord.hu.yml b/config/locales/activerecord.hu.yml
index 67bad4cb4..003afa817 100644
--- a/config/locales/activerecord.hu.yml
+++ b/config/locales/activerecord.hu.yml
@@ -40,7 +40,7 @@ hu:
         user:
           attributes:
             email:
-              blocked: egy letiltott e-mail szolgáltatót használ
+              blocked: egy letiltott email szolgáltatót használ
               unreachable: úgy tűnik, hogy nem létezik
             role_id:
               elevated: nem lehet magasabb, mint a jelenlegi szereped
diff --git a/config/locales/activerecord.ka.yml b/config/locales/activerecord.ka.yml
index cdd4f9c4c..cb8a03702 100644
--- a/config/locales/activerecord.ka.yml
+++ b/config/locales/activerecord.ka.yml
@@ -1,13 +1,50 @@
 ---
 ka:
   activerecord:
+    attributes:
+      poll:
+        expires_at: ბოლო ვადა
+        options: არჩევანი
+      user:
+        agreement: მომსახურების ხელშეკრულება
+        email: ელ-ფოსტის მისამართი
+        locale: ლოკალი
+        password: პაროლი
+      user/account:
+        username: მომხმარებლის სახელი
+      user/invite_request:
+        text: მიზეზი
     errors:
       models:
         account:
           attributes:
             username:
               invalid: მხოლოდ ასოები, ციფრები და "ქვედა-ტირე"
+              reserved: რეზერვირებულია
+        admin/webhook:
+          attributes:
+            url:
+              invalid: არასწორი URL
+        doorkeeper/application:
+          attributes:
+            website:
+              invalid: არასწორი URL
+        import:
+          attributes:
+            data:
+              malformed: არასწორად არის ფორმირებული
         status:
           attributes:
             reblog:
               taken: სტატუსის უკვე არსებობს
+        user:
+          attributes:
+            email:
+              blocked: იყენებს აკრძალულ ელ-ფოსტის პროვაიდერს
+              unreachable: როგორც ჩანს, არ არსებობს
+            role_id:
+              elevated: არ შეიძლება იყოს თქვენს ამჟამინდელ როლზე მაღალი
+        user_role:
+          attributes:
+            permissions_as_keys:
+              dangerous: მოიცავს ნებართვებს, რომლებიც არ არის უსაფრთხო საბაზისო როლისთვის
diff --git a/config/locales/activerecord.kab.yml b/config/locales/activerecord.kab.yml
index 5b3d06634..909ff68c2 100644
--- a/config/locales/activerecord.kab.yml
+++ b/config/locales/activerecord.kab.yml
@@ -25,3 +25,7 @@ kab:
           attributes:
             reblog:
               taken: n iddaden yellan yakan
+        user:
+          attributes:
+            email:
+              unreachable: ur d-ttban ara d akken yella
diff --git a/config/locales/activerecord.my.yml b/config/locales/activerecord.my.yml
index 5e1fc6bee..d5044bb6c 100644
--- a/config/locales/activerecord.my.yml
+++ b/config/locales/activerecord.my.yml
@@ -1 +1,55 @@
+---
 my:
+  activerecord:
+    attributes:
+      poll:
+        expires_at: နောက်ဆုံးတင်သွင်းချိန်
+        options: ရွေးချယ်မှူများ
+      user:
+        agreement: ဝန်ဆောင်မှု သဘောတူညီချက်
+        email: အီးမေးလ်လိပ်စာ
+        locale: ဒေသဆိုင်ရာ
+        password: စကားဝှက်
+      user/account:
+        username: အသုံးပြုသူအမည်
+      user/invite_request:
+        text: အကြောင်းပြချက်
+    errors:
+      models:
+        account:
+          attributes:
+            username:
+              invalid: တွင် အက္ခရာစာလုံး၊ ဂဏန်းနံပါတ်နှင့်underscores သာပါရမည်
+              reserved: သည် အသုံးပြုပြီးဖြစ်သည်
+        admin/webhook:
+          attributes:
+            url:
+              invalid: သည် မှန်ကန်သော URL မဟုတ်ပါ
+        doorkeeper/application:
+          attributes:
+            website:
+              invalid: သည် မှန်ကန်သော URL မဟုတ်ပါ
+        import:
+          attributes:
+            data:
+              malformed: သည် ပုံမှန်မဟုတ်ပါ
+        status:
+          attributes:
+            reblog:
+              taken: ပို့စ်တည်ရှိနှင့်ပြီးဖြစ်သည်
+        user:
+          attributes:
+            email:
+              blocked: တွင် ခွင့်မပြုထားသော အီးမေးလ်ထောက်ပံ့သူပါဝင်နေသည်။
+              unreachable: သည် တည်ရှိပုံ မပေါ်ပါ
+            role_id:
+              elevated: သင့်လက်ရှိအခန်းကဏ္ဍထက်ပို၍ မမြှင့်တင်နိုင်ပါ
+        user_role:
+          attributes:
+            permissions_as_keys:
+              dangerous: အခြေခံအခန်းကဏ္ဍအတွက် လုံခြုံမှုမရှိသော ခွင့်ပြုချက်များပါဝင်နေသည်
+              elevated: သင့်လက်ရှိအခန်းကဏ္ဍမပိုင်ဆိုင်သော ခွင့်ပြုချက်များမပါဝင်ပါ
+              own_role: သင့်လက်ရှိအခန်းကဏ္ဍဖြင့် ပြောင်းလဲ၍မရနိုင်ပါ
+            position:
+              elevated: သင့်လက်ရှိအခန်းကဏ္ဍထက်ပို၍ မမြှင့်တင်နိုင်ပါ
+              own_role: သင့်လက်ရှိအခန်းကဏ္ဍဖြင့် ပြောင်းလဲ၍မရနိုင်ပါ
diff --git a/config/locales/activerecord.pt-BR.yml b/config/locales/activerecord.pt-BR.yml
index 9aa9af62e..eb29b5a3a 100644
--- a/config/locales/activerecord.pt-BR.yml
+++ b/config/locales/activerecord.pt-BR.yml
@@ -43,7 +43,7 @@ pt-BR:
               blocked: usa provedor de e-mail não permitido
               unreachable: parece não existir
             role_id:
-              elevated: não pode ser maior do que seu cargo atual
+              elevated: não pode ser maior que seu cargo atual
         user_role:
           attributes:
             permissions_as_keys:
diff --git a/config/locales/activerecord.pt-PT.yml b/config/locales/activerecord.pt-PT.yml
index 388e21fe1..927c0bffa 100644
--- a/config/locales/activerecord.pt-PT.yml
+++ b/config/locales/activerecord.pt-PT.yml
@@ -3,11 +3,11 @@ pt-PT:
   activerecord:
     attributes:
       poll:
-        expires_at: Expira em
+        expires_at: Prazo
         options: Escolhas
       user:
         agreement: Acordo de serviço
-        email: Endereço de e-mail
+        email: Endereço de correio electrónico
         locale: Região
         password: Palavra-passe
       user/account:
@@ -36,11 +36,11 @@ pt-PT:
         status:
           attributes:
             reblog:
-              taken: do status já existe
+              taken: da publicação já existe
         user:
           attributes:
             email:
-              blocked: utiliza um provedor de e-mail não permitido
+              blocked: usa um fornecedor de e-mail que não é permitido
               unreachable: não parece existir
             role_id:
               elevated: não pode ser maior que o da sua função atual
diff --git a/config/locales/activerecord.tr.yml b/config/locales/activerecord.tr.yml
index c9695c1a6..ffdc68ac8 100644
--- a/config/locales/activerecord.tr.yml
+++ b/config/locales/activerecord.tr.yml
@@ -28,7 +28,7 @@ tr:
         doorkeeper/application:
           attributes:
             website:
-              invalid: geçerli bir URL değil
+              invalid: geçerli bir bağlantı değil
         import:
           attributes:
             data:
diff --git a/config/locales/activerecord.tt.yml b/config/locales/activerecord.tt.yml
index 5f28508d6..0526634be 100644
--- a/config/locales/activerecord.tt.yml
+++ b/config/locales/activerecord.tt.yml
@@ -5,8 +5,8 @@ tt:
       user:
         email: Почта адресы
         locale: Тел
-        password: Парол
+        password: Серсүз
       user/account:
         username: Кулланучы исеме
       user/invite_request:
-        text: Сәбаб
+        text: Сәбәп
diff --git a/config/locales/activerecord.uz.yml b/config/locales/activerecord.uz.yml
new file mode 100644
index 000000000..ed13813d1
--- /dev/null
+++ b/config/locales/activerecord.uz.yml
@@ -0,0 +1,55 @@
+---
+uz:
+  activerecord:
+    attributes:
+      poll:
+        expires_at: Tugatish muddati
+        options: Tanlovlar
+      user:
+        agreement: Foydalanish shartnomasi
+        email: E-mail
+        locale: Mahalliy
+        password: Parol
+      user/account:
+        username: Foydalanuvchi nomi
+      user/invite_request:
+        text: Sabab
+    errors:
+      models:
+        account:
+          attributes:
+            username:
+              invalid: faqat harflar, raqamlar va pastki chiziqdan iborat bo'lishi kerak
+              reserved: rezervlangan
+        admin/webhook:
+          attributes:
+            url:
+              invalid: haqiqiy URL emas
+        doorkeeper/application:
+          attributes:
+            website:
+              invalid: haqiqiy URL emas
+        import:
+          attributes:
+            data:
+              malformed: noto'g'ri shakllangan
+        status:
+          attributes:
+            reblog:
+              taken: post allaqachon mavjud
+        user:
+          attributes:
+            email:
+              blocked: ruxsat etilmagan elektron pochta provayderidan foydalangan
+              unreachable: mavjud emasga o'xshaydi
+            role_id:
+              elevated: sizning hozirgi rolingizdan yuqori bo'lishi mumkin emas
+        user_role:
+          attributes:
+            permissions_as_keys:
+              dangerous: asosiy rol uchun xavfsiz bo'lmagan ruxsatlarni o'z ichiga oladi
+              elevated: joriy rolingiz ega bo'lmagan ruxsatlarni o'z ichiga olmaydi
+              own_role: sizning hozirgi rolingizdan yuqori bo'lishi mumkin emas
+            position:
+              elevated: sizning hozirgi rolingizdan yuqori bo'lishi mumkin emas
+              own_role: joriy rolingiz bilan o‘zgartirib bo‘lmaydi
diff --git a/config/locales/activerecord.zh-TW.yml b/config/locales/activerecord.zh-TW.yml
index 002ca9519..4b8e5e4de 100644
--- a/config/locales/activerecord.zh-TW.yml
+++ b/config/locales/activerecord.zh-TW.yml
@@ -4,7 +4,7 @@ zh-TW:
     attributes:
       poll:
         expires_at: 截止時間
-        options: 選擇
+        options: 選項
       user:
         agreement: 服務同意書
         email: 電子郵件地址
diff --git a/config/locales/activerecord.zh_Hant.yml b/config/locales/activerecord.zh_Hant.yml
deleted file mode 100644
index 730ab3a51..000000000
--- a/config/locales/activerecord.zh_Hant.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-zh_Hant:
-  activerecord:
-    attributes:
-      status:
-        owned_poll: 投票
-    errors:
-      models:
-        account:
-          attributes:
-            username:
-              invalid: 只允許使用字母、數字和底線
-        status:
-          attributes:
-            reblog:
-              taken: 的嘟文已經存在
diff --git a/config/locales/af.yml b/config/locales/af.yml
index dadb61c75..ed8661991 100644
--- a/config/locales/af.yml
+++ b/config/locales/af.yml
@@ -96,9 +96,7 @@ af:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
   exports:
     bookmarks: Boekmerke
diff --git a/config/locales/an.yml b/config/locales/an.yml
index f3bb5a895..43c83efdc 100644
--- a/config/locales/an.yml
+++ b/config/locales/an.yml
@@ -116,6 +116,8 @@ an:
       redownloaded_msg: S'actualizó correctament lo perfil de %{username} dende l'orichen
       reject: Refusar
       rejected_msg: La solicitut de rechistro de %{username} ha estau refusada con exito
+      remote_suspension_irreversible: Los datos d'esta cuenta s'han borrau irreversiblement.
+      remote_suspension_reversible_hint_html: La cuenta ha estau suspendida en o suyo servidor, y los datos s'eliminarán completament lo %{date}. Dica alavez, lo servidor remoto puede restaurar esta cuenta sin garra efecto perchudicial. Si deseya eliminar totz los datos d'a cuenta immediatament, puede fer-lo debaixo.
       remove_avatar: Eliminar lo avatar
       remove_header: Eliminar capitero
       removed_avatar_msg: S'ha eliminau exitosament la imachen d'o avatar de %{username}
@@ -573,9 +575,10 @@ an:
         mark_as_sensitive_description_html: Los fichers multimedia en os mensaches informaus se marcarán como sensibles y s'aplicará una amonestación pa aduyar-te a escalar las futuras infraccions d'a mesma cuenta.
         other_description_html: Veyer mas opcions pa controlar lo comportamiento d'a cuenta y personalizar la comunicación d'a cuenta denunciada.
         resolve_description_html: No se prendrán medidas contra la cuenta denunciada, no se rechistrará l'amonestación, y se zarrará l'informe.
-        silence_description_html: Lo perfil será visible solo pa aquells que ya lo sigan u lo busquen manualment, limitando seriosament lo suyo aconsiga. Siempre puede estar revertiu.
-        suspend_description_html: Lo perfil y totz los suyos contenius serán inaccesibles dica que sían finalment eliminaus. La interacción con a cuenta será imposible. Reversible entre un plazo de 30 días.
+        silence_description_html: La cuenta será visible nomás pa aquells que ya en sigan u lo busquen manualment, limitando seriosament lo suyo alcance. Siempre puede estar revertiu. Se zarrarán totz los informes contre esta cuenta.
+        suspend_description_html: Esta cuenta y totz los suyos contenius serán inaccesibles y finalment eliminaus, y interaccionar con ella no será posible. Reversible en 30 días. Zarra totz los informes contra eta cuenta.
       actions_description_html: Decide qué medidas prener pa resolver esta denuncia. Si prenes una acción punitiva contra la cuenta denunciada, se le ninviará a dita cuenta una notificación per correu electronico, fueras de quan se tríe la categoría <strong>Spam</strong>.
+      actions_description_remote_html: Decide qué acción prener pa resolver este reporte. Ixo afectará nomás a como <strong>lo tuyo</strong> servidor se comunica con estea cuenta remota y a cómo chestiona lo suyo conteniu.
       add_to_report: Anyadir mas a lo reporte
       are_you_sure: Yes seguro?
       assign_to_self: Asignar-me-la
@@ -703,11 +706,16 @@ an:
       content_retention:
         preamble: Controlar cómo lo conteniu chenerau per l'usuario s'almagazena en Mastodon.
         title: Retención de conteniu
+      default_noindex:
+        desc_html: Afecta a totz los usuarios que no han cambiau esta configuración per ells mesmos
+        title: Excluyir per defecto los usuarios d'a indexación d'os motors de busqueda
       discovery:
         follow_recommendations: Recomendacions de cuentas
         preamble: Exposar conteniu interesant a la superficie ye fundamental pa incorporar nuevos usuarios que pueden no conoixer a dengún Mastodon. Controla cómo funcionan quantas opcions d'escubrimiento en o tuyo servidor.
         profile_directory: Directorio de perfils
         public_timelines: Linias de tiempo publicas
+        publish_discovered_servers: Publicar los servidors descubiertos
+        publish_statistics: Publicar las estatisticas
         title: Escubrimiento
         trends: Tendencias
       domain_blocks:
@@ -970,6 +978,9 @@ an:
       email_below_hint_html: Si l'adreza de correu electronico que amaneixe contino ye incorrecta, se puede cambiar-la aquí y recibir un nuevo correu electronico de confirmación.
       email_settings_hint_html: Lo correu electronico de confirmación estió ninviau a %{email}. Si ixa adreza de correu electronico no sía correcta, se puede cambiar-la en a configuración d'a cuenta.
       title: Configuración
+    sign_in:
+      preamble_html: Inicia sesión con as tuys credencials <strong>%{domain}</strong>. Si la tuya cuenta se troba en un servidor diferent, no podrás iniciar aquí una sesión.
+      title: Iniciar sesión en %{domain}
     sign_up:
       preamble: Con una cuenta en este servidor de Mastodon, podrás seguir a qualsequier atra persona en o ret, independientment d'o servidor en o qual se trobe.
       title: Creyar cuenta de Mastodon en %{domain}.
@@ -1105,8 +1116,6 @@ an:
     storage: Almagazenamiento
   featured_tags:
     add_new: Anyadir nuevo
-    errors:
-      limit: Ya has aconseguiu la cantidat maxima de hashtags
     hint_html: "<strong>Qué son las etiquetas destacadas?</strong> S'amuestran de forma prominent en o tuyo perfil publico y permiten a los usuarios navegar per las tuyas publicacions publicas especificament baixo ixas etiquetas. Son una gran ferramienta pa fer un seguimiento de treballos creativos u prochectos a largo plazo."
   filters:
     contexts:
@@ -1150,8 +1159,6 @@ an:
       index:
         hint: Este filtro s'aplica a la selección de publicacions individuals independientment d'atros criterios. Puede anyadir mas publicacions a este filtro dende la interficie web.
         title: Publicacions filtradas
-  footer:
-    trending_now: Tendencia agora
   generic:
     all: Totz
     all_items_on_page_selected_html:
@@ -1174,8 +1181,6 @@ an:
     validation_errors:
       one: Bella cosa no ye bien! Per favor, revisa la error
       other: Bella cosa no ye bien! Per favor, revise %{count} errors mas abaixo
-  html_validator:
-    invalid_markup: 'contiene codigo HTML no valido: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Fichero CSV no valido. Error: %{error}'
@@ -1216,9 +1221,6 @@ an:
       expires_at: Expira
       uses: Usos
     title: Convidar a chent
-  lists:
-    errors:
-      limit: Has aconseguiu la cantidat maxima de listas
   login_activities:
     authentication_methods:
       otp: aplicación d'autenticación en dos pasos
@@ -1359,6 +1361,9 @@ an:
       unrecognized_emoji: no ye un emoji conoixiu
   relationships:
     activity: Actividat d'a cuenta
+    confirm_follow_selected_followers: Yes seguro de querer seguir a los seguidors triaus?
+    confirm_remove_selected_followers: Yes seguro de querer eliminar a los seguidors triaus?
+    confirm_remove_selected_follows: Yes seguro de querer eliminar los seguimientos triaus?
     dormant: Inactivo
     follow_selected_followers: Seguir a los seguidores triaus
     followers: Seguidores
@@ -1422,7 +1427,6 @@ an:
       ios: iOS
       linux: Linux
       mac: Mac
-      other: plataforma desconoixida
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1535,7 +1539,6 @@ an:
       '7889238': 3 meses
     min_age_label: Branquil de tiempo
     min_favs: Mantener mensaches con un numero de favoritos mayor que
-    min_favs_hint: No borra garra d'as publicacions que haigan recibiu mas d'esta cantidat de favoritos. Deixa en blanco pa eliminar publicacions sin importar lo numero de favoritos
     min_reblogs: Mantener publicacions reblogueadas mas de
     min_reblogs_hint: No borra garra d'as publicacions que haigan estau reblogueadas mas d'este numero de vegadas. Deixa en blanco pa eliminar publicacions sin importar lo numero de reblogueos
   stream_entries:
@@ -1640,7 +1643,6 @@ an:
     seamless_external_login: Has iniciau sesión dende un servicio externo, asinas que los achustes de clau y correu no son disponibles.
     signed_in_as: 'Sesión iniciada como:'
   verification:
-    explanation_html: 'Puetz <strong> verificar-te a tu mesmo como lo duenyo d''os links en os metadatos d''o tuyo perfil </strong>. Pa ixo, lo puesto vinculau ha de contener un vinclo a lo tuyo perfil de Mastodon. Lo vinclo en o tuyo puesto <strong> debe </strong> tener un atributo <code> rel="me"</code>. Lo texto d''o vinclo no importa. Aquí un eixemplo:'
     verification: Verificación
   webauthn_credentials:
     add: Adhibir nueva clau de seguranza
diff --git a/config/locales/ar.yml b/config/locales/ar.yml
index 2bf9bcf26..04631f7fe 100644
--- a/config/locales/ar.yml
+++ b/config/locales/ar.yml
@@ -1,7 +1,7 @@
 ---
 ar:
   about:
-    about_mastodon_html: 'شبكة التواصل الإجتماعية المستقبَليّة: مِن دون إعلانات ، غير خاضعة لرقابة الشركات ، تصميم أخلاقي ولامركزية! بياناتكم مِلك لكم مع ماستدون!'
+    about_mastodon_html: 'شبكة التواصل الاجتماعية المستقبَليّة: مِن دون إعلانات، وغير خاضعة لرقابة الشركات، ذات تصميم أخلاقي ولامركزية! بياناتكم مِلك لكم مع ماستدون!'
     contact_missing: لم يتم تعيينه
     contact_unavailable: غير متوفر
     hosted_on: ماستدون مُستضاف على %{domain}
@@ -99,6 +99,7 @@ ar:
       moderation:
         active: نشِط
         all: الكل
+        disabled: مُعطَّل
         pending: قيد المراجعة
         silenced: محدود
         suspended: مُجَمَّد
@@ -113,6 +114,13 @@ ar:
       pending: في انتظار المراجعة
       perform_full_suspension: تعليق الحساب
       previous_strikes: العقوبات السابقة
+      previous_strikes_description_html:
+        few: لدى هذا الحساب <strong>%{count}</strong> إنذارات.
+        many: لدى هذا الحساب <strong>%{count}</strong> إنذارًا.
+        one: هذا الحساب لديه <strong>%{count}</strong>انذار.
+        other: لدى هذا الحساب <strong>%{count}</strong> إنذار.
+        two: لدى هذا الحساب <strong>%{count}</strong> إنذاران اثنان.
+        zero: هذا الحساب لديه <strong>%{count}</strong>انذار.
       promote: ترقية
       protocol: البروتوكول
       public: عمومي
@@ -138,6 +146,7 @@ ar:
       search: البحث
       search_same_email_domain: مستخدمون آخرون لديهم نفس نطاق البريد الإلكتروني
       search_same_ip: مستخدِمون آخرون بنفس الـ IP
+      security: الأمان
       security_measures:
         only_password: كلمة المرور فقط
         password_and_2fa: كلمة المرور و 2FA
@@ -359,6 +368,13 @@ ar:
         other: "<strong>%{count}</strong> تقارير معلقة"
         two: "<strong>%{count}</strong> مستخدمين معلقين"
         zero: "<strong>%{count}</strong> وسماً معلقاً"
+      pending_reports_html:
+        few: "<strong>%{count}</strong> تقارير قيد الإنتظار"
+        many: "<strong>%{count}</strong> تقريرًا قيد الإنتظار"
+        one: "<strong>%{count}</strong> تقرير واحد قيد الإنتظار"
+        other: "<strong>%{count}</strong> تقرير قيد الانتظار"
+        two: "<strong>%{count}</strong> تقريران قيد الانتظار"
+        zero: "<strong>%{count}</strong> تقارير قيد الانتظار"
       resolved_reports: تقارير تم حلها
       software: البرنامج
       sources: مصادر التسجيل
@@ -424,6 +440,7 @@ ar:
         resolve: العثور على عنوان النطاق
         title: إضافة نطاق بريد جديد إلى اللائحة السوداء
       no_email_domain_block_selected: لم يطرأ أي تغيير على أي نطاق بريد بما أنه لم يتم اختيار أي نطاق
+      not_permitted: غير مسموح به
       resolved_dns_records_hint_html: عنوان النطاق يعود لخوادم البريد (MX) التالية وهو ما يسمح للنطاق باستقبال البريد. حظر خوادم البريد هذه سوف يتسبب في منع أي تسجيل خلال أي نطاق يستخدم هذه الخوادم حتى لو كان اسم النطاق مختلف عن اسماء خوادم البريد.<strong>احذر من حظر مزودي البريد العالميين.</strong>
       resolved_through_html: الحصول على العنوان من خلال %{domain}
       title: القائمة السوداء للبريد الإلكتروني
@@ -438,6 +455,7 @@ ar:
         private_comment_description_html: 'لمساعدتك على تتبع سبب حظر النطاقات المستوردة، سيتم إنشاء الحظر مع التعليق الخاص التالي: <q>%{comment}</q>'
         private_comment_template: تم الاستيراد من %{source} بتاريخ %{date}
         title: استيراد قامة النطاقات المحظورة
+      invalid_domain_block: 'تم تخطي حجب نطاق أو أكثر بسبب الأخطاء التالية: %{error}'
       new:
         title: استيراد قامة النطاقات المحظورة
       no_file: لم يتم تحديد أيّ ملف
@@ -452,6 +470,13 @@ ar:
     instances:
       availability:
         failure_threshold_reached: تم الوصول إلى أقصى حد للفشل بتاريخ %{date}.
+        failures_recorded:
+          few: المحاولات الفاشلة في %{count} يومًا.
+          many: المحاولات الفاشلة في %{count} يوم مختلف.
+          one: محاولة فاشلة في يوم %{count} واحد.
+          other: المحاولات الفاشلة في %{count} أيام مختلفة.
+          two: المحاولات الفاشلة في يَومين %{count}.
+          zero: ليس هناك أية محاولة فاشلة في %{count} يوم.
         no_failures_recorded: لم يتم العثور على أي فشل.
         title: التوفر
         warning: فشلت المحاولة الأخيرة للاتصال بهذا النطاق
@@ -463,6 +488,7 @@ ar:
       content_policies:
         comment: ملاحظة داخلية
         description_html: يمكنك تحديد سياسات المحتوى التي سيتم تطبيقها على جميع حسابات هذا النطاق وأي من نطاقاته الفرعية.
+        limited_federation_mode_description_html: يمكنك أن تختار بين السماح أو عدم السماح بالفديرالية مع هذا النطاق.
         policies:
           reject_media: رفض الوسائط
           reject_reports: رفض الشكاوى
@@ -574,19 +600,22 @@ ar:
         mark_as_sensitive_description_html: سيتم تحديد الوسائط في المناشير المبلّغ عنها على أنها محتوى حساس وسيتم تسجيل إنذار لمساعدتك في التعامل المستقبلي مع أي مخالفة جديدة من نفس الحساب.
         other_description_html: عرض المزيد من الخيارات للتحكم في االحسابات وتخصيص التواصل مع الحسابات المُبلّغ عنها.
         resolve_description_html: ولن يُتخذ أي إجراء ضد الحساب المبلّغ عنه، ولن تسلَّط عليه أية عقوبة، وسوف يُغلق التقرير.
-        silence_description_html: الحساب سيكون ظاهر فقط لمن يتابعه بالفعل أو قام بالبحث عنه بشكل مباشر مما يتسبب في تخفيض إمكانية رؤيته بشكل شبه كامل. يمكنك دائما التراجع عن هذا القرار.
-        suspend_description_html: سيتم قفل الحساب ومنع الوصول إلى محتوياته إلى أن يتم حذفه. سيكون من غير الممكن التفاعل مع الحساب. يمكن التراجع عن هذا خلال 30 يوم.
+        silence_description_html: الحساب سيظهر فقط لمن يتابعه أو قام بالبحث عنه بشكل مباشر مما يخفض إمكانية رؤيته بشكل شبه كامل. يمكنك دائما التراجع عن هذا الإجراء. تُغلَق كافة الإبلاغات عن هذا الحساب.
       actions_description_html: تحديد الإجراءات التي يتعين اتخاذها لحل هذا التبليغ. إذا اتخذت إجراء عقابيا ضد الحساب المبلغ عنه، فسيتم إرسال إشعار بالبريد الإلكتروني إليهم، إلا عندما يتم تحديد فئة <strong>البريد المزعج.</strong>
+      actions_description_remote_html: حدّد الإجراءات التي يتعين اتخاذها لحل هذا التقرير. هذا سيؤثر فقط على كيفية اتصال <strong>خادمك</strong> بهذا الحساب البعيد والتعامل مع محتوياته.
       add_to_report: أضف المزيد إلى التقرير
       are_you_sure: هل أنت متأكد ؟
       assign_to_self: عين لي
       assigned: تعين رئيس
       by_target_domain: نطاق الحساب المبلّغ عنه
+      cancel: إلغاء
       category: الفئة
       category_description_html: سيشار إلى سبب الإبلاغ عن هذا الحساب و/أو المحتوى في الاتصال بالحساب المبلغ عنه
       comment:
         none: لا شيء
       comment_description_html: 'لتوفير المزيد من المعلومات، كتب %{name}:'
+      confirm: تأكيد
+      confirm_action: تأكيد اتخاذ إجراء إشراف على @%{acct}
       created_at: ذكرت
       delete_and_resolve: احذف المنشورات
       forwarded: أُعيد توجيهه
@@ -603,6 +632,7 @@ ar:
         placeholder: قم بوصف الإجراءات التي تم اتخاذها أو أي تحديثات أخرى ذات علاقة...
         title: الملاحظات
       notes_description_html: عرض الملاحظات وتركها للمشرفين الآخرين ولنفسك في المستقبل
+      processed_msg: 'تمت معالجة التقرير #%{id} بنجاح'
       quick_actions_description_html: 'قم بإجراء سريع أو التمرير للأسفل لرؤية المحتوى المبلغ عنه:'
       remote_user_placeholder: المستخدم البعيد من %{instance}
       reopen: إعادة فتح الشكوى
@@ -615,9 +645,19 @@ ar:
       status: الحالة
       statuses: المحتوى المبلغ عنه
       statuses_description_html: سيشار إلى المحتوى المخالف في الاتصال بالحساب المبلغ عنه
+      summary:
+        action_preambles:
+          suspend_html: 'أنت على وشك <strong>تعليق</strong> حساب <strong>@%{acct}</strong>. هذا سوف:'
+        actions:
+          delete_html: إزالة المنشورات المُخالِفة
+          mark_as_sensitive_html: تصنيف وسائط المنشورات المُخالفة كحساسة
+        close_report: 'تصنيف التقرير #%{id} كإبلاغ تمت معالجته'
+        send_email_html: إرسال بريد إلكتروني تحذيري إلى <strong>@%{acct}</strong>
+        warning_placeholder: مبررات إضافية اختيارية لإجراء الإشراف.
       target_origin: مصدر الحساب المبلغ عنه
       title: الشكاوى
       unassign: إلغاء تعيين
+      unknown_action_msg: 'إجراء غير معروف: %{action}'
       unresolved: غير معالجة
       updated_at: محدث
       view_profile: اعرض الصفحة التعريفية
@@ -641,6 +681,13 @@ ar:
       edit: تعديل رتبة '%{name}' '
       everyone: الصلاحيات الافتراضية
       everyone_full_description_html: هذا هو <strong>الدور الأساسي</strong> الذي يحمله <strong>جميع المستخدمين</strong>، حتى أولئك الذين ليس لديهم دور معين. جميع الأدوار الأخرى ترث الأذونات منه.
+      permissions_count:
+        few: "%{count} تصريحات"
+        many: "%{count} تصريحًا"
+        one: تصريح واحد %{count}
+        other: "%{count} تصريح"
+        two: تصريحان %{count}
+        zero: لا تصريح %{count}
       privileges:
         administrator: مدير
         administrator_description: المستخدمين الذين لديهم هذا التصريح سيتجاوزون جميع التصاريح
@@ -700,7 +747,7 @@ ar:
         preamble: تخصيص واجهة الويب لماستدون.
         title: المظهر
       branding:
-        preamble: العلامة التجارية للخادم الخاص بك تميزه عن الخوادم الأخرى في الشبكة. يمكن عرض هذه المعلومات عبر مجموعة متنوعة من البيئات، مثل واجهة الويب لماستدون, التطبيقات الأصلية، في معاينات الرابط على مواقع الويب الأخرى وداخل تطبيقات الرسائل، وما إلى ذلك. ولهذا السبب، من الأفضل إبقاء هذه المعلومات واضحة وقصيرة وموجزة.
+        preamble: العلامة التجارية لخادمك الخاص تميزه عن الخوادم الأخرى في الشبكة. يمكن عرض هذه المعلومات عبر مجموعة متنوعة من البيئات، مثل واجهة الويب لماستدون، في التطبيقات الأصلية، في معاينات الروابط على مواقع الويب الأخرى وضمن تطبيقات الرسائل، وما إلى ذلك. ولهذا السبب، من الأفضل إبقاء هذه المعلومات واضحة وقصيرة وموجزة.
         title: العلامة
       content_retention:
         preamble: التحكم في كيفية تخزين المحتوى الذي ينشئه المستخدم في ماستدون.
@@ -710,9 +757,11 @@ ar:
         title: عدم السماح مبدئيا لمحركات البحث بفهرسة الملفات التعريفية للمستخدمين
       discovery:
         follow_recommendations: اتبع التوصيات
-        preamble: تصفح المحتوى المثير للاهتمام أمر مهم في إستقبال المستخدمين الجدد الذين قد لا يعرفون أي شخص ماستدون. التحكم في كيفية عمل ميزات الاكتشاف المختلفة على الخادم الخاص بك.
+        preamble: يُعد إتاحة رؤية المحتوى المثير للاهتمام أمرًا ضروريًا لجذب مستخدمين جدد قد لا يعرفون أي شخص في Mastodon. تحكم في كيفية عمل ميزات الاكتشاف المختلفة على خادمك الخاص.
         profile_directory: دليل الصفحات التعريفية
         public_timelines: الخيوط الزمنية العامة
+        publish_discovered_servers: نشر الخوادم المكتشَفة
+        publish_statistics: نشر الإحصائيات
         title: الاستكشاف
         trends: المتداوَلة
       domain_blocks:
@@ -767,6 +816,7 @@ ar:
         suspend: قام %{name} بتعليق حساب %{target}
       appeal_approved: طُعِن فيه
       appeal_pending: طعن قيد المراجعة
+      appeal_rejected: رُفض الطعن
     system_checks:
       database_schema_check:
         message_html: هناك عمليات هجرة معلقة لقواعد البيانات. يرجى تشغيلها لضمان تصرف التطبيق كما هو متوقع
@@ -799,6 +849,7 @@ ar:
           no_publisher_selected: لم يطرأ أي تغيير على أي ناشر بما أنه لم يتم اختيار أي واحد
         title: الروابط المتداولة
         usage_comparison: تمت مشاركته %{today} مرات اليوم، مقارنة بـ %{yesterday} بالأمس
+      not_allowed_to_trend: غير مسموح ظهوره في المتداولة
       only_allowed: من سُمِحَ لهم فقط
       pending_review: في انتظار المراجعة
       preview_card_providers:
@@ -836,6 +887,13 @@ ar:
         trending_rank: 'المتداولة #%{rank}'
         usable: يمكن استخدامه
         usage_comparison: تم استخدامه %{today} مرات اليوم، مقارنة بـ %{yesterday} بالأمس
+        used_by_over_week:
+          few: مستخدَم من قِبل %{count} أشخاص خلال الأسبوع الماضي
+          many: مستخدَم من قِبل %{count} شخصا خلال الأسبوع الماضي
+          one: مستخدَم من قِبل %{count} شخص واحد خلال الأسبوع الماضي
+          other: مستخدَم من قِبل %{count} شخص خلال الأسبوع الماضي
+          two: مستخدَم من قِبل %{count} شخصين خلال الأسبوع الماضي
+          zero: مستخدَم من قِبل %{count} شخص خلال الأسبوع الماضي
       title: المتداوَلة
       trending: المتداولة
     warning_presets:
@@ -921,6 +979,7 @@ ar:
   applications:
     created: تم إنشاء التطبيق بنجاح
     destroyed: تم حذف التطبيق بنجاح
+    logout: الخروج
     regenerate_token: إعادة توليد رمز النفاذ
     token_regenerated: تم إعادة إنشاء الرمز الوصول بنجاح
     warning: كن حذرا مع هذه البيانات. لا تقم أبدا بمشاركتها مع الآخَرين!
@@ -928,12 +987,14 @@ ar:
   auth:
     apply_for_account: اطلُب حسابًا
     change_password: الكلمة السرية
+    confirmations:
+      wrong_email_hint: إذا كان عنوان البريد الإلكتروني هذا غير صحيح، يمكنك تغييره في إعدادات الحساب.
     delete_account: حذف الحساب
     delete_account_html: إن كنت ترغب في حذف حسابك يُمكنك <a href="%{path}">المواصلة هنا</a>. سوف يُطلَبُ منك التأكيد قبل الحذف.
     description:
       prefix_invited_by_user: يدعوك @%{name} للاتحاق بخادم ماستدون هذا!
       prefix_sign_up: أنشئ حسابًا على ماستدون اليوم!
-      suffix: بفضل حساب ، ستكون قادرا على متابعة الأشخاص ونشر تحديثات وتبادل رسائل مع مستخدمين مِن أي خادم Mastodon وأكثر!
+      suffix: بفضل حساب، ستكون قادرا على متابعة أشخاص ونشر تحديثات وتبادل رسائل مع مستخدمين مِن أي خادم Mastodon وأكثر!
     didnt_get_confirmation: لم تتلق تعليمات التأكيد ؟
     dont_have_your_security_key: ليس لديك مفتاح الأمان الخاص بك؟
     forgot_password: نسيت كلمة المرور ؟
@@ -955,6 +1016,8 @@ ar:
     resend_confirmation: إعادة إرسال تعليمات التأكيد
     reset_password: إعادة تعيين كلمة المرور
     rules:
+      accept: قبول
+      back: العودة
       preamble: يتم تعيين هذه القوانين وفرضها من قبل مشرفي %{domain}.
       title: بعض القواعد الأساسية.
     security: الأمان
@@ -1102,7 +1165,7 @@ ar:
   featured_tags:
     add_new: أضف واحدًا جديدا
     errors:
-      limit: لقد قمت بالفعل بعرض الحد الأقصى من الوسوم
+      limit: لقد قمت بالفعل بعرض الحد الأقصى من عدد الوسوم
     hint_html: "<strong>ما هي الوسوم الرائجة؟</strong> يتم عرضها بشكل بارز على ملفك الشخصي العام وتسمح للناس بتصفح منشوراتك العامة على وجه التحديد تحت تلك الوسوم. وهي أداة رائعة لتتبع الأعمال الإبداعية أو المشاريع الطويلة الأجل."
   filters:
     contexts:
@@ -1126,6 +1189,20 @@ ar:
       empty: ليست لديك أية عوامل تصفية.
       expires_in: تنتهي مدة صلاحيتها في غضون %{distance}
       expires_on: تنتهي مدة صلاحيتها في %{date}
+      keywords:
+        few: "%{count} كلمة مفتاحية"
+        many: "%{count} كلمة مفتاحية"
+        one: كلمة مفتاحية %{count} واحدة
+        other: "%{count} كلمة مفتاحية"
+        two: كلمتان مفتاحيتان %{count}
+        zero: "%{count} كلمة مفتاحية"
+      statuses:
+        few: "%{count} منشورات"
+        many: "%{count} منشورًا"
+        one: "%{count} منشور واحد"
+        other: "%{count} منشور"
+        two: "%{count} منشوران اثنان"
+        zero: "%{count} منشورات"
       title: عوامل التصفية
     new:
       save: حفظ عامل التصفية الجديد
@@ -1137,8 +1214,6 @@ ar:
       index:
         hint: ينطبق الفلتر هذا على اختيار المنشورات الفردية بغض النظر عن المعايير الأخرى. يمكنك إضافة المزيد من المنشورات إلى هذا الفلتر من واجهة الويب.
         title: الرسائل المصفّاة
-  footer:
-    trending_now: المتداولة الآن
   generic:
     all: الكل
     changes_saved_msg: تم حفظ التعديلات بنجاح!
@@ -1156,8 +1231,6 @@ ar:
       other: هناك شيء ما ليس على ما يرام! يُرجى مراجعة الأخطاء الـ %{count} أدناه
       two: هناك شيء ما ليس على ما يرام! يُرجى مراجعة الأخطاء الـ %{count} أدناه
       zero: هناك شيء ما ليس على ما يرام! يُرجى مراجعة الأخطاء الـ %{count} أدناه
-  html_validator:
-    invalid_markup: 'يحتوي على علامة HTML غير صالحة: %{error}'
   imports:
     errors:
       invalid_csv_file: 'ملف CSV غير صالح. خطأ: %{error}'
@@ -1345,7 +1418,11 @@ ar:
       unrecognized_emoji: لم يتم التعرف على أنه إيموجي
   relationships:
     activity: نشاط الحساب
+    confirm_follow_selected_followers: هل أنت متأكد من أنك تريد متابعة المتابِعين المحددين؟
+    confirm_remove_selected_followers: هل أنت متأكد من أنك تريد إزالة المتابِعين المحددين؟
+    confirm_remove_selected_follows: هل أنت متيقِّن من أنك تريد إزالة الاشتراكات المحدَّدة؟
     dormant: في سبات
+    follow_failure: تعذرت متابعة بعض الحسابات المختارة.
     follow_selected_followers: متابَعة المتابِعين المحددين
     followers: المتابِعون
     following: يُتابِع
@@ -1385,6 +1462,7 @@ ar:
       electron: إلكترون
       firefox: فايرفكس
       generic: متصفح مجهول
+      huawei_browser: متصفح هواواي
       ie: إنترنت إكسبلورر
       micro_messenger: مايكرو ميسنجر
       nokia: متصفح Nokia S40 Ovi
@@ -1394,6 +1472,7 @@ ar:
       qq: متصفح كيوكيو
       safari: سفاري
       uc_browser: متصفح UC Browser
+      unknown_browser: متصفح غير معروف
       weibo: وايبو
     current_session: الجلسة الحالية
     description: "%{browser} على %{platform}"
@@ -1406,9 +1485,10 @@ ar:
       chrome_os: نظام كروم أواس
       firefox_os: نظام فايرفكس أواس
       ios: نظام آي أواس
+      kai_os: KaiOS
       linux: لينكس
       mac: ماك
-      other: نظام مجهول
+      unknown_platform: منصة غير معروفة
       windows: ويندوز
       windows_mobile: ويندوز موبايل
       windows_phone: ويندوز فون
@@ -1545,7 +1625,7 @@ ar:
       '7889238': 3 أشهر
     min_age_label: عتبة العمر
     min_favs: إبقاء المشاركات المفضلة أكثر من
-    min_favs_hint: لم تقوم بحذف أي من المشاركات الخاصة بك التي حصلت على أكثر من هذه الكمية من المفضلة. اتركه فارغاً لحذف المشاركات بغض النظر عن عدد المفضلات لديها
+    min_favs_hint: لن تُحذف أي من منشوراتك التي تلقّت على الأقل هذا العدد من المفضلات. اتركه فارغاً لحذف المنشورات مهما كان عدد المفضلات التي تلقتها
     min_reblogs: إبقاء المشاركات المعززة أكثر من
     min_reblogs_hint: لن تُحذف أي من منشوراتك التي أعيد مشاركتها أكثر من هذا العدد من المرات. اتركه فارغاً لحذف المنشورات بغض النظر عن عدد إعادات المشاركة
   stream_entries:
@@ -1650,7 +1730,6 @@ ar:
     seamless_external_login: لقد قمت بتسجيل الدخول عبر خدمة خارجية، إنّ إعدادات الكلمة السرية و البريد الإلكتروني غير متوفرة.
     signed_in_as: 'تم تسجيل دخولك بصفة:'
   verification:
-    explanation_html: 'يمكنك <strong>التحقق من نفسك كمالك لروابط البيانات التعريفية على صفحتك الشخصية</strong>. لذلك، يجب أن يحتوي الموقع المقترِن على رابط إلى صفحتك التعريفية الشخصية على ماستدون. الرابط الخلفي <strong>يجب أن</strong> يحتوي على رمز <code>rel="me"</code>. محتوى النص في الرابط غير مهم. على سبيل المثال:'
     verification: التحقق
   webauthn_credentials:
     add: إضافة مفتاح أمان جديد
diff --git a/config/locales/ast.yml b/config/locales/ast.yml
index 0a85b4c9a..62da6ff90 100644
--- a/config/locales/ast.yml
+++ b/config/locales/ast.yml
@@ -6,7 +6,7 @@ ast:
     hosted_on: 'Mastodon ta agospiáu en: %{domain}'
     title: Tocante a
   accounts:
-    last_active: Última actividá
+    last_active: última actividá
     nothing_here: "¡Equí nun hai nada!"
     posts:
       one: Artículu
@@ -18,11 +18,16 @@ ast:
     account_moderation_notes:
       create: Dexar la nota
     accounts:
+      add_email_domain_block: Bloquiar el dominiu de corréu electrónicu
+      approved_msg: Aprobóse correutamente la solicitú de rexistru de «%{username}»
+      are_you_sure: "¿De xuru que quies facer esta aición?"
       avatar: Avatar
       by_domain: Dominiu
+      confirming: En confirmación
       disabled: Conxelóse
       display_name: Nome visible
       domain: Dominiu
+      edit: Editar
       email: Direición de corréu electrónicu
       followers: Siguidores
       header: Testera
@@ -32,19 +37,24 @@ ast:
         local: Llocal
         remote: Remotu
         title: Llugar
+      login_status: Estáu del aniciu de la sesión
       moderation:
         pending: Pendiente
+        title: Moderación
       most_recent_activity: L'actividá más recién
       most_recent_ip: La IP más recién
       perform_full_suspension: Suspender
       protocol: Protocolu
+      rejected_msg: Refugóse correutamente la solicitú de rexistru de «%{username}»
       resend_confirmation:
         already_confirmed: Esti perfil xá ta confirmáu
         send: Volver unviar el mensaxe de confirmación
+        success: "¡El mensaxe de confirmación unvióse correutamente!"
       role: Rol
       search: Buscar
       search_same_email_domain: Otros perfiles col mesmu dominiu de corréu electrónicu
       search_same_ip: Otros perfiles cola mesma IP
+      security: Seguranza
       show:
         created_reports: Informes fechos
       statuses: Artículos
@@ -60,6 +70,7 @@ ast:
         confirm_user_html: "%{name} confirmó la direición de corréu electrónicu del perfil %{target}"
         create_account_warning_html: "%{name} unvió una alvertencia a %{target}"
         create_announcement_html: "%{name} creó l'anunciu «%{target}»"
+        create_custom_emoji_html: "%{name} xubió un fustaxe nuevu «%{target}»"
         create_domain_allow_html: "%{name} permitió la federación col dominiu %{target}"
         create_domain_block_html: "%{name} bloquió'l dominiu %{target}"
         create_user_role_html: "%{name} creó'l rol «%{target}»"
@@ -72,6 +83,9 @@ ast:
         enable_custom_emoji_html: "%{name} activó'l fustaxe «%{target}»"
         reject_user_html: "%{name} refugó'l rexistru de: %{target}"
         remove_avatar_user_html: "%{name} quitó l'avatar de: %{target}"
+        reopen_report_html: "%{name} volvió abrir l'informe «%{target}»"
+        resend_user_html: "%{name} volvió unviar el mensaxe de confirmación pa: %{target}"
+        resolve_report_html: "%{name} resolvió l'informe «%{target}»"
         unblock_email_account_html: "%{name} desbloquió la direición de corréu electrónicu de: %{target}"
         update_announcement_html: "%{name} anovó l'anunciu «%{target}»"
         update_custom_emoji_html: "%{name} anovó'l fustaxe «%{target}»"
@@ -138,6 +152,7 @@ ast:
       space: Usu del espaciu
       title: Panel
       top_languages: Les llingües más actives
+      top_servers: Los sirvidores más activos
       website: Sitiu web
     disputes:
       appeals:
@@ -159,6 +174,7 @@ ast:
     export_domain_allows:
       no_file: Nun se seleicionó nengún ficheru
     follow_recommendations:
+      description_html: "<strong>La recomendación de cuentes ayuda a que los perfiles nuevos atopen aína conteníu interesante</strong>. Cuando una cuenta nun interactuó abondo con otros perfiles como pa formar recomendaciones personalizaes, estes cuentes van ser les que se recomienden. Recalcúlense caldía a partir d'un mecíu de cuentes con más actividá recién ya mayor númberu de siguidores llocales pa una llingua determinada."
       language: Pa la llingua
       status: Estáu
       title: Recomendación de cuentes
@@ -173,13 +189,34 @@ ast:
           suspend: Suspender
         policy: Política
         reason: Motivu públicu
+        title: Polítiques del conteníu
+      dashboard:
+        instance_accounts_dimension: Les cuentes más siguíes
+        instance_accounts_measure: cuentes atroxaes
+        instance_followers_measure: siguidores de nueso ellí
+        instance_follows_measure: siguidores de so equí
+        instance_languages_dimension: Les llingües más usaes
+        instance_media_attachments_measure: ficheros multimedia atroxaos
+        instance_reports_measure: informes d'esa instancia
+        instance_statuses_measure: artículos atroxaos
       empty: Nun s'atopó nengún dominiu.
       known_accounts:
         one: "%{count} cuenta conocida"
         other: "%{count} cuentes conocíes"
+      moderation:
+        title: Moderación
       private_comment: Comentariu priváu
       public_comment: Comentariu públicu
       title: Federación
+      total_reported: Informes d'esa instancia
+    invites:
+      deactivate_all: Desactivalo too
+      filter:
+        all: Too
+        available: Disponible
+        expired: Caducó
+        title: Peñera
+      title: Invitaciones
     ip_blocks:
       expires_in:
         '1209600': 2 selmanes
@@ -192,13 +229,18 @@ ast:
       title: 'Rellaciones de: %{acct}'
     relays:
       status: Estáu
+    report_notes:
+      created_msg: "¡La nota del informe creóse correutamente!"
     reports:
       account:
         notes:
           one: "%{count} nota"
           other: "%{count} notes"
+      actions:
+        silence_description_html: La cuenta va ser visible namás pa quien xá la siguiere o la buscare manualmente, lo que llenda'l so algame. Esta decisión pue desfacese en cualesquier momentu. Si escueyes esta opción, zárrense tolos informes escontra esta cuenta.
       actions_description_html: Decidi qué aición tomar pa resolver esti informe. Si tomes una aición punitiva escontra la cuenta de la que s'informó, va unviase un avisu per corréu electrónicu a esa cuenta, esceuto cuando se seleiciona la categoría <strong>Puxarra</strong>.
       add_to_report: Amestar más al informe
+      are_you_sure: "¿De xuru que quies facer esta aición?"
       category: Categoría
       category_description_html: El motivu pol que s'informó d'esta cuenta y/o conteníu cítase na comunicación cola cuenta de la que s'informó
       comment_description_html: 'Pa fornir más información, %{name} escribió:'
@@ -211,10 +253,13 @@ ast:
       notes:
         create: Amestar la nota
         create_and_resolve: Resolver con una nota
+        create_and_unresolve: Volver abrir con una nota
+        delete: Desaniciar
         title: Notes
       quick_actions_description_html: 'Toma una aición rápida o baxa pa ver el conteníu del que s''informó:'
       report: 'Informe #%{id}'
       reported_by: Perfil qu'informó
+      resolved: Resolvióse
       resolved_msg: "¡L'informe resolvióse correutamente!"
       skip_to_actions: Saltar a les aiciones
       status: Estáu
@@ -233,6 +278,7 @@ ast:
         administration: Alministración
         devops: DevOps
         invites: Invitaciones
+        moderation: Moderación
       edit: Edición del rol «%{name}»
       everyone: Permisos predeterminaos
       permissions_count:
@@ -246,7 +292,7 @@ ast:
         manage_invites: Xestionar les invitaciones
         manage_reports: Xestionar los informes
         manage_roles: Xestionar los roles
-        manage_rules: Xestionar les regles
+        manage_rules: Xestionar les normes
         manage_settings: Xestionar la configuración
         manage_taxonomies: Xestionar les taxonomíes
         manage_users: Xestionar los perfiles
@@ -255,11 +301,11 @@ ast:
         view_devops: DevOps
       title: Roles
     rules:
-      add_new: Amestar la regla
-      title: Regles del sirvidor
+      add_new: Amestar la norma
+      title: Normes del sirvidor
     settings:
       about:
-        manage_rules: Xestionar les regles del sirvidor
+        manage_rules: Xestionar les normes del sirvidor
         title: Tocante a
       appearance:
         preamble: Personaliza la interfaz web de Mastodon.
@@ -271,7 +317,12 @@ ast:
         preamble: Controla cómo s'atroxa'l conteníu xeneráu polos perfiles en Mastodon.
         title: Retención del conteníu
       discovery:
+        follow_recommendations: Recomendación de cuentes
+        preamble: L'apaición de conteníu interesante ye fundamental p'atrayer persones nueves que nun conozan nada de Mastodon. Controla'l funcionamientu de delles funciones de descubrimientu d'esti sirvidor.
+        profile_directory: Direutoriu de perfiles
         public_timelines: Llinies de tiempu públiques
+        publish_discovered_servers: Espublizamientu de sirvidores descubiertos
+        publish_statistics: Espublizamientu d'estadístiques
         title: Descubrimientu
         trends: Tendencies
       domain_blocks:
@@ -290,40 +341,58 @@ ast:
     site_uploads:
       delete: Desaniciar el ficheru xubíu
     statuses:
+      back_to_account: Volver a la páxina de la cuenta
       language: Llingua
       metadata: Metadatos
       original_status: Artículu orixinal
       visibility: Visibilidá
+      with_media: Con elementos multimedia
     strikes:
       actions:
         delete_statuses: "%{name} desanició l'artículu de: %{target}"
+        disable: "%{name} conxeló la cuenta de: %{target}"
         mark_statuses_as_sensitive: "%{name} marcó l'artículu de %{target} como sensible"
         none: "%{name} unvió una alvertencia a %{target}"
         sensitive: "%{name} marcó la cuenta de %{target} como sensible"
         suspend: "%{name} suspendió la cuenta de: %{target}"
       appeal_approved: Apellóse
+      appeal_pending: Apellación pendiente
     system_checks:
       elasticsearch_running_check:
         message_html: Nun se pudo conectar con Elasticsearch. Revisa que tea n'execución o desactiva la busca de testos completos
     title: Alministración
     trends:
+      allow: Permitir
+      disallow: Refugar
       links:
+        disallow: Refugar l'enllaz
         title: Enllaces en tendencia
       only_allowed: Namás lo permitío
       pending_review: Revisión pendiente
+      preview_card_providers:
+        title: Espublizadores
       statuses:
+        allow: Permitir l'artículu
+        disallow: Refugar l'artículu
         title: Artículos en tendencia
       tags:
         current_score: 'Puntuación total: %{score}'
         dashboard:
           tag_accounts_measure: usos únicos
+          tag_languages_dimension: Les llingües más usaes
+          tag_servers_dimension: Los sirvidores más destacaos
+          tag_servers_measure: sirvidores diferentes
+          tag_uses_measure: usos en total
         listable: Pue suxerise
         no_tag_selected: Nun camudó nenguna etiqueta darréu que nun se seleicionó nenguna
+        not_trendable: Nun apaez nes tendencies
         not_usable: Nun se pue usar
         title: Etiquetes en tendencia
         usable: Pue usase
       title: Tendencies
       trending: En tendencia
+    warning_presets:
+      title: Xestión d'alvertencies preconfiguraes
     webhooks:
       add_new: Amestar un estremu
       delete: Desaniciar
@@ -368,31 +437,52 @@ ast:
     discovery: Descubrimientu
     localization:
       body: Mastodon tradúcenlu voluntarios,
+      guide_link: https://crowdin.com/project/mastodon
       guide_link_text: tol mundu pue collaborar.
     sensitive_content: Conteníu sensible
     toot_layout: Distribución de los artículos
+  application_mailer:
+    notification_preferences: Camudar les preferencies de los mensaxes de corréu electrónicu
   applications:
     created: L'aplicación creóse correutamente
+    destroyed: L'aplicación desanicióse correutamente
     regenerate_token: Volver xenerar el pase d'accesu
     token_regenerated: El pase d'accesu volvió xenerase correutamente
     warning: Ten munchu curiáu con estos datos, ¡enxamás nun los compartas con naide!
     your_token: El pase d'accesu
   auth:
     change_password: Contraseña
+    confirmations:
+      wrong_email_hint: Si la direición de corréu electrónicu nun ye correuta, pues camudala na configuración de la cuenta.
+    delete_account: Desaniciu de la cuenta
     delete_account_html: Si quies desaniciar la cuenta, pues facelo <a href="%{path}">equí</a>. Va pidísete que confirmes l'aición.
+    description:
+      prefix_sign_up: "¡Rexístrate güei en Mastodon!"
     didnt_get_confirmation: "¿Nun recibiesti les instrucciones de confirmación?"
     dont_have_your_security_key: "¿Nun tienes una llave de seguranza?"
     forgot_password: "¿Escaeciesti la contraseña?"
     login: Aniciar la sesión
     logout: Zarrar la sesión
+    migrate_account: Cambéu de cuenta
+    migrate_account_html: Si quies redirixir esta cuenta a otra diferente, pues <a href="%{path}">configurar esta opción equí</a>.
     privacy_policy_agreement_html: Lleí ya acepto la <a href="%{privacy_policy_path}" target="_blank">política de privacidá</a>
     providers:
       cas: CAS
       saml: SAML
     register: Rexistrase
+    registration_closed: "%{instance} nun acepta cuentes nueves"
+    resend_confirmation: Volver unviar les instrucciones de confirmación
+    rules:
+      accept: Aceptar
+      back: Atrás
     security: Seguranza
     setup:
+      email_below_hint_html: Si la direición de corréu electrónicu ye incorreuta, pues camudala equí ya recibir un mensaxes de confirmación nuevu.
       email_settings_hint_html: Unvióse'l mensaxe de confirmación a %{email}. Si la direición de corréu electrónicu nun ye correuta, pues camudala na configuración de la cuenta.
+      title: Configuración
+    sign_in:
+      preamble_html: Anicia la sesión colos tos datos d'accesu en <strong>%{domain}</strong>. Si la cuenta ta agospiada n'otru sirvidor, nun vas ser a aniciar la sesión equí.
+      title: Aniciu de la sesión en «%{domain}»
     sign_up:
       preamble: Con una cuenta nesti sirvidor de Mastodon vas ser a siguir a cualesquier perfil de la rede, independientemente del sirvidor onde s'agospie la so cuenta.
       title: 'Creación d''una cuenta en: %{domain}.'
@@ -411,7 +501,9 @@ ast:
       return: Amosar el perfil de la cuenta
       web: Dir a la web
   challenge:
+    confirm: Siguir
     hint_html: "<strong>Conseyu:</strong> nun vamos volver pidite la contraseña hasta dientro d'una hora."
+    invalid_password: La contraseña nun ye válida
     prompt: Confirma la contraseña pa siguir
   crypto:
     errors:
@@ -441,13 +533,18 @@ ast:
       action_taken: Aición tomada
       appeal: Apellación
       appeal_rejected: Refugóse l'apellación
+      appeal_submitted_at: Data de l'apellación
+      appealed_msg: Unvióse l'apellación. Si s'aprueba, avisámoste.
       appeals:
         submit: Unviu d'una apellación
       approve_appeal: Aprobar l'apellación
+      associated_report: Informe asociáu
       created_at: Data
+      recipient: Dirixóse a
       reject_appeal: Refugar l'apellación
       status: 'Artículu #%{id}'
       status_removed: L'artículu xá se quitó del sistema
+      your_appeal_approved: Aprobóse la to apellación
       your_appeal_pending: Unviesti una apellación
       your_appeal_rejected: Refugóse la to apellación
   errors:
@@ -479,6 +576,7 @@ ast:
     lists: Llistes
     storage: Almacenamientu multimedia
   featured_tags:
+    add_new: Amestar
     hint_html: "<strong>¿Qué son les etiquetes destacaes?</strong> Apaecen de forma bien visible nel perfil públicu ya permite que les persones restolen los tos artículos públicos per duana d'eses etiquetes. Son una gran ferramienta pa tener un rexistru de trabayos creativos o de proyeutos a plazu llongu."
   filters:
     contexts:
@@ -487,6 +585,7 @@ ast:
       public: Llinies de tiempu públiques
       thread: Conversaciones
     edit:
+      add_keyword: Amestar una pallabra clave
       keywords: Pallabres clave
       statuses: Artículos individuales
     errors:
@@ -500,8 +599,8 @@ ast:
         one: "%{count} artículu"
         other: "%{count} artículos"
       title: Peñeres
-  footer:
-    trending_now: En tendencia
+    new:
+      title: Amestar una peñera
   generic:
     all: Too
     all_items_on_page_selected_html:
@@ -510,6 +609,7 @@ ast:
     all_matching_items_selected_html:
       one: Seleicionóse <strong>%{count}</strong> elementu que concasa cola busca.
       other: Seleicionáronse <strong>%{count}</strong> elementos que concasen cola busca.
+    changes_saved_msg: "¡Los cambeos guardáronse correutamente!"
     copy: Copiar
     delete: Desaniciar
     deselect: Deseleicionar too
@@ -517,6 +617,8 @@ ast:
     save_changes: Guardar los cambeos
     today: güei
   imports:
+    errors:
+      over_rows_processing_limit: contién más de %{count} fileres
     modes:
       merge: Mecíu
       merge_long: Caltién los rexistros esistentes ya amiesta otros nuevos
@@ -525,8 +627,10 @@ ast:
     preface: Pues importar los datos qu'esportares dende otru sirvidor, como la llista de perfiles bloquiaos o que sigas.
     types:
       blocking: Llista de perfiles bloquiaos
+      bookmarks: Marcadores
       domain_blocking: Llista de dominios bloquiaos
       following: Llista de siguidores
+      muting: Llista de perfiles colos avisos desactivaos
     upload: Xubir
   invites:
     expired: Caducó
@@ -543,15 +647,16 @@ ast:
     max_uses:
       one: 1 usu
       other: "%{count} usos"
+    prompt: Xenera ya comparti enllaces con otres persones pa conceder l'accesu a esti sirvidor
     table:
       expires_at: Data de caducidá
-  lists:
-    errors:
-      limit: Algamesti la cantidá máxima de llistes
+      uses: Usos
+    title: Invitación
   login_activities:
     authentication_methods:
       password: contraseña
       webauthn: llaves de seguranza
+    successful_sign_in_html: Anicióse correutamente la sesión col métodu «%{method}» dende %{ip} (%{browser})
   media_attachments:
     validations:
       images_and_video: Nun se pue axuntar nengún videu a un artículu que xá contién imáxenes
@@ -560,8 +665,11 @@ ast:
     errors:
       missing_also_known_as: nun ye un nomatu d'esta cuenta
       move_to_self: nun pue ser la cuenta actual
+    incoming_migrations_html: Pa migrar d'otra cuenta a esta, primero tienes de <a href="%{path}">crear un nomatu de cuenta</a>.
     warning:
       followers: Esta aición va mover tolos siguidores de la cuenta actual a la nueva
+  moderation:
+    title: Moderación
   notification_mailer:
     admin:
       sign_up:
@@ -571,6 +679,7 @@ ast:
       subject: "%{name} marcó'l to artículu como favoritu"
     follow:
       body: "¡Agora %{name} siguete!"
+      subject: "%{name} ta siguiéndote"
     follow_request:
       body: "%{name} solicitó siguite"
       title: Solicitú de siguimientu nueva
@@ -584,6 +693,7 @@ ast:
     update:
       subject: "%{name} editó un artículu"
   notifications:
+    email_events: Unviu d'avisos per corréu electrónicu
     email_events_hint: 'Seleiciona los eventos de los que quies recibir avisos:'
     other_settings: Configuración d'otros avisos
   number:
@@ -596,9 +706,13 @@ ast:
           thousand: mil
           trillion: B
   otp_authentication:
+    code_hint: Introduz el códigu que xeneró l'aplicación autenticadora pa confirmar l'aición
     description_html: Si actives l'<strong>autenticación en dos pasos</strong> con una aplicación autenticadora, al aniciar la sesión va ser obligatorio que tengas el teléfonu a mano, preséu que xenera los pases que tienes d'introducir.
+    enable: Activar
+    instructions_html: "<strong>Escania esti códigu QR con Google Authenticator o otra aplicación asemeyada nel móvil</strong>. Dende agora, esa aplicación va xenerar los pases que tienes d'introducir cuando anicies la sesión."
     manual_instructions: 'Si nun pues escaniar el códigu QR ya tienes d''introducilu manualmente, equí tienes el secretu en testu ensin formatu:'
     setup: Configurar
+    wrong_code: "¡El códigu introducíu nun yera válidu! ¿La hora del sirvidor ya la del preséu son correutes?"
   pagination:
     next: Siguiente
     truncate: "&hellip;"
@@ -609,6 +723,8 @@ ast:
       invalid_choice: La opción de votu escoyida nun esiste
       too_many_options: nun pue contener más de %{max} elementos
   preferences:
+    other: Otres preferencies
+    posting_defaults: Configuración predeterminada del espublizamientu d'artículos
     public_timelines: Llinies de tiempu públiques
   privacy_policy:
     title: Política de privacidá
@@ -617,6 +733,8 @@ ast:
     followers: Siguidores
     last_active: Última actividá
     most_recent: Lo más recién
+    mutual: Mutua
+    primary: Principal
     relationship: Rellación
     remove_selected_follows: Dexar de siguir a los perfiles seleicionaos
   scheduled_statuses:
@@ -632,6 +750,7 @@ ast:
       electron: Electron
       firefox: Firefox
       generic: Restolador desconocíu
+      huawei_browser: Restolador de Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Restolador Ovi pa S40 de Nokia
@@ -641,6 +760,7 @@ ast:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Restolador desconocíu
       weibo: Weibo
     current_session: Sesión actual
     ip: IP
@@ -651,9 +771,10 @@ ast:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: GNU/Linux
       mac: macOS
-      other: plataforma desconocida
+      unknown_platform: Plataforma desconocida
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -667,6 +788,7 @@ ast:
     authorized_apps: Aplicaciones autorizaes
     back: Volver a Mastodon
     development: Desendolcu
+    edit_profile: Edición del perfil
     export: Esportación de datos
     featured_tags: Etiquetes destacaes
     import: Importación
@@ -675,6 +797,7 @@ ast:
     notifications: Avisos
     preferences: Preferencies
     profile: Perfil
+    relationships: Perfiles que sigues ya te siguen
     statuses_cleanup: Desaniciu automáticu d'artículos
     two_factor_authentication: Autenticación en dos pasos
     webauthn_authentication: Llaves de seguranza
@@ -709,10 +832,23 @@ ast:
     title: "%{name}: «%{quote}»"
     visibilities:
       direct: Mensaxe direutu
+      private: Namás siguidores
       public_long: Tol mundu pue velos
       unlisted_long: Tol mundu pue velos, mas nun apaecen nes llinies de tiempu públiques
   statuses_cleanup:
     exceptions: Esceiciones
+    interaction_exceptions: Esceiciones basaes nes interaiciones
+    keep_direct: Caltener los mensaxes direutos
+    keep_direct_hint: Nun desanicia nengún mensaxe direutu
+    keep_media: Caltener los artículos con elementos multimedia
+    keep_media_hint: Nun desanicia nengún artículu de to que contenta elementos multimedia
+    keep_pinned: Caltener los artículos fixaos
+    keep_polls: Caltener les encuestes
+    keep_polls_hint: Nun desanicia nenguna encuesta de to
+    keep_self_bookmark: Caltener los artículos que metieres en Marcadores
+    keep_self_bookmark_hint: Nun desanicia nengún artículu que metieres en Marcadores
+    keep_self_fav: Caltener los artículos que seyan favoritos
+    keep_self_fav_hint: Nun desanicia nengún artículu que marcares como favoritu
     min_age:
       '1209600': 2 selmanes
       '15778476': 6 meses
@@ -745,6 +881,8 @@ ast:
       appeal_description: Si te paez que ye un error, pues unviar una apellación al personal de %{instance}.
       explanation:
         disable: Xá nun pues usar la cuenta mas el perfil ya otros datos siguen intautos. Pues solicitar una copia de seguranza de los datos, camudar la configuración de la cuenta o desaniciar la cuenta.
+        silence: Entá pues usar la cuenta mas namás vas ver los artículos de los perfiles que xá siguieres nesti sirvidor ya ye posible que se t'escluya de dalgunes funciones de descubrimientu. Por embargu, otros perfiles tovía puen siguite manualmente.
+        suspend: Xá nun pues usar la cuenta nin se pue acceder a los datos del to perfil. Entá pues aniciar la sesión pa pidir una copia de seguranza de los tos datos hasta que se desanicien en 30 díes, mas vamos caltener dalgunos datos básicos pa evitar que te saltes la suspensión.
       reason: 'Motivu:'
       statuses: 'Artículos citaos:'
       subject:
@@ -756,6 +894,7 @@ ast:
         suspend: Cuenta suspendida
     welcome:
       edit_profile_action: Configurar el perfil
+      edit_profile_step: Pues personalizar el perfil pente la xuba d'una semeya, el cambéu del nome visible ya muncho más. Tamién, si lo prefieres, pues revisar los perfiles nuevos enantes de que puedan siguite.
       explanation: Equí tienes dalgunos conseyos pa que comiences
       final_action: Comenzar a espublizar
       subject: Afáyate en Mastodon
@@ -766,7 +905,6 @@ ast:
     seamless_external_login: Aniciesti la sesión pente un serviciu esternu, polo que la configuración de la contraseña ya de la direición de corréu electrónicu nun tán disponibles.
     signed_in_as: 'Aniciesti la sesión como:'
   verification:
-    explanation_html: 'Pues <strong>verificate como la persona propietaria de los enllaces nos metadatos del to perfil</strong>. Pa ello, el sitiu web enllaciáu ha contener un enllaz al to perfil de Mastodon. Esti enllaz <strong>ha</strong> tener l''atributu <code>rel="me"</code>. El testu del enllaz nun importa. Equí tienes un exemplu:'
     verification: Verificación
   webauthn_credentials:
     create:
diff --git a/config/locales/be.yml b/config/locales/be.yml
index de6ac186d..0509fc830 100644
--- a/config/locales/be.yml
+++ b/config/locales/be.yml
@@ -95,6 +95,7 @@ be:
       moderation:
         active: Актыўны
         all: Усе
+        disabled: Адключана
         pending: Чакаюць
         silenced: Абмежаваны
         suspended: Прыпынены
@@ -139,6 +140,7 @@ be:
       search: Пошук
       search_same_email_domain: Іншыя карыстальнікі з такім жа даменам эл. пошты
       search_same_ip: Іншыя карыстальнікі з гэтым IP
+      security: Бяспека
       security_measures:
         only_password: Толькі пароль
         password_and_2fa: Пароль і 2FA
@@ -443,6 +445,7 @@ be:
         resolve: Вызначыць дамен
         title: Заблакіраваць новы дамен эл. пошты
       no_email_domain_block_selected: Блакіроўкі даменаў эл. пошты не былі змененыя, таму што ні адзін з іх не быў выбраны
+      not_permitted: Забаронена
       resolved_dns_records_hint_html: Даменнае імя ператвараецца ў наступныя дамены MX, якія ў канчатковым выніку адказваюць за прыём электроннай пошты. Блакаванне дамена MX заблакуе рэгістрацыю з любога адраса электроннай пошты, які выкарыстоўвае той жа дамен MX, нават калі бачнае імя дамена адрозніваецца. <strong>Будзьце асцярожныя, каб не заблакіраваць асноўных пастаўшчыкоў электроннай пошты.</strong>
       resolved_through_html: Вызначына каля %{domain}
       title: Заблакаваныя паштовыя дамены
@@ -457,6 +460,7 @@ be:
         private_comment_description_html: 'Каб дапамагчы вам адсочваць, адкуль паходзяць імпартаваныя блокі, імпартаваныя блокі будуць створаны з наступным прыватным каментарыем: <q>%{comment}</q>'
         private_comment_template: Імпартавана з %{source} %{date}
         title: Імпарт блакіровак дамену
+      invalid_domain_block: 'Адзін ці больш даменных блокаў былі прапушчаны праз наступную(-ыя) памылку(-і): %{error}'
       new:
         title: Імпарт блакіровак дамену
       no_file: Файл не выбраны
@@ -492,6 +496,7 @@ be:
       content_policies:
         comment: Унутраная нататка
         description_html: Вы можаце вызначыць палітыку кантэнту, якая будзе прымяняцца да ўсіх уліковых запісаў гэтага дамена і любога з яго субдаменаў.
+        limited_federation_mode_description_html: Вы можаце выбраць ці дазволіць уваходзіць у федэрацыю з гэтым даменам.
         policies:
           reject_media: Адхіліць мультымедыя
           reject_reports: Адхіліць справаздачы
@@ -599,19 +604,23 @@ be:
         mark_as_sensitive_description_html: Медыя кантэнт у паведамленнях, пра якія паведамляецца, будуць пазначаны як канфідэнцыяльныя, і будзе запісана папярэджанне, каб дапамагчы вам эскаліраваць наступныя парушэнні з боку таго ж уліковага запісу.
         other_description_html: Глядзіце дадатковыя параметры для кіравання паводзінамі ўліковага запісу і настройкі сувязі з уліковым запісам, пра які паведамляецца.
         resolve_description_html: Ніякіх дзеянняў супраць уліковага запісу, пра які паведамляецца, не будзе, папярэджэнне не зафіксавана, і скарга будзе закрыта.
-        silence_description_html: Профіль будзе бачны толькі тым, хто ўжо сочыць за ім або шукае яго ўручную, што значна абмяжоўвае яго ахоп. Заўсёды можна вярнуць.
-        suspend_description_html: Профіль і яго змесціва стане недаступным да моманту канчатковага выдалення. Камунікацыя з уліковым запісам будзе немагчымай. Можна скасаваць на працягу 30 дзён.
+        silence_description_html: Уліковы запіс будзе бачны толькі тым, хто ўжо сочыць за ім або шукае яго ўручную, што значна абмяжоўвае яго ахоп. Заўсёды можна вярнуць. Закрывае ўсе скаргі на гэты ўліковы запіс.
+        suspend_description_html: Уліковы запіс і ўсё яго змесціва будзе недаступна і ў далейшым выдалены, узаемадзеянне з ім будзе немагчыма. Магчыма адмяніць на працягу 30 дзён. Закрывае ўсе скаргі на гэты ўліковы запіс.
       actions_description_html: Вырашыце, якія дзеянні распачаць, каб вырашыць гэтую скаргу. Калі вы прымеце меры пакарання ў дачыненні да ўліковага запісу, пра які паведамляецца, ім будзе адпраўлена апавяшчэнне па электроннай пошце, за выключэннем выпадкаў, калі выбрана катэгорыя <strong>Спам</strong>.
+      actions_description_remote_html: Вырашыце як паступіць з гэтай скаргай. Гэта паўплывае толькі на тое як <strong>ваш</strong> сервер звязваецца з аддалёным уліковым запісам і апрацоўвае яго кантэнт.
       add_to_report: Дадаць яшчэ дэталяў да скаргі
       are_you_sure: Вы ўпэўнены?
       assign_to_self: Прызначыць мне
       assigned: Прызначаны мадэратар
       by_target_domain: Дамен уліковага запісу, на які падаецца скарга
+      cancel: Скасаваць
       category: Катэгорыя
       category_description_html: Прычына паведамлення аб гэтым уліковым запісе і/або кантэнце будзе згадана ў сувязі з уліковым запісам, на які пададзена скарга
       comment:
         none: Пуста
       comment_description_html: 'Каб даць больш інфармацыі, %{name} напісаў:'
+      confirm: Пацвердзіць
+      confirm_action: Пацвердзіць мадэрацыю супраць @%{acct}
       created_at: Створана
       delete_and_resolve: Выдаліць допісы
       forwarded: Пераслана
@@ -628,6 +637,7 @@ be:
         placeholder: Апішыце, якія дзеянні былі зроблены, або любыя іншыя звязаныя абнаўленні...
         title: Нататкі
       notes_description_html: Праглядвайце і пакідайце нататкі іншым мадэратарам і сабе ў будучыні
+      processed_msg: 'Скарга #%{id} паспяхова апрацавана'
       quick_actions_description_html: 'Выканайце хуткае дзеянне або пракруціце ўніз, каб убачыць змесціва, на якое пададзена скарга:'
       remote_user_placeholder: аддалены карыстальнік з %{instance}
       reopen: Пераадкрыць скаргу
@@ -640,9 +650,28 @@ be:
       status: Стан
       statuses: Змесціва, на якое паскардзіліся
       statuses_description_html: Крыўднае змесціва будзе згадвацца ў зносінах з уліковым запісам, на які пададзена скарга
+      summary:
+        action_preambles:
+          delete_html: 'Вы збіраецеся <strong>выдаліць</strong> некаторыя з допісаў <strong>@%{acct}</strong>. Гэта будуць:'
+          mark_as_sensitive_html: 'Вы збіраецеся <strong>пазначыць</strong> некаторыя з допісаў <strong>@%{acct}</strong> як <strong>уражальныя</strong>. Гэта будуць:'
+          silence_html: 'Вы збіраецеся <strong>абмежаваць</strong> уліковы запіс <strong>@%{acct}</strong>. Гэта будзе:'
+          suspend_html: 'Вы збіраецеся <strong>прыпыніць</strong> уліковы запіс <strong>@%{acct}</strong>. Гэта будзе:'
+        actions:
+          delete_html: Выдаліць абразлівы допіс
+          mark_as_sensitive_html: Пазначыць медыя абразлівага допіса як далікатнае
+          silence_html: Значна абмежаваць ахоп <strong>@%{acct}</strong>, зрабіўшы профіль і змесціва бачнымі толькі для людзей, якія ўжо падпісаныя, альбо шукае яго ўручную
+          suspend_html: Прыпыніць <strong>@%{acct}</strong>, зрабіць профіль і змесціва недаступным і не даваць магчымасці ўзаемадзейнічаць з імі
+        close_report: 'Пазначыць скаргу #%{id} як вырашаную'
+        close_reports_html: Адзначыць <strong>усе</strong> скаргі супраць <strong>@%{acct}</strong> як вырашаныя
+        delete_data_html: Выдаліць профіль <strong>@%{acct}</strong> і змесціва праз 30 дзён, калі тым часам гэтае дзеянне не будзе адменена
+        preview_preamble_html: "<strong>@%{acct}</strong> атрымае папярэджанне наступнага зместу:"
+        record_strike_html: Зарэгістраваць папярэджанне <strong>@%{acct}</strong>, каб дапамагчы вам эскаліраваць наступныя парушэнні з боку таго ж уліковага запісу
+        send_email_html: Адправіць <strong>@%{acct}</strong> папярэджанне па электроннай пошце
+        warning_placeholder: Неабавязковае дадатковае абгрунтаванне мадэрацыі.
       target_origin: Крыніца уліковага запісу на які пададзена скарга
       title: Скаргі
       unassign: Скінуць
+      unknown_action_msg: 'Невядомае дзеянне: %{action}'
       unresolved: Нявырашаныя
       updated_at: Абноўлена
       view_profile: Паглядзець профіль
@@ -741,6 +770,8 @@ be:
         preamble: Прадстаўленне цікавага кантэнту дапамагае прыцягнуць новых карыстальнікаў, якія могуць не ведаць нікога на Mastodon. Кантралюйце працу розных функцый выяўлення на вашым серверы.
         profile_directory: Дырэкторыя профіляў
         public_timelines: Публічная паслядоўнасць публікацый
+        publish_discovered_servers: Апублікаваць знойдзеныя серверы
+        publish_statistics: Апублікаваць статыстыку
         title: Выяўленне
         trends: Актуальныя
       domain_blocks:
@@ -795,6 +826,7 @@ be:
         suspend: Уліковы запіс %{target} выключаны %{name}
       appeal_approved: Абскарджана
       appeal_pending: Апеляцыя разглядаецца
+      appeal_rejected: Абскарджанне адхілена
     system_checks:
       database_schema_check:
         message_html: Ёсць незавершаныя міграцыі базы дадзеных. Калі ласка, запусціце іх, каб пераканацца, што дадатак паводзіць сябе належным чынам
@@ -808,6 +840,12 @@ be:
         message_html: Вы не вызначылі ніякіх правілаў сервера.
       sidekiq_process_check:
         message_html: Не працуе працэс Sidekiq для %{value} чаргі. Калі ласка праверце вашу канфігурацыю Sidekiq
+      upload_check_privacy_error:
+        action: Для падрабязнасцей націсніце тут
+        message_html: "<strong>Ваш сервер не наладжаны. Прыватнасць карыстальнікаў пад пагрозай.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Для падрабязнасцей націсніце тут
+        message_html: "<strong>Ваша сховішча не наладжана. Прыватнасць карыстальнікаў пад пагрозай.</strong>"
     tags:
       review: Стан праверкі
       updated_msg: Налады хэштэгаў паспяхова змененыя
@@ -832,6 +870,7 @@ be:
           other: Абагулілі %{count} чалавек за апошні тыдзень
         title: Актуальныя спасылкі
         usage_comparison: Выкарыстоўвалася %{today} разоў сёння, у параўнанні з %{yesterday} учора
+      not_allowed_to_trend: Забаронена выходзіць у актуальныя
       only_allowed: Толькі дазволенае
       pending_review: Чакае праверкі
       preview_card_providers:
@@ -969,6 +1008,7 @@ be:
   applications:
     created: Праграма паспяхова створана
     destroyed: Праграма паспяхова выдалена
+    logout: Выйсці
     regenerate_token: Стварыць новы токен доступу
     token_regenerated: Новы токен доступу паспяхова створаны
     warning: Будзьце вельмі асцярожныя з гэтымі данымі. Ніколі нікому не паведамляйце іх!
@@ -976,6 +1016,8 @@ be:
   auth:
     apply_for_account: Пакінуць заяўку
     change_password: Пароль
+    confirmations:
+      wrong_email_hint: Калі гэты адрас электроннай пошты памылковы, вы можаце змяніць яго ў наладах уліковага запісу.
     delete_account: Выдаліць уліковы запіс
     delete_account_html: Калі вы жадаеце выдаліць ваш уліковы запіс, можаце <a href="%{path}">працягнуць тут</a>. Ад вас будзе запатрабавана пацвярджэнне.
     description:
@@ -1003,6 +1045,8 @@ be:
     resend_confirmation: Адправіць інструкцыю пацвярджэння зноў
     reset_password: Скінуць пароль
     rules:
+      accept: Прыняць
+      back: Назад
       preamble: Правілы вызначаныя мадэратарамі дамена %{domain}.
       title: Некалькі базавых правілаў.
     security: Бяспека
@@ -1200,8 +1244,6 @@ be:
       index:
         hint: Гэты фільтр прымяняецца для выбару асобных допісаў незалежна ад іншых крытэрыяў. Вы можаце дадаць больш допісаў у гэты фільтр з вэб-інтэрфейсу.
         title: Адфільтраваныя допісы
-  footer:
-    trending_now: Актуальнае
   generic:
     all: Усе
     all_items_on_page_selected_html:
@@ -1232,8 +1274,6 @@ be:
       many: Штосьці пакуль не зусім правільна! Калі ласка, праглядзіце %{count} памылак ніжэй
       one: Штосьці пакуль не зусім правільна! Калі ласка, праглядзіце памылку ніжэй
       other: Штосьці пакуль не зусім правільна! Калі ласка, праглядзіце %{count} памылак ніжэй
-  html_validator:
-    invalid_markup: 'змяшчае несапраўдную разметку HTML: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Несапраўдны файл CSV. Памылка: %{error}'
@@ -1419,7 +1459,11 @@ be:
       unrecognized_emoji: невядомае эмодзі
   relationships:
     activity: Актыўнасць ул. запісу
+    confirm_follow_selected_followers: Вы ўпэўнены, што жадаеце падпісацца на выбраных падпісчыкаў?
+    confirm_remove_selected_followers: Вы ўпэўнены, што жадаеце выдаліць выбраных падпісчыкаў?
+    confirm_remove_selected_follows: Вы ўпэўнены, што жадаеце выдаліць выбраныя падпіскі?
     dormant: Занядбаны
+    follow_failure: Вы не можаце падпісацца на некаторыя акаўнты.
     follow_selected_followers: Падпісацца на выбраных падпісчыкаў
     followers: Падпісчыкі
     following: Падпісаны
@@ -1459,6 +1503,7 @@ be:
       electron: Electron
       firefox: Firefox
       generic: Невядомы браўзер
+      huawei_browser: Браўзер Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1468,6 +1513,7 @@ be:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Невядомы браўзер
       weibo: Weibo
     current_session: Бягучая сесія
     description: "%{browser} на %{platform}"
@@ -1480,9 +1526,10 @@ be:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: невядомай платформе
+      unknown_platform: Невядомая платформа
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1707,12 +1754,13 @@ be:
       title: Рады вітаць вас, %{name}!
   users:
     follow_limit_reached: Вы не можаце падпісацца на большую колькасць людзей чым %{limit}
+    go_to_sso_account_settings: Перайдзіце ў налады ідэнтыфікацыі вашага ўліковага запісу
     invalid_otp_token: Няправільны код двухфактарнай аўтэнтыфікацыі
     otp_lost_help_html: Калі вы страцілі доступ да абодвух, вы можаце скарыстацца %{email}
     seamless_external_login: Вы ўвайшлі праз знешні сэрвіс, таму налады пароля і эл. пошты недаступныя.
     signed_in_as: 'Увайшлі як:'
   verification:
-    explanation_html: 'Вы можаце <strong>пацвердзіць сябе як уладальніка спасылак у метададзеных вашага профілю</strong>. Для гэтага спасылка на вэб-сайт павінна ўтрымліваць спасылку на ваш профіль Mastodon. Зваротная спасылка <strong>павінна</strong> мець атрыбут <code>rel="me"</code>. Тэкставы змест спасылкі не мае значэння. Вось прыклад:'
+    explanation_html: 'Вы можаце <strong>пацвердзіць сябе як уладальніка спасылак у метададзеных вашага профілю</strong>. Для гэтага спасылка на вэб-сайт павінна ўтрымліваць спасылку на ваш профіль Mastodon. Пасля дадавання спасылка, вам спатрэбіцца вярнуцца і перазахаваць свой профіль, каб усё адбыдося. Зваротная спасылка <strong>павінна</strong> мець атрыбут <code>rel="me"</code>. Тэкставы змест спасылкі не мае значэння. Вось прыклад:'
     verification: Верыфікацыя
   webauthn_credentials:
     add: Дадаць новы ключ бяспекі
diff --git a/config/locales/bg.yml b/config/locales/bg.yml
index 9608fdbfb..4304966fa 100644
--- a/config/locales/bg.yml
+++ b/config/locales/bg.yml
@@ -91,6 +91,7 @@ bg:
       moderation:
         active: Дейно
         all: Всичко
+        disabled: Изключено
         pending: Чака
         silenced: Ограничено
         suspended: Спряно
@@ -104,7 +105,7 @@ bg:
       not_subscribed: Без абонамент
       pending: Изчаква преглед
       perform_full_suspension: Спиране
-      previous_strikes: Предишни нарушения
+      previous_strikes: Предишни предупреждения
       previous_strikes_description_html:
         one: Този акаунт има <strong>едно</strong> нарушение.
         other: Този акунт има <strong>%{count}</strong> нарушения.
@@ -133,6 +134,7 @@ bg:
       search: Търсене
       search_same_email_domain: Други потребители със същия домейн за имейл
       search_same_ip: Други потребители със същия IP
+      security: Сигурност
       security_measures:
         only_password: Само парола
         password_and_2fa: Парола и двуфакторно удостоверяване
@@ -229,7 +231,7 @@ bg:
       actions:
         approve_appeal_html: "%{name} одобри обжалването на решение за модериране от %{target}"
         approve_user_html: "%{name} одобри регистрирането от %{target}"
-        assigned_to_self_report_html: "%{name} назначете доклада %{target} на себе си"
+        assigned_to_self_report_html: "%{name} възложи на себе си доклад %{target}"
         change_email_user_html: "%{name} промени адреса на имейла на потребителя %{target}"
         change_role_user_html: "%{name} промени ролята на %{target}"
         confirm_user_html: "%{name} потвърди адреса на имейла на потребителя %{target}"
@@ -372,8 +374,8 @@ bg:
         empty: Няма намерени обжалвания.
         title: Жалби
     domain_allows:
-      add_new: Позволявам федерацията с домейна
-      created_msg: Домейнът е успешно позволен за федерацията
+      add_new: Позволявам федериране с домейна
+      created_msg: Успешно позволен домейн за федерацията
       destroyed_msg: Домейнът е забранен от федерацията
       export: Износ
       import: Внос
@@ -402,7 +404,7 @@ bg:
       obfuscate: Замъгляване на името на домейна
       obfuscate_hint: Частично замъгляване на името на домейна в списъка, ако е включено рекламирането на списъка с ограничения на домейни
       private_comment: Личен коментар
-      private_comment_hint: Коментирането за това ограничение на домейна за вътрешна употреба от модераторите.
+      private_comment_hint: Коментирането за това ограничение на домейна е за вътрешна употреба от модераторите.
       public_comment: Публичен коментар
       public_comment_hint: Оставяне на коментар за ограничението на домейна за широката публика, ако рекламирането на списъка с ограниченията на домейни е включено.
       reject_media: Отхвърляне на мултимедийните файлове
@@ -427,6 +429,7 @@ bg:
         resolve: Преобразуване на домейна
         title: Блокиране на нов домейн на имейл
       no_email_domain_block_selected: Няма промяна, тъй като няма избрани блокирания на имейл домейн
+      not_permitted: Няма позволение
       resolved_dns_records_hint_html: Името на домейна се преобразува към следните MX домейни, които са основно отговорни за получаване на имейл. Блокирането на MX домейн блокира записването от всеки имейл, използващ същия MX домейн, дори видимото име на домейна да е различно. <strong>Бъдете внимателни и не блокирайте често-срещани доставчици на имейл</strong>
       resolved_through_html: Преобразувано чрез %{domain}
       title: Блокирани домейни на имейл
@@ -441,6 +444,7 @@ bg:
         private_comment_description_html: 'За по-лесно проследяване откъде идват внесените блокирания, те ще се създадат със следния личен коментар: <q>%{comment}</q>'
         private_comment_template: Внесено от %{source} на %{date}
         title: Внос на блокирания на домейни
+      invalid_domain_block: 'Едно или повече блокирания на домейн са прескочени поради следнате грешки: %{error}'
       new:
         title: Внос на блокирания на домейни
       no_file: Няма избран файл
@@ -472,6 +476,7 @@ bg:
       content_policies:
         comment: Вътрешна бележка
         description_html: Може да определите политиките за съдържание, които ще се приложат към всички акаунти от този домейн и всеки от поддомейните му.
+        limited_federation_mode_description_html: Може да избирате дали да позволите федарацията с този домейн.
         policies:
           reject_media: Отхвърляне на мултимедия
           reject_reports: Отхвърляне на докладите
@@ -575,19 +580,23 @@ bg:
         mark_as_sensitive_description_html: Мултимедията в докладваните публикации ще се означи като деликатна, за да ви помогне при изострянето на бъдещи нарушения от същия акаунт.
         other_description_html: Показване на повече възможности за управляване на поведението на акаунта и за персонализиране на комуникацията с доклвадвания акаунт.
         resolve_description_html: Няма да се предприеме действие срещу докладвания акаунт, няма да се записва нарушение и докладът ще се затвори.
-        silence_description_html: Профилът ще е видим само за последователите му или търсещите го ръчно, което драстично ограничава обсега му. Настройката може да се промени по всяко време.
-        suspend_description_html: Профилът и цялото му съдържание ще бъдат недостъпни, до евентуалното му изтриване. Няма да може да се взаимодейства с акаунта. Настройката може да се отмени в рамките на 30 дни.
+        silence_description_html: Профилът ще е видим само за последователите му или търсещите го ръчно, което драстично ограничава обсега му. Настройката може да бъде отменена по всяко време. Затваря всички доклади срещу акаунта.
+        suspend_description_html: Акаунтът и неговото съдържание ще бъдат недостъпни и евентуално изтрити и взаимодействието с него ще е невъзможно. Обрамотимо до 30 дни. Затваря всички доклади срещу този акаунт.
       actions_description_html: Решете какво действие може да се предприеме, за да бъде отхвърлен докладът. Ако предприемете наказателно действие срещу докладвания акаунт, към него ще бъде изпратено известие по имейл, освен ако <strong>Спам</strong> категорията не е била избрана.
+      actions_description_remote_html: Преценете с какво действие да решите този доклад. Това ще има ефекет върху това как <strong>вашият</strong> сървър комуникира с този отдалечен акаунт и се справя с неговото съдържание.
       add_to_report: Добавяне на още към доклада
       are_you_sure: Сигурни ли сте?
       assign_to_self: Назначаване на мен
       assigned: Назначен модератор
       by_target_domain: Домейн на докладвания акаунт
+      cancel: Отказ
       category: Категория
       category_description_html: Причината, поради която акаунтът и/или съдържанието е докладвано ще се цитира в комуникацията с докладвания акаунт
       comment:
         none: Нищо
       comment_description_html: 'За да предостави повече информация, %{name} написа:'
+      confirm: Потвърждаване
+      confirm_action: Потвърждаване на модераторско действие срещу @%{acct}
       created_at: Докладвано
       delete_and_resolve: Изтриване на публикациите
       forwarded: Препратено
@@ -604,6 +613,7 @@ bg:
         placeholder: Опишете какви действия са били предприети или всякакви други свързани нови неща...
         title: Бележки
       notes_description_html: Прегледайте и оставете бележки за други модератори и за вас самите след време
+      processed_msg: Доклад №%{id} успешно обработен
       quick_actions_description_html: 'Предприемете бързо действие или превъртете надолу, за да видите докладваното съдържание:'
       remote_user_placeholder: отдалеченият потребител от %{instance}
       reopen: Отваряне пак на доклад
@@ -616,9 +626,28 @@ bg:
       status: Състояние
       statuses: Докладвано съдържание
       statuses_description_html: Непристойно съдържание ще бъде цитирано в комуникацията с докладвания акаунт
+      summary:
+        action_preambles:
+          delete_html: 'На път сте да <strong>премахнете</strong> някои от публикациите на <strong>@%{acct}</strong>. Това ще:'
+          mark_as_sensitive_html: 'На път сте да <strong>означите</strong> някои от публикациите на <strong>@%{acct}</strong> като <strong>деликатни</strong>. Това ще:'
+          silence_html: 'На път сте да <strong>ограничите</strong> акаунта на <strong>@%{acct}</strong>. Това ще:'
+          suspend_html: 'На път сте да <strong>спрете</strong> акаунта на <strong>@%{acct}</strong>. Това ще:'
+        actions:
+          delete_html: Премахване на обидните публикации
+          mark_as_sensitive_html: Означаване на мултимедийните обидни публикации като деликатни
+          silence_html: Силно ограничаване достигането на <strong>@%{acct}</strong>, което прави профилът и съдържанието на това лице видимо само до хората, които са го последвали или ръчно търсещите профила
+          suspend_html: Спирането на <strong>@%{acct}</strong> ще направи профилът и съдържанието недостъпни и невъзможни за взаимодействие
+        close_report: Отбелязване на доклад №%{id} като решен
+        close_reports_html: Означаване на <strong>всички</strong> доклади срещу <strong>@%{acct}</strong> като решени
+        delete_data_html: Изтриване на профила и съдържанието на <strong>@%{acct}</strong> за 30 дни от сега, освен ако междувременно не получи спиране
+        preview_preamble_html: "<strong>@%{acct}</strong> ще получи предупреждение със следното съдържание:"
+        record_strike_html: Запис на предупреждение против <strong>@%{acct}</strong>, за да ви помогне при изострянето на бъдещи нарушения от този акаунт
+        send_email_html: Изпращане на предупредително е-писмо на <strong>@%{acct}</strong>
+        warning_placeholder: Незадължителни допълнителни причини за модераторско действие.
       target_origin: Произход на докладвания акаунт
       title: Доклади
       unassign: Освобождаване
+      unknown_action_msg: 'Незнайно деяние: %{action}'
       unresolved: Неразрешено
       updated_at: Обновено
       view_profile: Преглед на профила
@@ -650,7 +679,7 @@ bg:
         invite_users_description: Позволява на потребителите да канят нови хора в сървъра
         manage_announcements: Управляване на оповестяванията
         manage_announcements_description: Позволява на потребителите да управляват оповестяванията в сървъра
-        manage_appeals: Управление на обжалванията
+        manage_appeals: Управление на жалбите
         manage_appeals_description: Позволява на потребителите да разглеждат обжалвания срещу модераторски действия
         manage_blocks: Управляване на блокиранията
         manage_blocks_description: Позволява на потребителите да блокират доставчици на е-поща и IP адреси
@@ -689,7 +718,7 @@ bg:
       description_html: Дори повечето хора да отбелязват, че са прочели и са съгласни с условията на услугата, обикновено хората не ги четат, докато не се сблъскат с проблем. <strong>Улеснете четенето на правилата за сървъра си, представяйки ги като списък с точки.</strong> Опитайте да се придържате към кратки и прости правила, но не ги разпилявайте в премного точки.
       edit: Промяна на правило
       empty: Още няма определени правила на сървъра.
-      title: Правила на сървъра
+      title: Сървърни правила
     settings:
       about:
         manage_rules: Управление на правилата на сървъра
@@ -711,10 +740,12 @@ bg:
       discovery:
         follow_recommendations: Препоръки за следване
         preamble: За потребители, които са нови и не познават никого в Mastodon, показването на интересно съдържание е ключово. Настройте начина, по който различни функции по откриване на съдържание работят на вашия сървър.
-        profile_directory: Директория на профила
+        profile_directory: Указател на профила
         public_timelines: Публични часови оси
+        publish_discovered_servers: Публикуване на откритите сървъри
+        publish_statistics: Публикуване на статистиката
         title: Откриване
-        trends: Налагащи се
+        trends: Изгряващи
       domain_blocks:
         all: До всеки
         disabled: До никого
@@ -724,9 +755,9 @@ bg:
         title: Регистрации
       registrations_mode:
         modes:
-          approved: Нужно е одобрение за записване
+          approved: Изисква се одобрение за регистриране
           none: Никой не може да се регистрира
-          open: Всеки може да се запише
+          open: Всеки може да се регистрира
       title: Настройки на сървъра
     site_uploads:
       delete: Изтриване на качения файл
@@ -767,6 +798,7 @@ bg:
         suspend: "%{name} преустанови акаунта на %{target}"
       appeal_approved: Обжалвано
       appeal_pending: Чака се обжалването
+      appeal_rejected: Отхвърлено обжалване
     system_checks:
       database_schema_check:
         message_html: Има миграции на базата данни, които чакат да бъдат изпълнени. Моля, изпълнете ги, за да осигурите изправността на приложението
@@ -780,6 +812,12 @@ bg:
         message_html: Не сте определили никакви правила на сървъра.
       sidekiq_process_check:
         message_html: Не работи процес Sidekiq за %{value} опашка/и. Прегледайте настройките си за Sidekiq
+      upload_check_privacy_error:
+        action: Щракнете тук за повече информация
+        message_html: "<strong>Вашият уеб сървър е погрешно конфигуриран. Поверителността на потребителите ви е изложена на риск.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Щракнете тук за повече информация
+        message_html: "<strong>Вашето съхранение на предмети е погрешно конфигурирано. Поверителността на потребителите ви е изложена на риск.</strong>"
     tags:
       review: Преглед на състояние
       updated_msg: Успешно осъвременени настройки на хаштага
@@ -791,29 +829,30 @@ bg:
       links:
         allow: Позволяване на връзка
         allow_provider: Позволяване на публикуващия
-        description_html: Това са линкове, които в момента са често споделяни от акаунти, чиито публикации вашият сървър вижда. Може да помогне на вашите потребители да разберат какво се случва по света. Никой линк няма да се покаже публично, докато не одобрите автора. Можете също и да одобрявате или забранявате отделни линкове.
+        description_html: Това са връзки, които в момента са много пъти споделяни от акаунти, чиито публикации сървърът ви вижда. Може да помогне на потребителите ви да разберат какво се случва по света. Никоя връзка няма да се показва публично, докато не одобрите публикуващия. Може още и да одобрявате или отхвърляте отделни връзки.
         disallow: Забранявам връзката
         disallow_provider: Забраняване на публикуващия
-        no_link_selected: Няма промяна, тъй като няма избрани линкове
+        no_link_selected: Няма променени връзки, тъй като нито една не е била избрана
         publishers:
           no_publisher_selected: Няма промяна, тъй като няма избрани публикуващи
         shared_by_over_week:
           one: Споделено от един човек през последната седмица
           other: Споделено от %{count} души през последната седмица
-        title: Нашумели линкове
+        title: Изгряващи линкове
         usage_comparison: Споделено %{today} пъти днес, в сравнение с %{yesterday} пъти вчера
+      not_allowed_to_trend: Не е позволено да са изгряващи
       only_allowed: Само позволените
-      pending_review: В очакване на преглед
+      pending_review: Чака се преглед
       preview_card_providers:
-        allowed: Линкове от този публикуващ може да са нашумели
+        allowed: Линкове от този публикуващ може да са изгряващи
         description_html: Това са домейни, линковете от които са често споделяни на вашия сървър. Линковете няма да се показват в нашумели, докато домейнът на линка не бъде одобрен. Вашите одобрения (забрани) влияят и на поддомейни.
-        rejected: Линкове от този автор няма да се включат в нашумели
+        rejected: Връзки от този публикуващ няма да са изгряващи
         title: Публикуващи
       rejected: Отхвърлено
       statuses:
         allow: Позволяване на публикацията
         allow_account: Позволяване на автора
-        description_html: Това са публикациите, за които вашият сървър знае, че в момента са често споделяни или маркирани като любими. Биха помогнали на вашите нови и завръщащи се потребители да открият повече хора, които да последват. Никоя от публикациите няма да бъде показана публично, докато не одобрите автора и докато авторът не позволи акаунтът му да бъде предложен. Можете също да позволявате и забранявате отделни публикации.
+        description_html: Има публикации, за които сървърът ви знае, че в момента са често споделяни или означавани като любими. Биха помогнали на вашите нови и завръщащи се потребители да открият повече хора за последване. Никоя от публикациите няма да бъде показана публично, докато не одобрите автора и докато авторът не позволи акаунтът му да бъде предлган на другите. Може още да позволявате или отхвърляте отделни публикации.
         disallow: Забраняване на публикацията
         disallow_account: Забрана на автора
         no_status_selected: Няма промяна, тъй като няма избрана нашумяла публикация
@@ -830,14 +869,14 @@ bg:
           tag_servers_dimension: Водещи сървъри
           tag_servers_measure: различни сървъри
           tag_uses_measure: обща употреба
-        description_html: Това са хаштагове, фигуриращи в момента в много публикации, които вашият сървър вижда. Помага на вашите потребители да виждат за какво говорят другите в момента. Хаштаговете няма да се покажат публично, докато не ги одобрите.
+        description_html: Има хаштагове, появяващи се в момента в много публикации, които сървърът ви вижда. Помага на вашите потребители да виждат за какво говорят другите в момента. Хаштаговете не се показват публично, докато не ги одобрите.
         listable: Може да бъде предложено
         no_tag_selected: Няма промяна, тъй като няма избран таг
         not_listable: Няма да бъде препоръчан
         not_trendable: Няма да се появи под налагащи се
-        not_usable: Не може да се използва
+        not_usable: Не може да се употребява
         peaked_on_and_decaying: Връх на актуалността на %{date}, сега е в спад
-        title: Налагащи се хаштагове
+        title: Изгряващи хаштагове
         trendable: Може да се появи под налагащи се
         trending_rank: 'Налагащи се #%{rank}'
         usable: Може да се употребява
@@ -845,7 +884,7 @@ bg:
         used_by_over_week:
           one: Употребено от един човек през последната седмица
           other: Използвано от %{count} души през последната седмица
-      title: Налагащи се
+      title: Изгряващи
       trending: Изгряващи
     warning_presets:
       add_new: Добавяне на ново
@@ -902,7 +941,7 @@ bg:
       new_trending_tags:
         no_approved_tags: Сега няма одобрени налагащи се хаштагове.
         requirements: 'Всеки от тези кандидати може да надмине #%{rank} одобрен актуален хаштаг, който в момента е #%{lowest_tag_name} с резултат %{lowest_tag_score}.'
-        title: Нашумели хаштагове
+        title: Налагащи се хаштагове
       subject: Нови нашумели, готови за преглед в %{instance}
   aliases:
     add_new: Създаване на псевдоним
@@ -933,6 +972,7 @@ bg:
   applications:
     created: Успешно създадено приложение
     destroyed: Успешно изтрито приложение
+    logout: Излизане
     regenerate_token: Регенериране на кода за достъп
     token_regenerated: Успешно генериране на код за достъп
     warning: Бъдете внимателни с тези данни. Никога не ги споделяйте с никого!
@@ -940,6 +980,8 @@ bg:
   auth:
     apply_for_account: Заявка за акаунт
     change_password: Парола
+    confirmations:
+      wrong_email_hint: Ако този адрес на е-поща не е правилен, то може да го промените в настройки на акаунта.
     delete_account: Изтриване на акаунта
     delete_account_html: Ако желаете да изтриете акаунта си, може <a href="%{path}">да сторите това тук</a>. Ще ви се поиска потвърждение.
     description:
@@ -967,6 +1009,8 @@ bg:
     resend_confirmation: Изпрати отново инструкции за потвърждение
     reset_password: Нулиране на паролата
     rules:
+      accept: Приемам
+      back: Назад
       preamble: Тези са зададени и наложени от модераторите на %{domain}.
       title: Някои основни правила.
     security: Сигурност
@@ -987,7 +1031,7 @@ bg:
       functional: Вашият акаунт е в изправност.
       pending: Вашето приложение чака преглед от нашия екип. Това може да отнеме време. Ще получите имейл, ако приложението е одобрено.
       redirecting_to: Вашият акаунт е бездеен, защото сега се пренасочва към %{acct}.
-      view_strikes: Преглед на предишните нарушения за вашия акаунт
+      view_strikes: Преглед на предишните предупреждения против акаунта ви
     too_fast: Образецът подаден пребързо, опитайте пак.
     use_security_key: Употреба на ключ за сигурност
   authorize_follow:
@@ -1050,7 +1094,7 @@ bg:
     strikes:
       action_taken: Предприето действие
       appeal: Обжалване
-      appeal_approved: Това нарушение беше успешно обжалвано и е вече невалидно
+      appeal_approved: Това предупреждение беше успешно обжалвано и е вече невалидно
       appeal_rejected: Обжалването е отхвърлено
       appeal_submitted_at: Подадено обжалване
       appealed_msg: Вашето обжалване е отхвърлено. Ако е одобрено, то ще бъдете известени.
@@ -1158,8 +1202,6 @@ bg:
       index:
         hint: Този филтър се прилага за избор на отделни публикации, независимо от други критерии. Може да добавите още публикации в този филтър от уебинтерфейса.
         title: Филтрирани публикации
-  footer:
-    trending_now: Налагащи се сега
   generic:
     all: Всичко
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ bg:
     validation_errors:
       one: Нещо още не е напълно наред! Прегледайте грешката долу
       other: Нещо още не е напълно наред! Прегледайте %{count} грешки долу
-  html_validator:
-    invalid_markup: 'съдържа невалидно HTML маркиране: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Невалиден файл CSV. Грешка: %{error}'
@@ -1367,7 +1407,11 @@ bg:
       unrecognized_emoji: не е разпознато емоджи
   relationships:
     activity: Дейност на акаунта
+    confirm_follow_selected_followers: Наистина ли искате да последвате избраните последователи?
+    confirm_remove_selected_followers: Наистина ли искате да премахнете избраните последователи?
+    confirm_remove_selected_follows: Наистина ли искате да премахнете избраните последвания?
     dormant: Неактивен
+    follow_failure: Не може да се последват някои от избраните акаунти.
     follow_selected_followers: Последване на избраните последователи
     followers: Последователи
     following: Последвани
@@ -1407,6 +1451,7 @@ bg:
       electron: Electron
       firefox: Firefox
       generic: Неизвестен браузър
+      huawei_browser: Браузър Хуауей
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Браузър Nokia S40 Ovi
@@ -1416,6 +1461,7 @@ bg:
       qq: Браузър QQ
       safari: Safari
       uc_browser: Браузър UC
+      unknown_browser: Неизвестен браузър
       weibo: Weibo
     current_session: Текуща сесия
     description: "%{browser} на %{platform}"
@@ -1428,9 +1474,10 @@ bg:
       chrome_os: ChromeOS
       firefox_os: Оп. сист. Firefox
       ios: iOS
+      kai_os: KaiOS
       linux: Линукс
       mac: macOS
-      other: неизвестна платформа
+      unknown_platform: Неизвестна платформа
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1458,7 +1505,7 @@ bg:
     profile: Профил
     relationships: Последвания и последователи
     statuses_cleanup: Автоматично изтриване на публикации
-    strikes: Нарушения
+    strikes: Модериране на предупреждения
     two_factor_authentication: Двустепенно удостоверяване
     webauthn_authentication: Ключове за сигурност
   statuses:
@@ -1543,7 +1590,7 @@ bg:
       '7889238': 3 месеца
     min_age_label: Възрастов праг
     min_favs: Запазване на публикации, маркирани като любими поне
-    min_favs_hint: Не се изтриват ваши публикации, маркирани като любими поне толкова пъти. Оставете празно, за да изтриете публикациите независимо от броя пъти маркирани като любими
+    min_favs_hint: Не се изтриват никоя от публикациите ви, маркирани като любими поне толкова пъти. Оставете празно, за да изтриете публикациите независимо от броя маркирания като любими
     min_reblogs: Запазване на публикации с поне толкова споделяния
     min_reblogs_hint: Не се изтриват ваши публикации, споделени поне толкова пъти. Оставете празно, за да изтриете публикациите независимо от техния брой споделяния
   stream_entries:
@@ -1552,7 +1599,7 @@ bg:
     sensitive_content: Деликатно съдържание
   strikes:
     errors:
-      too_late: Късно е за обжалване на това нарушение
+      too_late: Твърде късно е за обжалване на това предупреждение
   tags:
     does_not_match_previous_name: не съвпада с предишното име
   themes:
@@ -1643,12 +1690,13 @@ bg:
       title: Добре дошли на борда, %{name}!
   users:
     follow_limit_reached: Не може да последвате повече от %{limit} души
+    go_to_sso_account_settings: Отидете при настройките на акаунта на своя доставчик на идентичност
     invalid_otp_token: Невалиден код
     otp_lost_help_html: Ако загубите достъп до двете, то може да се свържете с %{email}
     seamless_external_login: Влезли сте чрез външна услуга, така че настройките за парола и имейл не са налични.
     signed_in_as: 'Влезли като:'
   verification:
-    explanation_html: 'Можете да <strong>удостоверите самоличността си като собственик на линковете в метаданните на своя профил</strong>. За целта сайтът, към който води линк от метаданните, трябва да съдържа линк обратно към вашия профил в Mastodon. Линкът от сайта <strong>трябва</strong> да съдържа атрибут <code>rel="me"</code>. Текстовото съдържание на линка няма значение. Ето пример:'
+    explanation_html: 'Може да <strong>потвърдите себе си като собственик на връзките в метаданните на профила си</strong>. За целта свързаният уебсайт трябва да съдържа обратна връзка към профилa ви в Mastodon. След добавянето на връзката, може да се наложи да се върнете тук и да запазите пак профила си, за да влезе в сила потвърждаването. Връзката обратно <strong>трябва</strong> да има атрибут <code>rel="me"</code>. Текстовото съдържание на връзката няма значение. Ето пример:'
     verification: Проверка
   webauthn_credentials:
     add: Добавяне на нов ключ за сигурност
diff --git a/config/locales/bn.yml b/config/locales/bn.yml
index 5a40fad8f..1f2550d29 100644
--- a/config/locales/bn.yml
+++ b/config/locales/bn.yml
@@ -232,9 +232,7 @@ bn:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
   verification:
     verification: সত্যতা নির্ধারণ
diff --git a/config/locales/br.yml b/config/locales/br.yml
index c53fac24f..ee465368a 100644
--- a/config/locales/br.yml
+++ b/config/locales/br.yml
@@ -342,9 +342,7 @@ br:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
   exports:
     archive_takeout:
@@ -471,7 +469,6 @@ br:
       ios: iOS
       linux: Linux
       mac: macOS
-      other: savenn dianav
       windows: Windows
     revoke: Dizober
   settings:
@@ -538,7 +535,6 @@ br:
   users:
     signed_in_as: 'Aet-tre evel:'
   verification:
-    explanation_html: 'Gallout a rit <strong>gwiriañ c''hwi a zo perc''henn. ez liammoù metadata ho profil</strong>. Ret eo d''al lec''hienn web staget enderc''hel ul liamm evit mont d''ho profil Mastodon. <strong>Ret eo<strong> d''al liamm-se enderc''hel un doarenn <code>rel="me"</code>. Ne ra forzh an destenn a zo e-barzh al liamm. Setu ur skouer:'
     verification: Amprouadur
   webauthn_credentials:
     add: Ouzphennañ un alc'hwez surentez nevez
diff --git a/config/locales/bs.yml b/config/locales/bs.yml
index adb1ccc2a..2709636c9 100644
--- a/config/locales/bs.yml
+++ b/config/locales/bs.yml
@@ -6,7 +6,5 @@ bs:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index 4bfb82f0a..0d84c2c90 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -31,7 +31,7 @@ ca:
       created_msg: La nota de moderació s'ha creat correctament!
       destroyed_msg: Nota de moderació destruïda amb èxit!
     accounts:
-      add_email_domain_block: Bloquejar el domini de l'adreça de correu electrònic
+      add_email_domain_block: Bloca el domini del correu
       approve: Aprova
       approved_msg: L’aplicació del registre de %{username} s’ha aprovat amb èxit
       are_you_sure: N'estàs segur?
@@ -91,6 +91,7 @@ ca:
       moderation:
         active: Actiu
         all: Tot
+        disabled: Desactivat
         pending: Pendent
         silenced: Limitat
         suspended: Suspès
@@ -133,6 +134,7 @@ ca:
       search: Cerca
       search_same_email_domain: Altres usuaris amb el mateix domini de correu
       search_same_ip: Altres usuaris amb la mateixa IP
+      security: Seguretat
       security_measures:
         only_password: Només contrasenya
         password_and_2fa: Contrasenya i 2FA
@@ -180,7 +182,7 @@ ca:
         create_custom_emoji: Crea un emoji personalitzat
         create_domain_allow: Crea un domini permès
         create_domain_block: Crea un bloqueig de domini
-        create_email_domain_block: Crea un bloqueig de domini d'adreça de correu
+        create_email_domain_block: Crea un bloqueig de domini de correu
         create_ip_block: Crear regla IP
         create_unavailable_domain: Crea un domini no disponible
         create_user_role: Crea Rol
@@ -188,9 +190,9 @@ ca:
         destroy_announcement: Esborra l'anunci
         destroy_canonical_email_block: Esborra el bloqueig de correu electrònic
         destroy_custom_emoji: Esborra l'emoji personalitzat
-        destroy_domain_allow: Esborra el domini permès
+        destroy_domain_allow: Esborra el permís del domini
         destroy_domain_block: Esborra el bloqueig de domini
-        destroy_email_domain_block: Esborra el bloqueig de domini de l'adreça de correu
+        destroy_email_domain_block: Esborra el bloqueig de domini de correu
         destroy_instance: Purga Domini
         destroy_ip_block: Eliminar regla IP
         destroy_status: Elimina la publicació
@@ -211,11 +213,11 @@ ca:
         reopen_report: Reobre l'informe
         resend_user: Torna a enviar el correu de confirmació
         reset_password_user: Restableix la contrasenya
-        resolve_report: Resolt l'informe
+        resolve_report: Resol l'informe
         sensitive_account: Marcar els mèdia en el teu compte com a sensibles
         silence_account: Silencia el compte
         suspend_account: Suspèn el compte
-        unassigned_report: Des-assigna l'informe
+        unassigned_report: Desassigna l'informe
         unblock_email_account: Desbloqueja l'adreça de correu
         unsensitive_account: Desmarcar els mèdia en el teu compte com a sensibles
         unsilence_account: Desfés el silenci del compte
@@ -229,7 +231,7 @@ ca:
       actions:
         approve_appeal_html: "%{name} ha aprovat l'apel·lació a la decisió de moderació de %{target}"
         approve_user_html: "%{name} ha aprovat el registre de %{target}"
-        assigned_to_self_report_html: "%{name} han assignat l'informe %{target} a ells mateixos"
+        assigned_to_self_report_html: "%{name} s'han assignat l'informe %{target} a ells mateixos"
         change_email_user_html: "%{name} ha canviat l'adreça de correu electrònic del usuari %{target}"
         change_role_user_html: "%{name} ha canviat el rol de %{target}"
         confirm_user_html: "%{name} ha confirmat l'adreça de correu electrònic de l'usuari %{target}"
@@ -372,15 +374,15 @@ ca:
         empty: No s'ha trobat cap apel·lació.
         title: Apel·lacions
     domain_allows:
-      add_new: Dominis autoritzats
-      created_msg: El domini ha estat correctament autoritzat
-      destroyed_msg: S'ha esborrat el domini de la llista blanca
+      add_new: Permet la federació amb el domini
+      created_msg: El domini ha estat autoritzat per a la federació
+      destroyed_msg: El domini ha estat desautoritzat de la federació
       export: Exporta
       import: Importa
-      undo: Treure de la llista blanca
+      undo: No permetis la federació amb el domini
     domain_blocks:
-      add_new: Afegir nou bloqueig de domini
-      created_msg: El bloqueig de domini ara s'està processant
+      add_new: Afegeix nou bloqueig de domini
+      created_msg: S'està processant el bloqueig de domini
       destroyed_msg: El bloqueig de domini s'ha desfet
       domain: Domini
       edit: Editar el bloqueig del domini
@@ -396,7 +398,7 @@ ca:
           noop: Cap
           silence: Limitar
           suspend: Suspensió
-        title: Bloqueig de domini nou
+        title: Nou bloqueig de domini
       no_domain_block_selected: No s'ha canviat cap bloqueig de domini perquè no se n'ha seleccionat cap
       not_permitted: No tens permís per a realitzar aquesta acció
       obfuscate: Oculta el nom del domini
@@ -427,6 +429,7 @@ ca:
         resolve: Resol domini
         title: Nova adreça de correu en la llista negra
       no_email_domain_block_selected: No s'han canviat els bloquejos de domini perquè cap s'ha seleccionat
+      not_permitted: No permés
       resolved_dns_records_hint_html: El nom del domini resol als següents dominis MX, els quals son els responsables finals per a acceptar els correus. Bloquejar un domini MX bloquejarà els registres des de qualsevol adreça de correu que utilitzi el mateix domini MX, encara que el nom visible del domini sigui diferent. <strong>Ves amb compte no bloquegis els grans proveïdors de correu electrònic.</strong>
       resolved_through_html: Resolt mitjançant %{domain}
       title: Llista negra de correus electrònics
@@ -441,6 +444,7 @@ ca:
         private_comment_description_html: 'Per a ajudar-te a fer un seguiment d''on provenen els bloquejos importats, es crearan amb el següent comentari privat: <q>%{comment}</q>'
         private_comment_template: Importar des de %{source} el %{date}
         title: Importa dominis bloquejats
+      invalid_domain_block: 'Un o més dominis blocats s''han omés degut al següent error(s): %{error}'
       new:
         title: Importa dominis bloquejats
       no_file: No s'ha seleccionat cap fitxer
@@ -472,6 +476,7 @@ ca:
       content_policies:
         comment: Nota interna
         description_html: Pots definir polítiques de contingut que seran aplicades a tots els comptes d'aquest domini i a qualsevol dels seus subdominis.
+        limited_federation_mode_description_html: Pots triar si permets la federació amb aquest domini.
         policies:
           reject_media: Rebutja mèdia
           reject_reports: Rebutja informes
@@ -573,22 +578,26 @@ ca:
       actions:
         delete_description_html: Els tuts reportats seran eliminats i un cop serà gravat per a ajudar-te a escalar en futures infraccions des del mateix compte.
         mark_as_sensitive_description_html: Els mèdia dels tuts reportats seran marcats com a sensibles i una acció serà gravada per ajudar a escalar en futures infraccions del mateix compte.
-        other_description_html: Veu més opcions controlant el comportament del compte i personalitza la comunicació al compte reportat.
-        resolve_description_html: No serà presa cap acció contra el compte reportat, cap cop serà gravat i l'informe es tancarà.
-        silence_description_html: El perfil serà visible només per aquells que ja el seguien o en cerca manual, limitant severament el seu alcanç. Sempre pot ser revertit.
-        suspend_description_html: El perfil i tot el seu contingut esdevindrà inaccessible fins que sigui eventualment esborrat. Interactuar amb el compte serà impossible. Reversible dins de 30 dies.
-      actions_description_html: Decideix quina acció a prendre per a resoldre aquest informe. Si prens un acció punitiva contra el compte reportat, se li enviarà una notificació per correu electrònic, excepte quan la categoria <strong>Spam</strong> és seleccionada.
+        other_description_html: Veu més opcions controlant el comportament del compte i personalitza la comunicació al compte denunciat.
+        resolve_description_html: No serà presa cap acció contra el compte denunciat, no se'n registrarà res i l'informe es tancarà.
+        silence_description_html: El compte només serà visible a qui ja el seguia o l'ha cercat manualment, limitant-ne fortament l'abast. Sempre es pot revertir. Es tancaran tots els informes contra aquest compte.
+        suspend_description_html: Aquest compte i tots els seus continguts seran inaccessibles i finalment eliminats, i interaccionar amb ell no serà possible. Reversible en 30 dies. Tanca tots els informes contra aquest compte.
+      actions_description_html: Decideix quina acció a prendre per a resoldre aquest informe. Si prens un acció punitiva contra el compte denunciat, se li enviarà una notificació per correu electrònic, excepte quan se selecciona la categoria <strong>Spam</strong>.
+      actions_description_remote_html: Decideix quina acció prendre per a resoldre aquest informe. Això només afectarà com <strong>el teu</strong> servidor es comunica amb aquest compte remot i en gestiona el contingut.
       add_to_report: Afegir més al informe
-      are_you_sure: N'estàs segur?
-      assign_to_self: Assignar-me
+      are_you_sure: Segur?
+      assign_to_self: Assigna'm
       assigned: Moderador assignat
-      by_target_domain: Domini del compte reportat
+      by_target_domain: Domini del compte denunciat
+      cancel: Cancel·la
       category: Categoria
-      category_description_html: El motiu pel qual aquest compte o contingut ha estat reportat serà citat en la comunicació amb el compte reportat
+      category_description_html: El motiu pel qual aquest compte o contingut ha estat denunciat se citarà en la comunicació amb el compte denunciat
       comment:
         none: Cap
       comment_description_html: 'Per a donar més informació, %{name} ha escrit:'
-      created_at: Reportat
+      confirm: Confirma
+      confirm_action: Confirma l'acció de moderació contra @%{acct}
+      created_at: Denunciat
       delete_and_resolve: Elimina les publicacions
       forwarded: Reenviat
       forwarded_to: Reenviat a %{domain}
@@ -597,28 +606,48 @@ ca:
       mark_as_unresolved: Marcar com a sense resoldre
       no_one_assigned: Ningú
       notes:
-        create: Afegir una nota
+        create: Afegeix una nota
         create_and_resolve: Resol amb una nota
         create_and_unresolve: Reobre amb una nota
         delete: Elimina
         placeholder: Descriu les accions que s'han pres o qualsevol altra actualització relacionada…
         title: Notes
-      notes_description_html: Veu i deixa notes als altres moderadors i a tu mateix
-      quick_actions_description_html: 'Pren una acció ràpida o desplaça''t avall per a veure el contingut reportat:'
+      notes_description_html: Mira i deixa notes als altres moderadors i a tu mateix
+      processed_msg: 'L''informe #%{id} ha estat processat amb èxit'
+      quick_actions_description_html: 'Pren una acció ràpida o desplaça''t avall per a veure el contingut denunciat:'
       remote_user_placeholder: l'usuari remot des de %{instance}
       reopen: Reobre l'informe
       report: 'Informe #%{id}'
-      reported_account: Compte reportat
-      reported_by: Reportat per
+      reported_account: Compte denunciat
+      reported_by: Denunciat per
       resolved: Resolt
-      resolved_msg: Informe resolt amb èxit!
+      resolved_msg: Informe resolt correctament!
       skip_to_actions: Salta a les accions
       status: Estat
       statuses: Contingut reportat
-      statuses_description_html: El contingut ofensiu serà citat en comunicació amb el compte reportat
-      target_origin: Origen del compte reportat
+      statuses_description_html: El contingut ofensiu se citarà en comunicació amb el compte denunciat
+      summary:
+        action_preambles:
+          delete_html: 'Estàs a punt d''<strong>esborrar</strong> alguns dels tuts de <strong>@%{acct}</strong>. Això provocarà:'
+          mark_as_sensitive_html: 'Estàs a punt de <strong>marcar</strong> alguns dels tuts de <strong>@%{acct}</strong> com a <strong>sensibles</strong>. Això provocarà:'
+          silence_html: 'Estàs a punt de <strong>limitar</strong> el compte de <strong>@%{acct}</strong>. Això provocarà:'
+          suspend_html: 'Estàs a punt de <strong>suspendre</strong> el compte de <strong>@%{acct}</strong>. Això provocarà:'
+        actions:
+          delete_html: Esborra els tuts ofensius
+          mark_as_sensitive_html: Marca el contingut multimèdia dels tuts com a sensibles
+          silence_html: Limita severament l'abast de <strong>@%{acct}</strong> fent el seus perfils i continguts només visibles per la gent que ja els estan seguint o que cerquin manualment els seus perfils
+          suspend_html: Suspèn <strong>@%{acct}</strong>, fent els seus perfils i continguts inaccessibles i impossibles d'interactuar
+        close_report: 'Marca l''informe #%{id} com a resolt'
+        close_reports_html: Marca <strong>tots</strong> els informes contra <strong>@%{acct}</strong> com a resolts
+        delete_data_html: Esborra el perfil de <strong>@%{acct}</strong> i els seus continguts dins de 30 dies des d'ara a no ser que es desactivi la suspensió abans
+        preview_preamble_html: "<strong>@%{acct}</strong> rebrà un avís amb el contingut següent:"
+        record_strike_html: Registra una acció contra <strong>@%{acct}</strong> per ajudar a escalar-ho en futures violacions des d'aquest compte
+        send_email_html: Envia un avís per correu electrònic a <strong>@%{acct}</strong>
+        warning_placeholder: Opcional raó adicional d'aquesta acció de moderació.
+      target_origin: Origen del compte denunciat
       title: Informes
       unassign: Treu l'assignació
+      unknown_action_msg: 'Acció desconeguda: %{action}'
       unresolved: No resolt
       updated_at: Actualitzat
       view_profile: Veure perfil
@@ -713,6 +742,8 @@ ca:
         preamble: L'aparició de contingut interessant és fonamental per atraure els nous usuaris que podrien no saber res de Mastodon. Controla com funcionen diverses opcions de descobriment en el teu servidor.
         profile_directory: Directori de perfils
         public_timelines: Línies de temps públiques
+        publish_discovered_servers: Publica els servidors descoberts
+        publish_statistics: Publica estadístiques
         title: Descobriment
         trends: Tendències
       domain_blocks:
@@ -735,10 +766,10 @@ ca:
       account: Autor
       application: Aplicació
       back_to_account: Torna a la pàgina del compte
-      back_to_report: Torna a la pàgina del informe
+      back_to_report: Torna a la pàgina de l'informe
       batch:
-        remove_from_report: Treu del informe
-        report: Informe
+        remove_from_report: Treu de l'informe
+        report: Denuncia
       deleted: Eliminada
       favourites: Favorits
       history: Històric de versions
@@ -767,6 +798,7 @@ ca:
         suspend: "%{name} ha suspès el compte de %{target}"
       appeal_approved: Apel·lat
       appeal_pending: Apel·lació pendent
+      appeal_rejected: Apel·lació rebutjada
     system_checks:
       database_schema_check:
         message_html: Hi ha pendents migracions de la base de dades. Si us plau executa-les per a assegurar que l'aplicació es comporta com s'espera
@@ -780,6 +812,12 @@ ca:
         message_html: No has definit cap norma del servidor.
       sidekiq_process_check:
         message_html: No hi ha cap procés de Sidekiq executant-se per a la cua (o cues) de %{value}. Si us plau revisa la teva configuració de Sidekiq
+      upload_check_privacy_error:
+        action: Consulta aquí per a més informació
+        message_html: "<strong>El teu servidor no està ben configurat. La privacitat dels teus usuaris està en risc.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Consulta aquí per a més informació
+        message_html: "<strong>El teu emagatzamatge d'objectes no està ben configurat. La privacitat dels teus usuaris està en risc.</strong>"
     tags:
       review: Revisar l'estat
       updated_msg: Ajustaments d'etiquetes actualitzats amb èxit
@@ -802,6 +840,7 @@ ca:
           other: Compartit per %{count} persones en la darrera setmana
         title: Enllaços en tendència
         usage_comparison: Compartit %{today} vegades avui, comparat amb %{yesterday} d'ahir
+      not_allowed_to_trend: No es permet la tendència
       only_allowed: Només permesos
       pending_review: Revisió pendent
       preview_card_providers:
@@ -891,7 +930,7 @@ ca:
       subject: Nou compte per a revisar a %{instance} (%{username})
     new_report:
       body: "%{reporter} ha informat de %{target}"
-      body_remote: Algú des de el domini %{domain} ha informat sobre %{target}
+      body_remote: Algú des del domini %{domain} ha informat sobre %{target}
       subject: Informe nou per a %{instance} (#%{id})
     new_trends:
       body: 'Els següents elements necessiten una revisió abans de que puguin ser mostrats públicament:'
@@ -933,6 +972,7 @@ ca:
   applications:
     created: L'aplicació s'ha creat correctament
     destroyed: L'aplicació s'ha suprimit correctament
+    logout: Surt
     regenerate_token: Torna a generar l'identificador d'accés
     token_regenerated: L'identificador d'accés s'ha generat correctament
     warning: Aneu amb compte amb aquestes dades. No les compartiu mai amb ningú!
@@ -940,6 +980,8 @@ ca:
   auth:
     apply_for_account: Sol·licitar un compte
     change_password: Contrasenya
+    confirmations:
+      wrong_email_hint: Si aquesta adreça de correu electrònic no és correcte, pots canviar-la en els ajustos del compte.
     delete_account: Elimina el compte
     delete_account_html: Si vols suprimir el compte pots <a href="%{path}">fer-ho aquí</a>. Se't demanarà confirmació.
     description:
@@ -967,6 +1009,8 @@ ca:
     resend_confirmation: Torna a enviar el correu de confirmació
     reset_password: Restableix la contrasenya
     rules:
+      accept: Accepta
+      back: Enrere
       preamble: Aquestes regles estan establertes i aplicades per els moderadors de %{domain}.
       title: Algunes regles bàsiques.
     security: Seguretat
@@ -1112,7 +1156,7 @@ ca:
     mutes: Persones silenciades
     storage: Emmagatzematge
   featured_tags:
-    add_new: Afegir nova etiqueta
+    add_new: Afegeix-ne una de nova
     errors:
       limit: Ja has mostrat la quantitat màxima d'etiquetes
     hint_html: "<strong>Què son les etiquetes destacades?</strong> Es mostren de manera destacada en el teu perfil públic i permeten a les persones navegar per els teus tuts gràcies a aquestes etiquetes. Són una gran eina per fer un seguiment de treballs creatius o de projectes a llarg termini."
@@ -1158,8 +1202,6 @@ ca:
       index:
         hint: Aquest filtre aplica als tuts seleccionats independentment d'altres criteris. Pots afegir més tuts a aquest filtre des de la interfície Web.
         title: Tuts filtrats
-  footer:
-    trending_now: En tendència
   generic:
     all: Tot
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ ca:
     validation_errors:
       one: Alguna cosa no va bé! Si us plau, revisa l'error
       other: Alguna cosa no va bé! Si us plau, revisa %{count} errors més a baix
-  html_validator:
-    invalid_markup: 'conté HTML markup no vàlid: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Fitxer CSV invàlid. Error: %{error}'
@@ -1367,7 +1407,11 @@ ca:
       unrecognized_emoji: no és un emoji reconegut
   relationships:
     activity: Activitat del compte
+    confirm_follow_selected_followers: Segur que vols seguir els seguidors seleccionats?
+    confirm_remove_selected_followers: Segur que vols esborrar els seguidors seleccionats?
+    confirm_remove_selected_follows: Segur que vols esborrar els seguits seleccionats?
     dormant: Inactiu
+    follow_failure: No es poden seguir alguns dels comptes seleccionats.
     follow_selected_followers: Segueix als seguidors seleccionats
     followers: Seguidors
     following: Seguint
@@ -1407,15 +1451,17 @@ ca:
       electron: Electron
       firefox: Firefox
       generic: Navegador desconegut
+      huawei_browser: Navegador Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
-      nokia: Nokia S40 Ovi Browser
+      nokia: Navegador Nokia S40 Ovi
       opera: Opera
       otter: Otter
       phantom_js: PhantomJS
-      qq: QQ Browser
+      qq: Navegador QQ
       safari: Safari
       uc_browser: Navegador UC
+      unknown_browser: Navegador Desconegut
       weibo: Weibo
     current_session: Sessió actual
     description: "%{browser} de %{platform}"
@@ -1428,9 +1474,10 @@ ca:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: plataforma desconeguda
+      unknown_platform: Plataforma desconeguda
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1543,7 +1590,7 @@ ca:
       '7889238': 3 mesos
     min_age_label: Antiguitat
     min_favs: Mantenir els tuts afavorits més de
-    min_favs_hint: No suprimeix cap dels teus tuts que hagin rebut més d'aquesta quantitat de favorits. Deixa-ho en blanc per a suprimir tuts independentment del nombre de favorits que tinguin
+    min_favs_hint: No suprimeix cap dels teus tuts que hagin rebut més d'aquesta quantitat de favorits. Deixa-ho en blanc per a suprimir tuts independentment del nombre de favorits
     min_reblogs: Mantenir les publicacions impulsades més de
     min_reblogs_hint: No suprimeix cap de les teves publicacions que s'hagin impulsat més que aquest nombre de vegades. Deixa-ho en blanc per a suprimir les publicacions independentment del nombre d'impulsos que tinguin.
   stream_entries:
@@ -1643,12 +1690,13 @@ ca:
       title: Benvingut a bord, %{name}!
   users:
     follow_limit_reached: No pots seguir més de %{limit} persones
+    go_to_sso_account_settings: Ves a la configuració del compte del teu proveïdor d'identitat
     invalid_otp_token: El codi de dos factors no és correcte
     otp_lost_help_html: Si has perdut l'accés a tots dos pots contactar per %{email}
     seamless_external_login: Has iniciat sessió via un servei extern per tant els ajustos de contrasenya i correu electrònic no estan disponibles.
     signed_in_as: 'Sessió iniciada com a:'
   verification:
-    explanation_html: 'Pots <strong>verificar-te com a propietari dels enllaços a les metadades del teu perfil</strong>. Per això, el lloc web enllaçat ha de contenir un enllaç al teu perfil de Mastodon. El vincle <strong>ha de</strong> tenir l''atribut <code>rel="me"</code>. El contingut del text de l''enllaç no importa. Aquí tens un exemple:'
+    explanation_html: 'Pots <strong>verificar-te com a propietari dels enllaços a les metadades del teu perfil</strong>. Per això, el lloc web enllaçat ha de contenir un enllaç al teu perfil de Mastodon. Després d''afegir l''enllaç, podries necessitar tornar aquí a desar el teu perfil per a fer efectiva la verificació. El vincle <strong>ha de</strong> tenir l''atribut <code>rel="me"</code>. El contingut del text de l''enllaç no importa. Aquí tens un exemple:'
     verification: Verificació
   webauthn_credentials:
     add: Afegir nova clau de seguretat
diff --git a/config/locales/ckb.yml b/config/locales/ckb.yml
index 0c29bb437..1970761e1 100644
--- a/config/locales/ckb.yml
+++ b/config/locales/ckb.yml
@@ -466,8 +466,6 @@ ckb:
         mark_as_sensitive_description_html: میدیاکان لە پۆستە ڕاپۆرتکراوەکاندا وەک هەستیار نیشانە دەکرێن و مانگرتنێک تۆمار دەکرێت بۆ ئەوەی یارمەتیت بدات لەسەر پێشێلکارییەکانی داهاتوو لەلایەن هەمان ئەکاونتەوە زیاد بکەیت.
         other_description_html: بژاردەی زیاتر ببینە بۆ کۆنترۆڵکردنی هەڵسوکەوتی ئەکاونتەکە و خۆکارکردنی پەیوەندی بۆ ئەژمێری ڕاپۆرتکراو.
         resolve_description_html: هیچ ئیجرائاتێک لە دژی ئەو حسابە ڕاپۆرت کراوە ناگیرێتەبەر، هیچ مانگرتنێک تۆمار ناکرێت، ڕاپۆرتەکەش دادەخرێت.
-        silence_description_html: پرۆفایلەکە تەنها بۆ ئەو کەسانە دیار دەبێت کە پێشتر فۆڵۆوی دەکەن یان بە دەست سەیری دەکەن، ئەمەش گەیشتنەکەی بە توندی سنووردار دەکات. هەمیشە دەتوانرێت بگەڕێندرێتەوە.
-        suspend_description_html: پرۆفایلەکە و هەموو ناوەڕۆکەکانی دەستڕاگەیشتنیان پێناگات تا لە کۆتاییدا دەسڕدرێتەوە. کارلێککردن لەگەڵ ئەکاونتەکەدا مەحاڵ دەبێت. لە ماوەی ٣٠ ڕۆژدا دەگەڕێتەوە.
       actions_description_html: بڕیار بدە کام رێوشوێن بگیرێتەبەر بۆ چارەسەرکردنی ئەم راپۆرتە. ئەگەر تۆ ڕێوشوێنی سزادان لە دژی ئەژمێری ڕاپۆرتکراو بگریتەبەر، ئاگادارکردنەوەیەکی ئیمەیڵیان بۆ دەنێردرێت، تەنها کاتێک نەبێت کە پۆلی <strong>سپام</strong> هەڵبژێردرابێت.
       add_to_report: زیاتر زیاد بکە بۆ ڕاپۆرت
       are_you_sure: دڵنیای?
@@ -728,8 +726,6 @@ ckb:
     storage: هەمارگەی میدیا
   featured_tags:
     add_new: زیادکردنی نوێ
-    errors:
-      limit: ئێوە ژمارەی بڕی ڕێگەپێدراوەی هاشتاگت هەیە
     hint_html: "<strong> هاشتاگی تایبەت چییە؟</strong> بە شێوەیەکی دیار نیشان دەدرێت لەسەر پرۆفایلی گشتی و ڕێگە بە خەڵک دەدات بۆ گەڕان لە نووسراوە گشتیەکانت بە تایبەتی لەژێر ئەو هاشتاگە. ئامرازێکی زۆر باشن بۆ پاراستنی کاری داهێنەرانە یان پڕۆژەی درێژخایەنی ئێوە."
   filters:
     contexts:
@@ -748,8 +744,6 @@ ckb:
       title: فلتەرەکان
     new:
       title: زیادکردنی فلتەری نوێ
-  footer:
-    trending_now: هەوادارانی ئێستا
   generic:
     all: هەموو
     changes_saved_msg: گۆڕانکاریەکان بە سەرکەوتوویی هەڵگیرا!
@@ -760,8 +754,6 @@ ckb:
     validation_errors:
       one: شتێک هێشتا تەواو ڕاست نیە تکایە چاو بە هەڵەکەی خوارەوە بخشێنەوە
       other: هێشتا تەواو ڕاست نیە تکایە چاو بە هەڵەی %{count} خوارەوە بخشێنەوە
-  html_validator:
-    invalid_markup: 'نیشانەی HTML نادروستی تێدایە: %{error}'
   imports:
     modes:
       merge: یەکخستن
@@ -799,9 +791,6 @@ ckb:
       expires_at: بەسەرچووە
       uses: بەکارهاوردنەکان
     title: بانگهێشتکردنی خەڵک
-  lists:
-    errors:
-      limit: تۆ گەیشتوویتە زۆرترین ڕێژەی لیستەکان
   media_attachments:
     validations:
       images_and_video: ناتوانرێت لەگەڵ ئەو نووسراوانە کە وێنەی لەگەڵە ،ڤیدیۆ بار بکەی
@@ -952,7 +941,6 @@ ckb:
       firefox_os: سیستەمی کارگێڕی فایەرفۆکس
       linux: لینۆکس
       mac: ماک
-      other: سیستەمیکارگێڕی نەناسراو
       windows: ویندۆز
       windows_mobile: ویندۆزموبایل
       windows_phone: ویندۆزفۆن
@@ -1082,7 +1070,6 @@ ckb:
     seamless_external_login: تۆ لە ڕێگەی خزمەتگوزاری دەرەکیەوە داخڵ بووی، بۆیە ڕێکبەندەکانی نهێنوشە و ئیمەیل بەردەست نین.
     signed_in_as: 'چوونە ژوورەوە وەک:'
   verification:
-    explanation_html: 'دەتوانیت <strong> خۆت بسەلمێنیت وەک خاوەنی لینکەکان لە مێتاداتای پرۆفایلەکەت</strong>. بۆ ئەمە، ماڵپەڕە لینککراوەکە پێویستە لینکێکی تێدا بێت بۆ پرۆفایلی ماستۆدۆنەکەت. بەستەری <strong> دەبێت </strong> هەبێت <code>="me"</code>. ناوەڕۆکی دەقی لینکەکە گرنگ نییە. ئەمە نموونەیەکە:'
     verification: ساغ کردنەوە
   webauthn_credentials:
     add: زیادکردنی کلیلی ئاسایشی نوێ
diff --git a/config/locales/co.yml b/config/locales/co.yml
index 2ab6c63f9..9f48bdb4b 100644
--- a/config/locales/co.yml
+++ b/config/locales/co.yml
@@ -686,8 +686,6 @@ co:
     storage: I vostri media
   featured_tags:
     add_new: Aghjunghje
-    errors:
-      limit: Avete digià messu in mostra u numeru massimale di hashtag
     hint_html: "<strong>Quale sò i hashtag in mostra?</strong> Sò messi in vista nant'à u vostru prufile pubblicu è permettenu à a ghjente di vede i vostri statuti ch'annu stu hashtag. Sò una bona manere di mustrà e vostre opere creative o i prughjetti à longu termine."
   filters:
     contexts:
@@ -706,8 +704,6 @@ co:
       title: Filtri
     new:
       title: Aghjunghje un novu filtru
-  footer:
-    trending_now: Tindenze d'avà
   generic:
     all: Tuttu
     changes_saved_msg: Cambiamenti salvati!
@@ -718,8 +714,6 @@ co:
     validation_errors:
       one: Qualcosa ùn và bè! Verificate u prublemu quì sottu
       other: Qualcosa ùn và bè! Verificate %{count} prublemi quì sottu
-  html_validator:
-    invalid_markup: 'cuntene codice HTML invalidu: %{error}'
   imports:
     errors:
       over_rows_processing_limit: cuntene più di %{count} filari
@@ -759,9 +753,6 @@ co:
       expires_at: Spira
       uses: Utiliza
     title: Invità ghjente
-  lists:
-    errors:
-      limit: Ùn pudete più creà altre liste
   login_activities:
     authentication_methods:
       otp: app d'identificazione à dui fattori
@@ -937,7 +928,6 @@ co:
       ios: iOS
       linux: Linux
       mac: macOS
-      other: piattaforma scunnisciuta
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1037,7 +1027,6 @@ co:
       '7889238': 3 mesi
     min_age_label: Età minima
     min_favs: Cunservà i statuti favurizzati più di
-    min_favs_hint: Ùn sguassa micca i vostri statuti chì anu ricevuti più chì stu numeru di favuriti. Lasciate viotu per sguassà i statuti senza piglià in contu u numaru di favuriti
     min_reblogs: Cunservà i statuti spartuti più di
     min_reblogs_hint: Ùn sguassa micca i vostri statuti chì anu ricevuti più chì stu numeru di spartere. Lasciate viotu per sguassà i statuti senza piglià in contu u numaru di spartere
   stream_entries:
@@ -1100,7 +1089,6 @@ co:
     seamless_external_login: Site cunnettatu·a dapoi un serviziu esternu, allora i parametri di chjave d’accessu è d’indirizzu e-mail ùn so micca dispunibili.
     signed_in_as: 'Cunnettatu·a cum’è:'
   verification:
-    explanation_html: 'Pudete <strong>verificavi cum''è u pruprietariu di i ligami in i metadati di u vostru prufile</strong>. Per quessa, u vostru situ deve avè un ligame versu a vostra pagina Mastodon. U ligame <strong>deve</strong> avè un''attributu <code>rel="me"</code>. U cuntenutu di u testu di u ligame ùn hè micca impurtante. Eccu un''esempiu:'
     verification: Verificazione
   webauthn_credentials:
     add: Aghjunghje una chjave di sicurità
diff --git a/config/locales/cs.yml b/config/locales/cs.yml
index 21ba100ea..9e7ecb6ed 100644
--- a/config/locales/cs.yml
+++ b/config/locales/cs.yml
@@ -95,6 +95,7 @@ cs:
       moderation:
         active: Aktivní
         all: Vše
+        disabled: Deaktivován
         pending: Čekající
         silenced: Omezeno
         suspended: Pozastavené
@@ -139,6 +140,7 @@ cs:
       search: Hledat
       search_same_email_domain: Ostatní uživatelé se stejnou e-mailovou doménou
       search_same_ip: Další uživatelé se stejnou IP adresou
+      security: Zabezpečení
       security_measures:
         only_password: Pouze heslo
         password_and_2fa: Heslo a 2FA
@@ -443,6 +445,7 @@ cs:
         resolve: Přeložit doménu
         title: Blokovat novou e-mailovou doménu
       no_email_domain_block_selected: Žádné blokace e-mailové domény nebyly změněny, protože nebyly žádné vybrány
+      not_permitted: Nepovoleno
       resolved_dns_records_hint_html: Doménové jméno vede na následující MX domény, které mají nakonec na starost přijímání e-mailů. Blokování MX domény zablokuje registrace z jakékoliv e-mailové adresy, která používá stejnou MX doménu, i když je viditelné doménové jméno jiné. <strong>Dejte si pozor, abyste nezablokovali velké e-mailové poskytovatele.</strong>
       resolved_through_html: Přeložena přes %{domain}
       title: Blokované e-mailové domény
@@ -492,6 +495,7 @@ cs:
       content_policies:
         comment: Interní poznámka
         description_html: Můžete definovat politiky obsahu, které budou aplikovány na všechny účty z této domény i jakoukoliv z jejích subdomén.
+        limited_federation_mode_description_html: Můžete si vybrat, zda povolit federaci s touto doménou.
         policies:
           reject_media: Odmítat média
           reject_reports: Odmítat hlášení
@@ -599,19 +603,22 @@ cs:
         mark_as_sensitive_description_html: Média v nahlášených příspěvcích budou označena jako citlivá a bude zaznamenán prohřešek pro pomoc s eskalací v případě budoucích porušení stejným účtem.
         other_description_html: Podívejte se na další možnosti kontroly chování účtu a přizpůsobte komunikaci k nahlášenému účtu.
         resolve_description_html: Nebudou učiněny žádné kroky proti nahlášenému účtu, žádný prohřešek zaznamenán a hlášení bude uzavřeno.
-        silence_description_html: Profil bude viditelný pouze těm, kdo ho již sledují nebo jej ručně vyhledají, což značně omezí jeho dosah. Lze vždy vrátit zpět.
-        suspend_description_html: Profil a veškerý jeho obsah se stane nedostupným, dokud nebude časem smazán. Interakce s účtem nebude možná. Vratné do 30 dnů.
+        silence_description_html: Účet bude viditelný pouze těm, kdo jej již sledují nebo si jej ručně vyhledají, což výrazně omezí jeho dosah. Vždy lze vrátit zpět. Uzavře všechna hlášení proti tomuto účtu.
+        suspend_description_html: Účet a veškerý jeho obsah se znepřístupní a bude nakonec smazán, interakce s ním nebude možná. Lze vrátit zpět do 30 dnů. Uzavře všechna hlášení proti tomuto účtu.
       actions_description_html: Rozhodněte, který krok učinit pro vyřešení tohoto hlášení. Pokud podniknete kárný krok proti nahlášenému účtu, bude mu zasláno e-mailové oznámení, s výjimkou případu, kdy je zvolena kategorie <strong>Spam</strong>.
+      actions_description_remote_html: Rozhodněte, co podniknout pro vyřešení tohoto hlášení. Toto ovlivní pouze to, jak <strong>váš</strong> server komunikuje s tímto vzdáleným účtem, a zpracuje jeho obsah.
       add_to_report: Přidat do hlášení další
       are_you_sure: Jste si jisti?
       assign_to_self: Přidělit ke mně
       assigned: Přiřazený moderátor
       by_target_domain: Doména nahlášeného účtu
+      cancel: Zrušit
       category: Kategorie
       category_description_html: Důvod nahlášení tohoto účtu a/nebo obsahu bude uveden v komunikaci s nahlášeným účtem
       comment:
         none: Žádné
       comment_description_html: 'Pro upřesnění uživatel %{name} napsal:'
+      confirm: Potvrdit
       created_at: Nahlášené
       delete_and_resolve: Smazat příspěvky
       forwarded: Přeposláno
@@ -628,6 +635,7 @@ cs:
         placeholder: Popište, jaké akce byly vykonány, nebo jakékoliv jiné související aktuality…
         title: Poznámky
       notes_description_html: Zobrazit a zanechat poznámky pro ostatní moderátory i sebe v budoucnu
+      processed_msg: Nahlášení č.%{id} bylo úspěšně zpracováno
       quick_actions_description_html: 'Proveďte rychlou akci nebo skrolujte dolů pro nahlášený obsah:'
       remote_user_placeholder: vzdálený uživatel z %{instance}
       reopen: Znovu otevřít hlášení
@@ -640,9 +648,14 @@ cs:
       status: Stav
       statuses: Nahlášený obsah
       statuses_description_html: Obsah porušující pravidla bude uveden v komunikaci s nahlášeným účtem
+      summary:
+        actions:
+          delete_html: Odstranit urážlivé příspěvky
+        close_report: Označit nahlášení č.%{id} za vyřešené
       target_origin: Původ nahlášeného účtu
       title: Hlášení
       unassign: Odebrat
+      unknown_action_msg: 'Neznámá akce: %{action}'
       unresolved: Nevyřešeno
       updated_at: Aktualizováno
       view_profile: Zobrazit profil
@@ -741,6 +754,8 @@ cs:
         preamble: Povrchový zajímavý obsah je užitečný pro zapojení nových uživatelů, kteří možná neznají žádného Mastodona. Mějte pod kontrolou, jak různé objevovací funkce fungují na vašem serveru.
         profile_directory: Adresář profilů
         public_timelines: Veřejné časové osy
+        publish_discovered_servers: Zveřejnit objevené servery
+        publish_statistics: Zveřejnit statistiku
         title: Objevujte
         trends: Trendy
       domain_blocks:
@@ -795,6 +810,7 @@ cs:
         suspend: Uživatel %{name} pozastavil účet %{target}
       appeal_approved: Podáno odvolání
       appeal_pending: Čekající odvolání
+      appeal_rejected: Odvolání zamítnuto
     system_checks:
       database_schema_check:
         message_html: Na spuštění čekají databázové migrace. Nechte je prosím proběhnout pro zajištění očekávaného chování aplikace
@@ -969,6 +985,7 @@ cs:
   applications:
     created: Aplikace úspěšně vytvořena
     destroyed: Aplikace úspěšně smazána
+    logout: Odhlásit se
     regenerate_token: Znovu vygenerovat přístupový token
     token_regenerated: Přístupový token byl úspěšně vygenerován
     warning: Zacházejte s těmito daty opatrně. Nikdy je s nikým nesdílejte!
@@ -976,6 +993,8 @@ cs:
   auth:
     apply_for_account: Požádat o účet
     change_password: Heslo
+    confirmations:
+      wrong_email_hint: Pokud není e-mail správný, můžete si ho změnit v nastavení účtu.
     delete_account: Odstranit účet
     delete_account_html: Chcete-li odstranit svůj účet, <a href="%{path}">pokračujte zde</a>. Budete požádáni o potvrzení.
     description:
@@ -1003,6 +1022,8 @@ cs:
     resend_confirmation: Znovu odeslat pokyny pro potvrzení
     reset_password: Obnovit heslo
     rules:
+      accept: Přijmout
+      back: Zpět
       preamble: Tohle nastavují a prosazují moderátoři %{domain}.
       title: Některá základní pravidla.
     security: Zabezpečení
@@ -1149,8 +1170,6 @@ cs:
     storage: Úložiště médií
   featured_tags:
     add_new: Přidat nový
-    errors:
-      limit: Již jste zvýraznili maximální počet hashtagů
     hint_html: "<strong>Co jsou zvýrazněné hashtagy?</strong> Zobrazují se prominentně na vašem veřejném profilu a dovolují lidem prohlížet si vaše veřejné příspěvky konkrétně pod těmi hashtagy. Je to skvělý nástroj pro sledování kreativních děl nebo dlouhodobých projektů."
   filters:
     contexts:
@@ -1200,8 +1219,6 @@ cs:
       index:
         hint: Tento filtr se vztahuje na výběr jednotlivých příspěvků bez ohledu na jiná kritéria. Do tohoto filtru můžete přidat více příspěvků z webového rozhraní.
         title: Filtrované příspěvky
-  footer:
-    trending_now: Právě populární
   generic:
     all: Všechny
     all_items_on_page_selected_html:
@@ -1232,8 +1249,6 @@ cs:
       many: Něco ještě není úplně v pořádku! Zkontrolujte prosím %{count} chyb uvedených níže
       one: Něco ještě není úplně v pořádku! Zkontrolujte prosím chybu uvedenou níže
       other: Něco ještě není úplně v pořádku! Zkontrolujte prosím %{count} chyb uvedených níže
-  html_validator:
-    invalid_markup: 'obsahuje neplatný HTML kód: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Neplatný soubor CSV. Chyba: %{error}'
@@ -1278,7 +1293,7 @@ cs:
     title: Pozvat lidi
   lists:
     errors:
-      limit: Dosáhli jste maximálního počtu seznamů
+      limit: Dosáhl jste maximálního počtu seznamů
   login_activities:
     authentication_methods:
       otp: aplikací pro dvoufaktorové ověření
@@ -1419,6 +1434,9 @@ cs:
       unrecognized_emoji: není známý smajlík
   relationships:
     activity: Aktivita účtu
+    confirm_follow_selected_followers: Jste si jisti, že chcete sledovat vybrané sledující?
+    confirm_remove_selected_followers: Jste si jisti, že chcete odebrat vybrané sledující?
+    confirm_remove_selected_follows: Jste si jisti, že chcete odstranit vybrané sledované?
     dormant: Nečinné
     follow_selected_followers: Sledovat vybrané sledující
     followers: Sledující
@@ -1459,6 +1477,7 @@ cs:
       electron: Electron
       firefox: Firefox
       generic: Neznámý prohlížeč
+      huawei_browser: Prohlížeč Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1468,6 +1487,7 @@ cs:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Neznámý prohlížeč
       weibo: Weibo
     current_session: Aktuální relace
     description: "%{browser} na systému %{platform}"
@@ -1480,9 +1500,10 @@ cs:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: neznámá platforma
+      unknown_platform: Neznámá platforma
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1607,7 +1628,7 @@ cs:
       '7889238': 3 měsíce
     min_age_label: Hranice stáří
     min_favs: Zachovat příspěvky oblíbené alespoň
-    min_favs_hint: Nesmaže žádný z vašich příspěvků, který obdržel alespoň dané množství oblíbení. Ponechte prázdné pro mazání příspěvků bez ohledu na počet jejich oblíbení
+    min_favs_hint: Neodstraňuje žádný z vašich příspěvků, které obdržely alespoň tento počet oblíbených. Nechte prázdné pro odstranění příspěvků bez ohledu na jejich počet oblíbených
     min_reblogs: Zachovat příspěvky boostnuté alespoň
     min_reblogs_hint: Nesmaže žádný z vašich příspěvků, který byl boostnut alespoň tolikrát. Ponechte prázdné pro mazání příspěvků bez ohledu na počet jejich boostnutí
   stream_entries:
@@ -1712,7 +1733,6 @@ cs:
     seamless_external_login: Jste přihlášeni přes externí službu, nastavení hesla a e-mailu proto nejsou dostupná.
     signed_in_as: 'Přihlášeni jako:'
   verification:
-    explanation_html: 'Můžete se <strong>ověřit jako vlastník odkazů v metadatech profilu</strong>. Pro tento účel musí stránka v odkazu obsahovat odkaz zpět na váš profil na Mastodonu. Odkaz zpět <strong>musí</strong> mít atribut <code>rel="me"</code>. Na textu odkazu nezáleží. Zde je příklad:'
     verification: Ověření
   webauthn_credentials:
     add: Přidat nový bezpečnostní klíč
diff --git a/config/locales/csb.yml b/config/locales/csb.yml
new file mode 100644
index 000000000..e2a6dd4de
--- /dev/null
+++ b/config/locales/csb.yml
@@ -0,0 +1,10 @@
+---
+csb:
+  errors:
+    '400': The request you submitted was invalid or malformed.
+    '403': You don't have permission to view this page.
+    '404': The page you are looking for isn't here.
+    '406': This page is not available in the requested format.
+    '410': The page you were looking for doesn't exist here anymore.
+    '429': Too many requests
+    '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/cy.yml b/config/locales/cy.yml
index 130a8b121..3f451ef72 100644
--- a/config/locales/cy.yml
+++ b/config/locales/cy.yml
@@ -2,7 +2,7 @@
 cy:
   about:
     about_mastodon_html: 'Rhwydwaith cymdeithasol y dyfodol: Dim hysbysebion, dim gwyliadwriaeth gorfforaethol, dylunio moesegol, a datganoli! Chi sy berchen eich data gyda Mastodon!'
-    contact_missing: Heb ei osod
+    contact_missing: Heb ei osodF
     contact_unavailable: Ddim yn berthnasol
     hosted_on: Mastodon wedi ei weinyddu ar %{domain}
     title: Ynghylch
@@ -99,6 +99,7 @@ cy:
       moderation:
         active: Yn weithredol
         all: Popeth
+        disabled: Analluogwyd
         pending: Yn aros
         silenced: Cyfyngedig
         suspended: Wedi ei atal
@@ -145,6 +146,7 @@ cy:
       search: Chwilio
       search_same_email_domain: Defnyddwyr eraill gyda'r un parth e-bost
       search_same_ip: Defnyddwyr eraill gyda'r un IP
+      security: Diogelwch
       security_measures:
         only_password: Cyfrinair yn unig
         password_and_2fa: Cyfrinair a 2FA
@@ -189,7 +191,7 @@ cy:
         create_account_warning: Creu Rhybydd
         create_announcement: Creu Cyhoeddiad
         create_canonical_email_block: Creu Bloc E-bost
-        create_custom_emoji: Creu Emoji Cyfaddas
+        create_custom_emoji: Creu Emoji Addasedig
         create_domain_allow: Creu Caniatáu Parth
         create_domain_block: Creu Gwaharddiad Parth
         create_email_domain_block: Creu Gwaharddiad Parth E-bost
@@ -199,7 +201,7 @@ cy:
         demote_user: Diraddio Defnyddiwr
         destroy_announcement: Dileu Cyhoeddiad
         destroy_canonical_email_block: Dileu Bloc E-bost
-        destroy_custom_emoji: Dileu Emoji Cyfaddas
+        destroy_custom_emoji: Dileu Emoji Addasedig
         destroy_domain_allow: Dileu Caniatáu Parth
         destroy_domain_block: Dileu Gwaharddiad Parth
         destroy_email_domain_block: Dileu gwaharddiad parth e-bost
@@ -207,12 +209,12 @@ cy:
         destroy_ip_block: Dileu rheol IP
         destroy_status: Dileu Postiad
         destroy_unavailable_domain: Dileu Parth Ddim ar Gael
-        destroy_user_role: Dinistrio Rôl
+        destroy_user_role: Dileu Rôl
         disable_2fa_user: Diffodd 2FA
-        disable_custom_emoji: Analluogi Emoji Cyfaddas
+        disable_custom_emoji: Analluogi Emoji Addasedig
         disable_sign_in_token_auth_user: Analluogi Dilysu Tocyn E-bost ar gyfer Defnyddiwr
         disable_user: Analluogi Defnyddiwr
-        enable_custom_emoji: Alluogi Emoji Cyfaddas
+        enable_custom_emoji: Galluogi Emoji Addasedig
         enable_sign_in_token_auth_user: Galluogi Dilysu Tocyn E-bost ar gyfer Defnyddiwr
         enable_user: Galluogi Defnyddiwr
         memorialize_account: Cofadeilio Cyfrif
@@ -233,7 +235,7 @@ cy:
         unsilence_account: Dad-gyfyngu Cyfrif
         unsuspend_account: Tynnu Ataliad Cyfrif Dros Dro
         update_announcement: Diweddaru Cyhoeddiad
-        update_custom_emoji: Diweddaru Emoji Cyfaddas
+        update_custom_emoji: Diweddaru Emoji Addasedig
         update_domain_block: Diweddaru'r Blocio Parth
         update_ip_block: Diweddaru rheol IP
         update_status: Diweddaru Postiad
@@ -459,6 +461,7 @@ cy:
         resolve: Datrys parth
         title: Rhwystro parth e-bost newydd
       no_email_domain_block_selected: Heb newid unrhyw flociau parth e-bost gan nad oes un wedi'i ddewis
+      not_permitted: Dim caniatâd
       resolved_dns_records_hint_html: Mae'r enw parth yn cyd-fynd â'r parthau MX canlynol, sy'n gyfrifol yn y pen draw am dderbyn e-bost. Bydd rhwystro parth MX yn rhwystro cofrestriadau o unrhyw gyfeiriad e-bost sy'n defnyddio'r un parth MX, hyd yn oed os yw'r enw parth gweladwy yn wahanol. <strong>Byddwch yn ofalus i beidio â rhwystro darparwyr e-bost mawr.</strong>
       resolved_through_html: Wedi'i ddatrys trwy %{domain}
       title: Parthau e-bost wedi'u rhwystro
@@ -473,6 +476,7 @@ cy:
         private_comment_description_html: 'Er mwyn eich helpu i olrhain o ble mae blociau a fewnforwyd yn dod, bydd blociau a fewnforwyd yn cael eu creu gyda''r sylw preifat canlynol: <q>%{comment}</q>'
         private_comment_template: Mewnforiwyd o %{source} ar %{date}
         title: Mewnforio blociau parth
+      invalid_domain_block: 'Cafodd un neu fwy o flociau parth eu hepgor oherwydd y gwall(au) canlynol: %{error}'
       new:
         title: Mewnforio blociau parth
       no_file: Heb ddewis ffeil
@@ -512,6 +516,7 @@ cy:
       content_policies:
         comment: Nodyn mewnol
         description_html: Gallwch ddiffinio polisïau cynnwys a fydd yn cael eu cymhwyso i bob cyfrif o'r parth hwn ac unrhyw un o'i is-barthau.
+        limited_federation_mode_description_html: Gallwch ddewis a ydych am ganiatáu ffedereiddio â'r parth hwn.
         policies:
           reject_media: Gwrthod cyfryngau
           reject_reports: Gwrthod adroddiadau
@@ -623,19 +628,23 @@ cy:
         mark_as_sensitive_description_html: Bydd y cyfryngau yn y postiadau sy'n cael eu hadrodd yn cael eu marcio'n sensitif a bydd rhybudd yn cael ei recordio i'ch helpu i gynyddu achosion o dorri rheolau yn y dyfodol gan yr un cyfrif.
         other_description_html: Gweld rhagor o ddewisiadau rheoli ymddygiad y cyfrif a chyfaddasu cyfathrebiad i'r cyfrif a adroddwyd.
         resolve_description_html: Ni fydd unrhyw gamau yn cael eu cymryd yn erbyn y cyfrif a adroddwyd, ni chofnodwyd rhybudd, a bydd yr adroddiad yn cael ei gau.
-        silence_description_html: Dim ond i'r rhai sydd eisoes yn ei ddilyn neu'n edrych arno â llaw y bydd y proffil yn weladwy, gan gyfyngu'n ddifrifol ar ei gyrhaeddiad. Mae modd ei ddychwelyd ar unrhyw adeg.
-        suspend_description_html: Bydd y proffil a'i holl gynnwys yn dod yn anhygyrch nes iddo gael ei ddileu yn y pen draw. Bydd rhyngweithio â'r cyfrif yn amhosibl. Mae modd ei adfer o fewn 30 diwrnod.
+        silence_description_html: Bydd y cyfrif yn weladwy i'r rhai sydd eisoes yn ei ddilyn neu'n edrych arno â llaw, gan gyfyngu'n ddifrifol ar ei gyrhaeddiad. Mae modd ei ddychwelyd bob amser. Yn cau pob adroddiad yn erbyn y cyfrif hwn.
+        suspend_description_html: Bydd y cyfrif a'i holl gynnwys yn anhygyrch ac yn cael ei ddileu yn y pen draw, a bydd rhyngweithio ag ef yn amhosibl. Yn gildroadwy o fewn 30 diwrnod. Yn cau pob adroddiad yn erbyn y cyfrif hwn.
       actions_description_html: Penderfynwch pa gamau i'w cymryd i ddatrys yr adroddiad hwn. Os byddwch yn cymryd camau cosbol yn erbyn y cyfrif a adroddwyd, bydd hysbysiad e-bost yn cael ei anfon atyn nhw, ac eithrio pan fydd y categori <strong>Sbam</strong> yn cael ei ddewis.
+      actions_description_remote_html: Penderfynwch pa gamau i'w cymryd i ddatrys yr adroddiad hwn. Bydd hyn ond yn effeithio ar sut <strong>mae'ch</strong> gweinydd yn cyfathrebu â'r cyfrif hwn o bell ac yn trin ei gynnwys.
       add_to_report: Ychwanegu rhagor i adroddiad
       are_you_sure: Ydych chi'n siŵr?
       assign_to_self: Neilltuo i mi
       assigned: Cymedrolwr wedi'i neilltuo
       by_target_domain: Parth y cyfrif a adroddwyd
+      cancel: Canslo
       category: Categori
       category_description_html: Bydd y rheswm dros adrodd am y cyfrif a/neu’r cynnwys hwn yn cael ei ddyfynnu wrth gyfathrebu â’r cyfrif a adroddwyd
       comment:
         none: Dim
       comment_description_html: 'I ddarparu rhagor o wybodaeth, ysgrifennodd %{name}:'
+      confirm: Cadarnhau
+      confirm_action: Cadarnhau gweithred cymedroli yn erbyn @%{acct}
       created_at: Adroddwyd
       delete_and_resolve: Dileu postiadau
       forwarded: Wedi'i anfon ymlaen
@@ -652,6 +661,7 @@ cy:
         placeholder: Disgrifiwch pa weithredoedd sydd wedi eu cymryd, neu unrhyw ddiweddariadau perthnasol eraill...
         title: Nodiadau
       notes_description_html: Gweld a gadael nodiadau i gymedrolwyr eraill a chi eich hun yn y dyfodol
+      processed_msg: 'Adroddiad ar #%{id} wedi''i brosesu''n llwyddiannus'
       quick_actions_description_html: 'Cymerwch gamau cyflym neu sgroliwch i lawr i weld cynnwys yr adroddwyd amdano:'
       remote_user_placeholder: y defnyddiwr pell o %{instance}
       reopen: Ailagor adroddiad
@@ -664,9 +674,28 @@ cy:
       status: Statws
       statuses: Cynnwys wedi'i adrodd
       statuses_description_html: Bydd cynnwys tramgwyddus yn cael ei ddyfynnu wrth gyfathrebu â'r cyfrif a adroddwyd
+      summary:
+        action_preambles:
+          delete_html: 'Rydych ar fin <strong>dileu</strong> rhai o <strong>bostiadau @%{acct}</strong>. Bydd hyn yn:'
+          mark_as_sensitive_html: 'Rydych ar fin <strong>marcio</strong> rhai o <strong>bostiadau @%{acct}</strong> fel rhai <strong>sensitif</strong>. Bydd hyn yn:'
+          silence_html: 'Rydych ar fin <strong>cyfyngu ar</strong> gyfrif <strong>@%{acct}</strong>. Bydd hyn yn:'
+          suspend_html: 'Rydych ar fin <strong>atal</strong> cyfrif <strong>@%{acct}</strong>. Bydd hyn yn:'
+        actions:
+          delete_html: Tynnu'r postiadau tramgwyddus
+          mark_as_sensitive_html: Nodi fod cyfryngau'r postiadau tramgwyddus yn sensitif
+          silence_html: Cyfyngu'n sylweddol ar gyrhaeddiad <strong>@%{acct}</strong> trwy wneud ei ph/broffil a'i gynnwys ond yn weladwy i bobl sydd eisoes yn ei d/ddilyn neu edrych ar ei ph/broffil â llaw
+          suspend_html: Atal <strong>@%{acct}</strong>, gan wneud ei ph/broffil a'i gynnwys yn anhygyrch ac yn amhosibl rhyngweithio ag ef
+        close_report: 'Nodi adroddiad #%{id} fel wedi''i ddatrys'
+        close_reports_html: Nodi bod <strong>pob</strong> adroddiad yn erbyn <strong>@%{acct}</strong> wedi'i ddatrys
+        delete_data_html: Dileu proffil a chynnwys <strong>@%{acct}</strong> 30 diwrnod o nawr oni bai ei b/fod heb ei h/atal yn y cyfamser
+        preview_preamble_html: 'Bydd <strong>@%{acct}</strong> yn derbyn rhybudd gyda''r cynnwys canlynol:'
+        record_strike_html: Recordio rhybudd yn erbyn <strong>@%{acct}</strong> i'ch helpu i ddwysáu ar achosion o dorri rheolau yn y dyfodol o'r cyfrif hwn
+        send_email_html: Anfon e-bost rhybudd at <strong>@%{acct}</strong>
+        warning_placeholder: Rhesymeg ychwanegol dewisol ar gyfer y cam cymedroli.
       target_origin: Tarddiad y cyfrif a adroddwyd
       title: Adroddiadau
       unassign: Dadneilltuo
+      unknown_action_msg: 'Gweithred anhysbys: %{action}'
       unresolved: Heb ei ddatrys
       updated_at: Diweddarwyd
       view_profile: Gweld proffil
@@ -769,8 +798,10 @@ cy:
         preamble: Mae amlygu cynnwys diddorol yn allweddol ar gyfer derbyn defnyddwyr newydd nad ydynt efallai'n gyfarwydd ag unrhyw un Mastodon. Rheolwch sut mae nodweddion darganfod amrywiol yn gweithio ar eich gweinydd.
         profile_directory: Cyfeiriadur proffiliau
         public_timelines: Ffrydiau cyhoeddus
+        publish_discovered_servers: Cyhoeddi gweinyddion a ddarganfuwyd
+        publish_statistics: Cyhoeddi ystadegau
         title: Darganfod
-        trends: Trendiau
+        trends: Tueddiadau
       domain_blocks:
         all: I bawb
         disabled: I neb
@@ -823,6 +854,7 @@ cy:
         suspend: Mae %{name} wedi atal cyfrif %{target}
       appeal_approved: Apeliwyd
       appeal_pending: Apêl yn aros
+      appeal_rejected: Mae'r apêl wedi'i gwrthod
     system_checks:
       database_schema_check:
         message_html: Mae mudo cronfa ddata ar fin digwydd. Rhedwch nhw i sicrhau bod y rhaglen yn ymddwyn yn ôl y disgwyl
@@ -836,6 +868,12 @@ cy:
         message_html: Nid ydych wedi diffinio unrhyw reolau gweinydd.
       sidekiq_process_check:
         message_html: Does dim proses Sidekiq yn rhedeg ar gyfer y ciw(iau) %{value}. Adolygwch eich ffurfweddiad Sidekiq
+      upload_check_privacy_error:
+        action: Ewch yma am fwy o wybodaeth
+        message_html: "<strong>Mae eich gweinydd gwe wedi'i gam ffurfweddu.. Mae preifatrwydd eich defnyddwyr mewn perygl.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Ewch yma am fwy o wybodaeth
+        message_html: "<strong>Mae eich storfa gwrthrychau wedi'i cham ffurfweddu. Mae preifatrwydd eich defnyddwyr mewn perygl.</strong>"
     tags:
       review: Adolygu statws
       updated_msg: Gosodiadau hashnodau wedi'i diweddaru'n llwyddiannus
@@ -862,6 +900,7 @@ cy:
           zero: Wedi'i rannu gan %{count} o bobl dros yr wythnos ddiwethaf
         title: Dolenni sy'n trendio
         usage_comparison: Wedi'i rannu %{today} gwaith heddiw, o'i gymharu â %{yesterday} ddoe
+      not_allowed_to_trend: Dim caniatâd i dueddu
       only_allowed: Derbyniwyd yn unig
       pending_review: Yn aros am adolygiad
       preview_card_providers:
@@ -1005,6 +1044,7 @@ cy:
   applications:
     created: Cais wedi ei greu'n llwyddiannus
     destroyed: Cais wedi ei ddileu'n llwyddiannus
+    logout: Allgofnodi
     regenerate_token: Adfywio tocyn mynediad
     token_regenerated: Adfywiwyd y tocyn mynediad yn llwyddiannus
     warning: Byddwch yn ofalus iawn gyda'r data hwn. Peidiwch byth â'i rannu ag unrhyw un!
@@ -1012,6 +1052,8 @@ cy:
   auth:
     apply_for_account: Gofyn am gyfrif
     change_password: Cyfrinair
+    confirmations:
+      wrong_email_hint: Os nad yw'r cyfeiriad e-bost hwnnw'n gywir, gallwch ei newid yng ngosodiadau'r cyfrif.
     delete_account: Dileu cyfrif
     delete_account_html: Os hoffech chi ddileu eich cyfrif, mae modd <a href="%{path}">parhau yma</a>. Bydd gofyn i chi gadarnhau.
     description:
@@ -1039,6 +1081,8 @@ cy:
     resend_confirmation: Ailanfon cyfarwyddiadau cadarnhau
     reset_password: Ailosod cyfrinair
     rules:
+      accept: Derbyn
+      back: Nôl
       preamble: Mae'r rhain yn cael eu gosod a'u gorfodi gan y %{domain} cymedrolwyr.
       title: Rhai rheolau sylfaenol.
     security: Diogelwch
@@ -1177,7 +1221,7 @@ cy:
       request: Gofynn am eich archif
       size: Maint
     blocks: Rydych chi'n blocio
-    bookmarks: Nodau Tudalen
+    bookmarks: Llyfrnodau
     csv: CSV
     domain_blocks: Blociau parth
     lists: Rhestrau
@@ -1186,7 +1230,7 @@ cy:
   featured_tags:
     add_new: Ychwanegu
     errors:
-      limit: Rydych chi eisoes wedi cynnwys yr uchafswm o hashnodau
+      limit: Rydych chi eisoes wedi cynnwys y nifer mwyaf o hashnodau
     hint_html: "<strong>Beth yw hashnodau dan sylw?</strong> Maen nhw'n cael eu dangos yn amlwg ar eich proffil cyhoeddus ac yn caniatáu i bobl bori'ch postiadau cyhoeddus yn benodol o dan yr hashnodau hynny. Maen nhw'n arf gwych ar gyfer cadw golwg ar weithiau creadigol neu brojectau tymor hir."
   filters:
     contexts:
@@ -1242,8 +1286,6 @@ cy:
       index:
         hint: Mae'r hidlydd hwn yn berthnasol i ddethol postiadau unigol waeth beth fo'r meini prawf eraill. Gallwch ychwanegu mwy o bostiadau at yr hidlydd hwn o'r rhyngwyneb gwe.
         title: Postiadau wedi'u hidlo
-  footer:
-    trending_now: Trendiau
   generic:
     all: Popeth
     all_items_on_page_selected_html:
@@ -1282,8 +1324,6 @@ cy:
       other: Mae rhywbeth o'i le o hyd! Edrychwch ar y %{count} gwall isod, os gwelwch yn dda
       two: Mae rhywbeth o'i le o hyd! Edrychwch ar y %{count} wall isod, os gwelwch yn dda
       zero: Mae rhywbeth o'i le o hyd! Edrychwch ar y %{count} gwall isod os gwelwch yn dda
-  html_validator:
-    invalid_markup: 'yn cynnwys markup HTML annilys: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Ffeil CSV annilys. Gwall: %{error}'
@@ -1297,7 +1337,7 @@ cy:
     success: Llwythwyd eich data yn llwyddiannus a bydd yn cael ei brosesu mewn da bryd
     types:
       blocking: Rhestr blocio
-      bookmarks: Nodau Tudalen
+      bookmarks: Llyfrnodau
       domain_blocking: Rhestr rhwystro parth
       following: Rhestr ddilynion
       muting: Rhestr tewi
@@ -1330,7 +1370,7 @@ cy:
     title: Gwahodd pobl
   lists:
     errors:
-      limit: Rydych wedi cyrraedd uchafswm y rhestrau
+      limit: Rydych chi wedi cyrraedd y nifer mwyaf o restrau
   login_activities:
     authentication_methods:
       otp: ap dilysu dau ffactor
@@ -1382,7 +1422,7 @@ cy:
     title: Cymedroil
   move_handler:
     carry_blocks_over_text: Symudodd y defnyddiwr hwn o %{acct}, yr oeddech wedi'i rwystro.
-    carry_mutes_over_text: Symudodd y defnyddiwr hwn o %{acct}, lle roeddech chi wedi'i dewi.
+    carry_mutes_over_text: Wnaeth y defnyddiwr symud o %{acct}, a oeddech chi wedi'i anwybyddu.
     copy_account_note_text: 'Symudodd y defnyddiwr hwn o %{acct}, dyma oedd eich nodiadau blaenorol amdanynt:'
   navigation:
     toggle_menu: Toglo'r ddewislen
@@ -1471,7 +1511,11 @@ cy:
       unrecognized_emoji: nid yw'n emoji cydnabyddedig
   relationships:
     activity: Gweithgareddau cyfrif
+    confirm_follow_selected_followers: Ydych chi'n siŵr eich bod am ddilyn y dilynwyr a ddewiswyd?
+    confirm_remove_selected_followers: Ydych chi'n siŵr eich bod am ddileu'r dilynwyr a ddewiswyd?
+    confirm_remove_selected_follows: Ydych chi'n siŵr eich bod am ddileu'r canlynol?
     dormant: Segur
+    follow_failure: Methu dilyn rhai o'r cyfrifon a ddewiswyd.
     follow_selected_followers: Dilynwch y dilynwyr a ddewiswyd
     followers: Dilynwyr
     following: Yn dilyn
@@ -1511,6 +1555,7 @@ cy:
       electron: Electron
       firefox: Firefox
       generic: Porwr anhysbys
+      huawei_browser: Porwr Huawei
       ie: Nid yw'r rhaglen hon yn gydnaws ag Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Porwr Nokia S40 Ovi
@@ -1520,6 +1565,7 @@ cy:
       qq: Porwr QQ
       safari: Agor yn Safari
       uc_browser: UC Browser
+      unknown_browser: Porwr Anhysbys
       weibo: Weibo
     current_session: Sesiwn gyfredol
     description: "%{browser} ar %{platform}"
@@ -1532,9 +1578,10 @@ cy:
       chrome_os: ChromeOS
       firefox_os: OS Firefox
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: platfform anhysbys
+      unknown_platform: Platfform Anhysbys
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Ffôn Windows
@@ -1656,8 +1703,8 @@ cy:
     keep_pinned_hint: Nid yw'n dileu unrhyw un o'ch postiadau wedi'u pinio
     keep_polls: Cadw arolygon
     keep_polls_hint: Nid yw'n dileu unrhyw un o'ch arolygon
-    keep_self_bookmark: Cadw y postiadau wedi'u cadw fel nodau tudalen
-    keep_self_bookmark_hint: Nid yw'n dileu eich postiadau eich hun os ydych wedi rhoi nod tudalen arnyn nhw
+    keep_self_bookmark: Cadw y postiadau wedi'u cadw fel llyfrnodau
+    keep_self_bookmark_hint: Nid yw'n dileu eich postiadau eich hun os ydych wedi rhoi llyfrnodau arnyn nhw
     keep_self_fav: Cadw'r postiadau yr oeddech yn eu ffefrynnu
     keep_self_fav_hint: Nid yw'n dileu eich postiadau eich hun os ydych wedi eu ffefrynnu
     min_age:
@@ -1671,7 +1718,7 @@ cy:
       '7889238': 3 mis
     min_age_label: Trothwy oedran
     min_favs: Cadw postiadau ffafriwyd am o leiaf
-    min_favs_hint: Nid yw'n dileu unrhyw un o'ch postiadau sydd wedi derbyn o leiaf y swm hwn o ffefrynnau. Gadewch yn wag i ddileu postiadau waeth beth fo'u ffefrynnau
+    min_favs_hint: Nid yw'n dileu unrhyw un o'ch postiadau sydd wedi derbyn o leiaf y nifer hwn o ffefrynnau. Gadewch yn wag i ddileu postiadau, beth bynnag yw eu ffefrynnau
     min_reblogs: Cadw postiadau wedi eu hybu o leiaf
     min_reblogs_hint: Nid yw'n dileu unrhyw un o'ch postiadau sydd wedi cael eu hybu o leiaf y nifer hwn o weithiau. Gadewch yn wag i ddileu postiadau waeth beth fo'u nifer o hybiadau
   stream_entries:
@@ -1771,12 +1818,13 @@ cy:
       title: Croeso, %{name}!
   users:
     follow_limit_reached: Nid oes modd i chi ddilyn mwy na %{limit} o bobl
+    go_to_sso_account_settings: Ewch i osodiadau cyfrif eich darparwr hunaniaeth
     invalid_otp_token: Côd dau-ffactor annilys
     otp_lost_help_html: Os colloch chi fynediad i'r ddau, mae modd i chi gysylltu a %{email}
     seamless_external_login: Yr ydych wedi'ch mewngofnodi drwy wasanaeth allanol, felly nid yw gosodiadau cyfrinair ac e-bost ar gael.
     signed_in_as: 'Wedi mewngofnodi fel:'
   verification:
-    explanation_html: 'Mae modd i chi <strong>ddilysu eich hun fel perchenog y dolenni yn metadata eich proffil</strong>. Rhaid i''r wefan â dolen iddi gynnwys dolen yn ôl i''ch proffil Mastodon. <strong>Rhaid</strong> i''r ddolen yn ôl cynnwys y nodwedd <code>rel="me"</code>. Does dim ots beth yw cynnwys testun y ddolen. Dyma enghraifft:'
+    explanation_html: 'Gallwch <strong>wirio eich hun fel perchennog y dolenni ym metadata eich proffil</strong> . Ar gyfer gwneud hynny, rhaid i''r wefan gysylltiedig gynnwys dolen yn ôl i''ch proffil Mastodon. Ar ôl ychwanegu''r ddolen efallai y bydd angen i chi ddod yn ôl yma ac ail-gadw''ch proffil er mwyn i''r dilysiad ddod i rym. <strong>Rhaid</strong> i''r ddolen yn ôl gael priodoledd <code>rêl="me"</code>. Nid yw cynnwys testun y ddolen o bwys. Dyma enghraifft:'
     verification: Dilysu
   webauthn_credentials:
     add: Ychwanegu allwedd ddiogelwch newydd
diff --git a/config/locales/da.yml b/config/locales/da.yml
index a66fdc5f2..c558aafd6 100644
--- a/config/locales/da.yml
+++ b/config/locales/da.yml
@@ -4,7 +4,7 @@ da:
     about_mastodon_html: 'Fremtidens sociale netværk: Ingen annoncer, ingen virksomhedsovervågning, etisk design og decentralisering! Vær ejer af egne data med Mastodon!'
     contact_missing: Ikke angivet
     contact_unavailable: Utilgængelig
-    hosted_on: Mostodon hostet på %{domain}
+    hosted_on: Mastodon hostet på %{domain}
     title: Om
   accounts:
     follow: Følg
@@ -91,6 +91,7 @@ da:
       moderation:
         active: Aktiv
         all: Alle
+        disabled: Deaktiveret
         pending: Afventer
         silenced: Begrænset
         suspended: Suspenderet
@@ -133,6 +134,7 @@ da:
       search: Søg
       search_same_email_domain: Øvrige brugere med samme e-maildomæne
       search_same_ip: Øvrige brugere med identisk IP
+      security: Sikkerhed
       security_measures:
         only_password: Kun adgangskode
         password_and_2fa: Adgangskode og 2FA
@@ -142,7 +144,7 @@ da:
       show:
         created_reports: Indsendte anmeldelser
         targeted_reports: Anmeldt af andre
-      silence: Brgræns
+      silence: Begræns
       silenced: Begrænset
       statuses: Indlæg
       strikes: Tidligere anmeldelser
@@ -209,7 +211,7 @@ da:
         reject_user: Afvis bruger
         remove_avatar_user: Fjern profilbillede
         reopen_report: Genåbn anmeldelse
-        resend_user: Gensend bekræftelsese-mail
+        resend_user: Gensend bekræftelses-e-mail
         reset_password_user: Nulstil adgangskode
         resolve_report: Løs anmeldelse
         sensitive_account: Gennemtving sensitiv konto
@@ -427,6 +429,7 @@ da:
         resolve: Opløs domæne
         title: Blokér nyt e-maildomæne
       no_email_domain_block_selected: Ingen e-mailblokeringer ændret (ingen var valgt)
+      not_permitted: Ikke tilladt
       resolved_dns_records_hint_html: Domænenavnet opløses til flg. MX-domæner, som i sidste ende er ansvarlige for e-mailmodtagelse. Blokering af et MX-domæne blokerer også tilmeldinger fra enhver e-mailadresse på det pågældende MX-domæne, selv hvis det synlige domænenavn er et andet. <strong>Pas på ikke ikke at blokere større e-mailudbydere.</strong>
       resolved_through_html: Opløst via %{domain}
       title: Blokerede e-maildomæner
@@ -441,6 +444,7 @@ da:
         private_comment_description_html: 'For at man lettere kan holde styr på, hvorfra importerede blokeringer kommer, oprettes disse med flg. private kommentar: <q>%{comment}</q>'
         private_comment_template: Importeret fra %{source} d. %{date}
         title: Import af domæneblokeringer
+      invalid_domain_block: 'En eller flere domæneblokke blev oversprunget grundet flg. fejl: %{error}'
       new:
         title: Import af domæneblokeringer
       no_file: Ingen fill udvalgt
@@ -472,6 +476,7 @@ da:
       content_policies:
         comment: Internt notat
         description_html: Der kan defineres indholdspolitikker, som anvendes på alle konti fra dette domæne samt alle dets underdomæner.
+        limited_federation_mode_description_html: Man kan vælge, om føderation med dette domæne skal tillades.
         policies:
           reject_media: Afvis medier
           reject_reports: Afvis anmeldelser
@@ -575,19 +580,23 @@ da:
         mark_as_sensitive_description_html: Medierne i det anmeldte indlæg markeres som sensitive, og en advarsel (strike) registreres mhp. eskalering ved evt. fremtidige overtrædelser fra samme konto.
         other_description_html: Se flere muligheder relateret til at adfærdshåndtering for, samt tilpasning af kommunikation til, den anmeldte konto.
         resolve_description_html: Ingen foranstaltninger træffes mod den anmeldte konto, ingen advarsel (strike) registreres og anmeldelsen lukkes.
-        silence_description_html: Profilen vil kun være synlig for dem, som allerede følger den eller manuelt slå den op, hvilket markant begrænser dens rækkevidde. Kan altid omgøres.
-        suspend_description_html: Profilen (inkl. alt indhold) gøres utilgængelig, indtil den til sidst slettes. Interaktion med kontoen vil være umulig. Kan fortrydes inden for 30 dage.
+        silence_description_html: Kontoen vil kun være synlig for følgerene eller dem, som manuelt slå den op, hvilket markant begrænser dens udbredelse. Kan altid omgøres. Lukker alle indrapporteringer af kontoen.
+        suspend_description_html: Kontoen inkl. alt indhold utilgængeliggøres og interaktion umuliggøres, og den slettes på et tidspunkt. Kan omgøres inden for 30 dage. Lukker alle indrapporteringer af kontoen.
       actions_description_html: Afgør, hvilke foranstaltning, der skal træffes for at løse denne anmeldelse. Træffes en straffende foranstaltning mod den anmeldte konto, fremsendes en e-mailnotifikation, undtagen når kategorien <strong>Spam</strong> er valgt.
+      actions_description_remote_html: Fastslå en nødvendig handling mhp. at løse denne anmeldelse. Dette vil kun påvirke <strong>din</strong> servers kommunikation med, og indholdshåndtering for, fjernkontoen.
       add_to_report: Føj mere til anmeldelse
       are_you_sure: Sikker?
       assign_to_self: Tildel til mig
       assigned: Tildelt moderator
       by_target_domain: Anmeldte kontos domæne
+      cancel: Afbryd
       category: Kategori
       category_description_html: Årsagen til anmeldelsen af denne konto og/eller indhold refereres i kommunikationen med den anmeldte konto
       comment:
         none: Ingen
       comment_description_html: 'For at give mere information, skrev %{name}:'
+      confirm: Bekræft
+      confirm_action: Bekræft moderatorhandling for %{acct}
       created_at: Anmeldt
       delete_and_resolve: Slet indlæg
       forwarded: Videresendt
@@ -603,7 +612,8 @@ da:
         delete: Slet
         placeholder: Beskriv udførte foranstaltninger eller andre relevante opdateringer...
         title: Notater
-      notes_description_html: Se og skriv notater til andre moderatorer og dit fremtid selv
+      notes_description_html: Se og skriv notater til andre moderatorer og dit fremtidige jeg
+      processed_msg: 'Anmeldelse #%{id} er blev behandlet'
       quick_actions_description_html: 'Træf en hurtig foranstaltning eller rul ned for at se anmeldt indhold:'
       remote_user_placeholder: fjernbrugeren fra %{instance}
       reopen: Genåbn anmeldelse
@@ -616,9 +626,28 @@ da:
       status: Status
       statuses: Anmeld indhold
       statuses_description_html: Krænkende indhold citeres i kommunikationen med den anmeldte konto
+      summary:
+        action_preambles:
+          delete_html: 'Du er ved at <strong>fjerne</strong> nogle indlæg fra <strong>@%{acct}</strong>. Dette vil:'
+          mark_as_sensitive_html: 'Du er ved at <strong>markere</strong> nogle indlæg fra <strong>@%{acct}</strong> som <strong>sensitive</strong>. Dette vil:'
+          silence_html: 'Du er ved at <strong>begrænse</strong> nogle indlæg fra kontoen <strong>@%{acct}</strong>. Dette vil:'
+          suspend_html: 'Du er ved at <strong>suspendere</strong> kontoen <strong>@%{acct}</strong>. Dette vil:'
+        actions:
+          delete_html: Fjern de overtrædende indlæg
+          mark_as_sensitive_html: Markér medier i overtrædende indlæg som sensitive
+          silence_html: Begræns kraftigt rækkeviden for <strong>@%{acct}</strong> ved at gøre vedkommendes profil og indhold synligt alene for personer, som allerede er følgere eller manuelt slår profilen op
+          suspend_html: Suspendér <strong>@%{acct}</strong>, hvilket gør vedkommendes profil og indhold utilgængeligt og umuligt at interagere med
+        close_report: 'Markér anmeldelsen #%{id} som løst'
+        close_reports_html: Markér <strong>alle</strong> anmeldelser af <strong>@%{acct}</strong> som løst
+        delete_data_html: Slet profil og indhold for <strong>@%{acct}</strong> 30 dage fra nu medmindre suspenderingen af vedkommende i mellemtiden fjernes
+        preview_preamble_html: "<strong>@%{acct}</strong> vil modtage en advarsel med flg. indhold:"
+        record_strike_html: Registrér en advarsel for <strong>@%{acct}</strong> som hjælpe til eskalering af fremtidige overtrædelser fra samme konto
+        send_email_html: Send en advarselsmail til <strong>@%{acct}</strong>
+        warning_placeholder: Valgfri yderligere begrundelse for modereringshandlingen.
       target_origin: Anmeldte kontos oprindelse
       title: Anmeldelser
       unassign: Fjern tildeling
+      unknown_action_msg: 'Ukendt handling: %{action}'
       unresolved: Uløst
       updated_at: Opdateret
       view_profile: Vis profil
@@ -713,6 +742,8 @@ da:
         preamble: At vise interessant indhold er vitalt ifm. at få nye brugere om bord, som måske ikke kender nogen på Mastodon. Styr, hvordan forskellige opdagelsesfunktioner fungerer på serveren.
         profile_directory: Profiloversigt
         public_timelines: Offentlige tidslinjer
+        publish_discovered_servers: Udgiv fundne servere
+        publish_statistics: Udgiv statistik
         title: Opdagelse
         trends: Trends
       domain_blocks:
@@ -767,6 +798,7 @@ da:
         suspend: "%{name} suspenderede %{target}s konto"
       appeal_approved: Appelleret
       appeal_pending: Appel afventer
+      appeal_rejected: Appel afvist
     system_checks:
       database_schema_check:
         message_html: Databasemigreringer afventer. Kør dem for at sikre den forventede adfærd fra applikationen
@@ -780,6 +812,12 @@ da:
         message_html: Ingen serverregler defineret.
       sidekiq_process_check:
         message_html: Ingen Sidekiq-proces kører for %{value}-kø(er). Gennemgå Sidekiq-opsætningen
+      upload_check_privacy_error:
+        action: Tjek her for flere oplysninger
+        message_html: "<strong>Webserveren er fejlopsat. Brugernes fortrolighed er i fare.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Tjek her for flere oplysninger
+        message_html: "<strong>Objektlageret er fejlopsat. Brugernes fortrolighed er i fare.</strong>"
     tags:
       review: Revisionsstatus
       updated_msg: Hashtag-indstillinger opdateret
@@ -802,6 +840,7 @@ da:
           other: Delt af %{count} personer den seneste uge
         title: Populære links
         usage_comparison: Delt %{today} gange i dag, sammenlignet med %{yesterday} i går
+      not_allowed_to_trend: Ikke tilladt at trende
       only_allowed: Kun tilladte
       pending_review: Afventer revision
       preview_card_providers:
@@ -933,6 +972,7 @@ da:
   applications:
     created: Applikation oprettet
     destroyed: Applikation slettet
+    logout: Log af
     regenerate_token: Regenerér adgangstoken
     token_regenerated: Adgangstoken regenereret
     warning: Vær meget påpasselig med disse data. Del dem aldrig med nogen!
@@ -940,6 +980,8 @@ da:
   auth:
     apply_for_account: Anmod om en konto
     change_password: Adgangskode
+    confirmations:
+      wrong_email_hint: Er denne e-mailadresse ikke korrekt, kan den ændres i kontoindstillinger.
     delete_account: Slet konto
     delete_account_html: Ønsker du at slette din konto, kan du <a href="%{path}">gøre dette hér</a>. Du vil blive bedt om bekræftelse.
     description:
@@ -958,7 +1000,7 @@ da:
     migrate_account: Flyt til en anden konto
     migrate_account_html: Ønsker du at omdirigere denne konto til en anden, kan du <a href="%{path}">opsætte dette hér</a>.
     or_log_in_with: Eller log ind med
-    privacy_policy_agreement_html: Jeg accepterer <a href="%{privacy_policy_path}" target="_blank">Fortrolighedspolitikken</a>
+    privacy_policy_agreement_html: Jeg accepterer <a href="%{privacy_policy_path}" target="_blank">privatlivspolitikken</a>
     providers:
       cas: CAS
       saml: SAML
@@ -967,13 +1009,15 @@ da:
     resend_confirmation: Gensend bekræftelsesinstruktioner
     reset_password: Nulstil adgangskode
     rules:
+      accept: Acceptér
+      back: Tilbage
       preamble: Disse er opsat og håndhæves af %{domain}-moderatorerne.
       title: Nogle grundregler.
     security: Sikkerhed
     set_new_password: Opsæt ny adgangskode
     setup:
       email_below_hint_html: Er nedenstående e-mailadresse forkert, kan du rette den hér og modtage en ny bekræftelses-e-mail.
-      email_settings_hint_html: Bekræftelsese-mailen er sendt til %{email}. Er denne e-mailadresse forkert, kan du rette den via kontoindstillingerne.
+      email_settings_hint_html: Bekræftelses-e-mailen er sendt til %{email}. Er denne e-mailadresse forkert, kan du rette den via kontoindstillingerne.
       title: Opsætning
     sign_in:
       preamble_html: Log ind med dine <strong>%{domain}</strong>-legitimationsoplysninger. Hostes kontoen på en anden server, vil der ikke kunne logges ind her.
@@ -1041,9 +1085,9 @@ da:
       data_removal: Dine indlæg og andre data fjernes permanent
       email_change_html: Du kan <a href="%{path}">skifte e-mailadresse</a> uden at slette din konto
       email_contact_html: Hvis det stadig ikke ankommer, kan du sende en e-mail til <a href="mailto:%{email}">%{email}</a> for hjælp
-      email_reconfirmation_html: Modtager du ikke bekræftelsese-mailen, kan du <a href="%{path}">anmode om en ny</a>
+      email_reconfirmation_html: Modtager du ikke bekræftelses-e-mailen, kan du <a href="%{path}">anmode om en ny</a>
       irreversible: Du vil ikke kunne gendanne/genaktivere din konto
-      more_details_html: For yderligere oplysningerer, tjek <a href="%{terms_path}">fortrolighedspolitikken</a>.
+      more_details_html: For yderligere oplysningerer, tjek <a href="%{terms_path}">privatlivspolitikken</a>.
       username_available: Dit brugernavn vil blive tilgængeligt igen
       username_unavailable: Dit brugernavn vil forblive utilgængeligt
   disputes:
@@ -1114,7 +1158,7 @@ da:
   featured_tags:
     add_new: Tilføj nyt
     errors:
-      limit: Du har allerede fremhævet det maksimale antal hashtags
+      limit: Det maksimale antal hashtags er allerede fremhævet
     hint_html: "<strong>Hvad er fremhævede hashtags?</strong> De vises i en fremtrædende position på din offentlige profil og giver folk mulighed for at gennemse dine offentlige indlæg specifikt under disse hashtags. De er et fantastisk værktøj til at holde styr på kreative værker eller langsigtede projekter."
   filters:
     contexts:
@@ -1158,8 +1202,6 @@ da:
       index:
         hint: Dette filter gælder for udvalgte, individuelle indlæg uanset andre kriterier. Flere indlæg kan føjes til filteret via webfladen.
         title: Filtrerede indlæg
-  footer:
-    trending_now: Trender lige nu
   generic:
     all: Alle
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ da:
     validation_errors:
       one: Noget er ikke er helt i vinkel! Tjek fejlen nedenfor
       other: Noget er ikke er helt i vinkel! Tjek de %{count} fejl nedenfor
-  html_validator:
-    invalid_markup: 'indeholder ugyldig HTML-markup: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Ugyldig CSV-fil. Fejl: %{error}'
@@ -1360,14 +1400,18 @@ da:
     posting_defaults: Standarder for indlæg
     public_timelines: Offentlige tidslinjer
   privacy_policy:
-    title: Fortrolighedspolitik
+    title: Privatlivspolitik
   reactions:
     errors:
       limit_reached: Grænse for forskellige reaktioner nået
       unrecognized_emoji: er ikke en genkendt emoji
   relationships:
     activity: Kontoaktivitet
+    confirm_follow_selected_followers: Sikker på, at de valgte følgere skal følges?
+    confirm_remove_selected_followers: Sikker på, at de valgte følgere skal fjernes?
+    confirm_remove_selected_follows: Sikker på, at de valgte følgere skal fjernes?
     dormant: I dvale
+    follow_failure: Kunne ikke følge alle af de valgte konti.
     follow_selected_followers: Følg valgte følgere
     followers: Følgere
     following: Følger
@@ -1407,6 +1451,7 @@ da:
       electron: Electron
       firefox: Firefox
       generic: Ukendt browser
+      huawei_browser: Huawei Browser
       ie: IE
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1416,6 +1461,7 @@ da:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Ukendt browser
       weibo: Weibo
     current_session: Aktuelle session
     description: "%{browser} på %{platform}"
@@ -1428,9 +1474,10 @@ da:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: ukendt platform
+      unknown_platform: Ukendt platform
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1543,7 +1590,7 @@ da:
       '7889238': 3 måneder
     min_age_label: Alderstærskel
     min_favs: Behold indlæg favoritmarkeret mindst
-    min_favs_hint: Sletter ingen egne indlæg, som har modtaget minimum dette antal favoritmarkeringer. Lad stå tomt for at ignorere denne tærskel under sletning
+    min_favs_hint: Sletter ingen egne indlæg, som har modtaget minimum dette antal favoritmarkeringer. Lad stå tomt for at slette indlæg uanset favoritmarkeringer
     min_reblogs: Behold indlæg boostet mindst
     min_reblogs_hint: Sletter ingen egne indlæg, som er boostet flere end dette antal gange. Lad stå tomt for at ignorere denne tærskel under sletning
   stream_entries:
@@ -1638,17 +1685,18 @@ da:
       final_action: Begynd at poste
       final_step: 'Begynd at poste! Selv uden følgere vil offentlige indlæg kunne ses af andre f.eks. på den lokale tidslinje og i hashtags. Man kan introducere sig selv via hastagget #introductions.'
       full_handle: Dit fulde brugernavn
-      full_handle_hint: Dette er, hvad du oplyser til dine venner, så de kan sende dig beskeder eller følge dig fra andre server.
+      full_handle_hint: Dette er, hvad du oplyser til dine venner, så de kan sende dig beskeder eller følge dig fra andre servere.
       subject: Velkommen til Mastodon
       title: Velkommen ombord, %{name}!
   users:
     follow_limit_reached: Du kan maks. følge %{limit} personer
+    go_to_sso_account_settings: Gå til identitetsudbyderens kontoindstillinger
     invalid_otp_token: Ugyldig tofaktorkode
     otp_lost_help_html: Har du mistet adgang til begge, kan du kontakte %{email}
     seamless_external_login: Du er logget ind via en ekstern tjeneste, så adgangskode- og e-mailindstillinger er utilgængelige.
     signed_in_as: 'Logget ind som:'
   verification:
-    explanation_html: 'Du kan <strong>bekræfte dig selv som ejer af linkene i din profilmetadata</strong>. For at gøre det, skal det linkede websted indeholde et link pegende tilbage til din Mastodon-profil. Returlinket <strong>skal</strong> have en <code>rel="me"</code>-attribut. Linkets tekstindhold betyder ikke noget. Her er et eksempel:'
+    explanation_html: 'Man kan <strong>bekræfte sig selv som ejer af linkene i profilmetadataene</strong>. For at gøre dette, skal det linkede websted indeholde et link tilbage til Mastodon-profilen. Efter tilføjelse af linket, skal man muligvis returnere hertil og gemme sin profil igen, før bekræftelsen effektueres. Returlinket <strong>skal</strong> have en <code>rel="me"</code>-attribut. Linkets tekstindhold er ligegyldigt. Her er et eksempel:'
     verification: Bekræftelse
   webauthn_credentials:
     add: Tilføj ny sikkerhedsnøgle
diff --git a/config/locales/de.yml b/config/locales/de.yml
index b6b2638fa..91e4e84ae 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -12,12 +12,12 @@ de:
       one: Follower
       other: Folgende
     following: Folge ich
-    instance_actor_flash: Dieses Konto ist ein virtueller Akteur, der den Server selbst repräsentiert und nicht ein einzelner Benutzer. Es wird für Föderationszwecke verwendet und sollte nicht gesperrt werden.
+    instance_actor_flash: Dieses Konto ist ein virtueller Akteur, der den Server selbst repräsentiert, und kein persönliches Profil. Es wird für Föderationszwecke verwendet und sollte daher nicht gesperrt werden.
     last_active: zuletzt aktiv
     link_verified_on: Das Profil mit dieser E-Mail-Adresse wurde bereits am %{date} bestätigt
     nothing_here: Keine Treffer mit dieser Auswahl
     pin_errors:
-      following: Du musst dieser Person bereits folgen, um sie empfehlen zu können
+      following: Du musst dieser Person folgen, um sie empfehlen zu können
     posts:
       one: Beitrag
       other: Beiträge
@@ -25,11 +25,11 @@ de:
   admin:
     account_actions:
       action: Aktion ausführen
-      title: Moderationsaktion auf %{acct} ausführen
+      title: "@%{acct} moderieren"
     account_moderation_notes:
       create: Notiz abspeichern
       created_msg: Moderationshinweis erfolgreich abgespeichert!
-      destroyed_msg: Moderationsnotiz erfolgreich gelöscht!
+      destroyed_msg: Moderationsnotiz erfolgreich entfernt!
     accounts:
       add_email_domain_block: E-Mail-Domain sperren
       approve: Genehmigen
@@ -58,39 +58,40 @@ de:
       demote: Zurückstufen
       destroyed_msg: Daten von %{username} wurden zum Löschen in die Warteschlange eingereiht
       disable: Einfrieren
-      disable_sign_in_token_auth: Deaktiviere die Zwei-Faktor-Authentisierung (2FA) per E-Mail
+      disable_sign_in_token_auth: E-Mail-Token-Authentisierung deaktivieren
       disable_two_factor_authentication: Zwei-Faktor-Authentisierung (2FA) deaktivieren
       disabled: Eingefroren
       display_name: Angezeigter Name
       domain: Domain
       edit: Bearbeiten
-      email: E-Mail
-      email_status: E-Mail-Status
+      email: E-Mail-Adresse
+      email_status: Status der E-Mail-Adresse
       enable: Freischalten
-      enable_sign_in_token_auth: Aktiviere die Zwei-Faktor-Authentisierung (2FA) per E-Mail
+      enable_sign_in_token_auth: E-Mail-Token-Authentisierung aktivieren
       enabled: Freigegeben
       enabled_msg: Konto von %{username} erfolgreich freigegeben
       followers: Follower
       follows: Folge ich
       header: Titelbild
-      inbox_url: Posteingangsadresse
+      inbox_url: Privates Postfach (URL)
       invite_request_text: Begründung für das Beitreten
       invited_by: Eingeladen von
       ip: IP-Adresse
-      joined: Beigetreten
+      joined: Registriert
       location:
         all: Alle
         local: Lokal
         remote: Extern
         title: Herkunft
-      login_status: Anmeldestatus
-      media_attachments: Medienanhänge
+      login_status: Status
+      media_attachments: Speicherplatz
       memorialize: In Gedenkseite umwandeln
       memorialized: Gedenkseite
       memorialized_msg: "%{username} wurde erfolgreich in ein Gedenkseiten-Konto umgewandelt"
       moderation:
         active: Aktiv
         all: Alle
+        disabled: Deaktiviert
         pending: In Warteschlange
         silenced: Stummgeschaltet
         suspended: Gesperrt
@@ -104,11 +105,11 @@ de:
       not_subscribed: Nicht abonniert
       pending: Überprüfung ausstehend
       perform_full_suspension: Sperren
-      previous_strikes: Vorherige Verstöße
+      previous_strikes: Vorherige Maßnahmen
       previous_strikes_description_html:
-        one: Dieses Konto hat <strong>einen</strong> Verstoß.
-        other: Dieses Konto hat <strong>%{count}</strong> Verstöße.
-      promote: Befördern
+        one: Gegen dieses Konto wurde <strong>eine</strong> Maßnahme verhängt.
+        other: Gegen dieses Konto wurden <strong>%{count}</strong> Maßnahmen verhängt.
+      promote: Berechtigungen erweitern
       protocol: Protokoll
       public: Öffentlich
       push_subscription_expires: PuSH-Abonnement läuft aus
@@ -125,27 +126,28 @@ de:
       resend_confirmation:
         already_confirmed: Dieses Profil wurde bereits bestätigt
         send: Bestätigungs-E-Mail erneut senden
-        success: Bestätigungs-E-Mail erfolgreich gesendet!
+        success: Bestätigungs-E-Mail erfolgreich verschickt!
       reset: Zurücksetzen
       reset_password: Passwort zurücksetzen
-      resubscribe: Wieder abonnieren
+      resubscribe: Erneut abonnieren
       role: Rolle
       search: Suchen
       search_same_email_domain: Andere Benutzer*innen mit der gleichen E-Mail-Domain
       search_same_ip: Andere Benutzer*innen mit derselben IP-Adresse
+      security: Sicherheit
       security_measures:
         only_password: Nur Passwort
         password_and_2fa: Passwort und 2FA
       sensitive: Inhaltswarnung
       sensitized: Mit Inhaltswarnung versehen
-      shared_inbox_url: Geteilte Posteingangsadresse
+      shared_inbox_url: Gemeinsames Postfach (URL)
       show:
         created_reports: Erstellte Meldungen
         targeted_reports: Von Anderen gemeldet
       silence: Stummschalten
       silenced: Stummgeschaltet
       statuses: Beiträge
-      strikes: Vorherige Verstöße
+      strikes: Vorherige Maßnahmen
       subscribe: Abonnieren
       suspend: Sperren
       suspended: Gesperrt
@@ -163,12 +165,12 @@ de:
       unsuspended_msg: Kontosperre von %{username} erfolgreich aufgehoben
       username: Profilname
       view_domain: Übersicht für Domain anzeigen
-      warn: Warnen
+      warn: Verwarnen
       web: Web
       whitelisted: Auf der Whitelist
     action_logs:
       action_types:
-        approve_appeal: Einspruch annehmen
+        approve_appeal: Einspruch zulassen
         approve_user: Benutzer*in genehmigen
         assigned_to_self_report: Bericht zuweisen
         change_email_user: E-Mail des Profils ändern
@@ -194,7 +196,7 @@ de:
         destroy_instance: Domain-Daten entfernen
         destroy_ip_block: IP-Regel löschen
         destroy_status: Beitrag löschen
-        destroy_unavailable_domain: Nicht verfügbare Domain löschen
+        destroy_unavailable_domain: Nicht-verfügbare Domain entfernen
         destroy_user_role: Rolle entfernen
         disable_2fa_user: 2FA deaktivieren
         disable_custom_emoji: Eigenes Emoji deaktivieren
@@ -211,7 +213,7 @@ de:
         reopen_report: Meldung wieder eröffnen
         resend_user: Bestätigungs-E-Mail erneut senden
         reset_password_user: Passwort zurücksetzen
-        resolve_report: Bericht lösen
+        resolve_report: Meldung klären
         sensitive_account: Konto mit erzwungener Inhaltswarnung
         silence_account: Konto stummschalten
         suspend_account: Konto sperren
@@ -225,68 +227,68 @@ de:
         update_domain_block: Domain-Sperre aktualisieren
         update_ip_block: IP-Regel aktualisieren
         update_status: Beitrag aktualisieren
-        update_user_role: Rolle aktualisieren
+        update_user_role: Rolle bearbeiten
       actions:
         approve_appeal_html: "%{name} genehmigte die Moderationsbeschlüsse von %{target}"
         approve_user_html: "%{name} genehmigte die Registrierung von %{target}"
-        assigned_to_self_report_html: "%{name} hat sich die Meldung %{target} selbst zugewiesen"
+        assigned_to_self_report_html: "%{name} wies sich die Meldung %{target} selbst zu"
         change_email_user_html: "%{name} hat die E-Mail-Adresse von %{target} geändert"
         change_role_user_html: "%{name} hat die Rolle von %{target} geändert"
-        confirm_user_html: "%{name} hat die E-Mail-Adresse von %{target} bestätigt"
-        create_account_warning_html: "%{name} hat eine Warnung an %{target} gesendet"
-        create_announcement_html: "%{name} hat die neue Ankündigung erstellt: %{target}"
+        confirm_user_html: "%{name} bestätigte die E-Mail-Adresse von %{target}"
+        create_account_warning_html: "%{name} sendete eine Warnung an %{target}"
+        create_announcement_html: "%{name} erstellte die neue Ankündigung: %{target}"
         create_canonical_email_block_html: "%{name} hat die E-Mail mit dem Hash %{target} gesperrt"
-        create_custom_emoji_html: "%{name} hat neues Emoji hochgeladen: %{target}"
-        create_domain_allow_html: "%{name} hat die Domain %{target} gewhitelistet"
+        create_custom_emoji_html: "%{name} lud das neue Emoji %{target} hoch"
+        create_domain_allow_html: "%{name} erlaubte die Föderation mit der Domain %{target}"
         create_domain_block_html: "%{name} hat die Domain %{target} gesperrt"
         create_email_domain_block_html: "%{name} hat die E-Mail-Domain %{target} gesperrt"
         create_ip_block_html: "%{name} hat eine IP-Regel für %{target} erstellt"
-        create_unavailable_domain_html: "%{name} hat die Lieferung an die Domain %{target} eingestellt"
-        create_user_role_html: "%{name} hat die Rolle %{target} erstellt"
+        create_unavailable_domain_html: "%{name} beendete die Zustellung an die Domain %{target}"
+        create_user_role_html: "%{name} erstellte die Rolle %{target}"
         demote_user_html: "%{name} hat %{target} heruntergestuft"
-        destroy_announcement_html: "%{name} hat die neue Ankündigung %{target} gelöscht"
+        destroy_announcement_html: "%{name} löschte die Ankündigung %{target}"
         destroy_canonical_email_block_html: "%{name} hat die E-Mail mit dem Hash %{target} entsperrt"
-        destroy_custom_emoji_html: "%{name} hat das Emoji gelöscht: %{target}"
-        destroy_domain_allow_html: "%{name} hat die Domain %{target} von der Whitelist entfernt"
+        destroy_custom_emoji_html: "%{name} löschte das Emoji %{target}"
+        destroy_domain_allow_html: "%{name} verwehrte die Föderation mit der Domain %{target}"
         destroy_domain_block_html: "%{name} hat die Domain %{target} entsperrt"
         destroy_email_domain_block_html: "%{name} hat die E-Mail-Domain %{target} entsperrt"
-        destroy_instance_html: "%{name} hat die Daten der Domain %{target} entfernt"
+        destroy_instance_html: "%{name} entfernte die Daten der Domain %{target} von diesem Server"
         destroy_ip_block_html: "%{name} hat eine IP-Regel für %{target} entfernt"
-        destroy_status_html: "%{name} hat einen Beitrag von %{target} entfernt"
-        destroy_unavailable_domain_html: "%{name} setzte die Lieferung an die Domain %{target} fort"
-        destroy_user_role_html: "%{name} hat die Rolle %{target} gelöscht"
+        destroy_status_html: "%{name} entfernte einen Beitrag von %{target}"
+        destroy_unavailable_domain_html: "%{name} nahm die Zustellung an die Domain %{target} wieder auf"
+        destroy_user_role_html: "%{name} löschte die Rolle %{target}"
         disable_2fa_user_html: "%{name} hat die Zwei-Faktor-Authentisierung für %{target} deaktiviert"
-        disable_custom_emoji_html: "%{name} hat das Emoji deaktiviert: %{target}"
-        disable_sign_in_token_auth_user_html: "%{name} hat die E-Mail-Token-Authentifizierung für %{target} deaktiviert"
+        disable_custom_emoji_html: "%{name} deaktivierte das Emoji %{target}"
+        disable_sign_in_token_auth_user_html: "%{name} hat die E-Mail-Token-Authentisierung für %{target} deaktiviert"
         disable_user_html: "%{name} hat den Zugang für %{target} deaktiviert"
-        enable_custom_emoji_html: "%{name} hat das Emoji aktiviert: %{target}"
+        enable_custom_emoji_html: "%{name} aktivierte das Emoji %{target}"
         enable_sign_in_token_auth_user_html: "%{name} hat die E-Mail-Token-Authentifizierung für %{target} aktiviert"
         enable_user_html: "%{name} hat den Zugang für %{target} aktiviert"
-        memorialize_account_html: "%{name} hat das Konto von %{target} in eine Gedenkseite umgewandelt"
-        promote_user_html: "%{name} hat %{target} befördert"
+        memorialize_account_html: "%{name} wandelte das Konto von %{target} in eine Gedenkseite um"
+        promote_user_html: "%{name} beförderte %{target}"
         reject_appeal_html: "%{name} hat den Moderations-Beschlussantrag von %{target} abgelehnt"
         reject_user_html: "%{name} hat die Registrierung von %{target} abgelehnt"
-        remove_avatar_user_html: "%{name} hat das Profilbild von %{target} entfernt"
-        reopen_report_html: "%{name} hat die Meldung %{target} wieder geöffnet"
+        remove_avatar_user_html: "%{name} entfernte das Profilbild von %{target}"
+        reopen_report_html: "%{name} öffnete die Meldung %{target} wieder"
         resend_user_html: "%{name} hat erneut eine Bestätigungs-E-Mail für %{target} gesendet"
-        reset_password_user_html: "%{name} hat das Passwort von %{target} zurückgesetzt"
-        resolve_report_html: "%{name} hat die Meldung %{target} bearbeitet"
+        reset_password_user_html: "%{name} setzte das Passwort von %{target} zurück"
+        resolve_report_html: "%{name} hat die Meldung %{target} geklärt"
         sensitive_account_html: "%{name} hat die Medien von %{target} mit einer Inhaltswarnung versehen"
-        silence_account_html: "%{name} hat das Konto von %{target} stummgeschaltet"
-        suspend_account_html: "%{name} hat das Konto von %{target} gesperrt"
-        unassigned_report_html: "%{name} hat die Zuweisung der Meldung %{target} entfernt"
+        silence_account_html: "%{name} schaltete das Konto von %{target} stumm"
+        suspend_account_html: "%{name} sperrte das Konto von %{target}"
+        unassigned_report_html: "%{name} entfernte die Zuweisung der Meldung %{target}"
         unblock_email_account_html: "%{name} hat die E-Mail-Adresse von %{target} entsperrt"
         unsensitive_account_html: "%{name} hat die Inhaltswarnung für Medien von %{target} aufgehoben"
-        unsilence_account_html: "%{name} hat die Stummschaltung von %{target} aufgehoben"
-        unsuspend_account_html: "%{name} hat die Kontosperre von %{target} aufgehoben"
-        update_announcement_html: "%{name} aktualisierte Ankündigung %{target}"
-        update_custom_emoji_html: "%{name} hat das Emoji geändert: %{target}"
+        unsilence_account_html: "%{name} hob die Stummschaltung von %{target} auf"
+        unsuspend_account_html: "%{name} entsperrte das Konto von %{target}"
+        update_announcement_html: "%{name} überarbeitete die Ankündigung %{target}"
+        update_custom_emoji_html: "%{name} bearbeitete das Emoji %{target}"
         update_domain_block_html: "%{name} hat die Domain-Sperre für %{target} aktualisiert"
-        update_ip_block_html: "%{name} hat die Regel für IP %{target} geändert"
-        update_status_html: "%{name} hat einen Beitrag von %{target} aktualisiert"
+        update_ip_block_html: "%{name} änderte die Regel für die IP-Adresse %{target}"
+        update_status_html: "%{name} überarbeitete einen Beitrag von %{target}"
         update_user_role_html: "%{name} hat die Rolle %{target} geändert"
       deleted_account: gelöschtes Konto
-      empty: Keine Protokolle gefunden.
+      empty: Protokolle nicht gefunden.
       filter_by_action: Nach Aktion filtern
       filter_by_user: Nach Benutzer*in filtern
       title: Protokoll
@@ -310,36 +312,36 @@ de:
     custom_emojis:
       assign_category: Kategorie zuweisen
       by_domain: Domain
-      copied_msg: Lokale Kopie des Emoji erfolgreich erstellt
+      copied_msg: Lokale Kopie des Emojis erfolgreich erstellt
       copy: Kopieren
-      copy_failed_msg: Es konnte keine lokale Kopie des Emojis erstellt werden
+      copy_failed_msg: Es konnte keine lokale Kopie dieses Emojis auf diesem Server erstellt werden
       create_new_category: Neue Kategorie erstellen
       created_msg: Emoji erfolgreich erstellt!
       delete: Löschen
       destroyed_msg: Emoji erfolgreich gelöscht!
       disable: Deaktivieren
       disabled: Deaktiviert
-      disabled_msg: Das Emoji wurde erfolgreich deaktiviert
+      disabled_msg: Dieses Emoji wurde erfolgreich deaktiviert
       emoji: Emoji
       enable: Aktivieren
       enabled: Aktiviert
-      enabled_msg: Das Emoji wurde erfolgreich aktiviert
+      enabled_msg: Dieses Emoji wurde erfolgreich aktiviert
       image_hint: PNG oder GIF bis %{size}
       list: Aufführen
       listed: Angezeigt
       new:
-        title: Eigenes Emoji hinzufügen
-      no_emoji_selected: Keine Emojis wurden geändert, da keine ausgewählt wurden
+        title: Benutzerdefiniertes Emoji hinzufügen
+      no_emoji_selected: Keine Emojis wurden bearbeitet, da keine ausgewählt wurden
       not_permitted: Du bist für die Durchführung dieses Vorgangs nicht berechtigt
       overwrite: Überschreiben
       shortcode: Shortcode
       shortcode_hint: Mindestens 2 Zeichen, nur Buchstaben, Ziffern und Unterstriche
       title: Eigene Emojis
-      uncategorized: Nicht kategorisiert
+      uncategorized: Unkategorisiert
       unlist: Nicht Aufführen
       unlisted: Nicht aufgeführt
-      update_failed_msg: Konnte dieses Emoji nicht aktualisieren
-      updated_msg: Emoji erfolgreich aktualisiert!
+      update_failed_msg: Konnte dieses Emoji nicht bearbeiten
+      updated_msg: Emoji erfolgreich bearbeitet!
       upload: Hochladen
     dashboard:
       active_users: aktive Benutzer*innen
@@ -360,10 +362,10 @@ de:
         one: "<strong>%{count}</strong> unerledigte*r Benutzer*in"
         other: "<strong>%{count}</strong> unerledigte Benutzer*innen"
       resolved_reports: erledigte Meldungen
-      software: Software
+      software: Programme
       sources: Registrierungsort
-      space: Speicherverbrauch
-      title: Übersicht
+      space: Speicherplatz
+      title: Dashboard
       top_languages: Häufigste Sprachen
       top_servers: Aktivste Server
       website: Website
@@ -372,31 +374,31 @@ de:
         empty: Keine Einsprüche gefunden.
         title: Einsprüche
     domain_allows:
-      add_new: Whitelist-Domain
+      add_new: Föderation mit Domain erlauben
       created_msg: Domain wurde erfolgreich zur Whitelist hinzugefügt
-      destroyed_msg: Domain wurde von der Whitelist entfernt
+      destroyed_msg: Domain wurde von der Föderation ausgeschlossen
       export: Exportieren
       import: Import
-      undo: Von der Whitelist entfernen
+      undo: Von der Föderation ausschließen
     domain_blocks:
-      add_new: Neue Domain-Sperre hinzufügen
+      add_new: Neue Domain einschränken
       created_msg: Die Domain ist jetzt gesperrt bzw. eingeschränkt
-      destroyed_msg: Die Domain-Sperre wurde aufgehoben
+      destroyed_msg: Die Einschränkungen zu dieser Domain wurde entfernt
       domain: Domain
-      edit: Domain-Sperre bearbeiten
+      edit: Einschränkungen bearbeiten
       existing_domain_block: Du hast %{name} bereits stärker eingeschränkt.
       existing_domain_block_html: Du hast bereits strengere Beschränkungen für die Domain %{name} verhängt. Du musst diese erst <a href="%{unblock_url}">aufheben</a>.
       export: Exportieren
       import: Importieren
       new:
-        create: Sperre einrichten
-        hint: Die Domainsperre wird nicht verhindern, dass Konteneinträge in der Datenbank erstellt werden, sondern rückwirkend und automatisch alle Moderationsmethoden auf diese Konten anwenden.
+        create: Server einschränken
+        hint: Die Einschränkung einer Domain wird nicht verhindern, dass Konteneinträge in der Datenbank erstellt werden. Es werden aber alle Moderationsmethoden rückwirkend und automatisch auf diese Konten angewendet.
         severity:
-          desc_html: "<strong>Stummschaltung</strong> wird die Beiträge von Konten unter dieser Domain für alle unsichtbar machen, die den Konten nicht folgen. Eine <strong>Sperre</strong> wird alle Inhalte, Medien und Profildaten für Konten dieser Domain von deinem Server entfernen. Verwende <strong>keine,</strong> um nur Mediendateien abzulehnen."
+          desc_html: "<strong>Stummschaltung</strong> wird die Beiträge von Konten unter dieser Domain für alle unsichtbar machen, die den Konten nicht folgen. Eine <strong>Sperre</strong> wird alle Inhalte, Medien und Profildaten für Konten dieser Domain von deinem Server entfernen. Verwende <strong>keine</strong>, um nur Mediendateien abzulehnen."
           noop: Kein
           silence: Stummschaltung
           suspend: Sperren
-        title: Neue Domain-Sperre
+        title: Neue Domain einschränken
       no_domain_block_selected: Keine Domains gesperrt, weil keine ausgewählt wurde(n)
       not_permitted: Dir ist es nicht erlaubt, diese Handlung durchzuführen
       obfuscate: Domain-Name verschleiern
@@ -409,7 +411,7 @@ de:
       reject_media_hint: Entfernt lokal gespeicherte Mediendateien und verhindert deren künftiges Herunterladen. Für Sperren irrelevant
       reject_reports: Meldungen ablehnen
       reject_reports_hint: Alle Meldungen von dieser Domain ignorieren. Irrelevant für Sperrungen.
-      undo: Domain-Sperre aufheben
+      undo: Einschränkungen aufheben
       view: Domain-Sperre ansehen
     email_domain_blocks:
       add_new: Neue hinzufügen
@@ -427,6 +429,7 @@ de:
         resolve: Domain auflösen
         title: Neue E-Mail-Domain sperren
       no_email_domain_block_selected: Keine E-Mail-Domain-Sperren wurden geändert, da keine ausgewählt wurden
+      not_permitted: Nicht gestattet
       resolved_dns_records_hint_html: Der Domain-Name wird an die folgenden MX-Domains aufgelöst, die letztendlich für die Annahme von E-Mails zuständig sind. Das Sperren einer MX-Domain sperrt Anmeldungen aller E-Mail-Adressen, die dieselbe MX-Domain verwenden, auch wenn die sichtbare Domain anders lautet. <strong>Achte daher darauf, große E-Mail-Anbieter versehentlich nicht auszusperren.</strong>
       resolved_through_html: Durch %{domain} aufgelöst
       title: Gesperrte E-Mail-Domains
@@ -436,31 +439,32 @@ de:
       no_file: Keine Datei ausgewählt
     export_domain_blocks:
       import:
-        description_html: Du bist dabei, eine Liste von Domain-Sperren zu importieren. Bitte überprüfe diese Liste sehr sorgfältig, insbesondere dann, wenn du sie nicht selbst erstellt hast.
+        description_html: Du bist dabei, eine Liste von Domains zu importieren, die auf diesem Server gesperrt oder anderweitig eingeschränkt werden. Bitte überprüfe diese Liste sehr sorgfältig, insbesondere dann, wenn du sie nicht selbst erstellt hast.
         existing_relationships_warning: Bestehende Folgebeziehungen
-        private_comment_description_html: 'Damit du später nachvollziehen kannst, woher die importierten Sperren stammen, kannst du diesem Eintrag eine private Notiz hinzufügen: <q>%{comment}</q>'
+        private_comment_description_html: 'Damit du später nachvollziehen kannst, woher die importierten Sperren stammen, werden sie mit diesem privaten Kommentar erstellt: <q>%{comment}</q>'
         private_comment_template: Importiert von %{source} am %{date}
-        title: Domain-Sperren importieren
+        title: Domains importieren
+      invalid_domain_block: 'Ein oder mehrere Domainsperren wurden wegen folgenden Fehler(n) übersprungen: %{error}'
       new:
-        title: Domain-Sperren importieren
+        title: Domains importieren
       no_file: Keine Datei ausgewählt
     follow_recommendations:
       description_html: "<strong>Folgeempfehlungen helfen neuen Nutzer*innen, interessante Inhalte schnell zu finden</strong>. Wenn ein*e Nutzer*in noch nicht genug mit anderen interagiert hat, um personalisierte Folgeempfehlungen zu erhalten, werden stattdessen diese Profile verwendet. Sie werden täglich, basierend auf einer Mischung aus am meisten interagierenden Konten und jenen mit den meisten Followern für eine bestimmte Sprache, neu berechnet."
       language: Für Sprache
       status: Status
-      suppress: Folgeempfehlungen unterdrücken
+      suppress: Folgeempfehlung unterbinden
       suppressed: Unterdrückt
       title: Folgeempfehlungen
-      unsuppress: Nicht mehr unterdrücken
+      unsuppress: Folgeempfehlung nicht mehr unterbinden
     instances:
       availability:
         description_html:
-          one: Wenn die Zustellung an die Domain seit <strong>%{count} Tag</strong> erfolglos bleibt, werden keine weiteren Zustellungsversuche unternommen, es sei denn, eine Zustellung <em>von</em> dieser Domain wird empfangen.
-          other: Wenn die Zustellung an die Domain seit <strong>%{count} Tagen</strong> erfolglos bleibt, werden keine weiteren Zustellungsversuche unternommen, es sei denn, eine Zustellung <em>von</em> dieser Domain wird empfangen.
+          one: Wenn die Zustellung an die Domain <strong>%{count} Tag</strong> lang erfolglos bleibt, werden keine weiteren Zustellversuche unternommen, bis eine Zustellung <em>von</em> der Domain empfangen wird.
+          other: Wenn die Zustellung an die Domain an <strong>%{count} unterschiedlichen Tagen</strong> erfolglos bleibt, werden keine weiteren Zustellversuche unternommen, bis eine Zustellung <em>von</em> der Domain empfangen wird.
         failure_threshold_reached: Fehlschlag-Schwelle am %{date} erreicht.
         failures_recorded:
-          one: Fehlgeschlagener Versuch am %{count}. Tag.
-          other: Fehlgeschlagener Versuch am %{count}. Tag.
+          one: Fehlgeschlagener Versuch an %{count} Tag.
+          other: Fehlgeschlagene Versuche an %{count} unterschiedlichen Tagen.
         no_failures_recorded: Keine Fehler bei der Aufzeichnung.
         title: Verfügbarkeit
         warning: Der letzte Versuch, sich mit diesem Server zu verbinden, war nicht erfolgreich
@@ -468,37 +472,38 @@ de:
       back_to_limited: Stummgeschaltet
       back_to_warning: Warnung
       by_domain: Domain
-      confirm_purge: Bist du dir sicher, dass du die Daten für diese Domain für immer löschen möchtest?
+      confirm_purge: Bist du dir sicher, dass du die Daten von dieser Domain dauerhaft löschen möchtest?
       content_policies:
         comment: Interne Notiz
         description_html: Du kannst Inhaltsrichtlinien definieren, die auf alle Konten dieser Domain und einer ihrer Subdomains angewendet werden.
+        limited_federation_mode_description_html: Du kannst wählen, ob du eine Föderation mit dieser Domain gestattest.
         policies:
           reject_media: Medien ablehnen
           reject_reports: Meldungen ablehnen
-          silence: Stummschalten
+          silence: Stummschaltung
           suspend: Gesperrt
-        policy: Richtlinie
-        reason: Öffentlicher Grund
+        policy: Einschränkung
+        reason: Öffentliche Begründung
         title: Inhaltsrichtlinien
       dashboard:
-        instance_accounts_dimension: Meiste gefolgte Konten
-        instance_accounts_measure: gespeicherte Konten
-        instance_followers_measure: unsere Follower dort
+        instance_accounts_dimension: Meistgefolgte Konten
+        instance_accounts_measure: deren Konten hier im Cache
+        instance_followers_measure: eigene Follower dort
         instance_follows_measure: deren Follower hier
-        instance_languages_dimension: Top Sprachen
-        instance_media_attachments_measure: gespeicherte Medienanhänge
-        instance_reports_measure: Meldungen über deren Accounts
-        instance_statuses_measure: gespeicherte Beiträge
+        instance_languages_dimension: Meistverwendete Sprachen
+        instance_media_attachments_measure: deren Medien hier im Cache
+        instance_reports_measure: Meldungen zu deren Accounts
+        instance_statuses_measure: deren Beiträge hier im Cache
       delivery:
         all: Alle
         clear: Zustellfehler löschen
         failing: Fehlerhaft
-        restart: Lieferung neu starten
-        stop: Lieferung stoppen
+        restart: Zustellung neu starten
+        stop: Zustellung beenden
         unavailable: Nicht verfügbar
       delivery_available: Zustellung funktioniert
-      delivery_error_days: Tage seitdem die Zustellung nicht funktioniert
-      delivery_error_hint: Wenn eine Lieferung für %{count} Tage nicht möglich ist, wird sie automatisch als nicht lieferbar markiert.
+      delivery_error_days: Tage der fehlerhaften Zustellung
+      delivery_error_hint: Wenn eine Zustellung %{count} Tage lang nicht möglich ist, wird sie automatisch als unzustellbar markiert.
       destroyed_msg: Daten von %{domain} sind nun in der Warteschlange für die bevorstehende Löschung.
       empty: Keine Domains gefunden.
       known_accounts:
@@ -507,11 +512,11 @@ de:
       moderation:
         all: Alle
         limited: Eingeschränkt
-        title: Moderation
+        title: Server
       private_comment: Privater Kommentar
       public_comment: Öffentlicher Kommentar
-      purge: Löschen
-      purge_description_html: Wenn du glaubst, dass diese Domain endgültig offline ist, kannst du alle Account-Datensätze und zugehörigen Daten aus dieser Domain löschen. Das kann eine Weile dauern.
+      purge: Säubern
+      purge_description_html: Wenn du glaubst, dass diese Domain endgültig offline ist, kannst du alle Account-Datensätze und zugehörigen Daten von diesem Server löschen. Das kann eine Weile dauern.
       title: Föderation
       total_blocked_by_us: Von uns gesperrt
       total_followed_by_them: Gefolgt von denen
@@ -545,49 +550,53 @@ de:
     relationships:
       title: Beziehungen von %{acct}
     relays:
-      add_new: Neues Relay hinzufügen
-      delete: Löschen
+      add_new: Neues Relais hinzufügen
+      delete: Entfernen
       description_html: Ein <strong>Föderierungsrelay</strong> ist ein vermittelnder Server, der eine große Anzahl öffentlicher Beiträge zwischen Servern austauscht, die es abonnieren und zu ihm veröffentlichen.<strong> Es kann kleinen und mittleren Servern dabei helfen, Inhalte des Fediverse zu entdecken</strong>, was andernfalls das manuelle Folgen anderer Leute auf entfernten Servern durch lokale Nutzer erfordern würde.
       disable: Ausschalten
       disabled: Ausgeschaltet
       enable: Einschalten
-      enable_hint: Sobald aktiviert, wird dein Server alle öffentlichen Beiträge dieses Relays abonnieren und alle öffentlichen Beiträge dieses Servers an dieses senden.
+      enable_hint: Sobald aktiviert, wird dein Server alle öffentlichen Beiträge dieses Relais abonnieren und alle öffentlichen Beiträge dieses Servers an dieses senden.
       enabled: Eingeschaltet
       inbox_url: Relay-URL
       pending: Warte auf Zustimmung des Relays
       save_and_enable: Speichern und aktivieren
-      setup: Relaisverbindung einrichten
-      signatures_not_enabled: Relais funktionieren nicht korrekt, während der sichere Modus oder der Whitelist-Modus aktiviert ist
+      setup: Neues Relais verbinden
+      signatures_not_enabled: Die Relais funktionieren nicht korrekt, wenn der "secure mode" aktiviert oder die Föderation eingeschränkt ist
       status: Status
       title: Relais
     report_notes:
-      created_msg: Meldungs-Kommentar erfolgreich erstellt!
-      destroyed_msg: Meldungs-Kommentar erfolgreich gelöscht!
+      created_msg: Notiz zur Meldung erfolgreich erstellt!
+      destroyed_msg: Notiz zur Meldung erfolgreich entfernt!
     reports:
       account:
         notes:
           one: "%{count} Notiz"
           other: "%{count} Notizen"
       action_log: Protokoll
-      action_taken_by: Maßnahme ergriffen durch
+      action_taken_by: Maßnahme ergriffen von
       actions:
-        delete_description_html: Der gemeldete Beitrag wird gelöscht und ein Strike wird aufgezeichnet, um dir bei zukünftigen Verstößen des gleichen Accounts zu helfen.
+        delete_description_html: Der gemeldete Beitrag wird gelöscht und die ergriffene Maßnahme wird aufgezeichnet, um dir bei zukünftigen Verstößen des gleichen Kontos zu helfen.
         mark_as_sensitive_description_html: Die Medien in den gemeldeten Beiträgen werden mit einer Inhaltswarnung versehen und ein Verstoß wird vermerkt, um bei zukünftigen Verstößen desselben Kontos besser reagieren zu können.
         other_description_html: Weitere Optionen zur Steuerung des Kontoverhaltens und zur Anpassung der Kommunikation mit dem gemeldeten Konto.
-        resolve_description_html: Es wird keine Maßnahme gegen das gemeldete Konto ergriffen, es wird kein Strike verzeichnet und die Meldung wird geschlossen.
-        silence_description_html: Das Profil wird nur für diejenigen sichtbar sein, die ihm bereits folgen oder es manuell nachschlagen, und die Reichweite wird stark begrenzt. Kann immer rückgängig gemacht werden.
-        suspend_description_html: Das Profil und alle seine Inhalte werden unzugänglich werden, bis es schließlich gelöscht wird. Interaktion mit dem Konto wird unmöglich sein. Reversibel innerhalb von 30 Tagen.
+        resolve_description_html: Es wird keine Maßnahme gegen das gemeldete Konto ergriffen und der Vorgang wird nicht aufgezeichnet – die Meldung wird hiermit geschlossen.
+        silence_description_html: Das Konto wird nur für diejenigen sichtbar sein, die dem Konto bereits folgen oder es manuell suchen, was die Reichweite stark einschränkt. Kann jederzeit rückgängig gemacht werden. Alle Meldungen zu diesem Konto werden geschlossen.
+        suspend_description_html: Das Konto und alle Inhalte werden unzugänglich und ggf. gelöscht. Eine Interaktion mit dem Konto wird unmöglich. Dies kann innerhalb von 30 Tagen rückgängig gemacht werden. Alle Meldungen zu diesem Konto werden geschlossen.
       actions_description_html: Entscheide, welche Maßnahmen zur Lösung dieses Berichts zu ergreifen sind. Wenn du eine Strafmaßnahme gegen das gemeldete Konto ergreifst, wird eine E-Mail-Benachrichtigung an diese gesendet, außer wenn die <strong>Spam</strong>-Kategorie ausgewählt ist.
-      add_to_report: Mehr zur Meldung hinzufügen
+      actions_description_remote_html: Entscheide, welche Maßnahmen du zur Lösung dieser Meldungen ergreifen möchtest. Dies wirkt sich lediglich darauf aus, wie <strong>dein</strong> Server mit diesem externen Konto kommuniziert und dessen Inhalt handhabt.
+      add_to_report: Meldung ergänzen
       are_you_sure: Bist du dir sicher?
       assign_to_self: Mir zuweisen
       assigned: Zugewiesene*r Moderator*in
       by_target_domain: Domain des gemeldeten Kontos
+      cancel: Abbrechen
       category: Kategorie
-      category_description_html: Der Grund, warum dieses Konto und/oder der Inhalt gemeldet wurden, wird in der Kommunikation mit dem gemeldeten Konto zitiert
+      category_description_html: Der Grund, weshalb dieses Konto und/oder der Inhalt gemeldet worden ist, wird in der Kommunikation mit dem gemeldeten Konto erwähnt
       comment:
         none: Kein
-      comment_description_html: 'Um weitere Informationen bereitzustellen, schrieb %{name} Folgendes:'
+      comment_description_html: "%{name} ergänzte die Meldung um folgende Hinweis:"
+      confirm: Bestätigen
+      confirm_action: Maßnahme gegen @%{acct} bestätigen
       created_at: Gemeldet
       delete_and_resolve: Beiträge löschen
       forwarded: Weitergeleitet
@@ -597,28 +606,48 @@ de:
       mark_as_unresolved: Als ungelöst markieren
       no_one_assigned: Niemand
       notes:
-        create: Kommentar hinzufügen
-        create_and_resolve: Mit Kommentar lösen
-        create_and_unresolve: Mit Kommentar wieder öffnen
+        create: Notiz hinzufügen
+        create_and_resolve: Mit Notiz wieder lösen
+        create_and_unresolve: Mit Notiz wieder öffnen
         delete: Löschen
-        placeholder: Bitte beschreibe, welche Maßnahmen ergriffen wurden oder andere damit verbundene Aktualisierungen …
+        placeholder: Bitte beschreibe, welche Maßnahmen bzw. Sanktionen ergriffen worden sind, und führe alles auf, was es Erwähnenswertes zu diesem Profil zu berichten gibt …
         title: Notizen
       notes_description_html: Notiz an dich und andere Moderator*innen hinterlassen
-      quick_actions_description_html: 'Führe eine schnelle Aktion aus oder scrolle nach unten, um gemeldete Inhalte zu sehen:'
+      processed_msg: 'Meldung #%{id} erfolgreich bearbeitet'
+      quick_actions_description_html: 'Eine schnelle Aktion ausführen oder nach unten rolle, um gemeldete Inhalte zu sehen:'
       remote_user_placeholder: das externe Profil von %{instance}
       reopen: Meldung wieder eröffnen
-      report: 'Meldung #%{id}'
+      report: "%{id}. Meldung"
       reported_account: Gemeldetes Konto
       reported_by: Gemeldet von
       resolved: Gelöst
-      resolved_msg: Meldung erfolgreich gelöst!
-      skip_to_actions: Zu Aktionen springen
+      resolved_msg: Meldung erfolgreich geklärt!
+      skip_to_actions: Zur Maßnahme springen
       status: Status
       statuses: Gemeldeter Inhalt
       statuses_description_html: Störende Inhalte werden in der Kommunikation mit dem gemeldeten Konto zitiert
+      summary:
+        action_preambles:
+          delete_html: 'Du bist dabei, einige Beiträge von <strong>@%{acct}</strong> zu <strong>entfernen</strong>. Dies wird:'
+          mark_as_sensitive_html: 'Du bist dabei, einige Beiträge von <strong>@%{acct}</strong> mit einer <strong>Inhaltswarnung</strong> zu <strong>versehen</strong>. Dies wird:'
+          silence_html: 'Du bist dabei, das Konto von <strong>@%{acct}</strong> <strong>einzuschränken</strong>. Dies wird:'
+          suspend_html: 'Du bist dabei, das Konto von <strong>@%{acct}</strong> zu <strong>sperren</strong>. Dies wird:'
+        actions:
+          delete_html: Die anstößigen Beiträge entfernen
+          mark_as_sensitive_html: Medien der anstößigen Beiträge mit einer Inhaltswarnung versehen
+          silence_html: Schränkt die Reichweite von <strong>@%{acct}</strong> stark ein, indem das Profil und dessen Inhalte nur für Personen sichtbar sind, die dem Profil bereits folgen oder es manuell aufrufen
+          suspend_html: "<strong>@%{acct}</strong> sperren, sodass das Profil und dessen Inhalte nicht mehr zugänglich sind und keine Interaktion mehr möglich ist"
+        close_report: 'Meldung #%{id} als erledigt markieren'
+        close_reports_html: "<strong>Alle</strong> Meldungen gegen <strong>@%{acct}</strong> als erledigt markieren"
+        delete_data_html: Das Profil und die Inhalte von <strong>@%{acct}</strong> werden in 30 Tagen gelöscht, es sei denn, sie werden in der Zwischenzeit entsperrt
+        preview_preamble_html: "<strong>@%{acct}</strong> wird eine Warnung mit folgenden Inhalten erhalten:"
+        record_strike_html: Einen Verstoß gegen <strong>@%{acct}</strong> eintragen, um bei zukünftigen Verstößen desselben Kontos besser reagieren zu können
+        send_email_html: "<strong>@%{acct}</strong> eine Verwarnung per E-Mail schicken"
+        warning_placeholder: Optional zusätzliche Begründung für die Moderationsmaßnahme.
       target_origin: Domain des gemeldeten Kontos
       title: Meldungen
-      unassign: Zuweisung entfernen
+      unassign: Zuweisung aufheben
+      unknown_action_msg: 'Unbekannte Aktion: %{action}'
       unresolved: Ungelöst
       updated_at: Aktualisiert
       view_profile: Profil anzeigen
@@ -632,11 +661,11 @@ de:
         devops: DevOps
         invites: Einladungen
         moderation: Moderation
-        special: Spezial
-      delete: Löschen
+        special: Besonderheit
+      delete: Entfernen
       description_html: Mit <strong>Benutzer*inn-Rollen</strong> kannst du die Funktionen und Bereiche von Mastodon anpassen, auf die deine Benutzer*innen zugreifen können.
       edit: Rolle „%{name}“ bearbeiten
-      everyone: Standardberechtigungen
+      everyone: Standard
       everyone_full_description_html: Das ist die <strong>Basis-Rolle</strong>, die für <strong>alle Benutzer*innen</strong> gilt – auch für diejenigen ohne zugewiesene Rolle. Alle anderen Rollen erben Berechtigungen davon.
       permissions_count:
         one: "%{count} Berechtigung"
@@ -657,7 +686,7 @@ de:
         manage_custom_emojis: Eigene Emojis verwalten
         manage_custom_emojis_description: Erlaubt es Benutzer*innen, eigene Emojis auf dem Server zu verwalten
         manage_federation: Föderation verwalten
-        manage_federation_description: Erlaubt Nutzer*innen, Domains anderer Mastodon-Server zu sperren oder zuzulassen – und die Zustellbarkeit zu steuern
+        manage_federation_description: Erlaubt Benutzer*innen, Domains anderer Mastodon-Server zu sperren oder zuzulassen – und die Zustellbarkeit zu steuern
         manage_invites: Einladungen verwalten
         manage_invites_description: Erlaubt es Benutzer*innen, Einladungslinks zu durchsuchen und zu deaktivieren
         manage_reports: Meldungen verwalten
@@ -706,13 +735,15 @@ de:
         preamble: Lege fest, wie lange nutzergenerierte Inhalte auf deiner Mastodon-Instanz gespeichert werden.
         title: Cache & Archive
       default_noindex:
-        desc_html: Betrifft alle Benutzer, die diese Einstellung nicht selbst geändert haben
-        title: Benutzer standardmäßig von der Suchmaschinen-Indizierung ausnehmen
+        desc_html: Betrifft alle Benutzer*innen, die diese Einstellung bei sich nicht geändert haben
+        title: Profile standardmäßig von der Suchmaschinen-Indizierung ausnehmen
       discovery:
         follow_recommendations: Folgeempfehlungen
         preamble: Das Auffinden interessanter Inhalte ist wichtig, um neue Nutzer einzubinden, die Mastodon noch nicht kennen. Bestimme, wie verschiedene Suchfunktionen auf deinem Server funktionieren.
         profile_directory: Profilverzeichnis
         public_timelines: Öffentliche Timeline
+        publish_discovered_servers: Entdeckte Server offenlegen
+        publish_statistics: Statistiken veröffentlichen
         title: Entdecken
         trends: Trends
       domain_blocks:
@@ -758,15 +789,16 @@ de:
       with_media: Mit Medien
     strikes:
       actions:
-        delete_statuses: "%{name} hat die Beiträge von %{target} entfernt"
-        disable: "%{name} hat das Konto von %{target} eingefroren"
+        delete_statuses: "%{name} entfernte die Beiträge von %{target}"
+        disable: "%{name} fror das Konto von %{target} ein"
         mark_statuses_as_sensitive: "%{name} hat die Beiträge von %{target} mit einer Inhaltswarnung versehen"
-        none: "%{name} hat eine Warnung an %{target} gesendet"
-        sensitive: "%{name} hat das Profil von %{target} mit einer Inhaltswarnung versehen"
-        silence: "%{name} hat das Konto von %{target} stummgeschaltet"
-        suspend: "%{name} hat das Konto von %{target} gesperrt"
+        none: "%{name} schickte eine Warnung an %{target}"
+        sensitive: "%{name} versah das Konto von %{target} mit einer Inhaltswarnung"
+        silence: "%{name} schaltete das Konto von %{target} stumm"
+        suspend: "%{name} sperrte das Konto von %{target}"
       appeal_approved: Einspruch angenommen
       appeal_pending: Einspruch ausstehend
+      appeal_rejected: Einspruch abgelehnt
     system_checks:
       database_schema_check:
         message_html: Es gibt ausstehende Datenbankmigrationen. Bitte führe sie aus, um sicherzustellen, dass sich die Anwendung wie erwartet verhält
@@ -780,6 +812,12 @@ de:
         message_html: Du hast keine Serverregeln definiert.
       sidekiq_process_check:
         message_html: Kein Sidekiq-Prozess läuft für die %{value} Warteschlange(n). Bitte überprüfe deine Sidekiq-Konfiguration
+      upload_check_privacy_error:
+        action: Für weitere Informationen hier klicken
+        message_html: "<strong>Die Konfiguration deines Servers ist fehlerhaft. Die Privatsphäre deiner Benutzer*innen ist gefährdet.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Für weitere Informationen hier klicken
+        message_html: "<strong>Die Konfiguration deines Objektspeichers ist fehlerhaft. Die Privatsphäre deiner Benutzer*innen ist gefährdet.</strong>"
     tags:
       review: Prüfstatus
       updated_msg: Hashtageinstellungen wurden erfolgreich aktualisiert
@@ -802,7 +840,8 @@ de:
           other: In der letzten Woche von %{count} Personen geteilt
         title: Angesagte Links
         usage_comparison: Heute %{today} Mal geteilt, gestern %{yesterday} Mal
-      only_allowed: Nur Erlaubte
+      not_allowed_to_trend: Darf nicht trenden
+      only_allowed: Nur Genehmigte
       pending_review: Überprüfung ausstehend
       preview_card_providers:
         allowed: Links von diesem Herausgeber können angesagt sein
@@ -820,13 +859,13 @@ de:
         not_discoverable: Autor*in hat sich dafür entschieden, nicht entdeckt zu werden
         shared_by:
           one: Einmal geteilt oder favorisiert
-          other: "%{friendly_count} mal geteilt oder favorisiert"
+          other: "%{friendly_count}-mal geteilt oder favorisiert"
         title: Angesagte Beiträge
       tags:
         current_score: Aktuelle Punktzahl %{score}
         dashboard:
           tag_accounts_measure: eindeutige Verwendungen
-          tag_languages_dimension: Top Sprachen
+          tag_languages_dimension: Meistverwendete Sprachen
           tag_servers_dimension: Top Server
           tag_servers_measure: verschiedene Server
           tag_uses_measure: Gesamtnutzungen
@@ -838,21 +877,21 @@ de:
         not_usable: Kann nicht verwendet werden
         peaked_on_and_decaying: In den Trends am %{date}, jetzt absteigend
         title: Angesagte Hashtags
-        trendable: Darf unter Trends erscheinen
-        trending_rank: 'Trend #%{rank}'
-        usable: Kann verwendet werden
-        usage_comparison: Heute %{today} Mal genutzt, gestern %{yesterday} Mal
+        trendable: Darf in den Trends erscheinen
+        trending_rank: Platz %{rank}
+        usable: Darf verwendet werden
+        usage_comparison: Heute %{today}-mal und gestern %{yesterday}-mal genutzt
         used_by_over_week:
           one: In der letzten Woche von einer Person genutzt
           other: In der letzten Woche von %{count} Personen genutzt
       title: Trends
-      trending: Häufig diskutiert
+      trending: Angesagt
     warning_presets:
       add_new: Neu hinzufügen
       delete: Löschen
       edit_preset: Warnungsvorlage bearbeiten
-      empty: Du hast noch keine Warnungsvorlagen hinzugefügt.
-      title: Warnungsvorlagen verwalten
+      empty: Du hast noch keine Moderationsvorlagen hinzugefügt.
+      title: Moderationsvorlagen verwalten
     webhooks:
       add_new: Endpunkt hinzufügen
       delete: Löschen
@@ -900,13 +939,13 @@ de:
       new_trending_statuses:
         title: Angesagte Beiträge
       new_trending_tags:
-        no_approved_tags: Derzeit gibt es keine genehmigten trendenen Hashtags.
+        no_approved_tags: Es gibt keine genehmigten Hashtags, die gerade im Trend liegen.
         requirements: 'Jeder dieser Kandidaten könnte den #%{rank} genehmigten angesagten Hashtag übertreffen, der derzeit #%{lowest_tag_name} mit einer Punktzahl von %{lowest_tag_score} ist.'
         title: Angesagte Hashtags
       subject: Neue Trends zur Überprüfung auf %{instance}
   aliases:
     add_new: Alias erstellen
-    created_msg: Ein neuer Alias wurde erfolgreich erstellt. Du kannst nun den Wechsel vom alten Konto starten.
+    created_msg: Neuer Alias erfolgreich erstellt. Du kannst nun den Umzug vom alten zum neuen Konto starten.
     deleted_msg: Der Alias wurde erfolgreich entfernt. Aus jenem Konto zu diesem zu verschieben, ist nicht mehr möglich.
     empty: Du hast keine Aliase.
     hint_html: Wenn du von einem Konto zu einem anderen Konto wechseln möchtest, dann kannst du einen Alias erstellen, welcher benötigt wird, bevor du deine Follower vom alten Account zu diesen migrierst. Die Aktion allein ist <strong>harmlos und wi­der­ruf­lich</strong>. <strong>Die Kontenmigration wird vom alten Konto aus eingeleitet</strong>.
@@ -919,7 +958,7 @@ de:
     discovery: Entdecken
     localization:
       body: Mastodon wird von Freiwilligen übersetzt.
-      guide_link: https://de.crowdin.com/project/mastodon
+      guide_link: https://de.crowdin.com/project/mastodon/de
       guide_link_text: Alle können mitmachen und etwas dazu beitragen.
     sensitive_content: Inhaltswarnung
     toot_layout: Timeline-Layout
@@ -927,12 +966,13 @@ de:
     notification_preferences: E-Mail-Einstellungen ändern
     salutation: "%{name},"
     settings: 'E-Mail-Einstellungen ändern: %{link}'
-    view: 'Ansehen:'
+    view: 'Hier überprüfen:'
     view_profile: Profil anzeigen
-    view_status: Beitrag öffnen
+    view_status: Beitrag anschauen
   applications:
     created: Anwendung erfolgreich erstellt
     destroyed: Anwendung erfolgreich gelöscht
+    logout: Abmelden
     regenerate_token: Zugangs-Token neu erstellen
     token_regenerated: Zugriffstoken erfolgreich neu erstellt
     warning: Sei mit diesen Daten sehr vorsichtig. Teile sie mit niemandem!
@@ -940,6 +980,8 @@ de:
   auth:
     apply_for_account: Konto beantragen
     change_password: Passwort
+    confirmations:
+      wrong_email_hint: Sollte diese E-Mail-Adresse nicht korrekt sein, kannst du sie in den Kontoeinstellungen ändern.
     delete_account: Konto löschen
     delete_account_html: Falls du dein Konto endgültig löschen möchtest, kannst du das <a href="%{path}">hier vornehmen</a>. Du musst dies zusätzlich bestätigen.
     description:
@@ -947,10 +989,10 @@ de:
       prefix_sign_up: Registriere dich noch heute bei Mastodon!
       suffix: Mit einem Konto kannst du Profilen folgen, neue Beiträge veröffentlichen, Nachrichten mit Personen von jedem Mastodon-Server austauschen und vieles mehr!
     didnt_get_confirmation: Keine Bestätigungsanweisungen erhalten?
-    dont_have_your_security_key: Hast du keinen Sicherheitsschlüssel?
+    dont_have_your_security_key: Du hast keinen Sicherheitsschlüssel?
     forgot_password: Passwort vergessen?
     invalid_reset_password_token: Das Token zum Zurücksetzen des Passworts ist ungültig oder abgelaufen. Bitte fordere ein neues an.
-    link_to_otp: Gib einen Zwei-Faktor-Code von deinem Handy oder einen Wiederherstellungscode ein
+    link_to_otp: Gib einen Zwei-Faktor-Code von deinem Smartphone oder einen Wiederherstellungscode ein
     link_to_webauth: Verwende dein Sicherheitsschlüsselgerät
     log_in_with: Anmelden mit
     login: Anmelden
@@ -964,13 +1006,15 @@ de:
       saml: SAML
     register: Registrieren
     registration_closed: "%{instance} akzeptiert keine neuen Mitglieder"
-    resend_confirmation: Bestätigungsanweisungen erneut senden
+    resend_confirmation: Bestätigungs-E-Mail erneut versenden
     reset_password: Passwort zurücksetzen
     rules:
+      accept: Akzeptieren
+      back: Zurück
       preamble: Diese werden von den %{domain}-Moderator*innen festgelegt und erzwungen.
       title: Einige Grundregeln.
     security: Sicherheit
-    set_new_password: Neues Passwort setzen
+    set_new_password: Neues Passwort einrichten
     setup:
       email_below_hint_html: Wenn die unten stehende E-Mail-Adresse falsch ist, kannst du sie hier ändern und eine neue Bestätigungs-E-Mail erhalten.
       email_settings_hint_html: Die Bestätigungs-E-Mail wurde an %{email} gesendet. Wenn diese E-Mail-Adresse nicht korrekt ist, kannst du sie in den Einstellungen ändern.
@@ -979,7 +1023,7 @@ de:
       preamble_html: Melde dich mit deinen Zugangsdaten für <strong>%{domain}</strong> an. Solltest du dein Konto auf einem anderen Server registriert haben, ist eine Anmeldung hier nicht möglich.
       title: Bei %{domain} anmelden
     sign_up:
-      preamble: Mit einem Account auf diesem Mastodon-Server kannst du jeder anderen Person im Netzwerk folgen, unabhängig davon, wo ihr Account gehostet wird.
+      preamble: Mit einem Konto auf diesem Mastodon-Server kannst du jeder anderen Person im Netzwerk folgen, unabhängig davon, wo ihr Account gehostet ist.
       title: Okay, lass uns mit %{domain} anfangen.
     status:
       account_status: Kontostatus
@@ -988,25 +1032,25 @@ de:
       pending: Die Prüfung deiner Bewerbung steht noch aus. Dies kann einige Zeit in Anspruch nehmen. Sobald deine Bewerbung genehmigt wurde, erhältst du eine E-Mail.
       redirecting_to: Dein Konto ist inaktiv, weil es zu %{acct} umgezogen ist.
       view_strikes: Vorherige Verstöße deines Kontos ansehen
-    too_fast: Formular zu schnell gesendet, versuche es erneut.
+    too_fast: Formular zu schnell abgeschickt, versuche es erneut.
     use_security_key: Sicherheitsschlüssel verwenden
   authorize_follow:
     already_following: Du folgst diesem Konto bereits
-    already_requested: Du hast bereits eine Anfrage zum Folgen diesen Accounts versendet
-    error: Das Remote-Konto konnte nicht geladen werden
+    already_requested: Du hast bereits eine Folgeanfrage an dieses Konto gestellt
+    error: Bedauerlicherweise konnte das externe Konto nicht geladen werden
     follow: Folgen
-    follow_request: 'Du hast eine Folgeanfrage gesendet an:'
+    follow_request: 'Du hast eine Folgeanfrage gestellt an:'
     following: 'Erfolg! Du folgst nun:'
     post_follow:
       close: Oder du schließt einfach dieses Fenster.
       return: Benutzerprofil anzeigen
-      web: In der Benutzeroberfläche öffnen
+      web: Im Webinterface öffnen
     title: "%{acct} folgen"
   challenge:
     confirm: Fortfahren
     hint_html: "<strong>Hinweis:</strong> Wir werden dich für die nächste Stunde nicht erneut nach deinem Passwort fragen."
     invalid_password: Ungültiges Passwort
-    prompt: Gib dein Passwort ein, um fortzufahren
+    prompt: Bestätige mit deinem Passwort, um fortzufahren
   crypto:
     errors:
       invalid_key: ist kein gültiger Ed25519- oder Curve25519-Schlüssel
@@ -1014,21 +1058,21 @@ de:
   date:
     formats:
       default: "%d. %b %Y"
-      with_month_name: "%B %d, %Y"
+      with_month_name: "%d. %B %Y"
   datetime:
     distance_in_words:
-      about_x_hours: "%{count}h"
-      about_x_months: "%{count}mo"
-      about_x_years: "%{count}y"
-      almost_x_years: "%{count}y"
+      about_x_hours: "%{count} Std."
+      about_x_months: "%{count} Mon."
+      about_x_years: "%{count} J."
+      almost_x_years: "%{count} J."
       half_a_minute: Gerade eben
-      less_than_x_minutes: "%{count}m"
+      less_than_x_minutes: "%{count} Min."
       less_than_x_seconds: Gerade eben
-      over_x_years: "%{count}J"
-      x_days: "%{count}T"
-      x_minutes: "%{count}m"
-      x_months: "%{count}mo"
-      x_seconds: "%{count}s"
+      over_x_years: "%{count} J."
+      x_days: "%{count} T."
+      x_minutes: "%{count} Min."
+      x_months: "%{count} Mon."
+      x_seconds: "%{count} Sek."
   deletes:
     challenge_not_passed: Die eingegebenen Informationen waren nicht korrekt
     confirm_password: Gib dein derzeitiges Passwort ein, um deine Identität zu bestätigen
@@ -1037,18 +1081,18 @@ de:
     success_msg: Dein Konto wurde erfolgreich gelöscht
     warning:
       before: 'Bevor du fortfährst, lies bitte diese Punkte sorgfältig durch:'
-      caches: Inhalte, die von anderen Servern zwischengespeichert wurden, können weiterhin bestehen
+      caches: Inhalte, die von anderen Servern zwischengespeichert wurden, können fortbestehen
       data_removal: Deine Beiträge und alle anderen Daten werden für immer entfernt
       email_change_html: Du kannst <a href="%{path}">deine E-Mail-Adresse ändern</a>, ohne dein Konto zu löschen
-      email_contact_html: Wenn die Bestätigungs-E-Mail immer noch nicht ankam, kannst du eine E-Mail an <a href="mailto:%{email}">%{email}</a> senden, um weitere Hilfe zu erhalten
+      email_contact_html: Sollte sie noch immer nicht angekommen sein, kannst du eine E-Mail an <a href="mailto:%{email}">%{email}</a> schicken, um weitere Hilfe zu erhalten
       email_reconfirmation_html: Wenn du die Bestätigungs-E-Mail nicht erhalten hast, kannst du sie <a href="%{path}">erneut anfordern</a>
-      irreversible: Du kannst dein Konto nicht mehr wiederherstellen oder reaktivieren
+      irreversible: Du wirst dein Konto nicht mehr wiederherstellen oder reaktivieren können
       more_details_html: Weitere Details findest du in der <a href="%{terms_path}">Datenschutzerklärung</a>.
       username_available: Dein Profilname wird wieder verfügbar sein
       username_unavailable: Dein Profilname wird auch nach dem Löschen für andere nicht zugänglich sein
   disputes:
     strikes:
-      action_taken: Maßnahme ergriffen
+      action_taken: Maßnahme
       appeal: Einspruch
       appeal_approved: Dieser Verstoß wurde erfolgreich angefochten und ist nicht mehr gültig
       appeal_rejected: Der Einspruch wurde abgelehnt
@@ -1059,38 +1103,38 @@ de:
       approve_appeal: Einspruch annehmen
       associated_report: Zugehöriger Bericht
       created_at: Datum
-      description_html: Dies sind Aktionen gegen dein Konto und Warnungen, die von den Mitarbeitern von %{instance} an dich gesendet wurden.
+      description_html: Dies sind Maßnahmen, die gegen dein Konto ergriffen worden sind, und Warnungen, die dir die Mitarbeiter*innen von %{instance} geschickt haben.
       recipient: Adressiert an
       reject_appeal: Einspruch ablehnen
-      status: 'Beitrag #%{id}'
+      status: "%{id}. Beitrag"
       status_removed: Beitrag bereits vom System entfernt
       title: "%{action} vom %{date}"
       title_actions:
         delete_statuses: Beitragsentfernung
-        disable: Einfrieren des Kontos
+        disable: Konto einfrieren
         mark_statuses_as_sensitive: Beiträge mit einer Inhaltswarnung versehen
-        none: Warnung
+        none: Verwarnung
         sensitive: Profil mit einer Inhaltswarnung versehen
         silence: Kontobeschränkung
         suspend: Kontosperre
       your_appeal_approved: Dein Einspruch wurde angenommen
-      your_appeal_pending: Du hast Einspruch eingelegt
+      your_appeal_pending: Du hast Einspruch erhoben
       your_appeal_rejected: Dein Einspruch wurde abgelehnt
   domain_validator:
-    invalid_domain: ist kein gültiger Domain-Name
+    invalid_domain: ist keine gültige Domain
   errors:
-    '400': Die Anfrage, die du gesendet hast, war ungültig oder fehlerhaft.
-    '403': Dir fehlt die Befugnis, diese Seite sehen zu können.
+    '400': Die Anfrage, die du gestellt hast, war ungültig oder fehlerhaft.
+    '403': Dir fehlt die Berechtigung, diese Seite aufzurufen.
     '404': Die Seite, nach der du gesucht hast, wurde nicht gefunden.
     '406': Diese Seite ist im gewünschten Format nicht verfügbar.
     '410': Die Seite, nach der du gesucht hast, existiert hier nicht mehr.
     '422':
       content: Sicherheitsüberprüfung fehlgeschlagen. Sperrst du Cookies aus?
       title: Sicherheitsüberprüfung fehlgeschlagen
-    '429': Du wurdest gedrosselt
+    '429': Zu viele Anfragen
     '500':
       content: Bitte verzeih', etwas ist bei uns schiefgegangen.
-      title: Diese Seite ist kaputt
+      title: Diese Seite enthält einen Fehler
     '503': Die Seite konnte wegen eines temporären Serverfehlers nicht angezeigt werden.
     noscript_html: Bitte aktiviere JavaScript, um die Mastodon-Web-Anwendung zu verwenden. Alternativ kannst du auch eine der <a href="%{apps_path}">nativen Mastodon-Anwendungen</a> für deine Plattform probieren.
   existing_username_validator:
@@ -1099,38 +1143,38 @@ de:
   exports:
     archive_takeout:
       date: Datum
-      download: Dein Archiv herunterladen
+      download: Archiv jetzt herunterladen
       hint_html: Du kannst ein Archiv deiner <strong>Beiträge, Listen, hochgeladenen Medien usw.</strong> anfordern. Die exportierten Daten werden im ActivityPub-Format gespeichert und können mit geeigneter Software ausgewertet und angezeigt werden. Du kannst alle 7 Tage ein Archiv erstellen lassen.
       in_progress: Persönliches Archiv wird erstellt …
       request: Dein Archiv anfordern
-      size: Größe
-    blocks: Gesperrte Accounts
+      size: Dateigröße
+    blocks: Gesperrte Profile
     bookmarks: Lesezeichen
     csv: CSV
     domain_blocks: Gesperrte Domains
     lists: Listen
-    mutes: Stummgeschaltete Accounts
+    mutes: Stummgeschaltete Profile
     storage: Medienspeicher
   featured_tags:
-    add_new: Neu hinzufügen
+    add_new: Neuen hinzufügen
     errors:
-      limit: Du hast bereits die maximale Anzahl an empfohlenen Hashtags erreicht
+      limit: Du hast bereits die maximale Anzahl an Hashtags erreicht
     hint_html: "<strong>Was sind empfohlene Hashtags?</strong> Sie werden in deinem öffentlichen Profil hervorgehoben und ermöglichen es den Menschen, deine öffentlichen Beiträge speziell unter diesen Hashtags zu durchsuchen. Sie sind ein großartiges Werkzeug, um kreative Werke oder langfristige Projekte zu verfolgen."
   filters:
     contexts:
       account: Profile
-      home: Startseite
+      home: Startseite und Listen
       notifications: Mitteilungen
       public: Öffentliche Timelines
       thread: Unterhaltungen
     edit:
-      add_keyword: Stichwort hinzufügen
-      keywords: Stichwörter
+      add_keyword: Schlagwort hinzufügen
+      keywords: Schlagwörter
       statuses: Individuelle Beiträge
-      statuses_hint_html: Dieser Filter gilt für die Auswahl einzelner Beiträge, unabhängig davon, ob sie mit den unten stehenden Schlüsselwörtern übereinstimmen. <a href="%{path}">Beiträge im Filter ansehen oder entfernen.</a>.
+      statuses_hint_html: Dieser Filter gilt für die Auswahl einzelner Beiträge, unabhängig davon, ob sie mit den unten aufgeführten Schlagwörtern übereinstimmen. <a href="%{path}">Beiträge überprüfen oder aus dem Filter entfernen</a>.
       title: Filter bearbeiten
     errors:
-      deprecated_api_multiple_keywords: Diese Parameter können von dieser Anwendung nicht geändert werden, da sie auf mehr als ein Filterschlüsselwort angewendet werden. Verwende eine neuere Anwendung oder das Webinterface.
+      deprecated_api_multiple_keywords: Diese Parameter können von dieser Anwendung nicht geändert werden, da sie für mehr als ein Filterschlagwort gelten. Verwende eine aktuellere Anwendung oder das Webinterface.
       invalid_context: Ungültiger oder fehlender Kontext übergeben
     index:
       contexts: Filter in %{contexts}
@@ -1139,8 +1183,8 @@ de:
       expires_in: Läuft ab in %{distance}
       expires_on: Läuft am %{date} ab
       keywords:
-        one: "%{count} Stichwort"
-        other: "%{count} Stichwörter"
+        one: "%{count} Schlagwort"
+        other: "%{count} Schlagwörter"
       statuses:
         one: "%{count} Beitrag"
         other: "%{count} Beiträge"
@@ -1152,14 +1196,12 @@ de:
       save: Neuen Filter speichern
       title: Neuen Filter hinzufügen
     statuses:
-      back_to_filter: Zurück zum Filter
+      back_to_filter: Zurück zu den Filtern
       batch:
         remove: Filter entfernen
       index:
         hint: Dieser Filter wird verwendet, um einzelne Beiträge unabhängig von anderen Kriterien auszuwählen. Du kannst mehr Beiträge zu diesem Filter über das Webinterface hinzufügen.
         title: Gefilterte Beiträge
-  footer:
-    trending_now: In den Trends
   generic:
     all: Alle
     all_items_on_page_selected_html:
@@ -1171,7 +1213,7 @@ de:
     changes_saved_msg: Änderungen gespeichert!
     copy: Kopieren
     delete: Löschen
-    deselect: Auswahl für alle aufheben
+    deselect: Alle abwählen
     none: Keine
     order_by: Sortieren nach
     save_changes: Änderungen speichern
@@ -1182,8 +1224,6 @@ de:
     validation_errors:
       one: Etwas ist noch nicht ganz richtig! Bitte korrigiere den Fehler
       other: Etwas ist noch nicht ganz richtig! Bitte korrigiere %{count} Fehler
-  html_validator:
-    invalid_markup: 'enthält ungültiges HTML-Markup: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Ungültige CSV-Datei. Fehler: %{error}'
@@ -1193,15 +1233,15 @@ de:
       merge_long: Behalte existierende Datensätze und füge neue hinzu
       overwrite: Überschreiben
       overwrite_long: Ersetze aktuelle Datensätze mit neuen
-    preface: Daten, die du von einem anderen Server exportiert hast, kannst du hierher importieren. Das betrifft beispielsweise die Listen von Profilen, denen du folgst oder die du gesperrt hast.
+    preface: Daten, die du von einem Mastodon-Server exportiert hast, kannst du hierher importieren. Das betrifft beispielsweise die Listen von Profilen, denen du folgst oder die du gesperrt hast.
     success: Deine Daten wurden erfolgreich hochgeladen und werden in Kürze verarbeitet
     types:
-      blocking: Sperrliste
+      blocking: Gesperrte Profile
       bookmarks: Lesezeichen
-      domain_blocking: Domain-Sperrliste
-      following: Folgeliste
-      muting: Stummschaltungsliste
-    upload: Liste importieren
+      domain_blocking: Gesperrte Domains
+      following: Folge ich
+      muting: Stummgeschaltete Profile
+    upload: Datei importieren
   invites:
     delete: Deaktivieren
     expired: Abgelaufen
@@ -1216,14 +1256,14 @@ de:
     generate: Einladungslink erstellen
     invited_by: 'Du wurdest eingeladen von:'
     max_uses:
-      one: 1 mal verwendet
-      other: "%{count} mal verwendet"
+      one: 1-mal verwendet
+      other: "%{count}-mal verwendet"
     max_uses_prompt: Keine Einschränkung
     prompt: Erstelle Einladungen und teile die dazugehörigen Links, um anderen einen Zugang zu diesem Server zu gewähren
     table:
       expires_at: Läuft ab
       uses: Verwendet
-    title: Leute einladen
+    title: Einladungen
   lists:
     errors:
       limit: Du hast die maximale Anzahl an Listen erreicht
@@ -1234,7 +1274,7 @@ de:
       sign_in_token: E-Mail-Sicherheitscode
       webauthn: Sicherheitsschlüssel
     description_html: Wenn du verdächtige Aktivitäten bemerkst, die du nicht verstehst oder zuordnen kannst, solltest du dringend dein Passwort ändern und ungeachtet dessen die Zwei-Faktor-Authentisierung (2FA) aktivieren.
-    empty: Kein Authentifizierungsverlauf verfügbar
+    empty: Kein Authentisierungsverlauf verfügbar
     failed_sign_in_html: Fehler beim Anmeldeversuch mit %{method} von %{ip} (%{browser})
     successful_sign_in_html: Erfolgreiche Anmeldung mit %{method} von %{ip} (%{browser})
     title: Authentifizierungsverlauf
@@ -1252,7 +1292,7 @@ de:
       already_moved: ist das gleiche Konto, zu dem du bereits umgezogen bist
       missing_also_known_as: referenziert nicht zurück auf dieses Konto
       move_to_self: darf nicht das aktuelles Konto sein
-      not_found: kann nicht gefunden werden
+      not_found: konnte nicht gefunden werden
       on_cooldown: Die Abklingzeit läuft gerade
     followers_count: Anzahl der Follower zum Zeitpunkt der Migration des Accounts
     incoming_migrations: Von einem anderen Konto umziehen
@@ -1260,20 +1300,20 @@ de:
     moved_msg: Dein altes Profil wird jetzt zum neuen Account %{acct} weitergeleitet und deine Follower werden übertragen.
     not_redirecting: Dein Konto wird derzeit nicht auf ein anderes Konto weitergeleitet.
     on_cooldown: Du hast dein Konto vor kurzem migriert. Diese Funktion wird in %{count} Tagen wieder verfügbar sein.
-    past_migrations: Vorherige Migrationen
+    past_migrations: Vorherige Umzüge
     proceed_with_move: Follower übertragen
     redirected_msg: Dein Konto wird nun zu %{acct} weitergeleitet.
     redirecting_to: Dein Konto wird zu %{acct} weitergeleitet.
     set_redirect: Umleitung einrichten
     warning:
-      backreference_required: Das neue Konto muss zuerst so konfiguriert werden, dass es auf das alte Konto referenziert
+      backreference_required: Das neue Konto muss zuerst auf das alte Konto verweisen
       before: 'Bevor du fortfährst, lies bitte diese Hinweise sorgfältig durch:'
-      cooldown: Nach dem Migrieren wird es eine Abklingzeit geben, in der du das Konto nicht noch einmal migrieren kannst
-      disabled_account: Dein aktuelles Konto wird nachher nicht vollständig nutzbar sein. Du hast jedoch Zugriff auf den Datenexport sowie die Reaktivierung.
-      followers: Alle Follower werden vom aktuellen zum neuen Konto übertragen
-      only_redirect_html: Alternativ kannst du <a href="%{path}">nur eine Weiterleitung auf dein Profil</a> erstellen.
-      other_data: Keine anderen Daten werden automatisch verschoben
-      redirect: Das Profil deines aktuellen Kontos wird mit einer Weiterleitungsnachricht versehen und von Suchanfragen ausgeschlossen
+      cooldown: Nach dem Umzug wird es eine Weile dauern, bis du erneut umziehen darfst
+      disabled_account: Dein altes Konto ist nur noch eingeschränkt nutzbar. Du kannst jedoch deine Daten exportieren und das Konto wieder reaktivieren.
+      followers: Alle Follower werden vom alten zum neuen Konto übertragen
+      only_redirect_html: Alternativ kannst du auch <a href="%{path}">nur eine Weiterleitung zu deinem neuen Profil</a> einrichten, ohne die Follower zu übertragen.
+      other_data: Keine anderen Daten werden automatisch zum neuen Konto übertragen
+      redirect: Dein altes Konto wird einen Hinweis erhalten, dass Du umgezogen bist. Außerdem wird das Profil von Suchanfragen ausgeschlossen
   moderation:
     title: Moderation
   move_handler:
@@ -1290,21 +1330,21 @@ de:
         subject: "%{name} registrierte sich"
     favourite:
       body: 'Dein Beitrag wurde von %{name} favorisiert:'
-      subject: "%{name} hat deinen Beitrag favorisiert"
+      subject: "%{name} favorisierte deinen Beitrag"
       title: Neue Favorisierung
     follow:
       body: "%{name} folgt dir jetzt!"
       subject: "%{name} folgt dir jetzt"
       title: Neuer Follower
     follow_request:
-      action: Verwalte Folge-Anfragen
+      action: Folgeanfragen verwalten
       body: "%{name} möchte dir folgen"
-      subject: 'Ausstehender Follower: %{name}'
-      title: Neue Folge-Anfrage
+      subject: 'Ausstehende Folgeanfragen: %{name}'
+      title: Neue Folgeanfrage
     mention:
       action: Antworten
-      body: "%{name} hat dich erwähnt:"
-      subject: "%{name} hat dich erwähnt"
+      body: 'Du wurdest von %{name} erwähnt:'
+      subject: "%{name} erwähnte dich"
       title: Neue Erwähnung
     poll:
       subject: Eine Umfrage von %{name} ist beendet
@@ -1313,7 +1353,7 @@ de:
       subject: "%{name} hat deinen Beitrag geteilt"
       title: Dein Beitrag wurde geteilt
     status:
-      subject: "%{name} hat gerade etwas gepostet"
+      subject: "%{name} veröffentlichte gerade einen Beitrag"
     update:
       subject: "%{name} bearbeitete einen Beitrag"
   notifications:
@@ -1323,21 +1363,21 @@ de:
   number:
     human:
       decimal_units:
-        format: "%n%u"
+        format: "%n %u"
         units:
-          billion: B
-          million: M
+          billion: Mrd
+          million: Mio
           quadrillion: Q
-          thousand: K
+          thousand: Tsd.
           trillion: T
   otp_authentication:
     code_hint: Gib den Code ein, den deine 2FA- bzw. TOTP-App generiert hat, um den Vorgang zu bestätigen
     description_html: Wenn du die <strong>Zwei-Faktor-Authentisierung</strong> (2FA) mit einer Authentifizierungs-App deines Smartphones aktivierst, benötigst du neben dem regulären Passwort zusätzlich auch den zeitbasierten Code der 2FA-App, um dich anmelden zu können.
     enable: Aktivieren
-    instructions_html: "<strong>Scanne diesen QR-Code mit einer TOTP-App (wie dem Google Authenticator)</strong>. Die 2FA-App generiert dann zeitbasierte Codes, die du beim Login zusätzlich zum regulären Passwort eingeben musst."
+    instructions_html: "<strong>Scanne diesen QR-Code mit einer beliebigen Authentisierungs-App (TOTP)</strong>. Diese App generiert dann zeitbasierte Codes, die du beim Anmelden zusätzlich zum regulären Passwort eingeben musst."
     manual_instructions: Wenn du den QR-Code nicht einscannen kannst, sondern die Zahlenfolge manuell eingeben musst, ist hier der geheime Token für deine 2FA-App.
     setup: Einrichten
-    wrong_code: Der eingegebene Code war ungültig! Sind die Serverzeit und die Gerätezeit korrekt?
+    wrong_code: Der eingegebene Code ist ungültig! Laufen Serverzeit und Gerätezeit synchron?
   pagination:
     newer: Neuer
     next: Weiter
@@ -1346,12 +1386,12 @@ de:
     truncate: "&hellip;"
   polls:
     errors:
-      already_voted: Du hast bereits für diese Umfrage abgestimmt
+      already_voted: Du hast an dieser Umfrage bereits teilgenommen
       duplicate_options: enthält doppelte Einträge
-      duration_too_long: ist zu weit in der Zukunft
+      duration_too_long: liegt zu weit in der Zukunft
       duration_too_short: ist zu früh
-      expired: Die Umfrage ist bereits vorbei
-      invalid_choice: Die gewählte Abstimmoption existiert nicht
+      expired: Diese Umfrage ist bereits beendet
+      invalid_choice: Diese Auswahl existiert nicht
       over_character_limit: kann nicht länger als jeweils %{max} Zeichen sein
       too_few_options: muss mindestens einen Eintrag haben
       too_many_options: kann nicht mehr als %{max} Einträge beinhalten
@@ -1367,34 +1407,38 @@ de:
       unrecognized_emoji: ist kein anerkanntes Emoji
   relationships:
     activity: Kontoaktivität
+    confirm_follow_selected_followers: Bist du dir sicher, dass du den ausgewählten Followern folgen möchtest?
+    confirm_remove_selected_followers: Bist du sicher, dass du den ausgewählten Konten entfolgen möchtest?
+    confirm_remove_selected_follows: Bist du sicher, dass du den ausgewählten Konten entfolgen möchtest?
     dormant: Inaktiv
+    follow_failure: Einigen der ausgewählten Konten konnte nicht gefolgt werden.
     follow_selected_followers: Ausgewählten Followern folgen
     followers: Follower
     following: Folge ich
     invited: Eingeladen
     last_active: Zuletzt aktiv
-    most_recent: Neuste
+    most_recent: Neueste
     moved: Umgezogen
     mutual: Gegenseitig
     primary: Primär
     relationship: Beziehung
-    remove_selected_domains: Entferne alle Follower von den ausgewählten Domains
-    remove_selected_followers: Entferne ausgewählte Follower
-    remove_selected_follows: Ausgewählten Benutzer*innen entfolgen
+    remove_selected_domains: Alle Follower von den ausgewählten Domains entfernen
+    remove_selected_followers: Ausgewählten Followern entfolgen
+    remove_selected_follows: Ausgewählten Profilen entfolgen
     status: Kontostatus
   remote_follow:
     missing_resource: Die erforderliche Weiterleitungs-URL für dein Konto konnte nicht gefunden werden
   reports:
     errors:
-      invalid_rules: verweist nicht auf gültige Regeln
+      invalid_rules: verweist nicht auf gültige Serverregeln
   rss:
     content_warning: 'Inhaltswarnung:'
     descriptions:
       account: Öffentliche Beiträge von @%{acct}
-      tag: 'Öffentliche Beiträge mit dem Tag #%{hashtag}'
+      tag: 'Öffentliche Beiträge mit dem Hashtag #%{hashtag}'
   scheduled_statuses:
-    over_daily_limit: Du hast das Limit für geplante Beiträge, welches %{limit} beträgt, für heute erreicht
-    over_total_limit: Du hast das Limit für geplante Beiträge, welches %{limit} beträgt, erreicht
+    over_daily_limit: Du hast das heutige Limit für geplante Beiträge, das %{limit} beträgt, erreicht
+    over_total_limit: Du hast das Limit für geplante Beiträge, das %{limit} beträgt, erreicht
     too_soon: Das geplante Datum muss in der Zukunft liegen
   sessions:
     activity: Letzte Aktivität
@@ -1403,10 +1447,11 @@ de:
       alipay: Alipay
       blackberry: BlackBerry
       chrome: Chrome
-      edge: Microsoft Edge
+      edge: Edge
       electron: Electron
       firefox: Firefox
       generic: Unbekannter Browser
+      huawei_browser: Huawei Browser
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1416,6 +1461,7 @@ de:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Unbekannter Browser
       weibo: Weibo
     current_session: Aktuelle Sitzung
     description: "%{browser} auf %{platform}"
@@ -1428,16 +1474,17 @@ de:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
-      mac: Mac
-      other: unbekanntes Betriebssystem
+      mac: macOS
+      unknown_platform: Unbekannte Plattform
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
     revoke: Widerrufen
-    revoke_success: Sitzung erfolgreich geschlossen
+    revoke_success: Sitzung erfolgreich widerrufen
     title: Sitzungen
-    view_authentication_history: Authentifizierungsverlauf deines Kontos anzeigen
+    view_authentication_history: Anmeldeverlauf deines Kontos anzeigen
   settings:
     account: Konto
     account_settings: Kontoeinstellungen
@@ -1445,20 +1492,20 @@ de:
     appearance: Design
     authorized_apps: Genehmigte Apps
     back: Zurück zu Mastodon
-    delete: Konto löschen
+    delete: Kontolöschung
     development: Entwicklung
     edit_profile: Profil bearbeiten
     export: Exportieren
     featured_tags: Empfohlene Hashtags
     import: Importieren
     import_and_export: Importieren und exportieren
-    migrate: Konto-Umzug
+    migrate: Kontoumzug
     notifications: Benachrichtigungen
     preferences: Einstellungen
     profile: Profil
-    relationships: Folge ich und Follower
+    relationships: Follower und Folge ich
     statuses_cleanup: Automatische Löschung
-    strikes: Moderieren von Verstößen
+    strikes: Maßnahmen
     two_factor_authentication: Zwei-Faktor-Authentisierung (2FA)
     webauthn_authentication: Sicherheitsschlüssel
   statuses:
@@ -1477,13 +1524,13 @@ de:
     content_warning: 'Inhaltswarnung: %{warning}'
     default_language: Wie die Sprache des Webinterface
     disallowed_hashtags:
-      one: 'enthält einen verbotenen Hashtag: %{tags}'
-      other: 'enthält verbotene Hashtags: %{tags}'
-    edited_at_html: Bearbeitet %{date}
+      one: 'enthält einen nicht-erlaubten Hashtag: %{tags}'
+      other: 'enthält nicht-erlaubte Hashtags: %{tags}'
+    edited_at_html: 'Bearbeitet: %{date}'
     errors:
       in_reply_not_found: Der Beitrag, auf den du antworten möchtest, scheint nicht zu existieren.
-    open_in_web: Im Web öffnen
-    over_character_limit: Zeichenlimit von %{max} überschritten
+    open_in_web: Im Webinterface öffnen
+    over_character_limit: Begrenzung von %{max} Zeichen überschritten
     pin_errors:
       direct: Beiträge, die nur für erwähnte Profile sichtbar sind, können nicht angeheftet werden
       limit: Du hast bereits die maximale Anzahl an Beiträgen angeheftet
@@ -1502,7 +1549,7 @@ de:
     show_older: Ältere anzeigen
     show_thread: Thread anzeigen
     sign_in_to_participate: Melde dich an, um an der Unterhaltung teilzunehmen
-    title: '%{name}: "%{quote}"'
+    title: "%{name}: „%{quote}“"
     visibilities:
       direct: Direktnachricht
       private: Nur eigene Follower
@@ -1510,7 +1557,7 @@ de:
       public: Öffentlich
       public_long: Für alle sichtbar
       unlisted: Nicht gelistet
-      unlisted_long: Für alle sichtbar, aber in öffentlichen Timelines nicht aufgelistet
+      unlisted_long: Für alle sichtbar (mit Ausnahme von öffentlichen Timelines)
   statuses_cleanup:
     enabled: Automatisch alte Beiträge löschen
     enabled_hint: Löscht automatisch deine Beiträge, sobald sie die angegebene Altersgrenze erreicht haben, es sei denn, sie entsprechen einer der unten angegebenen Ausnahmen
@@ -1552,7 +1599,7 @@ de:
     sensitive_content: Inhaltswarnung
   strikes:
     errors:
-      too_late: Es ist zu spät, um gegen diesen Verstoß Einspruch zu erheben
+      too_late: Es ist zu spät, um gegen diese Maßnahme Einspruch zu erheben
   tags:
     does_not_match_previous_name: entspricht nicht dem vorherigen Namen
   themes:
@@ -1561,9 +1608,9 @@ de:
     mastodon-light: Mastodon (Hell)
   time:
     formats:
-      default: "%d.%m.%Y %H:%M"
+      default: "%d.%m.%Y um %H:%M Uhr"
       month: "%b %Y"
-      time: "%H:%M"
+      time: "%H:%M Uhr"
   two_factor_authentication:
     add: Hinzufügen
     disable: Zwei-Faktor-Authentisierung (2FA) deaktivieren
@@ -1572,12 +1619,12 @@ de:
     enabled: Zwei-Faktor-Authentisierung (2FA) ist aktiviert
     enabled_success: Zwei-Faktor-Authentisierung (2FA) erfolgreich aktiviert
     generate_recovery_codes: Wiederherstellungscodes erstellen
-    lost_recovery_codes: Wiederherstellungscodes erlauben es dir, wieder Zugang zu deinem Konto zu erlangen, falls du keinen Zugriff mehr auf die Zwei-Faktor-Authentisierung (2FA) oder den Sicherheitsschlüssel hast. Solltest Du diese Wiederherstellungscodes verloren haben, kannst du sie hier neu generieren. Deine alten, bereits erstellten Wiederherstellungscodes werden dadurch ungültig.
+    lost_recovery_codes: Wiederherstellungscodes ermöglichen es dir, wieder Zugang zu deinem Konto zu erlangen, falls du keinen Zugriff mehr auf dein Smartphone oder zum Sicherheitsschlüssel hast. Solltest du deine Wiederherstellungscodes verloren haben, kannst du sie hier neu generieren. Die alten Wiederherstellungscodes werden dann ungültig.
     methods: Methoden der Zwei-Faktor-Authentisierung (2FA)
     otp: Authentifizierungs-App
     recovery_codes: Wiederherstellungscodes sichern
     recovery_codes_regenerated: Wiederherstellungscodes erfolgreich neu erstellt
-    recovery_instructions_html: Wenn du den Zugang zu deinem Telefon verlieren solltest, kannst du einen untenstehenden Wiederherstellungscode benutzen, um wieder auf dein Konto zugreifen zu können. <strong>Bewahre die Wiederherstellungscodes gut auf.</strong> Du könntest sie beispielsweise ausdrucken und bei deinen restlichen wichtigen Dokumenten aufbewahren.
+    recovery_instructions_html: Falls du jemals den Zugang zu deinem Smartphone verlierst, kannst du einen der unten aufgeführten Wiederherstellungscodes verwenden, um wieder Zugang zu deinem Konto zu erhalten. <strong>Bewahre die Wiederherstellungscodes sicher auf</strong>. Du kannst sie zum Beispiel ausdrucken und zusammen mit anderen wichtigen Dokumenten aufbewahren.
     webauthn: Sicherheitsschlüssel
   user_mailer:
     appeal_approved:
@@ -1590,22 +1637,22 @@ de:
       subject: Dein Einspruch vom %{date} wurde abgelehnt
       title: Einspruch abgelehnt
     backup_ready:
-      explanation: Du hast eine vollständige Sicherung von deinem Mastodon-Konto angefragt. Es kann jetzt heruntergeladen werden!
-      subject: Dein persönliches Archiv ist bereit zum Herunterladen
-      title: Archivmitnahme
+      explanation: Du hast eine vollständige Sicherung deines Mastodon-Kontos angefordert. Das Backup kann jetzt heruntergeladen werden!
+      subject: Dein persönliches Archiv kann heruntergeladen werden
+      title: Archiv-Download
     suspicious_sign_in:
-      change_password: dein Passwort zu ändern
-      details: 'Hier sind die Details des Versuchs:'
+      change_password: dein Passwort ändern
+      details: 'Hier sind die Details zu den Anmeldeversuchen:'
       explanation: Wir haben eine Anmeldung zu deinem Konto von einer neuen IP-Adresse festgestellt.
       further_actions_html: Wenn du das nicht warst, empfehlen wir dir schnellstmöglich, %{action} und die Zwei-Faktor-Authentisierung (2FA) für dein Konto zu aktivieren, um es abzusichern.
       subject: Es wurde auf dein Konto von einer neuen IP-Adresse zugegriffen
       title: Eine neue Anmeldung
     warning:
-      appeal: Einspruch einsenden
+      appeal: Einspruch erheben
       appeal_description: Wenn du glaubst, dass es sich um einen Fehler handelt, kannst du einen Einspruch an die Administration von %{instance} senden.
       categories:
         spam: Spam
-        violation: Inhalt verstößt gegen die folgenden Gemeinschaftsrichtlinien
+        violation: Inhalt verstößt gegen die folgenden Serverregeln
       explanation:
         delete_statuses: Einige deiner Beiträge wurden als Verstoß gegen eine oder mehrere Gemeinschaftsrichtlinien erkannt und von den Moderator*innen von %{instance} entfernt.
         disable: Du kannst dein Konto nicht mehr verwenden, aber dein Profil und andere Daten bleiben unversehrt. Du kannst eine Sicherung deiner Daten anfordern, die Kontoeinstellungen ändern oder dein Konto löschen.
@@ -1613,7 +1660,7 @@ de:
         sensitive: Von nun an werden alle deine hochgeladenen Mediendateien mit einer Inhaltswarnung versehen und hinter einer Warnung versteckt.
         silence: Solange dein Konto limitiert ist, können nur die Leute, die dir bereits folgen, deine Beiträge auf dem Server sehen, und es könnte sein, dass du von verschiedenen öffentlichen Listungen ausgeschlossen wirst. Andererseits können andere dir manuell folgen.
         suspend: Du kannst dein Konto nicht mehr verwenden und dein Profil und andere Daten sind nicht mehr verfügbar. Du kannst dich immer noch anmelden, um eine Sicherung deiner Daten anzufordern, bis die Daten innerhalb von 30 Tagen vollständig gelöscht wurden. Allerdings werden wir einige Daten speichern, um zu verhindern, dass du die Sperrung umgehst.
-      reason: 'Grund:'
+      reason: 'Begründung:'
       statuses: 'Zitierte Beiträge:'
       subject:
         delete_statuses: Deine Beiträge auf %{acct} wurden entfernt
@@ -1632,35 +1679,36 @@ de:
         silence: Konto stummgeschaltet
         suspend: Konto gesperrt
     welcome:
-      edit_profile_action: Profil einrichten
+      edit_profile_action: Profil bearbeiten
       edit_profile_step: Du kannst dein Profil anpassen, indem du ein Profilbild hochlädst, deinen Anzeigenamen änderst und vieles mehr. Du kannst dich dafür entscheiden, neue Follower zu überprüfen, bevor sie dir folgen dürfen.
       explanation: Hier sind ein paar Tipps, um loszulegen
-      final_action: Fang an zu posten
+      final_action: Mit erstem Beitrag starten
       final_step: 'Fang jetzt an zu posten! Selbst ohne Follower werden deine öffentlichen Beiträge von anderen gesehen, zum Beispiel in der lokalen Timeline oder über die Hashtags. Möglicherweise möchtest du dich allen mit dem Hashtag #neuhier vorstellen.'
       full_handle: Dein vollständiger Profilname
       full_handle_hint: Dies ist, was du deinen Freunden sagen kannst, damit sie dich anschreiben oder dir von einem anderen Server folgen können.
-      subject: Willkommen bei Mastodon
+      subject: Willkommen bei Mastodon!
       title: Willkommen an Bord, %{name}!
   users:
     follow_limit_reached: Du kannst nicht mehr als %{limit} Leuten folgen
+    go_to_sso_account_settings: Kontoeinstellungen des Identitätsanbieters aufrufen
     invalid_otp_token: Ungültiger Code der Zwei-Faktor-Authentisierung (2FA)
     otp_lost_help_html: Wenn du beides nicht mehr weißt, melde dich bitte bei uns unter der E-Mail-Adresse %{email}
     seamless_external_login: Du bist über einen externen Dienst angemeldet, daher sind Passwort- und E-Mail-Einstellungen nicht verfügbar.
     signed_in_as: 'Angemeldet als:'
   verification:
-    explanation_html: 'Du kannst <strong>bestätigen, dass die Links in deinen Profil-Metadaten dir gehören</strong>. Dafür muss die verlinkte Website einen Link zurück auf dein Mastodon-Profil enthalten. Dieser Link <strong>muss</strong> ein <code>rel="me"</code>-Attribut enthalten. Der Linktext ist dabei egal. Hier ist ein Beispiel:'
+    explanation_html: "Du kannst <strong>bestätigen, dass die Links in deinen Profil-Metadaten dir gehören</strong>. Dafür muss die verlinkte Website einen Link zurück auf dein Mastodon-Profil enthalten. \nNach dem Hinzufügen des Links musst du möglicherweise hierhin zurückkommen und dein Profil erneut speichern, um dass die Verifikation wirksam wird. Der Link zurück <strong>muss</strong> ein <code>rel=\"me\"</code>-Attribut enthalten. Der Linktext ist dabei egal. Hier ist ein Beispiel:"
     verification: Verifizierung
   webauthn_credentials:
     add: Sicherheitsschlüssel hinzufügen
     create:
       error: Beim Hinzufügen des Sicherheitsschlüssels ist ein Fehler aufgetreten. Bitte versuche es erneut.
       success: Dein Sicherheitsschlüssel wurde erfolgreich hinzugefügt.
-    delete: Löschen
-    delete_confirmation: Bist du sicher, dass du diesen Sicherheitsschlüssel löschen möchtest?
-    description_html: Wenn du die <strong>Authentifizierung mit Sicherheitsschlüssel</strong> aktivierst, musst du einen deiner Sicherheitsschlüssel verwenden, um dich anmelden zu können.
+    delete: Entfernen
+    delete_confirmation: Möchtest du diesen Sicherheitsschlüssel wirklich entfernen?
+    description_html: Wenn du die <strong>Authentisierung mit Sicherheitsschlüssel</strong> aktivierst, musst du dich mit einem deiner Sicherheitsschlüssel anmelden.
     destroy:
-      error: Es gab ein Problem beim Löschen deines Sicherheitsschlüssels. Bitte versuche es erneut.
-      success: Dein Sicherheitsschlüssel wurde erfolgreich gelöscht.
+      error: Beim Entfernen des Sicherheitsschlüssels ist ein Fehler aufgetreten. Bitte versuche es erneut.
+      success: Dein Sicherheitsschlüssel wurde erfolgreich entfernt.
     invalid_credential: Ungültiger Sicherheitsschlüssel
     nickname_hint: Gib den Spitznamen deines neuen Sicherheitsschlüssels ein
     not_enabled: Du hast WebAuthn noch nicht aktiviert
diff --git a/config/locales/devise.ar.yml b/config/locales/devise.ar.yml
index 7b8820771..1d268a6d1 100644
--- a/config/locales/devise.ar.yml
+++ b/config/locales/devise.ar.yml
@@ -37,8 +37,8 @@ ar:
         title: تم تغيير كلمة السر
       reconfirmation_instructions:
         explanation: ندعوك لتأكيد العنوان الجديد قصد تعديله في بريدك.
-        extra: إن لم تكن صاحب هذا الطلب ، يُرجى عدم إعارة الاهتمام لهذه الرسالة. فعنوان البريد الإلكتروني المتعلق بحساب ماستدون سوف يبقى هو مِن غير أي تعديل إلّا و فقط إن قمت بالنقر على الرابط أعلاه قصد تعديله.
-        subject: 'ماستدون: تأكيد كلمة السر الخاصة بـ %{instance}'
+        extra: إن لم تكن صاحب هذا الطلب، يُرجى عدم إعارة الاهتمام لهذه الرسالة. فعنوان البريد الإلكتروني المتعلق بحساب ماستدون سوف يبقى هو مِن غير أي تعديل إلّا و فقط إن قمت بالنقر على الرابط أعلاه قصد تعديله.
+        subject: 'ماستدون: تأكيد عنوان البريد الإلكتروني الخاص بـ %{instance}'
         title: التحقق من عنوان البريد الإلكتروني
       reset_password_instructions:
         action: تغيير كلمة السر
diff --git a/config/locales/devise.ast.yml b/config/locales/devise.ast.yml
index f48c70e2d..2f84b51fb 100644
--- a/config/locales/devise.ast.yml
+++ b/config/locales/devise.ast.yml
@@ -15,10 +15,12 @@ ast:
       unconfirmed: Tienes de confirmar la direición de corréu electrónicu enantes de siguir.
     mailer:
       confirmation_instructions:
+        action: Verificar la direición de corréu electrónicu
         explanation: Creesti una cuenta en %{host} con esta direición de corréu electrónicu ya tas a un calcu d'activala. Si nun fixesti esta aición, inora esti mensaxe.
         explanation_when_pending: Solicitesti una invitación a %{host} con esta direición de corréu electrónicu. Namás que confirmes la direición de corréu electrónicu, vamos revisar la solicitú. Entrín ya non, pues aniciar la sesión pa camudar los detalles o desaniciar la cuenta, mas nun pues acceder a la mayoría de funciones hasta que la cuenta nun tea aprobada. Si se refuga la solicitú, nun ye necesario que faigas nada más o si, per otra parte, tu nun fixesti esta aición, inora esti mensaxe.
-        extra_html: Revisa tamién <a href="%{terms_path}">les regles del sirvidor</a> ya <a href="%{policy_path}">los nuesos términos del serviciu</a>.
+        extra_html: Revisa tamién <a href="%{terms_path}">les normes del sirvidor</a> ya <a href="%{policy_path}">los nuesos términos del serviciu</a>.
         subject: 'Mastodon: instrucciones de confirmación de «%{instance}»'
+        title: Verificación de la direición de corréu electrónicu
       email_changed:
         explanation: 'La direición de corréu electrónicu de la cuenta camudó a:'
         extra: Si nun camudesti la direición de corréu electrónicu, ye probable que daquién accediere a la cuenta. Camuda la contraseña agora mesmo o ponte en contautu cola alministración del sirvidor si nun yes a acceder a la cuenta.
diff --git a/config/locales/devise.bg.yml b/config/locales/devise.bg.yml
index d3d5e9b70..2881ce4c6 100644
--- a/config/locales/devise.bg.yml
+++ b/config/locales/devise.bg.yml
@@ -41,9 +41,9 @@ bg:
         subject: 'Mastodon: Потвърдете имейла за %{instance}'
         title: Потвърдете своя имейл адрес
       reset_password_instructions:
-        action: Промяна на парола
+        action: Промяна на паролата
         explanation: Поискахте нова парола за акаунта си.
-        extra: Ако не сте поискали това, моля, игнорирайте този имейл. Паролата ви няма да се промени, докато не влезете в линка по-горе и не създадете нова.
+        extra: Ако не сте заявили това, то игнорирайте това е-писмо. Паролата ви няма да се променя, докато не влезете от връзката по-горе и не създадете нова.
         subject: 'Mastodon: Указания за задаване на нова парола'
         title: Нулиране на парола
       two_factor_disabled:
@@ -72,10 +72,10 @@ bg:
       webauthn_disabled:
         explanation: Удостоверяването с ключове за сигурност е изключено за акаунта ви. Влизането вече е възможно, използвайки само ключа, породен от сдвоеното приложение TOTP.
         subject: 'Mastodon: Изключено удостоверяване с ключове за сигурност'
-        title: Ключовете за сигурност са деактивирани
+        title: Изключени ключове за сигурност
       webauthn_enabled:
         explanation: Удостоверяването с ключ за сигурност е активирано за вашия акаунт. Вашият ключ за сигурност вече може да се използва за вход.
-        subject: 'Mastodon: Активирано удостоверяване с ключ за сигурност'
+        subject: 'Mastodon: Включено удостоверяване с ключ за сигурност'
         title: Ключовете за сигурност са активирани
     omniauth_callbacks:
       failure: Не успяхме да ви упълномощим от %{kind}, защото "%{reason}".
@@ -92,7 +92,7 @@ bg:
       signed_up_but_inactive: Регистрирахте се успешно. Въпреки това, не може да влезете, тъй като акаунтът ви още не е задействан.
       signed_up_but_locked: Регистрирахте се успешно. Въпреки това, не можете да влезете, тъй като акаунтът ви е заключен.
       signed_up_but_pending: Изпратено е съобщение до адреса на имейла ви с връзка за потвърждение. След като щракнете върху линка, ние ще прегледаме заявлението ви. Ще бъдете уведомени при одобрение.
-      signed_up_but_unconfirmed: Съобщение с линк за потвърждение беше изпратено до вашия имейл адрес. Последвайте линка, за да задействате акаунта си. Проверете спам папката си, ако не сте получили такъв имейл.
+      signed_up_but_unconfirmed: Е-писмо с връзка за потвърждение е изпратено до имейла ви. Последвайте връзката, за да задействате акаунта си. Проверете папката си за спам, ако не сте получили това е-писмо.
       update_needs_confirmation: Успешно обновихте акаунта си, но трябва да потвърдим вашия нов имейл адрес. Проверете електронната си поща и отворете отворете линка за потвърждение на новия си имейл адрес. Проверете спам папката си, ако не сте получили имейл за потвърждение.
       updated: Акаунтът ви е успешно осъвременен.
     sessions:
diff --git a/config/locales/devise.csb.yml b/config/locales/devise.csb.yml
new file mode 100644
index 000000000..0de706e41
--- /dev/null
+++ b/config/locales/devise.csb.yml
@@ -0,0 +1 @@
+csb:
diff --git a/config/locales/devise.de.yml b/config/locales/devise.de.yml
index 894744af0..0c25f21b6 100644
--- a/config/locales/devise.de.yml
+++ b/config/locales/devise.de.yml
@@ -2,27 +2,27 @@
 de:
   devise:
     confirmations:
-      confirmed: Deine E-Mail-Adresse wurde bestätigt.
+      confirmed: Deine E-Mail-Adresse wurde erfolgreich bestätigt.
       send_instructions: Du wirst in wenigen Minuten eine E-Mail erhalten. Darin wird erklärt, wie du deine E-Mail-Adresse bestätigen kannst. Schau bitte auch in deinem Spam-Ordner nach, wenn du diese E-Mail nicht erhalten hast.
       send_paranoid_instructions: Falls deine E-Mail-Adresse in unserer Datenbank hinterlegt ist, wirst du in wenigen Minuten eine E-Mail erhalten. Darin wird erklärt, wie du deine E-Mail-Adresse bestätigen kannst. Schau bitte auch in deinem Spam-Ordner nach, wenn du diese E-Mail nicht erhalten hast.
     failure:
       already_authenticated: Du bist bereits angemeldet.
       inactive: Dein Konto wurde noch nicht aktiviert.
       invalid: "%{authentication_keys} oder Passwort ungültig."
-      last_attempt: Du hast noch einen Versuch, bevor dein Konto gesperrt wird.
+      last_attempt: Du hast nur noch einen Versuch, bevor dein Zugang gesperrt wird.
       locked: Dein Konto ist gesperrt.
       not_found_in_database: "%{authentication_keys} oder Passwort ungültig."
-      pending: Dein Konto wird immer noch überprüft.
+      pending: Dein Konto wird weiterhin überprüft.
       timeout: Deine Sitzung ist abgelaufen. Bitte melde dich erneut an, um fortzufahren.
       unauthenticated: Du musst dich anmelden oder registrieren, bevor du fortfahren kannst.
       unconfirmed: Du musst deine E-Mail-Adresse bestätigen, bevor du fortfahren kannst.
     mailer:
       confirmation_instructions:
         action: E-Mail-Adresse verifizieren
-        action_with_app: Bestätigen und zu %{app} zurückkehren
-        explanation: Du hast auf %{host} mit dieser E-Mail-Adresse ein Konto erstellt. Du bist nur noch einen Klick von der Aktivierung entfernt. Wenn du das nicht warst, kannst du diese E-Mail ignorieren.
-        explanation_when_pending: Du hast dich für eine Einladung bei %{host} mit dieser E-Mailadresse beworben. Sobald du deine E-Mailadresse bestätigst hast, werden wir deine Anfrage überprüfen. Du kannst dich in dieser Zeit nicht anmelden. Wenn deine Anfrage abgelehnt wird, werden deine Daten entfernt, also wird keine weitere Handlung benötigt. Wenn du das nicht warst, kannst du diese E-Mail ignorieren.
-        extra_html: Bitte lies auch die <a href="%{terms_path}">Regeln des Servers</a> und <a href="%{policy_path}">unsere Nutzungsbedingungen</a>.
+        action_with_app: Bestätigen – und dann zur App %{app} zurückkehren
+        explanation: Du hast mit dieser E-Mail-Adresse ein Konto auf %{host} erstellt. Du bist nur noch einen Klick von der Aktivierung entfernt. Wenn du das nicht warst, kannst du diese E-Mail ignorieren.
+        explanation_when_pending: Du hast dich für eine Einladung bei %{host} mit dieser E-Mail-Adresse beworben. Sobald du deine E-Mail-Adresse bestätigt hast, werden wir deine Anfrage überprüfen. Du kannst dich in dieser Zeit nicht anmelden. Wenn deine Anfrage abgelehnt wird, werden deine Daten entfernt – von dir ist keine weitere Handlung notwendig. Wenn du das nicht warst, kannst du diese E-Mail ignorieren.
+        extra_html: Bitte beachte auch die <a href="%{terms_path}">Serverregeln</a> und <a href="%{policy_path}">unsere Datenschutzerklärung</a>.
         subject: 'Mastodon: Bestätigung deines Kontos bei %{instance}'
         title: E-Mail-Adresse verifizieren
       email_changed:
@@ -31,35 +31,35 @@ de:
         subject: 'Mastodon: E-Mail-Adresse geändert'
         title: Neue E-Mail-Adresse
       password_change:
-        explanation: Das Passwort für deinen Account wurde geändert.
+        explanation: Deine Zugangsdaten wurden geändert.
         extra: Wenn du dein Passwort nicht geändert hast, dann wird es vermutlich so sein, dass jemand Zugriff auf deinem Account erlangt hat. Bitte ändere sofort dein Passwort oder kontaktiere den Administrator des Servers, wenn du dich ausgesperrt hast.
         subject: 'Mastodon: Passwort geändert'
         title: Passwort geändert
       reconfirmation_instructions:
         explanation: Bestätige deine neue E-Mail-Adresse, um sie zu ändern.
         extra: Wenn diese Änderung nicht von dir ausgeführt wurde, dann solltest du diese E-Mail ignorieren. Die E-Mail-Adresse für deinen Mastodon-Account wird sich nicht ändern, bis du den obigen Link anklickst.
-        subject: 'Mastodon: Bestätige E-Mail-Adresse für %{instance}'
+        subject: 'Mastodon: E-Mail-Adresse für %{instance} bestätigen'
         title: E-Mail-Adresse verifizieren
       reset_password_instructions:
-        action: Ändere Passwort
+        action: Passwort ändern
         explanation: Du hast ein neues Passwort für deinen Account angefragt.
         extra: Wenn du diese Anfrage nicht gestellt hast, solltest du diese E-Mail ignorieren. Dein Passwort wird sich nicht ändern, solange du den obigen Link anklickst und ein neues erstellst.
-        subject: 'Mastodon: Passwort zurücksetzen'
+        subject: 'Mastodon: Anleitung zum Zurücksetzen deines Passworts'
         title: Passwort zurücksetzen
       two_factor_disabled:
-        explanation: Zwei-Faktor-Authentifizierung für dein Konto wurde deaktiviert. Login ist jetzt nur mit E-Mail-Adresse und Passwort möglich.
-        subject: 'Mastodon: Zwei‐Faktor‐Authentifizierung deaktiviert'
+        explanation: Zwei-Faktor-Authentisierung (2FA) für dein Konto wurde deaktiviert. Eine Anmeldung ist jetzt nur noch mit E-Mail-Adresse und Passwort möglich.
+        subject: 'Mastodon: Zwei‐Faktor‐Authentisierung (2FA) deaktiviert'
         title: 2FA deaktiviert
       two_factor_enabled:
-        explanation: Zwei-Faktor-Authentifizierung wurde für dein Konto aktiviert. Ein Token, das von der verbundenen TOTP-App generiert wird, wird für den Login benötigt.
-        subject: 'Mastodon: Zwei‐Faktor‐Authentifizierung aktiviert'
+        explanation: Die Zwei-Faktor-Authentisierung (2FA) wurde für dein Konto aktiviert. Das zeitbasierte Einmalkennwort, das von deiner TOTP-App generiert wird, muss bei jeder Anmeldung zusätzlich eingegeben werden.
+        subject: 'Mastodon: Zwei‐Faktor‐Authentisierung (2FA) aktiviert'
         title: 2FA aktiviert
       two_factor_recovery_codes_changed:
         explanation: Die vorherigen Wiederherstellungscodes wurden ungültig gemacht und es wurden neue erstellt.
         subject: 'Mastodon: Zwei-Faktor-Wiederherstellungscodes neu erstellt'
         title: 2FA-Wiederherstellungscodes geändert
       unlock_instructions:
-        subject: 'Mastodon: Konto entsperren'
+        subject: 'Mastodon: Anleitung zur Entsperrung deines Kontos'
       webauthn_credential:
         added:
           explanation: Der folgende Sicherheitsschlüssel wurde zu deinem Konto hinzugefügt
@@ -67,28 +67,28 @@ de:
           title: Ein neuer Sicherheitsschlüssel wurde hinzugefügt
         deleted:
           explanation: Der folgende Sicherheitsschlüssel wurde aus deinem Konto gelöscht
-          subject: 'Mastodon: Sicherheitsschlüssel gelöscht'
-          title: Einer deiner Sicherheitsschlüssel wurde gelöscht
+          subject: 'Mastodon: Sicherheitsschlüssel entfernt'
+          title: Einer deiner Sicherheitsschlüssel wurde entfernt
       webauthn_disabled:
-        explanation: Die Authentifizierung mit Sicherheitsschlüssel wurde für dein Konto deaktiviert. Der Login ist nun nur mit dem Token möglich, der von der eingerichteten TOTP-App generiert wird.
-        subject: 'Mastodon: Authentifizierung mit Sicherheitsschlüssel deaktiviert'
+        explanation: Die Authentisierung mit Sicherheitsschlüsseln wurde für dein Konto deaktiviert. Die Anmeldung ist jetzt nur noch mit dem Token möglich, der von der eingerichteten TOTP-App generiert wird.
+        subject: 'Mastodon: Authentisierung mit Sicherheitsschlüsseln deaktiviert'
         title: Sicherheitsschlüssel deaktiviert
       webauthn_enabled:
-        explanation: Die Authentifizierung mit einem Sicherheitsschlüssel wurde für dein Konto aktiviert. Dein Sicherheitsschlüssel kann nun für die Anmeldung verwendet werden.
-        subject: 'Mastodon: Authentifizierung mit Sicherheitsschlüssel aktiviert'
+        explanation: Die Authentisierung mit Sicherheitsschlüssel wurde für dein Konto aktiviert. Dein Sicherheitsschlüssel kann nun für die Anmeldung verwendet werden.
+        subject: 'Mastodon: Authentisierung mit Sicherheitsschlüssel aktiviert'
         title: Sicherheitsschlüssel aktiviert
     omniauth_callbacks:
       failure: Du konntest nicht mit deinem %{kind}-Konto angemeldet werden, weil „%{reason}“.
       success: Du hast dich erfolgreich mit deinem %{kind}-Konto angemeldet.
     passwords:
-      no_token: Du kannst diese Seite nur über den Link aus der E-Mail zum Passwort-Zurücksetzen aufrufen. Wenn du einen solchen Link aufgerufen hast, stelle bitte sicher, dass du die vollständige Adresse aufrufst.
+      no_token: Du kannst diese Seite nur über den Link aus der E-Mail zum Zurücksetzen des Passworts aufrufen. Wenn du einen solchen Link aufgerufen hast, vergewissere dich bitte, dass du die vollständige Adresse aufrufst.
       send_instructions: Du erhältst in wenigen Minuten eine E-Mail. Darin wird erklärt, wie du dein Passwort zurücksetzen kannst.
       send_paranoid_instructions: Falls deine E-Mail-Adresse in unserer Datenbank hinterlegt ist, erhältst du in wenigen Minuten eine E-Mail. Darin wird erklärt, wie du dein Passwort zurücksetzen kannst.
       updated: Ihr Passwort wurde erfolgreich geändert. Du bist jetzt angemeldet.
       updated_not_active: Dein Passwort wurde erfolgreich geändert.
     registrations:
-      destroyed: Dein Konto wurde gelöscht.
-      signed_up: Willkommen! Du hast dich erfolgreich registriert.
+      destroyed: Tschüss! Dein Konto wurde erfolgreich gelöscht. Wir hoffen, dich bald wiederzusehen.
+      signed_up: Herzlich willkommen! Du hast dich erfolgreich registriert.
       signed_up_but_inactive: Du hast dich erfolgreich registriert. Allerdings ist dein Konto noch nicht aktiviert und du kannst dich daher noch nicht anmelden.
       signed_up_but_locked: Du hast dich erfolgreich registriert. Allerdings ist dein Konto gesperrt und du kannst dich daher nicht anmelden.
       signed_up_but_pending: Eine Nachricht mit einem Bestätigungslink wurde an dich per E-Mail geschickt. Nachdem du diesen Link angeklickt hast, werden wir deine Anfrage überprüfen. Du wirst benachrichtigt werden, falls die Anfrage angenommen wurde.
@@ -111,5 +111,5 @@ de:
       not_found: nicht gefunden
       not_locked: ist nicht gesperrt
       not_saved:
-        one: '1 Fehler hat verhindert, dass %{resource} gespeichert wurde:'
-        other: "%{count} Fehler haben verhindert, dass %{resource} gespeichert wurde:"
+        one: '1 Fehler verhinderte, dass %{resource} gespeichert wurde:'
+        other: "%{count} Fehler verhinderten, dass %{resource} gespeichert wurde:"
diff --git a/config/locales/devise.eo.yml b/config/locales/devise.eo.yml
index 68a90171c..1c13490e5 100644
--- a/config/locales/devise.eo.yml
+++ b/config/locales/devise.eo.yml
@@ -6,7 +6,7 @@ eo:
       send_instructions: Vi ricevos retmesaĝon kun instrukcioj por konfirmi vian retadreson ene de kelkaj minutoj. Bonvolu kontroli vian spamujon se vi ne ricevis ĉi tiun retmesaĝon.
       send_paranoid_instructions: Se via retadreso ekzistas en nia datumbazo, vi ricevos retmesaĝon kun instrukcioj por konfirmi vian retadreson ene de kelkaj minutoj. Bonvolu kontroli vian spamujon se vi ne ricevis ĉi tiun retmesaĝon.
     failure:
-      already_authenticated: Vi jam ensalutis.
+      already_authenticated: Vi jam salutis.
       inactive: Via konto ankoraŭ ne estas konfirmita.
       invalid: Nevalida %{authentication_keys} aŭ pasvorto.
       last_attempt: Vi ankoraŭ povas provi unufoje antaŭ ol via konto estos ŝlosita.
diff --git a/config/locales/devise.et.yml b/config/locales/devise.et.yml
index 9fd54d142..da1cc989d 100644
--- a/config/locales/devise.et.yml
+++ b/config/locales/devise.et.yml
@@ -48,15 +48,15 @@ et:
         title: Salasõna lähtestamine
       two_factor_disabled:
         explanation: Kontol on kaheastmeline autentimine välja lülitatud. Sisenemine on võimalik ainult kasutades e-postiaadressi ja salasõna.
-        subject: 'Mastodon: Kahe-etapine autentimine välja lülitatud'
+        subject: 'Mastodon: Kaheastmeline autentimine välja lülitatud'
         title: 2FA keelatud
       two_factor_enabled:
         explanation: Kontol on sisse lülitatud kaheastmeline autentimine. Sisenemiseks on vajalik ühekordne aeguv võti TOTP-rakenduse poolt.
-        subject: 'Mastodon: Kahe-etapine autentimine sisse lülitatud'
+        subject: 'Mastodon: kaheastmeline autentimine sisse lülitatud'
         title: 2FA lubatud
       two_factor_recovery_codes_changed:
         explanation: Eelmised taastekoodid on nüüd kehtetud ning loodud uued.
-        subject: 'Mastodon: Kahe-etapise autentimise taastuskoodid taasloodud'
+        subject: 'Mastodon: Kaheastmelise autentimise taastuskoodid taasloodud'
         title: 2FA taastekoodid muudetud
       unlock_instructions:
         subject: 'Mastodon: Lahti lukustamis juhendid'
@@ -78,7 +78,7 @@ et:
         subject: 'Mastodon: turvavõtme autentimine sisse lülitatud'
         title: Turvavõtmed on sisse lülitatud
     omniauth_callbacks:
-      failure: Ei saanud teid tuvastada %{kind} kaudu, kuna "%{reason}".
+      failure: Ei saanud sind tuvastada %{kind} kaudu, kuna "%{reason}".
       success: Tuvastamine %{kind} konto järgi õnnestus.
     passwords:
       no_token: Sellele leheküljele ei pääse tulemata salasõna lähtestamise e-kirjast. Kui tuled salasõna lähtestamise e-kirjast, palun veendu, et kasutasid tervet saadetud URLi.
@@ -87,7 +87,7 @@ et:
       updated: Salasõna muutmine õnnestus. Oled nüüd sisse logitud.
       updated_not_active: Sinu salasõna muutmine õnnestus.
     registrations:
-      destroyed: Nägemist! sinu konto sulgemine õnnestus. Me loodame sind varsti taas näha.
+      destroyed: Nägemist! Sinu konto sulgemine õnnestus. Me loodame sind varsti taas näha.
       signed_up: Tere tulemast! Sinu konto loomine õnnestus.
       signed_up_but_inactive: Sinu konto loodi edukalt, kuid me ei saanud sind sisse logida, kuna konto pole veel aktiveeritud.
       signed_up_but_locked: Sinu konto loodi edukalt, kuid me ei saanud sind sisse logida, kuna konto on lukustatud.
diff --git a/config/locales/devise.fi.yml b/config/locales/devise.fi.yml
index ce5a35efd..86360c2ab 100644
--- a/config/locales/devise.fi.yml
+++ b/config/locales/devise.fi.yml
@@ -2,29 +2,29 @@
 fi:
   devise:
     confirmations:
-      confirmed: Sähköpostiosoitteesi on vahvistettu onnistuneesti.
+      confirmed: Sähköpostiosoitteesi on vahvistettu.
       send_instructions: Saat pian sähköpostitse ohjeet sähköpostiosoitteesi vahvistamiseen. Jos et saanut viestiä, tarkista roskapostikansiosi.
-      send_paranoid_instructions: Jos sähköpostiosoitteesi on tietokannassamme, saat pian ohjeet osoitteesi vahvistamiseen. Jos et saanut viestiä, tarkista roskapostikansiosi.
+      send_paranoid_instructions: Jos sähköpostiosoitteesi on tiedossammme, saat pian sähköpostiisi ohjeet sen vahvistamiseen. Jos et saanut viestiä, tarkista roskapostikansiosi.
     failure:
       already_authenticated: Olet jo kirjautunut sisään.
       inactive: Tiliäsi ei ole vielä aktivoitu.
       invalid: Virheellinen %{authentication_keys} tai salasana.
-      last_attempt: Sinulla on vielä yksi yritys ennen kuin tunnuksesi lukitaan.
+      last_attempt: Sinulla on vielä yksi yritys ennen kuin tilisi lukitaan.
       locked: Tilisi on lukittu.
       not_found_in_database: Virheellinen %{authentication_keys} tai salasana.
-      pending: Tämä tili on vielä tarkistamatta.
+      pending: Tilisi on vielä tarkistamatta.
       timeout: Istuntosi on umpeutunut. Jatka kirjautumalla uudelleen sisään.
       unauthenticated: Sinun pitää kirjautua sisään tai rekisteröityä ennen kuin voit jatkaa.
       unconfirmed: Vahvista sähköpostiosoitteesi, ennen kuin jatkat.
     mailer:
       confirmation_instructions:
-        action: Vahvista sähköpostiosoitteesi
+        action: Vahvista sähköpostiosoite
         action_with_app: Vahvista ja palaa %{app}
-        explanation: Olet luonut tilin palvelimelle %{host} käyttäen tätä sähköpostiosoitetta. Aktivoi tili yhdellä klikkauksella. Jos et luonut tiliä itse, voit jättää tämän viestin huomiotta.
+        explanation: Olet luonut tilin palvelimelle %{host} käyttäen tätä sähköpostiosoitetta. Olet painalluksen päässä tilin aktivoinnista. Jos et luonut tiliä itse, voit jättää tämän viestin huomiotta.
         explanation_when_pending: Teit hakemuksen kutsusta palvelimelle %{host} tällä sähköpostiosoitteella. Kun olet vahvistanut sähköpostiosoitteesi, tarkistamme hakemuksesi. Voit kirjautua sisään muuttaaksesi hakemuksen sisältöä tai poistaaksesi tilin, mutta et voi käyttää suurinta osaa toiminnallisuudesta ennen kuin hakemuksesi on hyväksytty. Jos hakemuksesi hylätään, tietosi poistetaan eikä sinulta tarvita enempää toimia. Jos sinä et tehnyt hakemusta, voit jättää tämän viestin huomiotta.
-        extra_html: Katso myös <a href="%{terms_path}">palvelimen säännöt</a> ja <a href="%{policy_path}">käyttöehdot</a>.
+        extra_html: Tutustu myös <a href="%{terms_path}">palvelimen sääntöihin</a> ja <a href="%{policy_path}">palveluehtoihimme</a>.
         subject: 'Mastodon: Vahvistusohjeet instanssille %{instance}'
-        title: Vahvista sähköpostiosoitteesi
+        title: Vahvista sähköpostiosoite
       email_changed:
         explanation: 'Tilin sähköpostiosoitteeksi vaihdetaan:'
         extra: Jos et vaihtanut sähköpostiosoitettasi, joku muu on todennäköisesti päässyt käyttämään tiliäsi. Vaihda salasanasi viipymättä. Jos et pääse kirjautumaan tilillesi, ota yhteyttä instanssin ylläpitäjään.
@@ -33,41 +33,41 @@ fi:
       password_change:
         explanation: Tilisi salasana on vaihdettu.
         extra: Jos et vaihtanut salasanaasi, joku muu on todennäköisesti päässyt käyttämään tiliäsi. Vaihda salasanasi viipymättä. Jos et pääse kirjautumaan tilillesi, ota yhteyttä instanssin ylläpitäjään.
-        subject: 'Mastodon: Salasana vaihdettu'
+        subject: 'Mastodon: salasana vaihdettu'
         title: Salasana vaihdettu
       reconfirmation_instructions:
         explanation: Vahvista uusi sähköpostiosoite, niin muutos astuu voimaan.
         extra: Jos et tehnyt muutosta itse, voit jättää tämän viestin huomiotta. Mastodon-tilin sähköpostiosoitetta ei vaihdeta, ennen kuin klikkaat yllä olevaa linkkiä.
-        subject: 'Mastodon: Vahvista sähköpostiosoite instanssille %{instance}'
+        subject: 'Mastodon: vahvista sähköpostiosoite: %{instance}'
         title: Vahvista sähköpostiosoite
       reset_password_instructions:
         action: Vaihda salasana
         explanation: Pyysit tilillesi uuden salasanan.
         extra: Jos et tehnyt pyyntöä itse, voit jättää tämän viestin huomiotta. Salasanaasi ei vaihdeta, ennen kuin klikkaat yllä olevaa linkkiä ja luot uuden salasanan.
-        subject: 'Mastodon: Ohjeet salasanan vaihtoon'
+        subject: 'Mastodon: ohjeet salasanan vaihtoon'
         title: Salasanan vaihto
       two_factor_disabled:
-        explanation: Kaksivaiheinen tunnistus tilillesi on otettu pois käytöstä. Kirjautuminen onnistuu nyt pelkällä sähköpostiosoitteella ja salasanalla.
-        subject: 'Mastodon: Kaksivaiheinen tunnistut otettu pois käytöstä'
-        title: 2FA poistettu käytöstä
+        explanation: Kaksivaiheinen todennus tilillesi poistettiin käytöstä. Kirjautuminen onnistuu nyt käyttäen pelkkää sähköpostiosoitetta ja salasanaa.
+        subject: 'Mastodon: kaksivaiheinen todennus poistettu käytöstä'
+        title: 2-vaiheinen todennus pois käytöstä
       two_factor_enabled:
-        explanation: Kaksivaiheinen tunnistus on otettu käyttöön tilillesi. Koodi kaksivaiheisen tunnistuksen sovelluksesta tarvitaan kirjautumiseen.
-        subject: 'Mastodon: Kaksivaiheinen tunnistus otettu käyttöön'
-        title: 2FA käytössä
+        explanation: Kaksivaiheinen tunnistus on otettu käyttöön tilillesi. Kaksivaiheisen tunnistuksen sovelluksesta saatu koodi tarvitaan kirjautumiseen.
+        subject: 'Mastodon: kaksivaiheinen todennus otettu käyttöön'
+        title: 2-vaiheinen todennus käytössä
       two_factor_recovery_codes_changed:
-        explanation: Aiemmat palautuskoodi on poistettu käytöstä ja uudet on luotu.
-        subject: 'Mastodon: Kaksivaiheisen tunnistuksen palautuskoodit uudelleenluotu'
-        title: 2FA palautuskoodit vaihdettu
+        explanation: Uudet palautuskoodit on nyt luotu ja vanhat on mitätöity.
+        subject: 'Mastodon: kaksivaiheisen todennuksen palautuskoodit luotiin uudelleen'
+        title: 2-vaiheisen todennuksen palautuskoodit vaihdettiin
       unlock_instructions:
-        subject: 'Mastodon: Ohjeet lukituksen poistoon'
+        subject: 'Mastodon: lukituksen poistamisen ohjeet'
       webauthn_credential:
         added:
           explanation: Seuraava suojausavain on lisätty tilillesi
-          subject: 'Mastodon: Uusi suojausavain'
+          subject: 'Mastodon: uusi suojausavain'
           title: Uusi suojausavain on lisätty
         deleted:
           explanation: Seuraava suojausavain on poistettu tililtäsi
-          subject: 'Mastodon: Suojausavain poistettu'
+          subject: 'Mastodon: suojausavain poistettu'
           title: Yksi suojausavaimistasi on poistettu
       webauthn_disabled:
         explanation: Suojausavaimilla todennus on poistettu käytöstä tililtäsi. Kirjautuminen on nyt mahdollista käyttämällä vain paritetun TOTP-sovelluksen luomaa tokenia.
diff --git a/config/locales/devise.fr.yml b/config/locales/devise.fr.yml
index 1047fae0b..8eb744d94 100644
--- a/config/locales/devise.fr.yml
+++ b/config/locales/devise.fr.yml
@@ -15,21 +15,21 @@ fr:
       pending: Votre compte est toujours en cours d'approbation.
       timeout: Votre session a expiré. Veuillez vous reconnecter pour continuer.
       unauthenticated: Vous devez vous connecter ou vous inscrire pour continuer.
-      unconfirmed: Vous devez valider votre adresse courriel pour continuer.
+      unconfirmed: Vous devez valider votre adresse de courriel pour continuer.
     mailer:
       confirmation_instructions:
-        action: Vérifier l’adresse courriel
+        action: Vérifier l’adresse de courriel
         action_with_app: Confirmer et retourner à %{app}
         explanation: Vous avez créé un compte sur %{host} avec cette adresse courriel. Vous êtes à un clic de l’activer. Si ce n’était pas vous, veuillez ignorer ce courriel.
         explanation_when_pending: Vous avez demandé à vous inscrire à %{host} avec cette adresse de courriel. Une fois que vous aurez confirmé cette adresse, nous étudierons votre demande. Vous ne pourrez pas vous connecter d’ici-là. Si votre demande est refusée, vos données seront supprimées du serveur, aucune action supplémentaire de votre part n’est donc requise. Si vous n’êtes pas à l’origine de cette demande, veuillez ignorer ce message.
         extra_html: Merci de consultez également <a href="%{terms_path}">les règles du serveur</a> et <a href="%{policy_path}">nos conditions d’utilisation</a>.
         subject: 'Mastodon : Merci de confirmer votre inscription sur %{instance}'
-        title: Vérifiez l’adresse courriel
+        title: Vérifiez l’adresse de courriel
       email_changed:
-        explanation: 'L’adresse courriel de votre compte est en cours de modification pour devenir :'
+        explanation: 'L’adresse de courriel de votre compte est en cours de modification pour devenir :'
         extra: Si vous n’avez pas changé votre adresse courriel, il est probable que quelqu’un ait eu accès à votre compte. Veuillez changer votre mot de passe immédiatement ou contacter l’administrateur·rice du serveur si vous êtes bloqué·e hors de votre compte.
         subject: 'Mastodon : Courriel modifié'
-        title: Nouvelle adresse courriel
+        title: Nouvelle adresse de courriel
       password_change:
         explanation: Le mot de passe de votre compte a été changé.
         extra: Si vous n’avez pas changé votre mot de passe, il est probable que quelqu’un ait eu accès à votre compte. Veuillez changer votre mot de passe immédiatement ou contacter l’administrateur·rice du serveur si vous êtes bloqué·e hors de votre compte.
@@ -37,8 +37,8 @@ fr:
         title: Mot de passe modifié
       reconfirmation_instructions:
         explanation: Confirmez la nouvelle adresse pour changer votre courriel.
-        extra: Si ce changement n’a pas été initié par vous, veuillez ignorer ce courriel. L’adresse courriel du compte Mastodon ne changera pas tant que vous n’aurez pas cliqué sur le lien ci-dessus.
-        subject: 'Mastodon : Confirmez l’adresse courriel pour %{instance}'
+        extra: Si ce changement n’a pas été initié par vous, veuillez ignorer ce courriel. L’adresse de courriel du compte Mastodon ne changera pas tant que vous n’aurez pas cliqué sur le lien ci-dessus.
+        subject: 'Mastodon : Confirmez l’adresse de courriel pour %{instance}'
         title: Vérifier l’adresse courriel
       reset_password_instructions:
         action: Modifier le mot de passe
@@ -82,8 +82,8 @@ fr:
       success: Authentifié avec succès via %{kind}.
     passwords:
       no_token: Vous ne pouvez accéder à cette page sans passer par un courriel de réinitialisation de mot de passe. Si vous êtes passé⋅e par un courriel de ce type, assurez-vous d’utiliser l’URL complète.
-      send_instructions: Vous allez recevoir les instructions de réinitialisation du mot de passe dans quelques instants. Veuillez, dans le cas où vous ne recevriez pas ce message, vérifier votre dossier d’indésirables.
-      send_paranoid_instructions: Si votre adresse électronique existe dans notre base de données, vous allez recevoir un lien de réinitialisation par courriel. Veuillez, dans le cas où vous ne recevriez pas ce message, vérifier votre dossier d’indésirables.
+      send_instructions: Si votre adresse de courriel existe dans notre base de données, vous allez recevoir un lien de réinitialisation par courriel. Veuillez, dans le cas où vous ne recevriez pas ce message, vérifier votre dossier d’indésirables.
+      send_paranoid_instructions: Si votre adresse de courriel existe dans notre base de données, vous allez recevoir un lien de réinitialisation par courriel. Veuillez, dans le cas où vous ne recevriez pas ce message, vérifier votre dossier d’indésirables.
       updated: Votre mot de passe a été modifié avec succès, vous êtes maintenant connecté.
       updated_not_active: Votre mot de passe a été modifié avec succès.
     registrations:
@@ -92,8 +92,8 @@ fr:
       signed_up_but_inactive: Vous êtes bien enregistré·e. Vous ne pouvez cependant pas vous connecter car votre compte n’est pas encore activé.
       signed_up_but_locked: Vous êtes bien enregistré·e. Vous ne pouvez cependant pas vous connecter car votre compte est verrouillé.
       signed_up_but_pending: Un message avec un lien de confirmation a été envoyé à votre adresse courriel. Après avoir cliqué sur le lien, nous examinerons votre demande. Vous serez informé·e si elle a été approuvée.
-      signed_up_but_unconfirmed: Un message contenant un lien de confirmation a été envoyé à votre adresse courriel. Ouvrez ce lien pour activer votre compte. Veuillez vérifier votre dossier d'indésirables si vous ne recevez pas le courriel.
-      update_needs_confirmation: Votre compte a bien été mis à jour, mais nous devons vérifier votre nouvelle adresse courriel. Merci de vérifier vos courriels et de cliquer sur le lien de confirmation pour finaliser la validation de votre nouvelle adresse. Si vous n'avez pas reçu le courriel, vérifiez votre dossier de spams.
+      signed_up_but_unconfirmed: Un message contenant un lien de confirmation a été envoyé à votre adresse de courriel. Ouvrez ce lien pour activer votre compte. Veuillez vérifier votre dossier d'indésirables si vous ne recevez pas le courriel.
+      update_needs_confirmation: Votre compte a bien été mis à jour, mais nous devons vérifier votre nouvelle adresse de courriel. Merci de vérifier vos courriels et de cliquer sur le lien de confirmation pour finaliser la validation de votre nouvelle adresse. Si vous n'avez pas reçu le courriel, vérifiez votre dossier d’indésirables.
       updated: Votre compte a été modifié avec succès.
     sessions:
       already_signed_out: Déconnecté·e avec succès.
diff --git a/config/locales/devise.fy.yml b/config/locales/devise.fy.yml
index 3a7ead83e..fd246227a 100644
--- a/config/locales/devise.fy.yml
+++ b/config/locales/devise.fy.yml
@@ -6,13 +6,13 @@ fy:
       send_instructions: Do ûntfangst fia in e-mailberjocht ynstruksjes hoe’tsto dyn account befêstigje kinst. Sjoch yn de map net-winske wannear’t neat ûntfongen waard.
       send_paranoid_instructions: As dyn e-mailadres yn de database stiet, ûntfangsto fia in e-mailberjocht ynstruksjes hoe’tsto dyn account befêstigje kinst. Sjoch yn de map net-winske wannear’t neat ûntfongen waard.
     failure:
-      already_authenticated: Do bist al oanmeld.
+      already_authenticated: Jo binne al oanmeld.
       inactive: Jo account is noch net aktivearre.
       invalid: "%{authentication_keys} of wachtwurd ûnjildich."
       last_attempt: Do hast noch ien besykjen oer eardat dyn account blokkearre wurdt.
-      locked: Dyn account is blokkearre.
+      locked: Jo account is blokkearre.
       not_found_in_database: "%{authentication_keys} of wachtwurd ûnjildich."
-      pending: Dyn account moat noch hieltyd beoardiele wurde.
+      pending: Jo account moat noch hieltyd beoardiele wurde.
       timeout: Dyn sesje is ferrûn, meld dy opnij oan.
       unauthenticated: Jo moatte oanmelde of registrearje.
       unconfirmed: Do moatst earst dyn account befêstigje.
diff --git a/config/locales/devise.ga.yml b/config/locales/devise.ga.yml
index 6a8e0ec75..0949e140d 100644
--- a/config/locales/devise.ga.yml
+++ b/config/locales/devise.ga.yml
@@ -1,7 +1,13 @@
 ---
 ga:
   devise:
+    failure:
+      locked: Tá do chuntas faoi ghlas.
     mailer:
+      email_changed:
+        title: Seoladh ríomhphoist nua
+      password_change:
+        title: Pasfhocal athraithe
       reset_password_instructions:
         action: Athraigh pasfhocal
   errors:
diff --git a/config/locales/devise.hu.yml b/config/locales/devise.hu.yml
index ddadd1789..3a37ec29e 100644
--- a/config/locales/devise.hu.yml
+++ b/config/locales/devise.hu.yml
@@ -7,43 +7,43 @@ hu:
       send_paranoid_instructions: Ha az e-mail címed már szerepel az adatbázisunkban, néhány percen belül kapsz egy levelet az e-mail cím megerősítésére vonatkozó utasításokkal. Kérjük, ellenőrizd a spam mappád, ha nem látod az e-mailt.
     failure:
       already_authenticated: Már bejelentkeztél.
-      inactive: A fiókod még nincs aktiválva.
+      inactive: Fiókodat még nem aktiválták.
       invalid: Helytelen %{authentication_keys} vagy jelszó.
       last_attempt: Már csak egy próbálkozásod maradt, mielőtt a fiókodat zároljuk.
       locked: A fiókodat zároltuk.
       not_found_in_database: Helytelen %{authentication_keys} vagy jelszó.
-      pending: A fiókod még engedélyezésre vár.
+      pending: A fiók még áttekintés alatt áll.
       timeout: A munkameneted lejárt. Kérjük, a folytatáshoz jelentkezz be újra.
       unauthenticated: A folytatás előtt be kell jelentkezned vagy regisztrálnod kell.
-      unconfirmed: A folytatás előtt meg kell erősítened az e-mail címed.
+      unconfirmed: A folytatás előtt meg kell erősíteni az email címet.
     mailer:
       confirmation_instructions:
-        action: Erősítsd meg az e-mail címedet
-        action_with_app: Megerősítés majd vissza ide %{app}
-        explanation: Ezzel az e-mail címmel kezdeményeztek regisztrációt a(z) %{host} oldalon. Csak egy kattintás, és a felhasználói fiókodat aktiváljuk. Ha a regisztrációt nem te kezdeményezted, kérjük tekintsd ezt az e-mailt tárgytalannak.
-        explanation_when_pending: Ezzel az e-mail címmel meghívást kértél a(z) %{host} oldalon. Ahogy megerősíted az e-mail címed, átnézzük a jelentkezésedet. Ennek ideje alatt nem tudsz belépni. Ha a jelentkezésed elutasítjuk, az adataidat töröljük, más teendőd nincs. Ha a kérelmet nem te kezdeményezted, kérjük tekintsd ezt az e-mailt tárgytalannak.
-        extra_html: Tekintsd át a <a href="%{terms_path}">a kiszolgáló szabályait</a> és <a href="%{policy_path}">a felhasználási feltételeket</a>.
-        subject: 'Mastodon: Megerősítési lépések ehhez az instancehez: %{instance}'
-        title: E-mail cím megerősítése
+        action: Email cím ellenőrzése
+        action_with_app: 'Megerősítés majd visszatérés: %{app}'
+        explanation: Ezzel az email címmel kezdeményeztek regisztrációt %{host} kiszolgálón. Csak egy kattintás, és a felhasználói fiók bekapcsolásra kerül. Ha a regisztráció kezdeményezése téves volt, tekintsük ezt az emailt tárgytalannak.
+        explanation_when_pending: Ezzel az email címmel meghívás kérés történt %{host} kiszolgálón. Az email cím megerősítése után a jelentkezés áttekintésre kerül. Ennek ideje alatt nem lehet belépni. Ha a jelentkezés elutasításra kerül, az adatok törlésre kerülnek, más teendő nincs. Ha a kérelem kezdeményezése téves volt, tekintsük ezt az emailt tárgytalannak.
+        extra_html: Tekintsük át a <a href="%{terms_path}">a kiszolgáló szabályait</a> és a <a href="%{policy_path}">felhasználási feltételeket</a>.
+        subject: 'Mastodon: Megerősítési utasítások: %{instance}'
+        title: Email cím megerősítése
       email_changed:
         explanation: 'A fiókodhoz tartozó e-mail cím a következőre változik:'
         extra: Ha nem változtattad meg az e-mail címed, akkor valószínű, hogy valaki hozzáférhetett a fiókodhoz. Kérjük, azonnal változtasd meg a jelszavadat, vagy lépj kapcsolatba a szerver adminisztrátorával, ha ki vagy zárva a fiókodból.
-        subject: 'Mastodon: a fiókodhoz tartozó e-mail címet megváltoztattuk'
-        title: Új e-mail cím
+        subject: 'Mastodon: a fiókhoz tartozó email cím megváltoztatásra került'
+        title: Új email cím
       password_change:
-        explanation: A fiókodhoz tartozó jelszót megváltoztattuk.
-        extra: Ha nem te kérted a fiókod jelszavának módosítását, akkor valaki hozzáférhetett a fiókodhoz. Legjobb, ha azonnal megváltoztatod a jelszavadat; ha nem férsz hozzá a fiókodhoz, vedd fel a kapcsolatot a kiszolgálód adminisztrátorával.
-        subject: 'Mastodon: Jelszavad megváltoztattuk'
-        title: Sikeres jelszómódosítás
+        explanation: A fiókhoz tartozó jelszó megváltoztatásra került.
+        extra: Ha a fiók jelszavának módosítási kérelme téves volt, akkor valaki hozzáférhetett a fiókhoz. Legjobb, a jelszó azonnali megváltoztatása vagy ha kizárásra kerültünk a fiókból, vegyük fel a kapcsolatot a kiszolgáló adminisztrátorával.
+        subject: 'Mastodon: A jelszó megváltoztatásra került'
+        title: A jelszó megváltoztatásra került
       reconfirmation_instructions:
-        explanation: Az e-mail cím megváltoztatásához meg kell erősítened az új címet.
-        extra: Amennyiben nem te kezdeményezted a módosítást, kérjük tekintsd ezt az e-mailt tárgytalannak. A Mastodon fiókodhoz tartozó e-mail címed változatlan marad mindaddig, amíg rá nem kattintasz a fenti linkre.
-        subject: 'Mastodon: erősítsd meg a(z) %{instance} szerverhez tartozó e-mail címed'
-        title: E-mail cím megerősítése
+        explanation: Az email cím megváltoztatásához meg kell erősíteni az új email címet.
+        extra: Amennyiben a kezdeményezés téves volt, tekintsük ezt az emailt tárgytalannak. A Mastodon fiókhoz tartozó email cím változatlan marad a fenti hivatkozásra kattintásig.
+        subject: 'Mastodon: Email cím megerősítése: %{instance}'
+        title: Email cím megerősítése
       reset_password_instructions:
         action: Jelszó módosítása
-        explanation: A fiókodhoz tartozó jelszó módosítását kezdeményezted.
-        extra: Amennyiben nem te kezdeményezted a módosítást, kérjük tekintsd ezt az e-mailt tárgytalannak. A Mastodon fiókodhoz tartozó jelszavad változatlan marad mindaddig, amíg újat nem hozol létre a fenti linkre kattintva.
+        explanation: A fiókhoz tartozó jelszó módosítása kezdeményezésre került.
+        extra: Amennyiben a kezdeményezés téves volt, tekintsük ezt az emailt tárgytalannak. A Mastodon fiókhoz tartozó jelszó változatlan marad a fenti hivatkozásra kattintásig.
         subject: 'Mastodon: Jelszó visszaállítási lépések'
         title: Jelszó visszaállítása
       two_factor_disabled:
@@ -55,54 +55,54 @@ hu:
         subject: Kétlépcsős azonosítás engedélyezve
         title: Kétlépcsős hitelesítés engedélyezve
       two_factor_recovery_codes_changed:
-        explanation: A korábbi helyreállítási kódokat letiltottuk, és újakat generáltunk.
+        explanation: A korábbi helyreállítási kódok letiltásra és újragenerálásra kerültek.
         subject: Kétlépcsős helyreállítási kódok újra létrejöttek
-        title: A kétlépcsős kódok megváltozott
+        title: A kétlépcsős kódok megváltoztak
       unlock_instructions:
         subject: 'Mastodon: Feloldási lépések'
       webauthn_credential:
         added:
-          explanation: A következő biztonsági kulcsot hozzáadtuk a fiókodhoz
+          explanation: A következő biztonsági kulcs a fiókhoz hozzáadásra került
           subject: 'Mastodon: Új biztonsági kulcs'
-          title: Új biztonsági kulcsot vettünk fel
+          title: Új biztonsági kulcs lett hozzáadva
         deleted:
-          explanation: A következő biztonsági kulcsot töröltük a fiókodból
-          subject: 'Mastodon: Biztonsági kulcs törölve'
-          title: Az egyik biztonsági kulcsodat törölték
+          explanation: A következő biztonsági kulcs törlésre került a fiókból
+          subject: 'Mastodon: A biztonsági kulcs törlésre került'
+          title: Az egyik biztonsági kulcs törlésre került
       webauthn_disabled:
-        explanation: A biztonsági kulccsal történő hitelesítést letiltottuk a fiókodon. Bejelentkezni csak a párosított TOTP app által generált tokennel lehet.
-        subject: 'Mastodon: Biztonsági kulccsal történő hitelesítés letiltva'
-        title: Biztonsági kulcsok letiltva
+        explanation: A biztonsági kulccsal történő hitelesítés letiltásra kerüt a fióknál. Bejelentkezni csak a párosított TOTP app által generált vezérjellel lehet.
+        subject: 'Mastodon: A biztonsági kulccsal történő hitelesítés letiltásra került'
+        title: A bztonsági kulcsok letiltásra kerültek
       webauthn_enabled:
-        explanation: A biztonsági kulccsal történő hitelesítést engedélyeztük a fiókodon. A biztonsági kulcsodat mostantól használhatod bejelentkezésre.
-        subject: 'Mastodon: Biztonsági kulcsos hitelesítés engedélyezve'
-        title: Biztonsági kulcsok engedélyezve
+        explanation: A biztonsági kulccsal történő hitelesítést engedélyezve lett a fióknál. A biztonsági kulcs mostantól használható a bejelentkezésre.
+        subject: 'Mastodon: A biztonsági kulcsos hitelesítés engedélyezésre került'
+        title: A bztonsági kulcsok engedélyezésre kerültek
     omniauth_callbacks:
       failure: Sikertelen hitelesítés %{kind} fiókról, mert "%{reason}".
       success: Sikeres hitelesítés %{kind} fiókról.
     passwords:
-      no_token: Nem férhetsz hozzá ehhez az oldalhoz jelszó visszaállító e-mail nélkül. Ha egy jelszó visszaállító e-mail hozott ide, ellenőrizd, hogy a megadott teljes URL-t használd.
-      send_instructions: Pár percen belül kapni fogsz egy e-mailt arról, hogy hogyan tudod visszaállítani a jelszavadat. Kérlek ellenőrizd a levélszemét mappádat, ha nem kaptál ilyen e-mailt.
-      send_paranoid_instructions: Ha létezik az e-mail cím, pár percen belül kapni fogsz egy e-mailt arról, hogy hogyan tudod visszaállítani a jelszavadat. Kérlek ellenőrizd a levélszemét mappádat, ha nem kaptál ilyen e-mailt.
-      updated: Jelszavad sikeresen frissült. Bejelentkeztél.
-      updated_not_active: Jelszavad sikeresen megváltoztattuk.
+      no_token: Nem lehet hozzáférni ehhez az oldalhoz jelszó visszaállító email nélkül. Ha egy jelszó visszaállító email miatt érkeztünk ide, ellenőrizzük a megadott teljes webcím használatát.
+      send_instructions: Ha az email cím létezik az adatbázisban, néhány perc alatt megérkezik jelszó helyreállítási hivatkozás az email címre. Ellenőrizzük a spam mappát, ha nem érkezett meg ez az email.
+      send_paranoid_instructions: Ha az email cím létezik az adatbázisban, néhány perc alatt megérkezik jelszó helyreállítási hivatkozás az email címre. Ellenőrizzük a spam mappát, ha nem érkezett meg ez az email.
+      updated: A jelszó sikeresen megváltozott. Megtörtént a bejelentkezés.
+      updated_not_active: A jelszó sikeresen megváltoztatásra került.
     registrations:
-      destroyed: Viszlát! A fiókodat sikeresen töröltük. Reméljük hamarosan viszontláthatunk.
+      destroyed: Viszontásátásra! A fiók sikeresen törlésre került. Reméljük hamarosan visszatér.
       signed_up: Üdvözlünk! Sikeresen regisztráltál.
       signed_up_but_inactive: Sikeresen regisztráltál. Ennek ellenére nem tudunk beléptetni, ugyanis a fiókodat még nem aktiválták.
       signed_up_but_locked: Sikeresen regisztráltál. Ennek ellenére nem tudunk beléptetni, ugyanis a fiókod le van zárva.
       signed_up_but_pending: Egy megerősítési hivatkozással ellátott üzenetet kiküldtünk az e-mail címedre. Ha kattintasz a hivatkozásra, átnézzük a kérelmedet. Értesítünk, ha jóváhagytuk.
       signed_up_but_unconfirmed: Egy megerősítési hivatkozással ellátott üzenetet kiküldtünk az e-mail címedre. Kérjük használd a hivatkozást a fiókod aktiválásához. Ellenőrizd a spam mappádat, ha nem kaptad meg ezt a levelet.
-      update_needs_confirmation: Sikeresen frissítetted a fiókodat, de szükségünk van az e-mail címed megerősítésére. Kérlek ellenőrizd az e-mailedet és kövesd a levélben szereplő megerősítési linket az e-mail címed megerősítéséhez. Ellenőrizd a levélszemét mappád, ha nem kaptál volna ilyen levelet.
-      updated: Fiókod frissítése sikeres.
+      update_needs_confirmation: A fiókodat sikeresen frissítésre került, de szükség van az email cím megerősítésére. Ellenőrizzük az emailt és kövessük a benne levő megerősítési hivatkozást az email cím megerősítéséhez. Ellenőrizzük a levélszemét mappát, ha nemérkezett volna meg ez az email.
+      updated: A fiók sikeresen frissítésre került.
     sessions:
-      already_signed_out: Sikeres kijelentkezés.
-      signed_in: Sikeres bejelentkezés.
-      signed_out: Sikeres kijelentkezés.
+      already_signed_out: A kijelentkezés sikeres volt.
+      signed_in: A bejelentkezés sikeres volt.
+      signed_out: A kijelentkezés sikeres volt.
     unlocks:
-      send_instructions: Pár percen belül egy e-mailt fogsz kapni a feloldáshoz szükséges lépésekkel. Ellenőrizd a levélszemét mappád, ha nem kaptál volna ilyen levelet.
-      send_paranoid_instructions: Ha a fiókod létezik, pár percen belül egy e-mailt fogsz kapni a feloldáshoz szükséges lépésekkel. Ellenőrizd a levélszemét mappád, ha nem kaptál volna ilyen levelet.
-      unlocked: A fiókodat sikeresen feloldottuk. Jelentkezz be a folytatáshoz.
+      send_instructions: Néhány perc múlva egy email érkezik a fiók feloldásáról. Ellenőrizzük a spam mappát, ha nem érkezett volna meg at email.
+      send_paranoid_instructions: Ha a fiók létezik, pár percen belül egy email érkezik a feloldáshoz szükséges lépésekkel. Ellenőrizzük a levélszemét mappát, ha nem érkezett volna ilyen email.
+      unlocked: A fiók sikeresen feloldásra került. Jelentkezzünk be a folytatáshoz.
   errors:
     messages:
       already_confirmed: már meg lett erősítve, kérjük jelentkezz be
diff --git a/config/locales/devise.ko.yml b/config/locales/devise.ko.yml
index 45e5e47f8..9dc9f2167 100644
--- a/config/locales/devise.ko.yml
+++ b/config/locales/devise.ko.yml
@@ -8,11 +8,11 @@ ko:
     failure:
       already_authenticated: 이미 로그인 된 상태입니다.
       inactive: 계정이 아직 활성화 되지 않았습니다.
-      invalid: 올바르지 않은 %{authentication_keys} 혹은 패스워드입니다.
+      invalid: 올바르지 않은 %{authentication_keys} 혹은 암호입니다.
       last_attempt: 계정이 잠기기까지 한 번의 시도가 남았습니다.
       locked: 계정이 잠겼습니다.
-      not_found_in_database: 올바르지 않은 %{authentication_keys} 혹은 패스워드입니다.
-      pending: 계정이 아직 심사 중입니다.
+      not_found_in_database: 올바르지 않은 %{authentication_keys} 혹은 암호입니다.
+      pending: 이 계정은 아직 검토 중입니다.
       timeout: 세션이 만료 되었습니다. 다시 로그인 해 주세요.
       unauthenticated: 계속 하려면 로그인을 해야 합니다.
       unconfirmed: 계속 하려면 이메일을 확인 받아야 합니다.
@@ -26,26 +26,26 @@ ko:
         subject: '마스토돈: %{instance}에 대한 확인 메일'
         title: 이메일 주소 확인
       email_changed:
-        explanation: '당신의 계정에 대한 이메일이 다음과 같이 바뀌려고 합니다:'
-        extra: 만약 당신이 메일을 바꾸지 않았다면 누군가가 당신의 계정에 대한 접근 권한을 얻은 것입니다. 즉시 패스워드를 바꾼 후, 계정이 잠겼다면 서버의 관리자에게 연락 하세요.
+        explanation: '귀하의 계정이 다음의 이메일 주소로 변경됩니다:'
+        extra: 만약 이메일을 바꾸지 않았다면 누군가 계정에 대한 접근 권한을 얻은 것입니다. 바로 암호를 바꾸셔야 하며, 만약 계정이 잠겼다면 서버의 운영자에게 연락 바랍니다.
         subject: '마스토돈: 이메일이 변경 되었습니다'
         title: 새 이메일 주소
       password_change:
-        explanation: 당신의 계정 패스워드가 변경되었습니다.
+        explanation: 계정 암호가 변경되었습니다.
         extra: 만약 패스워드 변경을 하지 않았다면 누군가가 당신의 계정에 대한 접근 권한을 얻은 것입니다. 즉시 패스워드를 바꾼 후, 계정이 잠겼다면 서버의 관리자에게 연락 하세요.
-        subject: '마스토돈: 패스워드가 변경 되었습니다'
-        title: 패스워드가 변경 되었습니다
+        subject: 'Mastodon: 암호 변경됨'
+        title: 암호 변경됨
       reconfirmation_instructions:
         explanation: 이메일 주소를 바꾸려면 새 이메일 주소를 확인해야 합니다.
         extra: 당신이 시도한 것이 아니라면 이 메일을 무시해 주세요. 위 링크를 클릭하지 않으면 이메일 변경은 일어나지 않습니다.
         subject: '마스토돈: %{instance}에 대한 이메일 확인'
         title: 이메일 주소 확인
       reset_password_instructions:
-        action: 패스워드 변경
+        action: 암호 변경
         explanation: 계정에 대한 패스워드 변경을 요청하였습니다.
         extra: 만약 당신이 시도한 것이 아니라면 이 메일을 무시해 주세요. 위 링크를 클릭해 패스워드를 새로 설정하기 전까지는 패스워드가 바뀌지 않습니다.
-        subject: '마스토돈: 패스워드 재설정 방법'
-        title: 패스워드 재설정
+        subject: 'Mastodon: 암호 재설정 설명'
+        title: 암호 재설정
       two_factor_disabled:
         explanation: 당신의 계정에 설정된 이중 인증이 비활성화 되었습니다. 이제 이메일과 암호만으로 로그인이 가능합니다.
         subject: '마스토돈: 이중 인증 비활성화'
@@ -81,11 +81,11 @@ ko:
       failure: '"%{reason}" 때문에 당신을 %{kind}에서 인증할 수 없습니다.'
       success: "%{kind} 계정을 성공적으로 인증했습니다."
     passwords:
-      no_token: 패스워드 재설정 이메일을 거치지 않고는 여기에 올 수 없습니다. 만약 패스워드 재설정 메일에서 온 것이라면 URL이 맞는지 확인해 주세요.
+      no_token: 이 페이지는 암호 재설정 이메일을 거쳐야 접근할 수 있습니다. 만약 암호 재설정 이메일 거치지 않았다면 입력한 URL이 맞는지 확인 바랍니다.
       send_instructions: 당신의 이메일 주소가 우리의 DB에 있다면 패스워드 복구 링크가 몇 분 이내에 메일로 발송 됩니다. 만약 메일을 받지 못 하신 경우 스팸 폴더를 확인해 주세요.
       send_paranoid_instructions: 당신의 이메일 주소가 우리의 DB에 있다면 패스워드 복구 링크가 몇 분 이내에 메일로 발송 됩니다. 만약 메일을 받지 못 하신 경우 스팸 폴더를 확인해 주세요.
-      updated: 패스워드가 재설정 되었습니다. 로그인 되었습니다.
-      updated_not_active: 패스워드가 성공적으로 변경 되었습니다.
+      updated: 암호를 잘 바꾸었습니다. 현재 로그인 상태입니다.
+      updated_not_active: 암호를 잘 변경하였습니다.
     registrations:
       destroyed: 안녕히 가세요! 계정이 성공적으로 제거되었습니다. 다시 만나기를 희망합니다.
       signed_up: 안녕하세요! 성공적으로 가입했습니다.
diff --git a/config/locales/devise.my.yml b/config/locales/devise.my.yml
index 5e1fc6bee..04d744cf2 100644
--- a/config/locales/devise.my.yml
+++ b/config/locales/devise.my.yml
@@ -1 +1,114 @@
+---
 my:
+  devise:
+    confirmations:
+      confirmed: သင်၏ အီးမေးလ်လိပ်စာ အောင်မြင်စွာအတည်ပြုပြီးပါပြီ။
+      send_instructions: မိနစ်အနည်းငယ်အတွင်း သင့်အီးမေးလ်လိပ်စာကို အတည်ပြုရန်အတွက် ညွှန်ကြားချက်များပါရှိသည့် အီးမေးလ်တစ်စောင်ကို သင်ရရှိမည်ဖြစ်ပါသည်။ ဤအီးမေးလ်ကို လက်ခံမရရှိပါက သင်၏ Spam ဖိုင်ကို စစ်ဆေးပါ။
+      send_paranoid_instructions: အကယ်၍ သင့်အီးမေးလ်လိပ်စာသည် ကျွန်ုပ်တို့၏ဒေတာဘေ့စ်တွင် ရှိနေပါက မိနစ်အနည်းငယ်အတွင်း သင့်အီးမေးလ်လိပ်စာကို အတည်ပြုရန်အတွက် ညွှန်ကြားချက်များပါရှိသည့် အီးမေးလ်တစ်စောင်ကို သင်ရရှိမည်ဖြစ်ပါသည်။ ဤအီးမေးလ်ကို လက်ခံမရရှိပါက သင်၏ Spam ဖိုင်ကို စစ်ဆေးပါ။
+    failure:
+      already_authenticated: သင်ဝင်ရောက်ပြီးဖြစ်သည်။
+      inactive: သင်၏အကောင့်သည်အသက်မဝင်သေးပါ
+      invalid: မှားယွင်းသော %{authentication_keys} သို့မဟုတ် စကားဝှက် ဖြစ်ပါသည်။
+      last_attempt: သင့်အကောင့်ကို လော့ခ်မချမီ နောက်ထပ်ကြိုးစားမှုတစ်ခု ရှိသေးသည်။
+      locked: သင့်အကောင့်ကို လော့ခ်ချထားသည်။
+      not_found_in_database: မှားယွင်းသော %{authentication_keys} သို့မဟုတ် စကားဝှက် ဖြစ်ပါသည်။
+      pending: သင့်အကောင့်အား စိစစ်ဆဲဖြစ်သည်။
+      timeout: သင့် Session သက်တမ်းကုန်သွားပါပြီ။ ရှေ့ဆက်ရန်အတွက် ကျေးဇူးပြုပြီး ထပ်မံဝင်ရောက်ပါ။
+      unauthenticated: ဆက်မလုပ်မီ သင်သည် အကောင့်ဝင်ရန် သို့မဟုတ် အကောင့်ဖွင့်ရန် လိုအပ်သည်။
+      unconfirmed: ဆက်မလုပ်မီ သင့်အီးမေးလ်လိပ်စာကို အတည်ပြုရပါမည်။
+    mailer:
+      confirmation_instructions:
+        action: အီးမေးလ်လိပ်စာကို အတည်ပြုပါ
+        action_with_app: အတည်ပြုပြီး %{app} သို့ပြန်သွားပါ
+        explanation: "%{host} တွင် ဤအီးမေးလ်လိပ်စာဖြင့် သင် အကောင့်တစ်ခု ဖန်တီးထားပြီးဖြစ်သည်။ သင် ကလစ်တစ်ချက်နှိပ်ရုံဖြင့် အကောင့်အား အသက်ဝင်စေနိုင်သည်။ အကယ်၍ သင်ဖန်တီးထားခြင်းမဟုတ်ခဲ့လျှင် ယခုအီးမေးလ်အား ကျေးဇူးပြု၍ လျစ်လျူရှုပါ။"
+        explanation_when_pending: "%{host} သို့ ဖိတ်ခေါ်မှုတစ်ခုကို ဤအီးမေးလ်လိပ်စာဖြင့် သင်တောင်းဆိုခဲ့သည်။ အီးမေးလ်လိပ်စာကို သင်အတည်ပြုပြီးပါက ကျွန်ုပ်တို့အနေဖြင့် သင့်တောင်းဆိုမှုအား စိစစ်မည်ဖြစ်သည်။ သင့်အနေဖြင့် မိမိအကောင့်အတွင်း ဝင်ရောက်၍ အသေးစိတ်အချက်အလက်များအား ပြုပြင်နိုင်သည် (သို့) မိမိအကောင့်အား ဖျက်နိုင်သည်။ သို့သော် သင့်အကောင့်ကို အတည်ပြုမပြီးမချင်း သင့်အနေဖြင့် လုပ်ငန်းဆောင်တာအများစုအား ဆောင်ရွက်နိုင်မည် မဟုတ်ပေ။ သင်၏ အကောင့်တောင်းဆိုမှု ငြင်းဆန်ခံရပါက သင့်အချက်အလက်များအား ဖယ်ရှားပေးမည်ဖြစ်သည်။ သင့်အနေဖြင့် တခြားအရေးယူဆောင်ရွက်ရန် မလိုအပ်ပါ။ အကယ်၍ ယခုတောင်းဆိုမှုအား သင်ပြုလုပ်ထားခြင်းမဟုတ်ခဲ့လျှင် ယခုအီးမေးလ်အား ကျေးဇူးပြု၍ လျစ်လျူရှုပါ။"
+        extra_html: ကျေးဇူးပြု၍ <a href="%{terms_path}">ဆာဗာစည်းမျဉ်းများ</a> နှင့် <a href="%{policy_path}">ကျွန်ုပ်တို့၏ဝန်ဆောင်မှုစည်းမျဉ်းများ</a>ကိုလည်း စစ်ဆေးကြည့်ပါ။
+        subject: Mastodon - %{instance} အတွက် အတည်ပြုချက် ညွှန်ကြားချက်များ
+        title: အီးမေးလ်လိပ်စာကို အတည်ပြုပါ
+      email_changed:
+        explanation: သင့်အကောင့်အတွက် အီးမေးလ်လိပ်စာကို ပြောင်းလဲနေပါသည် -
+        extra: သင့်အီးမေးလ်လိပ်စာ မပြောင်းထားပါက တစ်စုံတစ်ဦးသည် သင့်အကောင့်သို့ ဝင်ရောက်ခွင့်ရရှိသွားနိုင်ပါသည်။ သင့်အကောင့် လော့ခ်ကျသွားပါက သင့်စကားဝှက်ကို ချက်ချင်းပြောင်းပါ သို့မဟုတ် ဆာဗာစီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။
+        subject: 'Mastodon: အီးမေးလ်ပြောင်းလဲသွားပြီ'
+        title: အီးမေးလ်လိပ်စာအသစ်
+      password_change:
+        explanation: သင့်အကောင့်အတွက် စကားဝှက်ကို ပြောင်းလဲလိုက်ပါပြီ။
+        extra: သင့်စကားဝှက် မပြောင်းထားပါက တစ်စုံတစ်ဦးသည် သင့်အကောင့်သို့ ဝင်ရောက်ခွင့်ရရှိသွားနိုင်ပါသည်။ သင့်အကောင့် လော့ခ်ကျသွားပါက သင့်စကားဝှက်ကို ချက်ချင်းပြောင်းပါ သို့မဟုတ် ဆာဗာစီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။
+        subject: 'Mastodon: စကားဝှက်ပြောင်းလဲသွားပြီ'
+        title: စကားဝှက်ပြောင်းလဲသွားပြီ
+      reconfirmation_instructions:
+        explanation: သင်၏အီးမေးလ်လိပ်စာပြောင်းရန် လိပ်စာအသစ်အတည်ပြုပါ။
+        extra: တစ်ကယ်လို့ ပြောင်းလဲမှုကို သင်တောင်းဆိုတာမဟုတ်ဘူးဆိုရင် ဤအီးမေးလ်ကို လျစ်လျူရှုလိုက်ပါ။ အပေါ်မှ လင့်ခ်ကိုမဝင်မချင်း သင့်အီးမေးလ်ပြောင်းသွားမည်မဟုတ်ပါ
+        subject: 'Mastodon: %{instance} အတွက်အီးမေးလ်အတည်ပြုပါ'
+        title: အီးမေးလ်လိပ်စာစစ်ဆေးပါ
+      reset_password_instructions:
+        action: စကားဝှက်ပြောင်းမည်
+        explanation: သင့်အကောင့်အတွက် စကားဝှက်အသစ်တစ်ခု တောင်းဆိုခဲ့သည်။
+        extra: သင်မတောင်းဆိုထားပါက ဤအီးမေးလ်ကို လျစ်လျူရှုလိုက်ပါ။ အထက်ဖော်ပြပါလင့်ခ်ကို သင်ဝင်ရောက်ပြီး အသစ်တစ်ခုမဖန်တီးမချင်း သင့်စကားဝှက်သည် ပြောင်းလဲမည်မဟုတ်ပါ။
+        subject: 'Mastodon: စကားဝှက်ညွှန်ကြားချက် ပြန်လည်သတ်မှတ်မည်'
+        title: စကားဝှက်ပြန်လည်သတ်မှတ်မည်
+      two_factor_disabled:
+        explanation: သင့်အကောင့်အတွက် နှစ်ဆင့်ခံလုံခြုံရေးစနစ်အထောက်အထားပြခြင်းကို ပိတ်ထားသည်။ အီးမေးလ်လိပ်စာနှင့် စကားဝှက်ကိုသာ အသုံးပြု၍ အကောင့်ဝင်ရောက်နိုင်ပါသည်။
+        subject: 'Mastodon: နှစ်ဆင့်ခံလုံခြုံရေးစနစ်ပိတ်ထားသည်'
+        title: နှစ်ဆင့်ခံလုံခြုံရေးစနစ်ပိတ်ထားသည်
+      two_factor_enabled:
+        explanation: သင့်အကောင့်အတွက် နှစ်ဆင့်ခံလုံခြုံရေးစနစ်အထောက်အထားပြခြင်းကို ဖွင့်ထားသည်။ TOTP အက်ပ်မှ ထုတ်ပေးသည့် တိုကင်တစ်ခုမှာ အကောင့်ဝင်ရန်အတွက် လိုအပ်ပါသည်။
+        subject: 'Mastodon: နှစ်ဆင့်ခံလုံခြုံရေးစနစ်ဖွင့်ထားသည်'
+        title: နှစ်ဆင့်ခံလုံခြုံရေးစနစ်ဖွင့်ထားသည်
+      two_factor_recovery_codes_changed:
+        explanation: ယခင်ပြန်လည်ရယူသည့်ကုဒ်များမှာ မမှန်ကန်သောကြောင့် အသစ်များကို ထုတ်ပေးခဲ့သည်။
+        subject: Mastodon - နှစ်ဆင့်ခံလုံခြုံရေး ပြန်လည်ရယူသည့်ကုဒ်များကို ပြန်လည်ထုတ်ပေးခြင်း
+        title: နှစ်ဆင့်ခံလုံခြုံရေးစနစ် ပြန်လည်ရယူသည့်ကုဒ်နံပါတ်များ ပြောင်းလဲခဲ့သည်
+      unlock_instructions:
+        subject: Mastodon - ညွှန်ကြားချက်များကို လော့ခ်ဖွင့်ပါ
+      webauthn_credential:
+        added:
+          explanation: ဖော်ပြပါလုံခြုံရေးသော့ချက်အား သင့်အကောင့်ထဲသို့ထည့်ပြီးပါပြီ
+          subject: 'Mastodon: လုံခြုံရေးသော့ချက်အသစ်'
+          title: လုံခြုံရေးသော့ချက်အသစ် ထည့်လိုက်ပါပြီ
+        deleted:
+          explanation: ဖော်ပြပါလုံခြုံရေးသော့ချက်အား သင့်အကောင့်ထဲမှဖျက်လိုက်ပါပြီ
+          subject: 'Mastodon: လုံခြုံရေးသော့ချက် ဖျက်လိုက်ပါပြီ'
+          title: လုံခြုံရေးသော့ချက်များထဲမှတစ်ခု ဖျက်လိုက်ပါပြီ
+      webauthn_disabled:
+        explanation: သင့်အကောင့်အတွက် လုံခြုံရေးကီးများဖြင့် အထောက်အထားပြခြင်းကို ပိတ်ထားသည်။ TOTP အက်ပ်မှထုတ်ပေးသည့် တိုကင်ကိုသာ အသုံးပြု၍ အကောင့်ဝင်ရောက်နိုင်ပါသည်။
+        subject: Mastodon - လုံခြုံရေးကီးများဖြင့် အထောက်အထားစိစစ်ခြင်းကို ပိတ်ထားသည်
+        title: လုံခြုံရေးသော့ချက်များ ပိတ်ပြီးပါပြီ
+      webauthn_enabled:
+        explanation: သင့်အကောင့်အတွက် လုံခြုံရေးကီး အထောက်အထားစိစစ်ခြင်းကို ဖွင့်ထားသည်။ အကောင့်ဝင်ရန်အတွက် သင်၏လုံခြုံရေးကီးကို ယခုအသုံးပြုနိုင်ပါပြီ။
+        subject: Mastodon - လုံခြုံရေးကီး အထောက်အထားစိစစ်ခြင်းကို ဖွင့်ထားသည်
+        title: လုံခြုံရေးသော့ချက်များ ဖွင့်ပြီးပါပြီ
+    omniauth_callbacks:
+      failure: "“%{reason}” ကြောင့် %{kind} မှ သင့်ကို စစ်မှန်ကြောင်း အထောက်အထား မပြနိုင်ပါ။"
+      success: "%{kind} အကောင့်မှ အထောက်အထားပြပြီးပါပြီ။"
+    passwords:
+      no_token: စကားဝှက်ပြန်လည်သတ်မှတ်ခြင်းအီးမေးလ်မှ မလာပါက ဤစာမျက်နှာကို သင်ဝင်ရောက်၍မရနိုင်ပါ။ အကယ်၍ ၎င်းမှလာပါက ပေးထားသော URL အပြည့်အစုံကို အသုံးပြုရပါမည်။
+      send_instructions: အကယ်၍ သင့်အီးမေးလ်လိပ်စာသည် ကျွန်ုပ်တို့၏ဒေတာဘေ့စ်တွင် ရှိနေပါက မိနစ်အနည်းငယ်အတွင်း သင့်အီးမေးလ်လိပ်စာတွင် စကားဝှက်ပြန်လည်ရယူရေးလင့်ခ် ရရှိမည်ဖြစ်ပါသည်။ ဤအီးမေးလ်ကို လက်ခံမရရှိပါက သင်၏ Spam ဖိုင်ကို စစ်ဆေးပါ။
+      send_paranoid_instructions: အကယ်၍ သင့်အီးမေးလ်လိပ်စာသည် ကျွန်ုပ်တို့၏ဒေတာဘေ့စ်တွင် ရှိနေပါက မိနစ်အနည်းငယ်အတွင်း သင့်အီးမေးလ်လိပ်စာတွင် စကားဝှက်ပြန်လည်ရယူရေးလင့်ခ် ရရှိမည်ဖြစ်ပါသည်။ ဤအီးမေးလ်ကို လက်ခံမရရှိပါက သင်၏ Spam ဖိုင်ကို စစ်ဆေးပါ။
+      updated: သင့်စကားဝှက်ကို အောင်မြင်စွာ ပြောင်းလဲပြီးပါပြီ။ သင်သည် ယခု အကောင့်ဝင်ထားပြီးဖြစ်ပါသည်။
+      updated_not_active: သင့်စကားဝှက်ကို အောင်မြင်စွာ ပြောင်းလဲပြီးပါပြီ။
+    registrations:
+      destroyed: တာ့တာ၊ သင့်အကောင့်ကို ပယ်ဖျက်လိုက်ပါပြီ။ မကြာခင် ပြန်တွေ့နိုင်ရန်မျှော်လင့်မိပါသည်။
+      signed_up: ကြိုဆိုပါသည်။ သင်သည် အောင်မြင်စွာ စာရင်းသွင်းပြီးပါပြီ။
+      signed_up_but_inactive: သင်သည် အောင်မြင်စွာ အကောင့်ဖွင့်ပြီးပါပြီ။ သို့သော်လည်း သင့်အကောင့် အတည်မပြုရသေးသောကြောင့် ဝင်၍မရနိုင်ပါ။
+      signed_up_but_locked: သင်သည် အောင်မြင်စွာ အကောင့်ဖွင့်ပြီးပါပြီ။ သို့သော်လည်း သင့်အကောင့်လော့ခ်ချထားသောကြောင့် လက်မှတ်ထိုးဝင်၍မရနိုင်ပါ။
+      signed_up_but_pending: အတည်ပြုချက်လင့်ခ်ပါရှိသော စာတိုတစ်စောင်ကို သင့်အီးမေးလ်လိပ်စာသို့ ပေးပို့လိုက်ပါပြီ။ လင့်ခ်ကို နှိပ်ပြီးနောက် သင့်အက်ပလီကေးရှင်းကို ကျွန်ုပ်တို့ ပြန်လည်စစ်ဆေးပါမည်။ အတည်ပြုပြီးပါက သင့်ထံ အကြောင်းကြားပါမည်။
+      signed_up_but_unconfirmed: အကောင့်ဖွင့်ရန်အတွက် လင့်ခ်တစ်ခုကို ထောက်ခံချက်အီးမေးလ််နှင့်အတူပို့ပေးလိုက်ပါပြီ။ သင့်အကောင့်ဖွင့်နိုင်ရန် လင့်ခ်ကိုဝင်ရောက်ပေးပါ။ အီးမေးလ်မတွေ့ပါက စပမ်းဖိုင်တွဲထဲတွင်စစ်ကြည့်ပါ။
+      update_needs_confirmation: သင့်အကောင့်ကို မွမ်းမံပြင်ဆင်ထားသော်လည်း သင့်အီးမေးလ်လိပ်စာအသစ်ကို အတည်ပြုရန် လိုအပ်ပါသည်။ ကျေးဇူးပြု၍ သင့်အီးမေးလ်ကို စစ်ဆေးပြီး သင့်အီးမေးလ်လိပ်စာအသစ်ကို အတည်ပြုရန်အတွက် အတည်ပြုလင့်ခ်အား နှိပ်ပါ။ ဤအီးမေးလ်ကို မရရှိပါက သင်၏ Spam ဖိုင်ကို စစ်ဆေးပါ။
+      updated: သင့်အကောင့်အားအောင်မြင်စွာ ပြင်ဆင်ပြီးပါပြီ။
+    sessions:
+      already_signed_out: အောင်မြင်စွာအကောင့်မှထွက်ပြီးပါပြီ။
+      signed_in: အောင်မြင်စွာအကောင့်ဝင်ပြီးပါပြီ။
+      signed_out: အောင်မြင်စွာအကောင့်မှထွက်ပြီးပါပြီ။
+    unlocks:
+      send_instructions: မိနစ်အနည်းငယ်အတွင်း သင့်အကောင့်လော့ခ်ဖွင့်နည်းအတွက် ညွှန်ကြားချက်များပါရှိသည့် အီးမေးလ်တစ်စောင်ကို သင်လက်ခံရရှိမည်ဖြစ်သည်။ ဤအီးမေးလ်ကို မရရှိပါက သင်၏ Spam ဖိုင်ကို စစ်ဆေးပါ။
+      send_paranoid_instructions: အကယ်၍ သင့်တွင် အကောင့်ရှိပါက မိနစ်အနည်းငယ်အတွင်း လော့ခ်ဖွင့်နည်းအတွက် ညွှန်ကြားချက်များပါရှိသည့် အီးမေးလ်တစ်စောင်ကို သင်ရရှိမည်ဖြစ်သည်။ ဤအီးမေးလ်ကို လက်ခံမရရှိပါက သင်၏ Spam ဖိုင်ကို စစ်ဆေးပါ။
+      unlocked: သင့်အကောင့်ကို လော့ခ်ဖွင့်ပြီးပါပြီ။ ရှေ့ဆက်ရန်အတွက် အကောင့်ဝင်ပါ။
+  errors:
+    messages:
+      already_confirmed: အတည်ပြုပြီးဖြစ်သည်။ ကျေးဇူးပြု၍ အကောင့်ဝင်ကြည့်ပါ။
+      confirmation_period_expired: "%{period} အတွင်း အတည်ပြုရန် လိုအပ်ပြီး အသစ်တစ်ခု တောင်းဆိုပါ"
+      expired: သည် သက်တမ်းကျော်လွန်သွားပြီ။ ကျေးဇူးပြု၍အသစ်တစ်ခု တောင်းဆိုပါ
+      not_found: ရှာမတွေ့ပါ
+      not_locked: လော့ခ် မချထားပါ
+      not_saved:
+        other: "%{count} အမှားသည် ဤ %{resource} အား သိမ်းဆည်းခြင်းမှ တားမြစ်ထားသည်-"
diff --git a/config/locales/devise.pt-PT.yml b/config/locales/devise.pt-PT.yml
index 7af16faf2..0571598ae 100644
--- a/config/locales/devise.pt-PT.yml
+++ b/config/locales/devise.pt-PT.yml
@@ -2,14 +2,14 @@
 pt-PT:
   devise:
     confirmations:
-      confirmed: O teu endereço de e-mail foi confirmado com sucesso.
+      confirmed: O seu endereço correio electrónico foi correctamente confirmado.
       send_instructions: Vais receber um e-mail com as instruções para confirmar o teu endereço de e-mail dentro de alguns minutos. Por favor, verifica a caixa de spam se não recebeu o e-mail.
       send_paranoid_instructions: Se o teu endereço de e-mail já existir na nossa base de dados, vais receber um e-mail com as instruções de confirmação dentro de alguns minutos. Por favor, verifica a caixa de spam se não recebeu o e-mail.
     failure:
       already_authenticated: A tua sessão já está aberta.
       inactive: A tua conta ainda não está ativada.
       invalid: "%{authentication_keys} ou palavra-passe inválida."
-      last_attempt: Tens mais uma tentativa antes de a tua conta ser bloqueada.
+      last_attempt: Tem só mais uma tentativa antes da sua conta ser bloqueada.
       locked: A tua conta está bloqueada.
       not_found_in_database: "%{authentication_keys} ou palavra-passe inválida."
       pending: A sua conta está ainda a aguardar revisão.
@@ -21,8 +21,8 @@ pt-PT:
         action: Verificar o endereço de e-mail
         action_with_app: Confirmar e regressar a %{app}
         explanation: Criou uma conta em %{host} com este endereço de e-mail. Está a um clique de ativá-la. Se não foi você que fez este registo, por favor ignore esta mensagem.
-        explanation_when_pending: Você solicitou um convite para %{host} com este endereço de e-mail. Logo que confirme o seu endereço de e-mail, iremos rever a sua inscrição. Pode iniciar sessão para alterar os seus dados ou eliminar a sua conta, mas não poderá aceder à maioria das funções até que a sua conta seja aprovada. Se a sua inscrição for rejeitada, os seus dados serão eliminados, pelo que não será necessária qualquer ação adicional da sua parte. Se não solicitou este convite, por favor, ignore este e-mail.
-        extra_html: Por favor leia <a href="%{terms_path}">as regras da instância</a> e os <a href="%{policy_path}"> nossos termos de serviço</a>.
+        explanation_when_pending: Candidatou-se com um convite para %{host} com este endereço de e-mail. Logo que confirme o seu endereço de e-mail, iremos rever a sua candidatura. Pode iniciar sessão para alterar os seus dados ou eliminar a sua conta, mas não poderá aceder à maioria das funções até que a sua conta seja aprovada. Se a sua inscrição for indeferida, os seus dados serão eliminados, pelo que não será necessária qualquer ação adicional da sua parte. Se não solicitou este convite, queira ignorar este e-mail.
+        extra_html: Por favor leia as <a href="%{terms_path}">regras da instância</a> e os <a href="%{policy_path}">nossos termos de serviço</a>.
         subject: 'Mastodon: Instruções de confirmação %{instance}'
         title: Verificar o endereço de e-mail
       email_changed:
@@ -33,18 +33,18 @@ pt-PT:
       password_change:
         explanation: A palavra-passe da tua conta foi alterada.
         extra: Se não alterou a sua palavra-passe, é possível que alguém tenha conseguido aceder à sua conta. Por favor altere a sua palavra-passe imediatamente ou entre em contacto com um administrador da instância se tiver ficado sem acesso à sua conta.
-        subject: 'Mastodon: Nova palavra-passe'
+        subject: 'Mastodon: palavra-passe alterada'
         title: Palavra-passe alterada
       reconfirmation_instructions:
         explanation: Confirme o seu novo endereço para alterar o e-mail.
-        extra: Se esta mudança não foi iniciada por si, por favor ignore este e-mail. O endereço de e-mail para a sua conta do Mastodon não irá mudar enquanto não aceder ao link acima.
+        extra: Se esta mudança não foi iniciada por si, queira ignorar este e-mail. O endereço de correio electrónico da sua conta do Mastodon não irá mudar enquanto não aceder à hiperligação acima.
         subject: 'Mastodon: Confirmação de e-mail %{instance}'
         title: Validar o endereço de e-mail
       reset_password_instructions:
         action: Alterar palavra-passe
         explanation: Pediste a alteração da palavra-passe da tua conta.
-        extra: Se não fez este pedido, por favor ignore este e-mail. A sua palavra-passe não irá mudar se não aceder ao link acima e criar uma nova.
-        subject: 'Mastodon: Instruções para alterar a palavra-passe'
+        extra: Se não fez este pedido, queira ignorar este e-mail. A sua palavra-passe não irá mudar se não aceder à hiperligação acima e criar uma nova.
+        subject: 'Mastodon: Instruções para redefinir a palavra-passe'
         title: Solicitar nova palavra-passe
       two_factor_disabled:
         explanation: A autenticação em duas etapas para a sua conta foi desativada. É agora possível aceder apenas com o seu endereço de e-mail e palavra-passe.
@@ -70,7 +70,7 @@ pt-PT:
           subject: 'Mastodon: Chave de segurança eliminada'
           title: Uma das suas chaves de segurança foi eliminada
       webauthn_disabled:
-        explanation: A autenticação com chave de segurança foi desativada para sua conta. É agora possível aceder à sua conta utilizando apenas o token gerado pelo aplicativo TOTP pareado.
+        explanation: A autenticação com chave de segurança foi desativada para a sua conta. Tornou-se possível aceder à sua conta usando apenas o token gerado pela aplicação TOTP emparelhada.
         subject: 'Mastodon: Autenticação com chave de segurança desativada'
         title: Chaves de segurança desativadas
       webauthn_enabled:
@@ -79,30 +79,30 @@ pt-PT:
         title: Chaves de segurança ativadas
     omniauth_callbacks:
       failure: Não foi possível autenticar %{kind} porque "%{reason}".
-      success: Autenticado com sucesso na conta %{kind}.
+      success: Autenticado correctamente na conta %{kind}.
     passwords:
-      no_token: Não pode aceder a esta página se não vier através do link enviado por e-mail para alteração da sua palavra-passe. Se usou esse link para chegar aqui, por favor verifique que o endereço URL atual é igual ao que foi enviado no e-mail.
+      no_token: Não pode aceder a esta página se não vier através da ligação enviada por e-mail para alteração da sua palavra-passe. Se de facto usou essa ligação para chegar até aqui, queira garantir de que usou o endereço URL completo.
       send_instructions: Vai receber um e-mail com instruções para alterar a palavra-passe dentro de alguns minutos.
-      send_paranoid_instructions: Se o seu endereço de e-mail existir na nossa base de dados, vai receber um link para recuperar a palavra-passe dentro de alguns minutos.
+      send_paranoid_instructions: Se o seu endereço de e-mail existir na nossa base de dados, dentro de alguns minutos irá receber uma ligação para recuperar a palavra-passe.
       updated: A tua palavra-passe foi alterada. Estás agora autenticado na tua conta.
       updated_not_active: A tua palavra-passe foi alterada.
     registrations:
       destroyed: Adeus! A tua conta foi cancelada. Esperamos ver-te em breve.
-      signed_up: Bem-vindo! A tua conta foi registada com sucesso.
+      signed_up: Seja bem-vindo! A sua conta foi correctamente registada.
       signed_up_but_inactive: A tua conta foi registada. No entanto ainda não está activa.
-      signed_up_but_locked: A sua conta foi registada com sucesso. No entanto, não pudemos iniciar a sua sessão porque a conta está bloqueada.
-      signed_up_but_pending: Uma mensagem com um link de confirmação foi enviada para o seu endereço de e-mail. Depois de clicar no link, iremos rever a sua inscrição. Será notificado se a sua conta for aprovada.
-      signed_up_but_unconfirmed: Uma mensagem com um link de confirmação foi enviada para o seu e-mail. Por favor utilize esse link para ativar a sua conta.
-      update_needs_confirmation: Alteraste o teu endereço de e-mail ou palavra-passe, mas é necessário confirmar essa alteração. Por favor vá ao seu e-mail e utilize o link que lhe enviámos.
-      updated: A tua conta foi actualizada com sucesso.
+      signed_up_but_locked: A sua conta foi correctamente registada. Contudo, não pudemos iniciar sessão porque a sua conta está bloqueada.
+      signed_up_but_pending: Foi enviada uma hiperligação de confirmação para o seu correio electrónico. Só depois de clicar na hiperligação avaliaremos a sua inscrição. Será notificado caso a sua conta seja aprovada.
+      signed_up_but_unconfirmed: Foi enviada uma hiperligação de confirmação para o seu correio electrónico. Queira usar essa hiperligação para activar a sua conta.
+      update_needs_confirmation: Solicitou uma alteração da informação da sua conta, mas para tal é necessário confirmá-la. Queira ver o seu correio electrónico e seguir a hiperligação para a confirmar. Se não encontrar essa mensagem, veja se está na pasta de lixo electrónico.
+      updated: A sua conta foi correctamente actualizada.
     sessions:
       already_signed_out: Sessão encerrada.
       signed_in: Sessão iniciada.
       signed_out: Sessão encerrada.
     unlocks:
-      send_instructions: Vai receber um e-mail com instruções para desbloquear a sua conta dentro de alguns minutos.
-      send_paranoid_instructions: Se a sua conta existe, vais receber um e-mail com instruções a detalhar como a desbloquear dentro de alguns minutos.
-      unlocked: A sua conta foi desbloqueada com sucessos. Por favor, inicie uma nova sessão para continuar.
+      send_instructions: Dentro de alguns momentos, irá receber um e-mail com instruções para desbloquear a sua conta. Consulte a sua pasta de lixo electrónico se não o encontrar.
+      send_paranoid_instructions: Se a sua conta existir, dentro de momentos irá receber um e-mail com instruções para a desbloquear. Consulte a sua pasta de lixo electrónico se não o encontrar.
+      unlocked: A sua conta foi correctamente desbloqueada. Queira iniciar uma nova sessão para continuar.
   errors:
     messages:
       already_confirmed: já confirmado, por favor tente iniciar sessão
@@ -111,5 +111,5 @@ pt-PT:
       not_found: não encontrado
       not_locked: não estava bloqueada
       not_saved:
-        one: '1 erro impediu este %{resource} de ser guardado:'
+        one: 'Um erro impediu este %{resource} de ser guardado:'
         other: "%{count} erros impediram este %{resource} de ser guardado:"
diff --git a/config/locales/devise.uz.yml b/config/locales/devise.uz.yml
new file mode 100644
index 000000000..fab6a0655
--- /dev/null
+++ b/config/locales/devise.uz.yml
@@ -0,0 +1,13 @@
+---
+uz:
+  devise:
+    confirmations:
+      confirmed: Sizning elektron pochta manzilingiz muvaffaqiyatli tasdiqlandi.
+      send_instructions: Bir necha daqiqadan so'ng elektron pochta manzilingizni qanday tasdiqlash bo'yicha ko'rsatmalar bilan elektron pochta xabarini olasiz. Agar siz ushbu xatni olmagan bo'lsangiz, spam jildini tekshiring.
+      send_paranoid_instructions: Agar sizning elektron pochta manzilingiz bizning ma'lumotlar bazamizda mavjud bo'lsa, sizga bir necha daqiqadan so'ng elektron pochta manzilingizni qanday tasdiqlash bo'yicha ko'rsatmalar yozilgan elektron pochta xabari keladi. Agar siz ushbu xatni olmagan bo'lsangiz, spam jildini tekshiring.
+    failure:
+      already_authenticated: Siz allaqachon tizimga kirgansiz.
+    mailer:
+      reset_password_instructions:
+        action: Parolni almashtirish
+        title: Parolni tiklash
diff --git a/config/locales/devise.zh-CN.yml b/config/locales/devise.zh-CN.yml
index c752ceed8..d21c0c7f1 100644
--- a/config/locales/devise.zh-CN.yml
+++ b/config/locales/devise.zh-CN.yml
@@ -74,7 +74,7 @@ zh-CN:
         subject: Mastodon:安全密钥认证已禁用
         title: 安全密钥已禁用
       webauthn_enabled:
-        explanation: 你的帐户已启用安全密钥身份验证。你的安全密钥现在可以用于登录。
+        explanation: 你的账户已启用安全密钥身份验证。你的安全密钥现在可以用于登录。
         subject: Mastodon:安全密钥认证已启用
         title: 已启用安全密钥
     omniauth_callbacks:
@@ -96,9 +96,9 @@ zh-CN:
       update_needs_confirmation: 账号信息更新成功,但我们需要验证你的新电子邮件地址,请点击邮件中的链接以确认。如果没有收到邮件,请检查你的垃圾邮箱。
       updated: 账户资料更新成功。
     sessions:
-      already_signed_out: 已成功登出。
+      already_signed_out: 已成功退出登录。
       signed_in: 已成功登录。
-      signed_out: 已成功登出。
+      signed_out: 已成功退出登录。
     unlocks:
       send_instructions: 几分钟后,你将收到一封解锁账户的邮件。如果没有,请检查你的垃圾邮箱。
       send_paranoid_instructions: 如果你的账号存在,你将会在几分钟内收到一封指引你如何解锁账号的邮件。如果你没有收到这封邮件,请检查你邮箱的垃圾箱。
diff --git a/config/locales/devise.zh-TW.yml b/config/locales/devise.zh-TW.yml
index e500e1d9e..f69434dc4 100644
--- a/config/locales/devise.zh-TW.yml
+++ b/config/locales/devise.zh-TW.yml
@@ -21,7 +21,7 @@ zh-TW:
         action: 驗證電子郵件地址
         action_with_app: 確認並返回 %{app}
         explanation: 您已經在 %{host} 上以此電子郵件地址建立了一支帳號。您距離啟用它只剩一點之遙了。若這不是您,請忽略此信件。
-        explanation_when_pending: 您使用此電子郵件地址申請了 %{host} 的邀請。當您確認電子郵件信箱後我們將審核您的申請。您可以登入以改變您的細節或刪除您的帳號,但直到您的帳號被核准之前,您無法操作大部分的功能。若您的申請遭拒絕,您的資料將被移除而不必做後續動作。如果這不是您,請忽略此信件。
+        explanation_when_pending: 您使用此電子郵件地址申請了 %{host} 的邀請。當您確認電子郵件地址後我們將審核您的申請。您可以在登入後變更詳細資訊或刪除您的帳號,但直到您的帳號被核准之前,您無法操作大部分的功能。若您的申請遭拒絕,您的資料將被移除而不必做後續動作。如果這不是您本人,請忽略此郵件。
         extra_html: 同時也請看看<a href="%{terms_path}">伺服器規則</a>與<a href="%{policy_path}">服務條款</a>。
         subject: Mastodon:%{instance} 確認說明
         title: 驗證電子郵件地址
@@ -49,15 +49,15 @@ zh-TW:
       two_factor_disabled:
         explanation: 您帳號的兩階段驗證已停用。現在只使用電子郵件及密碼登入。
         subject: Mastodon:已停用兩階段驗證
-        title: 已停用 2FA
+        title: 已停用兩階段驗證
       two_factor_enabled:
         explanation: 已對您的帳號啟用兩階段驗證。登入時將需要已配對的 TOTP 應用程式所產生之 Token。
         subject: Mastodon:已啟用兩階段驗證
-        title: 已啟用 2FA
+        title: 已啟用兩階段驗證
       two_factor_recovery_codes_changed:
         explanation: 之前的備用驗證碼已經失效,且已產生新的。
         subject: Mastodon:兩階段驗證備用驗證碼已經重新產生
-        title: 2FA 備用驗證碼已變更
+        title: 兩階段驗證備用驗證碼已變更
       unlock_instructions:
         subject: Mastodon:解鎖指引
       webauthn_credential:
@@ -82,8 +82,8 @@ zh-TW:
       success: 成功透過 %{kind} 帳號登入。
     passwords:
       no_token: 您必須透過密碼重設信件才能存取此頁面。若確實如此,請確定輸入的網址是完整的。
-      send_instructions: 若電子郵件地址存在於我們的資料庫,幾分鐘後您將在信箱中收到密碼復原連結。若未收到請檢查垃圾郵件資料夾。
-      send_paranoid_instructions: 若電子郵件地址存在於我們的資料庫,幾分鐘後您將在信箱中收到密碼復原連結。若未收到請檢查垃圾郵件資料夾。
+      send_instructions: 若電子郵件地址存在於我們的資料庫,幾分鐘後您將於信箱中收到密碼復原連結。若未收到請檢查垃圾郵件資料夾。
+      send_paranoid_instructions: 若電子郵件地址存在於我們的資料庫,幾分鐘後您將於信箱中收到密碼復原連結。若未收到請檢查垃圾郵件資料夾。
       updated: 您的密碼已成功變更,現在已經登入。
       updated_not_active: 您的密碼已成功變更。
     registrations:
@@ -101,7 +101,7 @@ zh-TW:
       signed_out: 已成功登出。
     unlocks:
       send_instructions: 幾分鐘後您將收到解鎖帳號的指引信件。若未收到請檢查垃圾郵件資料夾。
-      send_paranoid_instructions: 若此帳號存在,您將在幾分鐘後收到解鎖指引信件。若未收到請檢查垃圾郵件資料夾。
+      send_paranoid_instructions: 若此帳號存在,您將於幾分鐘後收到解鎖指引信件。若未收到請檢查垃圾郵件資料夾。
       unlocked: 已解鎖您的帳號,請登入繼續。
   errors:
     messages:
diff --git a/config/locales/doorkeeper.af.yml b/config/locales/doorkeeper.af.yml
index c2aab2646..a513a6a45 100644
--- a/config/locales/doorkeeper.af.yml
+++ b/config/locales/doorkeeper.af.yml
@@ -103,14 +103,12 @@ af:
         admin/accounts: Administrasie van rekeninge
         admin/all: Alle administratiewe funksies
         admin/reports: Administrasie van rapporteringe
-        all: Alles
         blocks: Blokkerings
         bookmarks: Boekmerke
         conversations: Gesprekke
         crypto: End-tot-end-enkripsie
         favourites: Gunstelinge
         filters: Filters
-        follow: Verhoudings
         lists: Lyste
         media: Media-aanhegsels
         mutes: Uitdowings
diff --git a/config/locales/doorkeeper.an.yml b/config/locales/doorkeeper.an.yml
index d3dd67158..127366d1c 100644
--- a/config/locales/doorkeeper.an.yml
+++ b/config/locales/doorkeeper.an.yml
@@ -122,14 +122,14 @@ an:
         admin/accounts: Administración de cuentas
         admin/all: Totas las funcions administrativas
         admin/reports: Administración d'informes
-        all: Tot
+        all: Acceso completo a la tuya cuenta de Mastodon
         blocks: Bloqueyos
         bookmarks: Marcadors
         conversations: Conversacions
         crypto: Zifrau de cabo a cabo
         favourites: Favoritos
         filters: Filtros
-        follow: Relacions
+        follow: Seguius, silenciaus y blocaus
         follows: Seguius
         lists: Listas
         media: Adjuntos multimedia
@@ -149,9 +149,19 @@ an:
     scopes:
       admin:read: leyer totz los datos en o servidor
       admin:read:accounts: leyer información sensible de totas las cuentas
+      admin:read:canonical_email_blocks: leyer información sensible de totz los bloqueyos canonicos de correu
+      admin:read:domain_allows: leyer información sensible de totz los dominios permesos
+      admin:read:domain_blocks: leyer información sensible de totz los bloqueyos de dominio
+      admin:read:email_domain_blocks: leyer información sensible de totz los bloqueyos de dominio de correu-e
+      admin:read:ip_blocks: leyer información sensible de totz los bloqueyos d'IP
       admin:read:reports: leyer información sensible de totz los informes y cuentas denunciadas
       admin:write: modificar totz los datos en o servidor
       admin:write:accounts: realizar accions de moderación en cuentas
+      admin:write:canonical_email_blocks: executar accions de moderación en bloqueyos canonicos de correu electronico
+      admin:write:domain_allows: executar accions de moderación en os dominios permesos
+      admin:write:domain_blocks: executar accions de moderación en bloqueyos de dominio
+      admin:write:email_domain_blocks: executar accions de moderación en bloqueyos de dominio de correu-e
+      admin:write:ip_blocks: executar accions de moderación en bloqueyos d'IP
       admin:write:reports: realizar accions de moderación en informes
       crypto: usar zifrau de cabo a extremo
       follow: seguir, blocar, desblocar y deixar de seguir cuentas
diff --git a/config/locales/doorkeeper.ar.yml b/config/locales/doorkeeper.ar.yml
index 88aaaa345..b10f5dbeb 100644
--- a/config/locales/doorkeeper.ar.yml
+++ b/config/locales/doorkeeper.ar.yml
@@ -122,14 +122,14 @@ ar:
         admin/accounts: إدارة الحسابات
         admin/all: جميع المهام الإدارية
         admin/reports: إدارة التقارير
-        all: كل شيء
+        all: وصول كامل إلى حساب ماستدون الخاص بك
         blocks: تم حجبها
         bookmarks: الفواصل المرجعية
         conversations: المحادثات
         crypto: التشفير من الطرف إلى نهاية الطرف
         favourites: المفضلة
         filters: عوامل التصفية
-        follow: العلاقات
+        follow: الإشتراكات والحسابات المكتومة والحسابات المحجوبة
         follows: الإشتراكات
         lists: القوائم
         media: الوسائط المرفقة
diff --git a/config/locales/doorkeeper.ast.yml b/config/locales/doorkeeper.ast.yml
index 445d1093c..61d6c4ad1 100644
--- a/config/locales/doorkeeper.ast.yml
+++ b/config/locales/doorkeeper.ast.yml
@@ -14,6 +14,8 @@ ast:
         destroy: Destruyir
         edit: Editar
         submit: Unviar
+      confirmations:
+        destroy: "¿De xuru que quies facer esta aición?"
       form:
         error: "¡Meca! Revisa los errores posibles del formulariu"
       help:
@@ -23,6 +25,7 @@ ast:
         name: Nome
         new: Aplicación nueva
         scopes: Ámbitos
+        title: Les tos aplicaciones
       new:
         title: Aplicación nueva
       show:
@@ -45,10 +48,13 @@ ast:
     authorized_applications:
       buttons:
         revoke: Revocar
+      confirmations:
+        revoke: "¿De xuru que quies facer esta aición?"
       index:
         description_html: Estes son les aplicaciones que puen acceder a la cuenta cola API. Si equí hai aplicaciones que nun conoces o hai dalguna aplicación que nun funciona correutamente, pues revocar el so accesu.
         never_used: Enxamás s'usó
         scopes: Permisos
+        title: Les aplicaciones qu'autoricesti
     errors:
       messages:
         access_denied: El propietariu del recursu o'l sirvidor d'autorización negó la solicitú.
@@ -65,12 +71,22 @@ ast:
         write: Accesu de namás escritura
       title:
         accounts: Cuentes
+        admin/accounts: Alministración de cuentes
         admin/all: Toles funciones alministratives
+        admin/reports: Alministración d'informes
+        all: Accesu completu a la cuenta de Mastodon
+        bookmarks: Marcadores
         conversations: Conversaciones
         crypto: Cifráu de puntu a puntu
+        favourites: Favoritos
         filters: Peñeres
-        follow: Rellaciones
+        lists: Llistes
+        media: Elementos multimedia
+        mutes: Perfiles colos avisos desativaos
         notifications: Avisos
+        reports: Informes
+        search: Busca
+        statuses: Artículos
     layouts:
       admin:
         nav:
@@ -82,7 +98,7 @@ ast:
       admin:write: modifica tolos datos del sirvidor
       crypto: usa'l cifráu de puntu a puntu
       follow: modifica les rellaciones de les cuentes
-      push: receición d'avisos push
+      push: recibe avisos push
       read: llee tolos datos de les cuentes
       read:accounts: mira la información de les cuentes
       read:blocks: mira les cuentes bloquiaes
@@ -97,6 +113,8 @@ ast:
       write: modifica los datos de les cuentes
       write:accounts: modifica los perfiles
       write:blocks: bloquia cuentes ya dominios
+      write:bookmarks: meter artículos en Marcadores
+      write:conversations: desaniciar ya desactivar los avisos de conversaciones
       write:favourites: artículos favoritos
       write:filters: crea peñeres
       write:follows: sigue a perfiles
@@ -104,5 +122,5 @@ ast:
       write:media: xube ficheros multimedia
       write:mutes: desactiva los avisos de perfiles ya de conversaciones
       write:notifications: borra los avisos
-      write:reports: informa d'otres perfiles
+      write:reports: informa d'otros perfiles
       write:statuses: espubliza artículos
diff --git a/config/locales/doorkeeper.be.yml b/config/locales/doorkeeper.be.yml
index 2a7447449..1d0ffda4b 100644
--- a/config/locales/doorkeeper.be.yml
+++ b/config/locales/doorkeeper.be.yml
@@ -122,14 +122,14 @@ be:
         admin/accounts: Кіраванне акаўнтамі
         admin/all: Усе кіравальныя функцыі
         admin/reports: Кіраванне скардамі
-        all: Усе
+        all: Поўны доступ да акаўнта Mastodon
         blocks: Блакаванні
         bookmarks: Закладкі
         conversations: Размовы
         crypto: Скразное шыфраванне
         favourites: Абраныя
         filters: Фільтры
-        follow: Зносіны
+        follow: Падпіскі, ігнараванне і блакіроўка
         follows: Падпіскі
         lists: Спісы
         media: Мультымедыйныя ўкладанні
diff --git a/config/locales/doorkeeper.bg.yml b/config/locales/doorkeeper.bg.yml
index 9a6aaf92f..ec1f351ee 100644
--- a/config/locales/doorkeeper.bg.yml
+++ b/config/locales/doorkeeper.bg.yml
@@ -74,7 +74,7 @@ bg:
         authorized_at: Упълномощено на %{date}
         description_html: Има приложения, можещи да имат достъп до акаунта ви, използвайки API. Ако тук има приложения, които не знаете, или работещи неправилно, то може да им откажете достъпа.
         last_used_at: Последно обновено на %{date}
-        never_used: Нивга ползвано
+        never_used: Никога употребявано
         scopes: Разрешения
         superapp: Вътрешно
         title: Упълномощените ви приложения
@@ -122,14 +122,14 @@ bg:
         admin/accounts: Администриране на акаунтите
         admin/all: Всички административни функции
         admin/reports: Администриране на докладите
-        all: Всичко
+        all: Пълен достъп до акаунта ви в Mastodon
         blocks: Блокирания
         bookmarks: Отметки
         conversations: Разговори
         crypto: Криптиране от край до край
         favourites: Любими
         filters: Филтри
-        follow: Отношения
+        follow: Последвания, заглушавания и блокирания
         follows: Последвания
         lists: Списъци
         media: Прикачена мултимедия
diff --git a/config/locales/doorkeeper.ca.yml b/config/locales/doorkeeper.ca.yml
index 13f0dbef0..93fb071b3 100644
--- a/config/locales/doorkeeper.ca.yml
+++ b/config/locales/doorkeeper.ca.yml
@@ -122,14 +122,14 @@ ca:
         admin/accounts: Administració dels comptes
         admin/all: Totes les funcions administratives
         admin/reports: Administració dels informes
-        all: Tot
+        all: Accés complet al teu compte de Mastodon
         blocks: Bloquejos
         bookmarks: Marcadors
         conversations: Converses
         crypto: Xifrat d'extrem a extrem
         favourites: Preferits
         filters: Filtres
-        follow: Relacions
+        follow: Seguits, Silenciats i Blocats
         follows: Seguits
         lists: Llistes
         media: Adjunts multimèdia
diff --git a/config/locales/doorkeeper.ckb.yml b/config/locales/doorkeeper.ckb.yml
index 3262cc17f..39b9f65f6 100644
--- a/config/locales/doorkeeper.ckb.yml
+++ b/config/locales/doorkeeper.ckb.yml
@@ -122,14 +122,12 @@ ckb:
         admin/accounts: بەڕێوەبردنی ژمێریارییەکان
         admin/all: هەموو ئەرکە کارگێڕییەکان
         admin/reports: بەڕێوەبردنی ڕاپۆرتەکان
-        all: هەمووی
         blocks: بلۆک
         bookmarks: نیشانەکان
         conversations: گفتوگۆکان
         crypto: کۆدکردنی کۆتایی بۆ کۆتایی
         favourites: دڵخوازەکان
         filters: پاڵێوراوەکان
-        follow: پەیوەندیەکان
         follows: بەدواداچووان
         lists: پێرستەکان
         media: هاوپێچەکانی میدیا
diff --git a/config/locales/doorkeeper.cs.yml b/config/locales/doorkeeper.cs.yml
index c9f8777fa..b8bb6b854 100644
--- a/config/locales/doorkeeper.cs.yml
+++ b/config/locales/doorkeeper.cs.yml
@@ -122,14 +122,14 @@ cs:
         admin/accounts: Správa účtů
         admin/all: Všechny správcovské funkce
         admin/reports: Správa hlášení
-        all: Všechno
+        all: Plný přístup k vašemu Mastodon účtu
         blocks: Blokace
         bookmarks: Záložky
         conversations: Konverzace
         crypto: End-to-end šifrování
         favourites: Oblíbení
         filters: Filtry
-        follow: Vztahy
+        follow: Sledování, ztlumení a blokování
         follows: Sledovaní
         lists: Seznamy
         media: Mediální přílohy
diff --git a/config/locales/doorkeeper.csb.yml b/config/locales/doorkeeper.csb.yml
new file mode 100644
index 000000000..0de706e41
--- /dev/null
+++ b/config/locales/doorkeeper.csb.yml
@@ -0,0 +1 @@
+csb:
diff --git a/config/locales/doorkeeper.cy.yml b/config/locales/doorkeeper.cy.yml
index 609826f51..e79aa0359 100644
--- a/config/locales/doorkeeper.cy.yml
+++ b/config/locales/doorkeeper.cy.yml
@@ -122,14 +122,14 @@ cy:
         admin/accounts: Gweinyddu cyfrifon
         admin/all: Pob swyddogaeth weinyddol
         admin/reports: Gweinyddu adroddiadau
-        all: Popeth
+        all: Mynediad llawn i'ch cyfrif Mastodon
         blocks: Blociau
-        bookmarks: Nodau Tudalen
+        bookmarks: Llyfrnodau
         conversations: Sgyrsiau
         crypto: Amgryptio o ben i ben
         favourites: Ffefrynnau
         filters: Hidlyddion
-        follow: Perthnasau
+        follow: Dilynion, Anwybyddiadau a Blociau
         follows: Yn dilyn
         lists: Rhestrau
         media: Atodiadau cyfryngau
@@ -149,9 +149,19 @@ cy:
     scopes:
       admin:read: darllen yr holl ddata ar y gweinydd
       admin:read:accounts: darllen gwybodaeth sensitif o bob cyfrif
+      admin:read:canonical_email_blocks: darllen gwybodaeth sensitif pob bloc e-bost canonaidd
+      admin:read:domain_allows: darllen gwybodaeth sensitif pob caniatád parth
+      admin:read:domain_blocks: darllen gwybodaeth sensitif pob bloc parth
+      admin:read:email_domain_blocks: darllen gwybodaeth sensitif pob bloc parth ebost
+      admin:read:ip_blocks: darllen gwybodaeth sensitif pob bloc IP
       admin:read:reports: darllen gwybodaeth sensitif o'r holl adroddiadau a'r cyfrifon a adroddwyd
       admin:write: addasu'r holl ddata ar y gweinydd
       admin:write:accounts: cyflawni camau cymedroli ar gyfrifon
+      admin:write:canonical_email_blocks: cyflawni cymedroli ar gyfer pob bloc e-bost canonaidd
+      admin:write:domain_allows: cyflawni cymedroli ar gyfer pob caniatád parth
+      admin:write:domain_blocks: cyflawni cymedroli ar gyfer pob bloc parth
+      admin:write:email_domain_blocks: cyflawni cymedroli ar gyfer pob bloc parth ebost
+      admin:write:ip_blocks: cyflawni cymedroli ar gyfer pob bloc IP
       admin:write:reports: cyflawni camau cymedroli ar adroddiadau
       crypto: defnyddio amgryptio ben i ben
       follow: addasu perthnasau cyfrif
@@ -159,7 +169,7 @@ cy:
       read: darllen holl ddata eich cyfrif
       read:accounts: gweld gwybodaeth y cyfrif
       read:blocks: gweld eich blociau
-      read:bookmarks: gweld eich nodau tudalen
+      read:bookmarks: gweld eich llyfrnodau
       read:favourites: gweld eich ffefrynnau
       read:filters: gweld eich hidlwyr
       read:follows: gweld eich dilynwyr
@@ -172,7 +182,7 @@ cy:
       write: addasu holl ddata eich cyfrif
       write:accounts: addasu eich proffil
       write:blocks: blocio cyfrifon a parthau
-      write:bookmarks: gosod nod tudalen postiadau
+      write:bookmarks: llyfrnodi postiadau
       write:conversations: anwybyddu a dileu sgyrsiau
       write:favourites: hoff bostiadau
       write:filters: creu hidlwyr
diff --git a/config/locales/doorkeeper.da.yml b/config/locales/doorkeeper.da.yml
index 43908f020..2c8b0d13e 100644
--- a/config/locales/doorkeeper.da.yml
+++ b/config/locales/doorkeeper.da.yml
@@ -122,14 +122,14 @@ da:
         admin/accounts: Kontihåndtering
         admin/all: Alle håndteringsfunktioner
         admin/reports: Rapporthåndteringer
-        all: Alt
+        all: Fuld adgang til din Mastodon-konto
         blocks: Blokeringer
         bookmarks: Bogmærker
         conversations: Konversationer
         crypto: Ende-til-ende kryptering
         favourites: Favoritter
         filters: Filtre
-        follow: Relationer
+        follow: Følger, Tavsgør og Blokerer
         follows: Følger
         lists: Lister
         media: Medievedhæftninger
diff --git a/config/locales/doorkeeper.de.yml b/config/locales/doorkeeper.de.yml
index 6c68935a4..3b69cb055 100644
--- a/config/locales/doorkeeper.de.yml
+++ b/config/locales/doorkeeper.de.yml
@@ -3,10 +3,10 @@ de:
   activerecord:
     attributes:
       doorkeeper/application:
-        name: Name der Anwendung
+        name: Anwendungsname
         redirect_uri: Weiterleitungs-URI
         scopes: Befugnisse
-        website: Webseite der Anwendung
+        website: Website der Anwendung
     errors:
       models:
         doorkeeper/application:
@@ -25,11 +25,11 @@ de:
         edit: Bearbeiten
         submit: Speichern
       confirmations:
-        destroy: Bist du sicher?
+        destroy: Bist du dir sicher?
       edit:
         title: Anwendung bearbeiten
       form:
-        error: Hoppla! Bitte überprüfe das Formular auf mögliche Fehler
+        error: Ups! Bitte überprüfe das Formular auf mögliche Fehler
       help:
         native_redirect_uri: Benutze %{native_redirect_uri} für lokale Tests
         redirect_uri: Benutze eine Zeile pro URI
@@ -60,8 +60,8 @@ de:
       error:
         title: Ein Fehler ist aufgetreten
       new:
-        prompt_html: "%{client_name} möchte auf dein Konto zugreifen. Es ist eine Anwendung von Drittanbietern. <strong>Wenn du ihr nicht vertraust, dann solltest du sie nicht autorisieren.</strong>"
-        review_permissions: Rechte überprüfen
+        prompt_html: "%{client_name} möchte auf dein Konto zugreifen. Es handelt sich um eine Drittanbieter-Software. <strong>Wenn du der Anwendung nicht vertraust, solltest du ihr keinen Zugriff auf dein Konto geben.</strong>"
+        review_permissions: Berechtigungen überprüfen
         title: Autorisierung erforderlich
       show:
         title: Kopiere diesen Autorisierungs-Code und füge ihn in die Anwendung ein.
@@ -69,7 +69,7 @@ de:
       buttons:
         revoke: Widerrufen
       confirmations:
-        revoke: Bist du sicher?
+        revoke: Bist du dir sicher?
       index:
         authorized_at: Autorisiert am %{date}
         description_html: Dies sind Anwendungen, die über die Programmierschnittstelle (API) auf dein Konto zugreifen können. Wenn es Anwendungen gibt, die du hier nicht zuordnen kannst oder wenn sich eine Anwendung verdächtig verhält, kannst du den Zugriff widerrufen.
@@ -82,7 +82,7 @@ de:
       messages:
         access_denied: Die Anfrage wurde durch Benutzer_in oder Autorisierungs-Server verweigert.
         credential_flow_not_configured: Das Konto konnte nicht gefunden werden, da Doorkeeper.configure.resource_owner_from_credentials nicht konfiguriert ist.
-        invalid_client: 'Client-Authentifizierung ist fehlgeschlagen: Client unbekannt, keine Authentisierung mitgeliefert oder Authentisierungsmethode wird nicht unterstützt.'
+        invalid_client: 'Client-Authentisierung ist fehlgeschlagen: Client unbekannt, keine Authentisierung mitgeliefert oder Authentisierungsmethode wird nicht unterstützt.'
         invalid_grant: Die beigefügte Autorisierung ist ungültig, abgelaufen, wurde widerrufen oder einem anderen Client ausgestellt, oder der Weiterleitungs-URI stimmt nicht mit der Autorisierungs-Anfrage überein.
         invalid_redirect_uri: Der beigefügte Weiterleitungs-URI ist ungültig.
         invalid_request:
@@ -114,22 +114,22 @@ de:
           notice: Anwendung widerrufen.
     grouped_scopes:
       access:
-        read: Nur-Lese-Zugriff
+        read: Nur Lesezugriff
         read/write: Lese- und Schreibzugriff
-        write: Schreibzugriff
+        write: Nur Schreibzugriff
       title:
         accounts: Konten
-        admin/accounts: Verwaltung der Konten
+        admin/accounts: Kontenverwaltung
         admin/all: Alle administrativen Funktionen
         admin/reports: Verwaltung der Berichte
-        all: Alles
-        blocks: Blocks
+        all: Voller Zugriff auf dein Mastodon-Konto
+        blocks: Blockierungen
         bookmarks: Lesezeichen
         conversations: Unterhaltungen
         crypto: Ende-zu-Ende-Verschlüsselung
         favourites: Favoriten
         filters: Filter
-        follow: Beziehungen
+        follow: Folge ich, Stummschaltungen und Sperrungen
         follows: Folge ich
         lists: Listen
         media: Medienanhänge
@@ -168,9 +168,9 @@ de:
       push: deine Push-Benachrichtigungen erhalten
       read: all deine Daten lesen
       read:accounts: deine Konteninformationen einsehen
-      read:blocks: deine Blockaden einsehen
+      read:blocks: deine Sperren einsehen
       read:bookmarks: deine Lesezeichen lesen
-      read:favourites: deine Favoriten ansehen
+      read:favourites: deine Favoriten lesen
       read:filters: deine Filter ansehen
       read:follows: sehen, wem du folgst
       read:lists: deine Listen sehen
@@ -186,10 +186,10 @@ de:
       write:conversations: Unterhaltungen stummschalten und löschen
       write:favourites: Beiträge favorisieren
       write:filters: Filter erstellen
-      write:follows: Leuten folgen
+      write:follows: Profilen folgen
       write:lists: Listen erstellen
       write:media: Mediendateien hochladen
       write:mutes: Profile und Unterhaltungen stummschalten
       write:notifications: deine Benachrichtigungen leeren
-      write:reports: andere Leute melden
+      write:reports: andere Profile melden
       write:statuses: Beiträge veröffentlichen
diff --git a/config/locales/doorkeeper.el.yml b/config/locales/doorkeeper.el.yml
index 31fe3a8aa..abb6ccd68 100644
--- a/config/locales/doorkeeper.el.yml
+++ b/config/locales/doorkeeper.el.yml
@@ -60,6 +60,8 @@ el:
       error:
         title: Εμφανίστηκε σφάλμα
       new:
+        prompt_html: Ο/Η %{client_name} θα ήθελε άδεια πρόσβασης στο λογαριασμό σας. Είναι μια εφαρμογή από τρίτους. <strong>Αν δεν το εμπιστεύεστε, τότε δεν πρέπει να το εξουσιοδοτήσετε.</strong>
+        review_permissions: Αναθεώρηση δικαιωμάτων
         title: Απαιτείται έγκριση
       show:
         title: Αντέγραψε αυτό τον κωδικό έγκρισης στην εφαρμογή.
@@ -69,8 +71,12 @@ el:
       confirmations:
         revoke: Σίγουρα;
       index:
+        authorized_at: Εξουσιοδοτήθηκε στις %{date}
+        description_html: Αυτές είναι εφαρμογές που μπορούν να έχουν πρόσβαση στο λογαριασμό σας χρησιμοποιώντας το API. Αν υπάρχουν εφαρμογές που δεν αναγνωρίζετε εδώ ή μια εφαρμογή δεν συμπεριφέρεται σωστά, μπορείτε να ανακαλέσετε την πρόσβασή της.
+        last_used_at: Τελευταία χρήση στις %{date}
         never_used: Ποτέ σε χρήση
         scopes: Δικαιώματα
+        superapp: Εσωτερική
         title: Οι εφαρμογές που έχεις εγκρίνει
     errors:
       messages:
@@ -116,12 +122,15 @@ el:
         admin/accounts: Διαχείριση λογαριασμών
         admin/all: Όλες οι λειτουργίες διαχείρησης
         admin/reports: Διαχείριση αναφορών
+        all: Πλήρης πρόσβαση στο λογαριασμό σας στο Mastodon
+        blocks: Αποκλεισμοί
         bookmarks: Σελιδοδείκτες
         conversations: Συνομιλίες
         crypto: Κρυπτογράφηση από άκρο σε άκρο
         favourites: Αγαπημένα
         filters: Φίλτρα
-        follow: Σχέσεις
+        follow: Ακολουθείτε, σε Σίγαση και Αποκλεισμοί
+        follows: Ακολουθείτε
         lists: Λίστες
         media: Συνημμένα πολυμέσα
         mutes: Αποσιωπήσεις
@@ -140,9 +149,19 @@ el:
     scopes:
       admin:read: ανάγνωση δεδομένων στον διακομιστή
       admin:read:accounts: ανάγνωση ευαίσθητων πληροφοριών όλων των λογαριασμών
+      admin:read:canonical_email_blocks: ανάγνωση ευαίσθητων πληροφοριών όλων των αποκλεισμένων email
+      admin:read:domain_allows: ανάγνωση ευαίσθητων πληροφοριών όλων των επιτρεπόμενων τομέων
+      admin:read:domain_blocks: ανάγνωση ευαίσθητων πληροφοριών όλων των αποκλεισμένων τομέων
+      admin:read:email_domain_blocks: ανάγνωση ευαίσθητων πληροφοριών όλων των αποκλεισμένων τομέων email
+      admin:read:ip_blocks: ανάγνωση ευαίσθητων πληροφοριών όλων των αποκλεισμένων IP
       admin:read:reports: ανάγνωση ευαίσθητων πληροφοριών όλων των καταγγελιών και των καταγγελλομένων λογαριασμών
       admin:write: αλλαγή δεδομένων στον διακομιστή
       admin:write:accounts: εκτέλεση διαχειριστικών ενεργειών σε λογαριασμούς
+      admin:write:canonical_email_blocks: εκτέλεση ενεργειών διαχείρισης σε αποκλεισμένα email
+      admin:write:domain_allows: εκτέλεση ενεργειών διαχείρισης σε επιτρεπτούς τομείς
+      admin:write:domain_blocks: εκτέλεση ενεργειών διαχείρισης σε αποκλεισμένους τομείς
+      admin:write:email_domain_blocks: εκτελέστε ενέργειες διαχείρισης σε αποκλεισμένους τομείς email
+      admin:write:ip_blocks: εκτέλεση ενεργειών διαχείρισης σε αποκλεισμένες IP
       admin:write:reports: εκτέλεση διαχειριστικών ενεργειών σε καταγγελίες
       crypto: χρήση κρυπτογράφησης από άκρο σε άκρο
       follow: να αλλάζει τις σχέσεις με λογαριασμούς
diff --git a/config/locales/doorkeeper.en-GB.yml b/config/locales/doorkeeper.en-GB.yml
index 7d2716359..b3ceffb13 100644
--- a/config/locales/doorkeeper.en-GB.yml
+++ b/config/locales/doorkeeper.en-GB.yml
@@ -122,14 +122,14 @@ en-GB:
         admin/accounts: Administration of accounts
         admin/all: All administrative functions
         admin/reports: Administration of reports
-        all: Everything
+        all: Full access to your Mastodon account
         blocks: Blocks
         bookmarks: Bookmarks
         conversations: Conversations
         crypto: End-to-end encryption
         favourites: Favourites
         filters: Filters
-        follow: Relationships
+        follow: Follows, Mutes and Blocks
         follows: Follows
         lists: Lists
         media: Media attachments
diff --git a/config/locales/doorkeeper.eo.yml b/config/locales/doorkeeper.eo.yml
index b0bbeaec0..18005fc3a 100644
--- a/config/locales/doorkeeper.eo.yml
+++ b/config/locales/doorkeeper.eo.yml
@@ -122,17 +122,17 @@ eo:
         admin/accounts: Administro de kontoj
         admin/all: Ĉiuj administraj funkcioj
         admin/reports: Administro de raportoj
-        all: Ĉio
+        all: Plena aliro al via Mastodon-konto
         blocks: Blokita
         bookmarks: Legosignoj
         conversations: Konversacioj
         crypto: Fin-al-fina ĉifrado
         favourites: Preferaĵoj
         filters: Filtriloj
-        follow: Rilatoj
+        follow: Abonadoj, silentigitaj kontoj kaj blokitaj kontoj
         follows: Sekvas
         lists: Listoj
-        media: Aŭdovidaj aldonaĵoj
+        media: Plurmediaj aldonaĵoj
         mutes: Silentigitaj
         notifications: Sciigoj
         push: Puŝsciigoj
@@ -149,9 +149,19 @@ eo:
     scopes:
       admin:read: legu ĉiujn datumojn en la servilo
       admin:read:accounts: legi konfidencajn informojn de ĉiuj kontoj
+      admin:read:canonical_email_blocks: legi sentemajn informojn de ĉiuj kanonikaj retpoŝtaj blokoj
+      admin:read:domain_allows: legi sentemajn informojn de ĉiuj domajno permesas
+      admin:read:domain_blocks: legi sentemajn informojn de ĉiuj domajnaj blokoj
+      admin:read:email_domain_blocks: legi sentemajn informojn pri ĉiuj retpoŝtaj domajnaj blokoj
+      admin:read:ip_blocks: legi sentivajn informojn de ĉiuj IP-blokoj
       admin:read:reports: legi konfidencajn informojn de ĉiuj signaloj kaj signalitaj kontoj
       admin:write: modifi ĉiujn datumojn en la servilo
       admin:write:accounts: plenumi agojn de kontrolo sur kontoj
+      admin:write:canonical_email_blocks: fari moderigajn agojn sur kanonikaj retpoŝtaj blokoj
+      admin:write:domain_allows: fari moderigajn agojn sur domajno permesas
+      admin:write:domain_blocks: fari moderigajn agojn sur domajnaj blokoj
+      admin:write:email_domain_blocks: fari moderigajn agojn pri retpoŝtaj domajnaj blokoj
+      admin:write:ip_blocks: fari moderigajn agojn pri IP-blokoj
       admin:write:reports: plenumi agojn de kontrolo sur signaloj
       crypto: uzi fin-al-finan ĉifradon
       follow: ŝanĝi rilatojn al aliaj kontoj
@@ -178,7 +188,7 @@ eo:
       write:filters: krei filtrilojn
       write:follows: sekvi homojn
       write:lists: krei listojn
-      write:media: alŝuti aŭdovidaĵojn
+      write:media: alŝuti plurmediojn
       write:mutes: silentigi homojn kaj konversaciojn
       write:notifications: forigi viajn sciigojn
       write:reports: signali aliajn homojn
diff --git a/config/locales/doorkeeper.es-AR.yml b/config/locales/doorkeeper.es-AR.yml
index 789e7ab1d..532db1f30 100644
--- a/config/locales/doorkeeper.es-AR.yml
+++ b/config/locales/doorkeeper.es-AR.yml
@@ -122,14 +122,14 @@ es-AR:
         admin/accounts: Administración de cuentas
         admin/all: Todas las funciones administrativas
         admin/reports: Administración de denuncias
-        all: Todo
+        all: Acceso total a tu cuenta de Mastodon
         blocks: Bloqueos
         bookmarks: Marcadores
         conversations: Conversaciones
         crypto: Cifrado de extremo a extremo
         favourites: Favoritos
         filters: Filtros
-        follow: Relaciones
+        follow: Seguimientos, silencios y bloqueos
         follows: Seguimientos
         lists: Listas
         media: Adjuntos de medios
diff --git a/config/locales/doorkeeper.es-MX.yml b/config/locales/doorkeeper.es-MX.yml
index 8358ce9bc..f6b5e4c5d 100644
--- a/config/locales/doorkeeper.es-MX.yml
+++ b/config/locales/doorkeeper.es-MX.yml
@@ -122,14 +122,14 @@ es-MX:
         admin/accounts: Administración de cuentas
         admin/all: Todas las funciones administrativas
         admin/reports: Administración de reportes
-        all: Todo
+        all: Acceso completo a tu cuenta de Mastodon
         blocks: Bloques
         bookmarks: Marcadores
         conversations: Conversaciones
         crypto: Cifrado de extremo a extremo
         favourites: Favoritos
         filters: Filtros
-        follow: Relaciones
+        follow: Seguimientos, silenciados y bloqueos
         follows: Seguidos
         lists: Listas
         media: Archivos adjuntos
diff --git a/config/locales/doorkeeper.es.yml b/config/locales/doorkeeper.es.yml
index dbd760f05..dc9308e5f 100644
--- a/config/locales/doorkeeper.es.yml
+++ b/config/locales/doorkeeper.es.yml
@@ -122,14 +122,14 @@ es:
         admin/accounts: Administración de cuentas
         admin/all: Todas las funciones administrativas
         admin/reports: Administración de informes
-        all: Todo
+        all: Acceso completo a tu cuenta de Mastodon
         blocks: Bloqueos
         bookmarks: Marcadores
         conversations: Conversaciones
         crypto: Cifrado de extremo a extremo
         favourites: Favoritos
         filters: Filtros
-        follow: Relaciones
+        follow: Seguimientos, silenciados y bloqueos
         follows: Seguidos
         lists: Listas
         media: Adjuntos multimedia
diff --git a/config/locales/doorkeeper.et.yml b/config/locales/doorkeeper.et.yml
index 8749121e1..0f8ddd277 100644
--- a/config/locales/doorkeeper.et.yml
+++ b/config/locales/doorkeeper.et.yml
@@ -122,14 +122,14 @@ et:
         admin/accounts: Kontode haldus
         admin/all: Kõik haldusfunktsioonid
         admin/reports: Raportite haldus
-        all: Kõik
+        all: Täielik ligipääs Su Mastodoni kontole
         blocks: Keelud
         bookmarks: Järjehoidjad
         conversations: Vestlused
         crypto: Otspunktkrüpteerimine
         favourites: Lemmikud
         filters: Filtrid
-        follow: Seosed
+        follow: Jälgitavad, Vaigistatud ja Blokeeritud
         follows: Jälgimised
         lists: Nimekirjad
         media: Lisatud meedia
@@ -181,7 +181,7 @@ et:
       read:statuses: näha kõiki postitusi
       write: muuta kogu konto andmeid
       write:accounts: muuta profiili
-      write:blocks: blokeerida kontosid ja domeene
+      write:blocks: kontode ja domeenide blokeerimine
       write:bookmarks: lisada postitusele järjehoidja
       write:conversations: vaigista ja kustuta vestlused
       write:favourites: lisada postitusi lemmikuks
diff --git a/config/locales/doorkeeper.eu.yml b/config/locales/doorkeeper.eu.yml
index 12f6aaee0..8d2a9f3b6 100644
--- a/config/locales/doorkeeper.eu.yml
+++ b/config/locales/doorkeeper.eu.yml
@@ -122,14 +122,14 @@ eu:
         admin/accounts: Kontuen administrazioa
         admin/all: Funtzio administratibo guztiak
         admin/reports: Salaketen administrazioa
-        all: Dena
+        all: Sarbide osoa zure Mastodon kontura
         blocks: Blokeoak
         bookmarks: Laster-markak
         conversations: Elkarrizketak
         crypto: Muturretik-muturrerako zifraketa
         favourites: Gogokoak
         filters: Iragazkiak
-        follow: Erlazioak
+        follow: Jarraitzeak, mututzeak eta blokeatzeak
         follows: Jarraipenak
         lists: Zerrendak
         media: Multimedia eranskinak
@@ -149,9 +149,19 @@ eu:
     scopes:
       admin:read: zerbitzariko datu guztiak irakurri
       admin:read:accounts: kontu guztien informazio sentsiblea irakurri
+      admin:read:canonical_email_blocks: irakurri eposta kanonikoen blokeatzeari buruzko informazio sentikorra
+      admin:read:domain_allows: irakurri onartutako domeinu guztien informazio sentikorra
+      admin:read:domain_blocks: irakurri blokeatutako domeinu guztien informazio sentikorra
+      admin:read:email_domain_blocks: irakurri blokeatutako eposta domeinu guztien informazio sentikorra
+      admin:read:ip_blocks: irakurri blokeatutako IP guztien informazio sentikorra
       admin:read:reports: salaketa guztietako eta salatutako kontu guztietako informazio sentsiblea irakurri
       admin:write: zerbitzariko datu guztiak aldatu
       admin:write:accounts: kontuetan moderazio ekintzak burutu
+      admin:write:canonical_email_blocks: gauzatu moderazio ekintzak eposta kanonikoen blokeatzean
+      admin:write:domain_allows: gauzatu moderazio ekintzak onartutako domeinuetan
+      admin:write:domain_blocks: gauzatu moderazio ekintzak domeinuen blokeatzeetan
+      admin:write:email_domain_blocks: gauzatu moderazio ekintzak eposta domeinuen blokeatzeetan
+      admin:write:ip_blocks: gauzatu moderazio ekintzak IP blokeatzeetan
       admin:write:reports: salaketetan moderazio ekintzak burutu
       crypto: erabili muturretik muturrerako zifraketa
       follow: aldatu kontuaren erlazioak
diff --git a/config/locales/doorkeeper.fa.yml b/config/locales/doorkeeper.fa.yml
index 799f46945..36c2ead4a 100644
--- a/config/locales/doorkeeper.fa.yml
+++ b/config/locales/doorkeeper.fa.yml
@@ -122,14 +122,14 @@ fa:
         admin/accounts: مدیریت حساب های کاربری
         admin/all: تمامی عملکرد های مدیریتی
         admin/reports: مدیریت بازخورد ها
-        all: همه چیز
+        all: دسترسی کامل به حساب ماستودونتان
         blocks: مسدودها
         bookmarks: نشانک‌ها
         conversations: گفت‌وگوها
         crypto: رمزگذاری سرتاسری
         favourites: پسندیده‌ها
         filters: پالایه‌ها
-        follow: ارتباطات
+        follow: پی‌گیری، خموشی و مسدودی‌ها
         follows: پی‌گرفتگان
         lists: سیاهه‌ها
         media: پیوست‌های رسانه‌ای
diff --git a/config/locales/doorkeeper.fi.yml b/config/locales/doorkeeper.fi.yml
index b939431bf..00a23f3a0 100644
--- a/config/locales/doorkeeper.fi.yml
+++ b/config/locales/doorkeeper.fi.yml
@@ -14,14 +14,14 @@ fi:
             redirect_uri:
               fragment_present: ei voi sisältää osia.
               invalid_uri: on oltava kelvollinen URI.
-              relative_uri: on oltava täydellinen URI.
+              relative_uri: on oltava täysi URI.
               secured_uri: on oltava HTTPS/SSL-URI.
   doorkeeper:
     applications:
       buttons:
         authorize: Valtuuta
         cancel: Peruuta
-        destroy: Tuhoa
+        destroy: Poista
         edit: Muokkaa
         submit: Lähetä
       confirmations:
@@ -29,11 +29,11 @@ fi:
       edit:
         title: Muokkaa sovellusta
       form:
-        error: Hups! Tarkista, että lomakkeessa ei ole virheitä
+        error: Hupsis! Tarkista, ettei lomakkeessasi ole virheitä
       help:
         native_redirect_uri: Käytä %{native_redirect_uri} paikallisiin testeihin
         redirect_uri: Lisää jokainen URI omalle rivilleen
-        scopes: Erota oikeudet välilyönnein. Jos kenttä jätetään tyhjäksi, käytetään oletusoikeuksia.
+        scopes: Erota oikeudet välilyönneillä. Jätä kenttä tyhjäksi, jos haluat käyttää oletusoikeuksia.
       index:
         application: Sovellus
         callback_url: Takaisinkutsu-URL
@@ -48,19 +48,19 @@ fi:
         title: Uusi sovellus
       show:
         actions: Toiminnot
-        application_id: Asiakasohjelman tunnus
+        application_id: Ohjelman tunnus
         callback_urls: Takaisinkutsu-URL:t
         scopes: Oikeudet
-        secret: Asiakasohjelman salainen avain
+        secret: Ohjelman salaisuus
         title: 'Sovellus: %{name}'
     authorizations:
       buttons:
         authorize: Valtuuta
-        deny: Evää
+        deny: Estä
       error:
         title: Tapahtui virhe
       new:
-        prompt_html: "%{client_name} pyytää lupaa käyttää tiliäsi. Se on kolmannen osapuolen sovellus. <strong>Jos et luota siihen, sinun ei pitäisi sallia sitä.</strong>"
+        prompt_html: "%{client_name} pyytää lupaa käyttää tiliäsi. Se on kolmannen osapuolen sovellus. <strong>Jos et luota siihen, älä valtuuta sitä.</strong>"
         review_permissions: Tarkista käyttöoikeudet
         title: Valtuutus vaaditaan
       show:
@@ -81,7 +81,7 @@ fi:
     errors:
       messages:
         access_denied: Resurssin omistaja tai valtuutuspalvelin hylkäsi pyynnön.
-        credential_flow_not_configured: Resurssin omistajan salasana epäonnistui, koska asetusta Doorkeeper.configure.resource_owner_from_credentials ei ole konfiguroitu.
+        credential_flow_not_configured: Resurssin omistajan salasanatietojen luku epäonnistui, koska asetusta Doorkeeper.configure.resource_owner_from_credentials ei ole konfiguroitu.
         invalid_client: Asiakasohjelman valtuutus epäonnistui, koska asiakas on tuntematon, asiakkaan valtuutus ei ollut mukana tai valtuutustapaa ei tueta.
         invalid_grant: Valtuutuslupa on virheellinen, umpeutunut, peruttu, valtuutuspyynnössä käytettyä uudelleenohjaus-URI:tä vastaamaton tai myönnetty toiselle asiakkaalle.
         invalid_redirect_uri: Uudelleenohjaus-URI on virheellinen.
@@ -111,25 +111,25 @@ fi:
           notice: Sovellus päivitetty.
       authorized_applications:
         destroy:
-          notice: Sovellus peruttu.
+          notice: Sovellus poistettu.
     grouped_scopes:
       access:
         read: Vain luku
         read/write: Luku- ja kirjoitusoikeudet
-        write: Vain kirjoitus
+        write: Vain kirjoitusoikeus
       title:
         accounts: Tilit
         admin/accounts: Tilien hallinta
         admin/all: Kaikki hallinnolliset toiminnot
         admin/reports: Raporttien hallinta
-        all: Kaikki
+        all: Täysi pääsy Mastodon-tiliisi
         blocks: Torjutut
         bookmarks: Kirjanmerkit
         conversations: Keskustelut
         crypto: Päästä päähän -salaus
         favourites: Suosikit
         filters: Suodattimet
-        follow: Liitokset
+        follow: Seuraamiset, mykistykset ja estot
         follows: Seuraa
         lists: Listat
         media: Medialiitteet
diff --git a/config/locales/doorkeeper.fo.yml b/config/locales/doorkeeper.fo.yml
index 71fa73a0f..e4c1a8b8a 100644
--- a/config/locales/doorkeeper.fo.yml
+++ b/config/locales/doorkeeper.fo.yml
@@ -122,14 +122,14 @@ fo:
         admin/accounts: Umsiting av kontum
         admin/all: Allar umsitingarligar funktiónir
         admin/reports: Umsiting av meldingum
-        all: Alt
+        all: Full atgongd til tína Mastodon kontu
         blocks: Blokeringar
         bookmarks: Bókamerki
         conversations: Samrøður
         crypto: Enda-til-enda bronglan
         favourites: Yndispostar
         filters: Filtur
-        follow: Sambond
+        follow: Fylgingar, doyvingar og blokeringar
         follows: Fylgir
         lists: Listar
         media: Viðfestir miðlar
diff --git a/config/locales/doorkeeper.fr-QC.yml b/config/locales/doorkeeper.fr-QC.yml
index 45ad8f186..6cccf4847 100644
--- a/config/locales/doorkeeper.fr-QC.yml
+++ b/config/locales/doorkeeper.fr-QC.yml
@@ -122,14 +122,14 @@ fr-QC:
         admin/accounts: Gestion des comptes
         admin/all: Toutes les fonctionnalités d'administration
         admin/reports: Gestion des rapports
-        all: Tout
+        all: Accès complet à votre compte Mastodon
         blocks: Bloqués
         bookmarks: Marque-pages
         conversations: Conversations
         crypto: Chiffrement de bout-en-bout
         favourites: Favoris
         filters: Filtres
-        follow: Relations
+        follow: Abonnements, comptes masqués et comptes bloqués
         follows: Abonnements
         lists: Listes
         media: Fichiers médias
@@ -149,18 +149,18 @@ fr-QC:
     scopes:
       admin:read: lire toutes les données du serveur
       admin:read:accounts: lire les informations sensibles de tous les comptes
-      admin:read:canonical_email_blocks: lire les informations sensibles de tous les bloqueurs d'e-mails canoniques
+      admin:read:canonical_email_blocks: lire les informations sensibles de tous les bloqueurs de courriels canoniques
       admin:read:domain_allows: lire les informations sensibles de tous les domaines autorisés
-      admin:read:domain_blocks: lire les informations sensibles de tous les blocqueurs de domaines
-      admin:read:email_domain_blocks: lire les informations sensibles de tous les blocqueurs d'e-mails de domaines
+      admin:read:domain_blocks: lire les informations sensibles de tous les bloqueurs de domaines
+      admin:read:email_domain_blocks: lire les informations sensibles de tous les bloqueurs de domaines de courriel
       admin:read:ip_blocks: lire les informations sensibles de tous les blocqueurs d'IP
       admin:read:reports: lire les informations sensibles de tous les signalements et des comptes signalés
       admin:write: modifier toutes les données sur le serveur
       admin:write:accounts: effectuer des actions de modération sur les comptes
-      admin:write:canonical_email_blocks: effectuer des actions de modération sur les bloqueurs d'e-mails canoniques
+      admin:write:canonical_email_blocks: effectuer des actions de modération sur les bloqueurs de courriels canoniques
       admin:write:domain_allows: effectuer des actions de modération sur les autorisations de domaines
       admin:write:domain_blocks: effectuer des actions de modération sur des bloqueurs de domaines
-      admin:write:email_domain_blocks: effectuer des actions de modération sur des bloqueurs d'e-mails de domaines
+      admin:write:email_domain_blocks: effectuer des actions de modération sur des bloqueurs de domaines de courriel
       admin:write:ip_blocks: effectuer des actions de modération sur des bloqueurs d'IP
       admin:write:reports: effectuer des actions de modération sur les signalements
       crypto: utiliser le chiffrement de bout-en-bout
diff --git a/config/locales/doorkeeper.fr.yml b/config/locales/doorkeeper.fr.yml
index 079842de3..d777c925a 100644
--- a/config/locales/doorkeeper.fr.yml
+++ b/config/locales/doorkeeper.fr.yml
@@ -86,7 +86,7 @@ fr:
         invalid_grant: L’autorisation accordée est invalide, expirée, annulée, ne concorde pas avec l’URL de redirection utilisée dans la requête d’autorisation, ou a été délivrée à un autre client.
         invalid_redirect_uri: L’URL de redirection n’est pas valide.
         invalid_request:
-          missing_param: 'Paramètre requis manquant: %{value}.'
+          missing_param: 'Paramètre requis manquant : %{value}.'
           request_not_authorized: La requête doit être autorisée. Le paramètre requis pour l'autorisation de la requête est manquant ou non valide.
           unknown: La requête omet un paramètre requis, inclut une valeur de paramètre non prise en charge ou est autrement mal formée.
         invalid_resource_owner: Les identifiants fournis par le propriétaire de la ressource ne sont pas valides ou le propriétaire de la ressource ne peut être trouvé
@@ -122,14 +122,14 @@ fr:
         admin/accounts: Gestion des comptes
         admin/all: Toutes les fonctionnalités d'administration
         admin/reports: Gestion des rapports
-        all: Tout
+        all: Accès complet à votre compte Mastodon
         blocks: Bloqués
         bookmarks: Marque-pages
         conversations: Conversations
         crypto: Chiffrement de bout-en-bout
         favourites: Favoris
         filters: Filtres
-        follow: Relations
+        follow: Abonnements, comptes masqués et comptes bloqués
         follows: Abonnements
         lists: Listes
         media: Fichiers médias
@@ -149,18 +149,18 @@ fr:
     scopes:
       admin:read: lire toutes les données du serveur
       admin:read:accounts: lire les informations sensibles de tous les comptes
-      admin:read:canonical_email_blocks: lire les informations sensibles de tous les bloqueurs d'e-mails canoniques
+      admin:read:canonical_email_blocks: lire les informations sensibles de tous les bloqueurs de courriels canoniques
       admin:read:domain_allows: lire les informations sensibles de tous les domaines autorisés
-      admin:read:domain_blocks: lire les informations sensibles de tous les blocqueurs de domaines
-      admin:read:email_domain_blocks: lire les informations sensibles de tous les blocqueurs d'e-mails de domaines
+      admin:read:domain_blocks: lire les informations sensibles de tous les bloqueurs de domaines
+      admin:read:email_domain_blocks: lire les informations sensibles de tous les bloqueurs de domaines de courriel
       admin:read:ip_blocks: lire les informations sensibles de tous les blocqueurs d'IP
       admin:read:reports: lire les informations sensibles de tous les signalements et des comptes signalés
       admin:write: modifier toutes les données sur le serveur
       admin:write:accounts: effectuer des actions de modération sur les comptes
-      admin:write:canonical_email_blocks: effectuer des actions de modération sur les bloqueurs d'e-mails canoniques
+      admin:write:canonical_email_blocks: effectuer des actions de modération sur les bloqueurs de courriels canoniques
       admin:write:domain_allows: effectuer des actions de modération sur les autorisations de domaines
       admin:write:domain_blocks: effectuer des actions de modération sur des bloqueurs de domaines
-      admin:write:email_domain_blocks: effectuer des actions de modération sur des bloqueurs d'e-mails de domaines
+      admin:write:email_domain_blocks: effectuer des actions de modération sur des bloqueurs de domaines de courriel
       admin:write:ip_blocks: effectuer des actions de modération sur des bloqueurs d'IP
       admin:write:reports: effectuer des actions de modération sur les signalements
       crypto: utiliser le chiffrement de bout-en-bout
diff --git a/config/locales/doorkeeper.fy.yml b/config/locales/doorkeeper.fy.yml
index 27637f39b..3617b65c1 100644
--- a/config/locales/doorkeeper.fy.yml
+++ b/config/locales/doorkeeper.fy.yml
@@ -122,14 +122,14 @@ fy:
         admin/accounts: Accountbehear
         admin/all: Alle behearfunksjes
         admin/reports: Rapportaazjebehear
-        all: Alles
+        all: Folsleine tagong ta jo Mastodon-account
         blocks: Blokkearje
         bookmarks: Blêdwizers
         conversations: Petearen
         crypto: End-to-end-fersifering
         favourites: Favoriten
         filters: Filters
-        follow: Relaasjes
+        follow: Folgers, negearre en blokkearre brûkers
         follows: Folgjend
         lists: Listen
         media: Mediabylagen
diff --git a/config/locales/doorkeeper.ga.yml b/config/locales/doorkeeper.ga.yml
index 6245e14aa..9bd0d2912 100644
--- a/config/locales/doorkeeper.ga.yml
+++ b/config/locales/doorkeeper.ga.yml
@@ -17,6 +17,7 @@ ga:
       confirmations:
         destroy: An bhfuil tú cinnte?
       index:
+        application: Ainm feidhmchláir
         delete: Scrios
         name: Ainm
         show: Taispeáin
@@ -26,14 +27,17 @@ ga:
         title: 'Ainm feidhmchláir: %{name}'
     authorizations:
       buttons:
+        authorize: Ceadaigh
         deny: Diúltaigh
     authorized_applications:
       confirmations:
         revoke: An bhfuil tú cinnte?
+      index:
+        scopes: Ceadanna
+        superapp: Inmheánach
     grouped_scopes:
       title:
         accounts: Cuntais
-        all: Gach Rud
         bookmarks: Leabharmharcanna
         conversations: Comhráite
         favourites: Toghanna
@@ -41,4 +45,8 @@ ga:
         follows: Cuntais leanta
         lists: Liostaí
         notifications: Fógraí
+        search: Cuardaigh
         statuses: Postálacha
+    scopes:
+      write:filters: cruthaigh scagairí
+      write:lists: cruthaigh liostaí
diff --git a/config/locales/doorkeeper.gd.yml b/config/locales/doorkeeper.gd.yml
index 6d4cbecfe..f025a0c55 100644
--- a/config/locales/doorkeeper.gd.yml
+++ b/config/locales/doorkeeper.gd.yml
@@ -122,14 +122,14 @@ gd:
         admin/accounts: Rianachd nan cunntas
         admin/all: Gach gleus na rianachd
         admin/reports: Rianachd nan gearan
-        all: A h-uile rud
+        all: Làn-inntrigeadh dhan chunntas Mastodon agad
         blocks: Bacaidhean
         bookmarks: Comharran-lìn
         conversations: Còmhraidhean
         crypto: Crioptachadh o cheann gu ceann
         favourites: Annsachdan
         filters: Criathragan
-        follow: Dàimhean
+        follow: Leantainn, mùchaidhean is bacaidhean
         follows: Leantainn
         lists: Liostaichean
         media: Ceanglachain mheadhanan
@@ -149,9 +149,19 @@ gd:
     scopes:
       admin:read: dàta sam bith a leughadh air an fhrithealaiche
       admin:read:accounts: fiosrachadh dìomhair air a h-uile cunntas a leughadh
+      admin:read:canonical_email_blocks: fiosrachadh dìomhair air a h-uile bacadh puist-d gnàthach a leughadh
+      admin:read:domain_allows: fiosrachadh dìomhair air a h-uile cead àrainne a leughadh
+      admin:read:domain_blocks: fiosrachadh dìomhair air a h-uile bacadh àrainne a leughadh
+      admin:read:email_domain_blocks: fiosrachadh dìomhair air a h-uile bacadh àrainn puist-d a leughadh
+      admin:read:ip_blocks: fiosrachadh dìomhair air a h-uile bacadh IP a leughadh
       admin:read:reports: fiosrachadh dìomhair air a h-uile gearan is cunntasan a chaidh a ghearan mun dèidhinn a leughadh
       admin:write: dàta sam bith atharrachadh air an fhrithealaiche
       admin:write:accounts: gnìomhan na maorsainneachd a ghabhail air cunntasan
+      admin:write:canonical_email_blocks: gnìomhan na maorsainneachd a ghabhail air bacaidhean puist-d gnàthach
+      admin:write:domain_allows: gnìomhan na maorsainneachd a ghabhail air ceadan àrainn
+      admin:write:domain_blocks: gnìomhan na maorsainneachd a ghabhail air bacaidhean àrainne
+      admin:write:email_domain_blocks: gnìomhan na maorsainneachd a ghabhail air bacaidhean àrainn puist-d
+      admin:write:ip_blocks: gnìomhan na maorsainneachd a ghabhail air bacaidhean IP
       admin:write:reports: gnìomhan na maorsainneachd a ghabhail air gearanan
       crypto: crioptachadh o cheann gu ceann a chleachdadh
       follow: dàimhean chunntasan atharrachadh
diff --git a/config/locales/doorkeeper.gl.yml b/config/locales/doorkeeper.gl.yml
index b71df1c77..c7f21d1ba 100644
--- a/config/locales/doorkeeper.gl.yml
+++ b/config/locales/doorkeeper.gl.yml
@@ -74,7 +74,7 @@ gl:
         authorized_at: Autorizada o %{date}
         description_html: Estas aplicacións poden acceder á túa conta usando a API. Se ves aplicacións que non recoñeces, ou hai comportamentos non consentidos dalgunha delas, podes revogar o acceso.
         last_used_at: Último acceso o %{date}
-        never_used: Nunca usada
+        never_used: Nunca empregada
         scopes: Permisos
         superapp: Interno
         title: As túas aplicacións autorizadas
@@ -120,16 +120,16 @@ gl:
       title:
         accounts: Contas
         admin/accounts: Administración das contas
-        admin/all: Tódalas funcións administrativas
+        admin/all: Todas as funcións administrativas
         admin/reports: Administración das denuncias
-        all: Todo
+        all: Acceso completo á túa conta de Mastodon
         blocks: Bloqueos
         bookmarks: Marcadores
         conversations: Conversas
         crypto: Cifrado extremo-a-extremo
         favourites: Favoritas
         filters: Filtros
-        follow: Relacións
+        follow: Seguidas, Acaladas e Bloqueadas
         follows: Seguimentos
         lists: Listas
         media: Anexos multimedia
@@ -149,21 +149,21 @@ gl:
     scopes:
       admin:read: ler todos os datos no servidor
       admin:read:accounts: ler información sensible de todas as contas
-      admin:read:canonical_email_blocks: ler a información sensible de tódolos bloqueos de email canónicos
-      admin:read:domain_allows: ler a información sensible de tódolos dominios permitidos
+      admin:read:canonical_email_blocks: ler a información sensíbel de tódolos bloqueos de correos electrónicos canónicos
+      admin:read:domain_allows: ler a información sensible de todos os dominios permitidos
       admin:read:domain_blocks: ler a información sensible de tódolos bloqueos de dominio
-      admin:read:email_domain_blocks: ler a información sensible de tódolos dominios de email
+      admin:read:email_domain_blocks: ler a información sensible de tódolos dominios de correo electrónico
       admin:read:ip_blocks: ler a información sensible de tódolos bloqueos de IP
       admin:read:reports: ler información sensible de todos os informes e contas denunciadas
       admin:write: modificar todos os datos no servidor
       admin:write:accounts: executar accións de moderación nas contas
-      admin:write:canonical_email_blocks: realizar accións de moderación en bloqueos de email canónicos
+      admin:write:canonical_email_blocks: realizar accións de moderación en bloqueos de correo electrónico canónicos
       admin:write:domain_allows: realizar accións de moderación en dominios permitidos
       admin:write:domain_blocks: realizar accións de moderación en bloqueos de dominio
-      admin:write:email_domain_blocks: realizar accións de moderación en bloqueos de dominio de email
+      admin:write:email_domain_blocks: realizar accións de moderación en bloqueos de dominio de correo electrónico
       admin:write:ip_blocks: realizar accións de moderación en bloqueos de IPs
       admin:write:reports: executar accións de moderación nas denuncias
-      crypto: usar cifrado extremo-a-extremo
+      crypto: usar cifrado de extremo-a-extremo
       follow: modificar as relacións da conta
       push: recibir notificacións push
       read: ler todos os datos da tua conta
diff --git a/config/locales/doorkeeper.he.yml b/config/locales/doorkeeper.he.yml
index 3e25e637f..6d6b5866e 100644
--- a/config/locales/doorkeeper.he.yml
+++ b/config/locales/doorkeeper.he.yml
@@ -122,14 +122,14 @@ he:
         admin/accounts: חשבונות ניהול
         admin/all: כל פעולות האדמין
         admin/reports: ניהול דיווחים
-        all: הכל
+        all: גישה מלאה לחשבון המסטודון שלך
         blocks: חסימות
         bookmarks: סימניות
         conversations: שיחות
         crypto: הצפנה מקצה לקצה
         favourites: חיבובים
         filters: מסננים
-        follow: יחסים
+        follow: נעקבים, מושתקים וחסומים
         follows: נעקבים
         lists: רשימות
         media: קבצי מדיה מצורפים
diff --git a/config/locales/doorkeeper.hu.yml b/config/locales/doorkeeper.hu.yml
index 1157d0ee3..1d75beef5 100644
--- a/config/locales/doorkeeper.hu.yml
+++ b/config/locales/doorkeeper.hu.yml
@@ -5,7 +5,7 @@ hu:
       doorkeeper/application:
         name: Alkalmazás neve
         redirect_uri: Átirányító URI
-        scopes: Hatáskör
+        scopes: Hatókör
         website: Az alkalmazás weboldala
     errors:
       models:
@@ -19,65 +19,65 @@ hu:
   doorkeeper:
     applications:
       buttons:
-        authorize: Engedélyezés
+        authorize: Hitelesítés
         cancel: Mégsem
         destroy: Törlés
         edit: Szerkesztés
         submit: Elküldés
       confirmations:
-        destroy: Biztos vagy benne?
+        destroy: Biztos így legyen?
       edit:
         title: Alkalmazás szerkesztése
       form:
-        error: Hoppá! Ellenőrizd az űrlapot az esetleges hibák miatt
+        error: Hoppá! Ellenőrizük az űrlapot az esetleges hibák miatt
       help:
-        native_redirect_uri: Használj %{native_redirect_uri} a helyi tesztekhez
+        native_redirect_uri: "%{native_redirect_uri} használata a helyi tesztekhez"
         redirect_uri: Egy sor URI-nként
-        scopes: A hatásköröket szóközzel válaszd el. Hagyd üresen az alapértelmezett hatáskörökhöz.
+        scopes: A hatóköröket szóközzel válasszuk el. Hagyjuk üresen az alapértelmezett hatókörökhöz.
       index:
         application: Alkalmazás
         callback_url: Visszahívási URL
         delete: Eltávolítás
-        empty: Nincsenek alkalmazásaid.
+        empty: Nincsenek saját alkalmazások.
         name: Név
         new: Új alkalmazás
-        scopes: Hatáskör
-        show: Mutat
-        title: Alkalmazásaid
+        scopes: Hatókörök
+        show: Megjelenítés
+        title: Saját alkalmazások
       new:
         title: Új alkalmazás
       show:
         actions: Műveletek
-        application_id: Alkalmazás azonosító
-        callback_urls: Callback URL-ek
+        application_id: Ügyfél kulcs
+        callback_urls: Visszahívási URL-ek
         scopes: Hatáskörök
-        secret: Titok
+        secret: Ügyfél titkos kulcs
         title: 'Alkalmazás: %{name}'
     authorizations:
       buttons:
-        authorize: Engedélyezés
+        authorize: Hitelesítés
         deny: Tiltás
       error:
         title: Hiba történt
       new:
-        prompt_html: "%{client_name} szeretné elérni a fiókodat. Ez egy harmadik féltől származó alkalmazás. <strong>Ha nem bízol meg benne, ne addj felhatalmazást neki.</strong>"
-        review_permissions: Engedélyek áttekintése
-        title: Engedély szükséges
+        prompt_html: "%{client_name} szeretné elérni a fiókomat. Ez egy harmadik féltől származó alkalmazás. <strong>Ha nem bízunk meg benne, ne adjunk hitlesítést.</strong>"
+        review_permissions: Jogosultságok áttekintése
+        title: Hitelesítés szükséges
       show:
         title: Másold le ezt az engedélyező kódot és írd be az alkalmazásba.
     authorized_applications:
       buttons:
         revoke: Visszavonás
       confirmations:
-        revoke: Biztos vagy benne?
+        revoke: Biztos így legyen?
       index:
-        authorized_at: 'Felhatalmazva ekkor: %{date}'
+        authorized_at: 'Hitelesítés: %{date}'
         description_html: Ezek olyan alkalmazások, melyek API-n keresztül érhetik el a fiókodat. Ha vannak itt olyanok, melyeket nem ismersz fel, vagy valamelyik alkalmazás rosszul működik, visszavonhatod az engedélyét.
-        last_used_at: 'Utoljára ekkor használva: %{date}'
+        last_used_at: 'Utolsó használat: %{date}'
         never_used: Soha sem volt használva
-        scopes: Engedélyek
+        scopes: Jogosultságok
         superapp: Belső
-        title: Engedélyezett alkalmazásaid
+        title: Hitelesített saját alkalmazások
     errors:
       messages:
         access_denied: Az erőforrás tulajdonosa vagy az engedélyező kiszolgáló elutasította a kérést.
@@ -92,10 +92,10 @@ hu:
         invalid_resource_owner: A biztosított erőforrás tulajdonosának hitelesítő adatai nem valósak, vagy az erőforrás tulajdonosa nem található.
         invalid_scope: A kért nézet érvénytelen, ismeretlen, vagy hibás.
         invalid_token:
-          expired: Hozzáférési kulcs lejárt
-          revoked: Hozzáférési kulcsot visszavonták
-          unknown: Hozzáférési kulcs érvénytelen
-        resource_owner_authenticator_not_configured: Erőforrás tulajdonos keresés megszakadt, ugyanis a Doorkeeper.configure.resource_owner_authenticator beállítatlan.
+          expired: A hozzáférési kulcs lejárt.
+          revoked: A hozzáférési kulcsot visszavonták.
+          unknown: A hozzáférési kulcs érvénytelen.
+        resource_owner_authenticator_not_configured: Az erőforrás tulajdonos keresés megszakadt, ugyanis a Doorkeeper.configure.resource_owner_authenticator beállítatlan.
         server_error: Az engedélyező kiszolgáló váratlan körülménybe ütközött, ami megakadályozta, hogy teljesítse a kérést.
         temporarily_unavailable: Az engedélyezési kiszolgáló jelenleg nem tudja kezelni a kérelmet a kiszolgáló ideiglenes túlterhelése vagy karbantartása miatt.
         unauthorized_client: A kliens nincs feljogosítva erre a kérésre.
@@ -104,14 +104,14 @@ hu:
     flash:
       applications:
         create:
-          notice: Alkalmazás létrehozva.
+          notice: Az alkalmazás létrejött.
         destroy:
-          notice: Alkalmazás törölve.
+          notice: Az alkalmazás törlésre került.
         update:
           notice: Alkalmazás frissítve.
       authorized_applications:
         destroy:
-          notice: Alkalmazás visszavonva.
+          notice: Az alkalmazás visszavonásra került.
     grouped_scopes:
       access:
         read: Csak olvasási elérés
@@ -122,14 +122,14 @@ hu:
         admin/accounts: Fiókok adminisztrációja
         admin/all: Minden adminisztratív funkció
         admin/reports: Bejelentések adminisztrációja
-        all: Minden
+        all: Teljes hozzáférés a Mastodon saját fiókhoz
         blocks: Letiltások
         bookmarks: Könyvjelzők
         conversations: Beszélgetések
         crypto: Végpontok közti titkosítás
         favourites: Kedvencek
         filters: Szűrők
-        follow: Kapcsolatok
+        follow: Követések, Némítások és Letiltások
         follows: Követések
         lists: Listák
         media: Médiamellékletek
@@ -164,12 +164,12 @@ hu:
       admin:write:ip_blocks: moderáció végrehajtása IP-blokkokon
       admin:write:reports: moderációs műveletek végzése bejelentéseken
       crypto: végpontok közti titkosítás használata
-      follow: fiókok követése, letiltása, tiltás feloldása és követés abbahagyása
+      follow: fiókok kapcsolatok módosítása
       push: push értesítések fogadása
-      read: fiókod adatainak olvasása
+      read: saját fiók adatainak olvasása
       read:accounts: fiók adatainak megtekintése
       read:blocks: letiltások megtekintése
-      read:bookmarks: könyvjelzőid megtekintése
+      read:bookmarks: könyvjelzőik megtekintése
       read:favourites: kedvencek megtekintése
       read:filters: szűrök megtekintése
       read:follows: követések megtekintése
@@ -177,10 +177,10 @@ hu:
       read:mutes: némítások megtekintése
       read:notifications: értesítések megtekintése
       read:reports: bejelentések megtekintése
-      read:search: nevedben keresés
+      read:search: keresés saját nevemben
       read:statuses: bejegyzések megtekintése
       write: fiókod adatainak megváltoztatása
-      write:accounts: profilod megváltoztatása
+      write:accounts: saját profil megváltoztatása
       write:blocks: fiókok és domainek letiltása
       write:bookmarks: bejegyzések könyvjelzőzése
       write:conversations: beszélgetések némítása és törlése
diff --git a/config/locales/doorkeeper.hy.yml b/config/locales/doorkeeper.hy.yml
index 94ef091f3..6548d1e5e 100644
--- a/config/locales/doorkeeper.hy.yml
+++ b/config/locales/doorkeeper.hy.yml
@@ -114,17 +114,16 @@ hy:
         write: Միայն գրելու հնարաւորութիւն
       title:
         accounts: Հաշիւներ
-        all: Բոլորը
         blocks: Արգելափակումներ
         bookmarks: Էջանիշեր
         conversations: Զրոյցներ
         favourites: Հաւանածներ
         filters: Զտիչներ
-        follow: Կապեր
         lists: Ցանկեր
         mutes: Լռեցուածներ
         notifications: Ծանուցումներ
         push: Հրելու ծանուցումներ
+        search: Որոնել
         statuses: Գրառումներ
     layouts:
       admin:
diff --git a/config/locales/doorkeeper.id.yml b/config/locales/doorkeeper.id.yml
index 019f33947..55bda04fe 100644
--- a/config/locales/doorkeeper.id.yml
+++ b/config/locales/doorkeeper.id.yml
@@ -122,14 +122,12 @@ id:
         admin/accounts: Administrasi akun
         admin/all: Semua fungsi administratif
         admin/reports: Administrasi laporan
-        all: Segalanya
         blocks: Blokir
         bookmarks: Markah
         conversations: Percakapan
         crypto: Enkripsi end-to-end
         favourites: Favorit
         filters: Saringan
-        follow: Hubungan
         follows: Mengikuti
         lists: Daftar
         media: Lampiran media
diff --git a/config/locales/doorkeeper.io.yml b/config/locales/doorkeeper.io.yml
index 4d41f9e93..df56335f8 100644
--- a/config/locales/doorkeeper.io.yml
+++ b/config/locales/doorkeeper.io.yml
@@ -122,14 +122,12 @@ io:
         admin/accounts: Administrar di konti
         admin/all: Omna administrofuncioni
         admin/reports: Administro di raporti
-        all: Omno
         blocks: Restriktita
         bookmarks: Libromarki
         conversations: Konversi
         crypto: Intersequanta chifro
         favourites: Favorati
         filters: Filtrili
-        follow: Relataji
         follows: Sequati
         lists: Listi
         media: Mediatachaji
diff --git a/config/locales/doorkeeper.is.yml b/config/locales/doorkeeper.is.yml
index 243b45c80..c1e2d4cfc 100644
--- a/config/locales/doorkeeper.is.yml
+++ b/config/locales/doorkeeper.is.yml
@@ -122,14 +122,14 @@ is:
         admin/accounts: Stjórnun aðganga
         admin/all: Allar stjórnunaraðgerðir
         admin/reports: Stjórnun kæra
-        all: Allt
+        all: Fullur aðgangur að Mastodon-aðgangnum þínum
         blocks: Útilokanir
         bookmarks: Bókamerki
         conversations: Samtöl
         crypto: Enda-í-enda dulritun
         favourites: Eftirlæti
         filters: Síur
-        follow: Vensl
+        follow: Fylgist með, þagganir og útilokanir
         follows: Fylgist með
         lists: Listar
         media: Myndefnisviðhengi
diff --git a/config/locales/doorkeeper.it.yml b/config/locales/doorkeeper.it.yml
index 602c50fa9..31e09a6af 100644
--- a/config/locales/doorkeeper.it.yml
+++ b/config/locales/doorkeeper.it.yml
@@ -122,14 +122,14 @@ it:
         admin/accounts: Amministrazione dei profili
         admin/all: Tutte le funzioni amministrative
         admin/reports: Amministrazione dei rapporti
-        all: Tutto
+        all: Accesso completo al tuo account Mastodon
         blocks: Blocchi
         bookmarks: Segnalibri
         conversations: Conversazioni
         crypto: Crittografia end-to-end
         favourites: Preferiti
         filters: Filtri
-        follow: Relazioni
+        follow: Seguiti, silenziati e bloccati
         follows: Seguiti
         lists: Elenchi
         media: Allegati multimediali
diff --git a/config/locales/doorkeeper.ja.yml b/config/locales/doorkeeper.ja.yml
index 30c48f0dc..0e52e1f09 100644
--- a/config/locales/doorkeeper.ja.yml
+++ b/config/locales/doorkeeper.ja.yml
@@ -122,14 +122,14 @@ ja:
         admin/accounts: アカウント管理
         admin/all: すべての管理機能
         admin/reports: 通報の管理
-        all: すべて
+        all: Mastodonアカウントへのフルアクセス
         blocks: ブロック
         bookmarks: ブックマーク
         conversations: 会話
         crypto: エンドツーエンド暗号化
         favourites: お気に入り
         filters: フィルター
-        follow: フォロー・フォロワー
+        follow: フォロー、ミュート、ブロック
         follows: フォロー
         lists: リスト
         media: メディアの添付
@@ -149,9 +149,19 @@ ja:
     scopes:
       admin:read: サーバーのすべてのデータの読み取り
       admin:read:accounts: すべてのアカウントの機密情報の読み取り
+      admin:read:canonical_email_blocks: すべてのブロックしたメールアドレスの読み取り
+      admin:read:domain_allows: すべての許可したドメインの読み取り
+      admin:read:domain_blocks: すべてのブロックしたドメインの読み取り
+      admin:read:email_domain_blocks: すべてのブロックしたメールドメインの読み取り
+      admin:read:ip_blocks: すべてのブロックしたIPアドレスの読み取り
       admin:read:reports: すべての通報と通報されたアカウントの機密情報の読み取り
       admin:write: サーバーのすべてのデータの変更
       admin:write:accounts: アカウントに対するアクションの実行
+      admin:write:canonical_email_blocks: メールアドレスのブロックに関するアクションの実行
+      admin:write:domain_allows: ドメイン許可に関するアクションの実行
+      admin:write:domain_blocks: ドメインのブロックに関するアクションの実行
+      admin:write:email_domain_blocks: メールドメインのブロックに関するアクションの実行
+      admin:write:ip_blocks: IPアドレスのブロックに関するアクションの実行
       admin:write:reports: 通報に対するアクションの実行
       crypto: エンドツーエンド暗号化の使用
       follow: アカウントのつながりを変更
diff --git a/config/locales/doorkeeper.ko.yml b/config/locales/doorkeeper.ko.yml
index 214e2c9d8..ef020bd85 100644
--- a/config/locales/doorkeeper.ko.yml
+++ b/config/locales/doorkeeper.ko.yml
@@ -4,7 +4,7 @@ ko:
     attributes:
       doorkeeper/application:
         name: 애플리케이션 이름
-        redirect_uri: 리다이렉트 URI
+        redirect_uri: 리디렉트 URI
         scopes: 범위
         website: 애플리케이션 웹사이트
     errors:
@@ -15,7 +15,7 @@ ko:
               fragment_present: fragment를 포함할 수 없습니다
               invalid_uri: 올바른 URI 이어야 합니다.
               relative_uri: 절대경로 URI 이어야 합니다
-              secured_uri: " HTTPS/SSL URI 이어야 합니다."
+              secured_uri: HTTPS/SSL URI 이어야 합니다.
   doorkeeper:
     applications:
       buttons:
@@ -31,14 +31,14 @@ ko:
       form:
         error: 이런! 오류를 확인하세요
       help:
-        native_redirect_uri: "%{native_redirect_uri} 을/를 이용해 로컬 테스트를 할 수 있습니다"
+        native_redirect_uri: "%{native_redirect_uri}에서 로컬 테스트를 할 수 있습니다."
         redirect_uri: 한 줄에 하나의 URI를 작성하세요
         scopes: 스페이스로 범위를 구분하세요. 빈 칸으로 놔두면 기본 범위를 사용합니다.
       index:
         application: 애플리케이션
         callback_url: 콜백 URL
         delete: 삭제
-        empty: 애플리케이션이 없습니다.
+        empty: 앱이 없습니다.
         name: 이름
         new: 새 애플리케이션
         scopes: 범위
@@ -52,7 +52,7 @@ ko:
         callback_urls: 콜백 URL
         scopes: 범위
         secret: 클라이언트 비밀키
-        title: '애플리케이션: %{name}'
+        title: '앱: %{name}'
     authorizations:
       buttons:
         authorize: 승인
@@ -60,19 +60,19 @@ ko:
       error:
         title: 오류가 발생하였습니다
       new:
-        prompt_html: "%{client_name}이 당신의 계정에 대한 접근 권한을 요청합니다. 이것은 제3자의 응용프로그램입니다. <strong>이것을 신뢰하지 않는다면, 권한을 승인하지 말아야 합니다.</strong>"
+        prompt_html: "%{client_name} 제3자 앱이 귀하의 계정에 접근하기 위한 권한을 요청했습니다. <strong>이 앱을 신뢰할 수 없다면 요청을 승인하지 마십시오.</strong>"
         review_permissions: 권한 검토
         title: 승인 필요
       show:
-        title: 이 승인 코드를 복사하여 애플리케이션에 붙여넣으세요
+        title: 이 승인 코드를 복사해 앱에 붙여 넣어야 합니다.
     authorized_applications:
       buttons:
-        revoke: 삭제
+        revoke: 취소
       confirmations:
         revoke: 확실합니까?
       index:
         authorized_at: "%{date}에 승인됨"
-        description_html: 당신의 계정에 API를 통해 접근 가능한 응용프로그램의 목록입니다. 알 수 없는 응용프로그램, 혹은 잘못된 행동을 하고 있는 응용프로그램이 있다면, 권한을 취소할 수 있습니다.
+        description_html: 이 계정에 API를 통해 접근 가능한 애플리케이션의 목록입니다. 알 수 없는 애플리케이션이나 잘못된 행위를 하는 애플리케이션이 있다면 권한을 취소할 수 있습니다.
         last_used_at: "%{date}에 마지막으로 사용됨"
         never_used: 사용되지 않음
         scopes: 권한
@@ -81,8 +81,8 @@ ko:
     errors:
       messages:
         access_denied: 리소스 소유자 또는 인증 서버가 요청을 거부했습니다.
-        credential_flow_not_configured: Doorkeeper.configure.resource_owner_from_credentials의 설정이 되어있지 않아 리소스 소유자 패스워드 자격증명이 실패하였습니다.
-        invalid_client: 알 수 없는 클라이언트이기 때문에 클라이언트 인증이 실패하였습니다, 클라이언트 자격증명이 포함되지 않았거나, 지원 되지 않는 메소드입니다.
+        credential_flow_not_configured: Doorkeeper.configure.resource_owner_from_credentials의 설정이 되어있지 않아 리소스 소유자 암호 자격증명이 실패하였습니다.
+        invalid_client: 클라이언트를 확인할 수 없기 때문에 인증이 실패하였습니다. 클라이언트 자격 증명이 포함되지 않았거나 지원되지 않는 메소드입니다.
         invalid_grant: 제공된 권한 부여가 잘못되거나, 만료되었거나, 취소되었거나, 권한 부여 요청에 사용된 리디렉션 URI가 일치하지 않거나, 다른 클라이언트에 지정되었습니다.
         invalid_redirect_uri: 리디렉션 URI가 올바르지 않습니다
         invalid_request:
@@ -93,7 +93,7 @@ ko:
         invalid_scope: 요청한 범위가 올바르지 않거나, 알 수 없거나, 잘못 된 형식입니다.
         invalid_token:
           expired: 액세스 토큰이 만료되었습니다.
-          revoked: 액세스 토큰이 삭제되었습니다.
+          revoked: 액세스 토큰이 취소되었습니다.
           unknown: 액세스 토큰이 잘못되었습니다.
         resource_owner_authenticator_not_configured: Doorkeeper.configure.resource_owner_authenticator가 설정되지 않아 리소스 소유자 찾기가 실패하였습니다.
         server_error: 권한 부여 서버에 예기치 않은 상태가 발생하여, 요청을 수행할 수 없습니다.
@@ -104,14 +104,14 @@ ko:
     flash:
       applications:
         create:
-          notice: 애플리케이션이 생성 되었습니다.
+          notice: 애플리케이션을 만들었습니다.
         destroy:
-          notice: 애플리케이션이 삭제 되었습니다.
+          notice: 애플리케이션을 삭제하였습니다.
         update:
-          notice: 애플리케이션이 갱신 되었습니다.
+          notice: 애플리케이션을 갱신했습니다.
       authorized_applications:
         destroy:
-          notice: 애플리케이션이 삭제되었습니다.
+          notice: 애플리케이션을 취소하였습니다.
     grouped_scopes:
       access:
         read: 읽기 전용 권한
@@ -122,14 +122,14 @@ ko:
         admin/accounts: 계정 관리
         admin/all: 모든 관리자 기능
         admin/reports: 신고 관리
-        all: 전체
+        all: 마스토돈 계정에 대한 모든 권한
         blocks: 차단
         bookmarks: 북마크
         conversations: 대화
         crypto: 종단간 암호화
         favourites: 좋아요
         filters: 필터
-        follow: 관계
+        follow: 팔로우, 뮤트와 차단
         follows: 팔로우
         lists: 리스트
         media: 첨부된 미디어
@@ -142,7 +142,7 @@ ko:
     layouts:
       admin:
         nav:
-          applications: 애플리케이션
+          applications: 앱
           oauth2_provider: OAuth2 제공자
       application:
         title: OAuth 인증이 필요합니다
@@ -164,26 +164,26 @@ ko:
       admin:write:ip_blocks: IP 차단에 모더레이션 조치 취하기
       admin:write:reports: 신고에 모더레이션 조치 취하기
       crypto: 종단간 암호화 사용
-      follow: 계정의 관계를 수정
-      push: 푸시 알림을 받기
-      read: 계정의 모든 데이터를 읽기
-      read:accounts: 계정의 정보를 보기
-      read:blocks: 차단을 보기
+      follow: 계정 관계 수정
+      push: 푸시 알림 받기
+      read: 계정의 모든 데이터 읽기
+      read:accounts: 계정 정보 보기
+      read:blocks: 차단 보기
       read:bookmarks: 내 북마크 보기
-      read:favourites: 관심글을 보기
-      read:filters: 필터를 보기
-      read:follows: 팔로우를 보기
-      read:lists: 리스트를 보기
-      read:mutes: 뮤트를 보기
+      read:favourites: 좋아요 보기
+      read:filters: 필터 보기
+      read:follows: 팔로우 보기
+      read:lists: 리스트 보기
+      read:mutes: 뮤트 보기
       read:notifications: 알림 보기
       read:reports: 신고 보기
       read:search: 당신의 권한으로 검색
-      read:statuses: 게시물 모두 보기
+      read:statuses: 모든 게시물 보기
       write: 계정 정보 수정
       write:accounts: 프로필 수정
-      write:blocks: 계정이나 도메인 차단
+      write:blocks: 계정 및 도메인 차단
       write:bookmarks: 게시물을 북마크에 넣기
-      write:conversations: 뮤트와 대화 삭제
+      write:conversations: 뮤트 및 대화 삭제
       write:favourites: 관심글 지정
       write:filters: 필터 만들기
       write:follows: 사람을 팔로우
diff --git a/config/locales/doorkeeper.ku.yml b/config/locales/doorkeeper.ku.yml
index b04e86d88..3f8d7d068 100644
--- a/config/locales/doorkeeper.ku.yml
+++ b/config/locales/doorkeeper.ku.yml
@@ -122,14 +122,12 @@ ku:
         admin/accounts: Rêveberiya ajimêran
         admin/all: Hemû fonksiyonên reveberî
         admin/reports: Rêveberiya ragihandinan
-        all: Her tişt
         blocks: Astengkirin
         bookmarks: Şûnpel
         conversations: Axaftin
         crypto: Dawî bi dawî şifrekirî
         favourites: Bijarte
         filters: Parzûn
-        follow: Pêwendî
         follows: Dişopîne
         lists: Lîste
         media: Pêvekên medya
diff --git a/config/locales/doorkeeper.lv.yml b/config/locales/doorkeeper.lv.yml
index d6fca1ff2..08d7a0d64 100644
--- a/config/locales/doorkeeper.lv.yml
+++ b/config/locales/doorkeeper.lv.yml
@@ -122,14 +122,14 @@ lv:
         admin/accounts: Kontu administrēšana
         admin/all: Visas administrēšanas funkcijas
         admin/reports: Ziņojumu administrēšana
-        all: Visi
+        all: Pilna piekļuve tavam Mastodon kontam
         blocks: Bloķētie
         bookmarks: Grāmatzīmes
         conversations: Sarunas
         crypto: Pilnīga šifrēšana
         favourites: Izlases
         filters: Filtri
-        follow: Attiecības
+        follow: Seko, Izslēdz un Bloķē
         follows: Seko
         lists: Saraksti
         media: Multividesu pielikumi
diff --git a/config/locales/doorkeeper.ms.yml b/config/locales/doorkeeper.ms.yml
index 87c1078c1..b84bde805 100644
--- a/config/locales/doorkeeper.ms.yml
+++ b/config/locales/doorkeeper.ms.yml
@@ -33,9 +33,7 @@ ms:
         admin/accounts: Pengurusan akaun
         admin/all: Semua fungsi pengurusan
         admin/reports: Pengurusan laporan
-        all: Semua
         conversations: Perbualan
-        follow: Hubungan
         follows: Ikutan
         media: Lampiran media
         notifications: Pemberitahuan
diff --git a/config/locales/doorkeeper.my.yml b/config/locales/doorkeeper.my.yml
index 5e1fc6bee..241dcb691 100644
--- a/config/locales/doorkeeper.my.yml
+++ b/config/locales/doorkeeper.my.yml
@@ -1 +1,195 @@
+---
 my:
+  activerecord:
+    attributes:
+      doorkeeper/application:
+        name: အက်ပလီကေးရှင်းအမည်
+        redirect_uri: URI ကို ပြန်ညွှန်းရန်
+        scopes: နယ်ပယ်များ
+        website: အက်ပလီကေးရှင်းဝဘ်ဆိုဒ်
+    errors:
+      models:
+        doorkeeper/application:
+          attributes:
+            redirect_uri:
+              fragment_present: အပိုင်းတစ်ပိုင်း မပါဝင်နိုင်ပါ။
+              invalid_uri: သည် မှန်ကန်သော URI ဖြစ်ရမည်။
+              relative_uri: URI ဖြစ်ရမည်။
+              secured_uri: သည် HTTPS/SSL URI ဖြစ်ရမည်။
+  doorkeeper:
+    applications:
+      buttons:
+        authorize: လုပ်ပိုင်ခွင့်
+        cancel: ပယ်ဖျက်မည်
+        destroy: ဖျက်ဆီးမည်
+        edit: ပြင်မည်
+        submit: တင်သွင်းမည်
+      confirmations:
+        destroy: သေချာပါသလား?
+      edit:
+        title: အက်ပလီကေးရှင်းကို ပြင်ဆင်ရန်
+      form:
+        error: အိုး၊ သင့်ဖောင်မှာ အမှားအယွင်းများရှိနိုင်သောကြောင့် စစ်ဆေးပါ
+      help:
+        native_redirect_uri: ဒေသတွင်းစမ်းသပ်မှုများအတွက် %{native_redirect_uri} ကို အသုံးပြုပါ
+        redirect_uri: URI တစ်ခုစီအတွက် လိုင်းတစ်ကြောင်းသုံးပါ
+        scopes: နယ်ပယ်များကို နေရာလွတ်များဖြင့် ခွဲခြားပါ။ မူလသတ်မှတ်ထားသည့်နယ်ပယ်များ အသုံးပြုရန်အတွက် အလွတ် ချန်ထားပါ။
+      index:
+        application: အက်ပလီကေးရှင်း
+        callback_url: URL ပြန်ခေါ်ရန်
+        delete: ဖျက်မည်
+        empty: သင့်တွင် အက်ပလီကေးရှင်းများ မရှိပါ။
+        name: အမည်
+        new: အက်ပလီကေးရှင်းအသစ်
+        scopes: နယ်ပယ်များ
+        show: ပြရန်
+        title: သင့်အက်ပလီကေးရှင်းများ
+      new:
+        title: အက်ပလီကေးရှင်းအသစ်
+      show:
+        actions: လုပ်ဆောင်ချက်များ
+        application_id: Client ကီး
+        callback_urls: URLs ပြန်ခေါ်ရန်
+        scopes: နယ်ပယ်များ
+        secret: Client လျှို့ဝှက်ချက်
+        title: အက်ပလီကေးရှင်း - %{name}
+    authorizations:
+      buttons:
+        authorize: လုပ်ပိုင်ခွင့်
+        deny: ငြင်းရန်
+      error:
+        title: အမှားအယွင်းတစ်ခု ဖြစ်ပေါ်ခဲ့သည်
+      new:
+        prompt_html: "%{client_name} က သင့်အကောင့်သို့ ဝင်ရောက်ရန် ခွင့်ပြုချက်ရယူလိုပါသည်။ ၎င်းမှာ ပြင်ပကြားခံအက်ပလီကေးရှင်းတစ်ခုဖြစ်သည်။ <strong>သင် မယုံကြည်ပါက ၎င်းကိုခွင့်မပြုသင့်ပါ။</strong>"
+        review_permissions: ခွင့်ပြုချက်များကို ပြန်လည်သုံးသပ်ပါ
+        title: ခွင့်ပြုချက် လိုအပ်သည်
+      show:
+        title: ဤခွင့်ပြုချက်ကုဒ်ကို ကူးယူပြီး အက်ပလီကေးရှင်းသို့ ကူးထည့်ပါ။
+    authorized_applications:
+      buttons:
+        revoke: ပြန်ရုပ်သိမ်းရန်
+      confirmations:
+        revoke: သေချာပါသလား။
+      index:
+        authorized_at: "%{date} တွင် ခွင့်ပြုခဲ့သည်"
+        description_html: "၎င်းတို့မှာ API အသုံးပြု၍ သင့်အကောင့်ကို ဝင်ရောက်ကြည့်ရှုနိုင်သော အက်ပလီကေးရှင်းများဖြစ်သည်။ ဤနေရာတွင် သင်မသိသော အက်ပလီကေးရှင်းများ ရှိပါက သို့မဟုတ် အက်ပလီကေးရှင်းတစ်ခုသည် လွဲမှားစွာ လုပ်ဆောင်နေပါက ၎င်း၏ ဝင်ရောက်ခွင့်ကို သင် ပြန်လည်ရုပ်သိမ်းနိုင်သည်။"
+        last_used_at: "%{date} တွင် နောက်ဆုံးအသုံးပြုခဲ့သည်"
+        never_used: မသုံးဖူးပါ
+        scopes: ခွင့်ပြုချက်များ
+        superapp: အတွင်းပိုင်း
+        title: ခွင့်ပြုထားသော အက်ပလီကေးရှင်းများ
+    errors:
+      messages:
+        access_denied: မူလပိုင်ရှင် သို့မဟုတ် ခွင့်ပြုချက်ရှိသောဆာဗာမှ တောင်းဆိုချက်ကို ငြင်းပယ်ခဲ့သည်။
+        credential_flow_not_configured: Doorkeeper.configure.resource_owner_from_credentials ကို သတ်မှတ်မထားသည့်အတွက် မူလပိုင်ရှင် စကားဝှက် အထောက်အထားများထည့်သွင်းခြင်းမှာ မအောင်မြင်တော့ပါ။
+        invalid_client: Client အထောက်အထားစိစစ်မှု မအောင်မြင်ခြင်းမှာ အမည်မသိ Client ဖြစ်ခြင်း၊ Client စစ်မှန်ကြောင်းအထောက်အထားမပါဝင်ခြင်း သို့မဟုတ် ပံ့ပိုးမထားသည့် အထောက်အထားဖြစ်နေခြင်းကြောင့် ဖြစ်ပါသည်။
+        invalid_grant: ပံ့ပိုးပေးထားသည့် ခွင့်ပြုချက်ပေးသည် မမှန်ကန်ပါ၊ သက်တမ်းကုန်၊ ရုပ်သိမ်းလိုက်သည်၊ ခွင့်ပြုချက်တောင်းခံမှုတွင် အသုံးပြုထားသော ပြန်ညွှန်း URI နှင့် မကိုက်ညီပါ သို့မဟုတ် အခြားအသုံးပြုသူထံသို့ ထုတ်ပေးထားသည်။
+        invalid_redirect_uri: ပြန်ညွှန်းထားခြင်းတွင် ပါဝင်သော URI မှာ မမှန်ကန်ပါ။
+        invalid_request:
+          missing_param: ပျောက်နေသည့် လိုအပ်သောအရာ - %{value}။
+          request_not_authorized: ခွင့်ပြုချက်တောင်းခံရန် လိုအပ်ပါသည်။ တောင်းဆိုမှုခွင့်ပြုချက်အတွက် လိုအပ်သောအရာမှာ ပျောက်ဆုံးနေခြင်း သို့မဟုတ် မမှန်ကန်ခြင်း ကြောင့်ဖြစ်ပါသည်။
+          unknown: တောင်းဆိုချက်တွင် ပံ့ပိုးမထားသော တန်ဖိုးတစ်ခုပါဝင်သည့် လိုအပ်သောအချက်အလက်တစ်ခု ပျောက်ဆုံးနေပါသည် သို့မဟုတ် ပုံစံမှားယွင်းနေပါသည်။
+        invalid_resource_owner: ပံ့ပိုးပေးထားသည့် မူလပိုင်ရှင်အထောက်အထားများမှာ မမှန်ကန်ပါ သို့မဟုတ် မူလပိုင်ရှင်ကို ရှာမတွေ့ပါ။
+        invalid_scope: တောင်းဆိုထားသော နယ်ပယ်မှာ မမှန်ကန်ခြင်း၊ မသိခြင်း သို့မဟုတ် ပုံစံမမှန်ကန်ခြင်းတို့ကြောင့် ဖြစ်ပါသည်။
+        invalid_token:
+          expired: အသုံးပြုခွင့် တိုကင် သက်တမ်းကုန်သွားပါပြီ
+          revoked: အသုံးပြုခွင့်တိုကင်ကို ရုပ်သိမ်းခဲ့သည်
+          unknown: အသုံးပြုခွင့်တိုကင်မှာ မမှန်ကန်ပါ
+        resource_owner_authenticator_not_configured: Doorkeeper.configure.resource_owner_authenticator ကို ပြုပြင်မွမ်းမံမှုအားဖယ်ရှားထားခြင်းကြောင့် မူလပိုင်ရှင်ရှာဖွေခြင်းမှာ မအောင်မြင်ပါ။
+        server_error: ခွင့်ပြုထားသောဆာဗာသည် တောင်းဆိုချက်ကို မဖြည့်ဆည်းပေးနိုင်သည့် မမျှော်လင့်ထားသော အခြေအနေတစ်ခုကို ကြုံတွေ့ခဲ့ရသည်။
+        temporarily_unavailable: ဆာဗာမှာ ယာယီအလုပ်များပိုလုပ်နေရခြင်း သို့မဟုတ် ဆာဗာပြုပြင်ထိန်းသိမ်းမှုတို့ကြောင့် တောင်းဆိုချက်ကို လောလောဆယ်တွင် ကိုင်တွယ်နိုင်ခြင်းမရှိသေးပါ။
+        unauthorized_client: ဤနည်းလမ်းကို အသုံးပြု၍ Client မှ ဤတောင်းဆိုမှုကို ဆောင်ရွက်ခွင့်မရှိပါ။
+        unsupported_grant_type: တရားဝင်ခွင့်ပြုချက်အမျိုးအစားကို ခွင့်ပြုချက်ဆာဗာမှ မပံ့ပိုးထားပါ။
+        unsupported_response_type: ခွင့်ပြုထားသောဆာဗာသည် ဤတုံ့ပြန်မှုအမျိုးအစားကို မပံ့ပိုးပါ။
+    flash:
+      applications:
+        create:
+          notice: အက်ပလီကေးရှင်းကို ဖန်တီးခဲ့သည်။
+        destroy:
+          notice: အက်ပလီကေးရှင်းကို ဖျက်ခဲ့သည်။
+        update:
+          notice: အက်ပလီကေးရှင်းကို ပြင်ဆင်ခဲ့သည်။
+      authorized_applications:
+        destroy:
+          notice: အက်ပလီကေးရှင်းကို ပြန်ရုပ်သိမ်းခဲ့သည်။
+    grouped_scopes:
+      access:
+        read: ဖတ်ခွင့်သာရှိသည်
+        read/write: ဖတ်ပြီးပြင်ဆင်ခွင့်ရှိသည်
+        write: ပြင်ခွင့်သာရှိသည်
+      title:
+        accounts: အကောင့်များ
+        admin/accounts: အကောင့်စီမံခန့်ခွဲမှု
+        admin/all: စီမံခွင့်ဆိုင်ရာ လုပ်ငန်းဆောင်တာအားလုံး
+        admin/reports: မှတ်တမ်းများ စီမံခန့်ခွဲခြင်း
+        all: သင်၏ Mastodon အကောင့်သို့ အပြည့်အဝ ဝင်ရောက်ခွင့်
+        blocks: ပိတ်ပင်ထားမှုများ
+        bookmarks: မှတ်ထားသည်များ
+        conversations: စကားဝိုင်းများ
+        crypto: ပေးပို့သူနှင့် ရရှိသူများသာသိနိုင်သော လုံခြုံမှုနည်းလမ်း
+        favourites: အကြိုက်ဆုံးများ
+        filters: စစ်ထုတ်ထားခြင်းများ
+        follow: စောင့်ကြည့်ခြင်း၊ အသံပိတ်ခြင်းနှင့် ပိတ်ပင်ခြင်းများ
+        follows: စောင့်ကြည့်မယ်
+        lists: စာရင်းများ
+        media: မီဒီယာ ပူးတွဲချက်များ
+        mutes: အသံပိတ်ထားရန်
+        notifications: အသိပေးချက်များ
+        push: အသိပေးချက်များအား ရအောင်ပို့ခြင်း
+        reports: မှတ်တမ်းများ
+        search: ရှာရန်
+        statuses: ပို့စ်များ
+    layouts:
+      admin:
+        nav:
+          applications: အက်ပလီကေးရှင်းများ
+          oauth2_provider: OAuth2 ပံ့ပိုးပေးသူ
+      application:
+        title: OAuth ခွင့်ပြုချက် လိုအပ်ပါသည်
+    scopes:
+      admin:read: ဆာဗာရှိ အချက်အလက်အားလုံးကို ဖတ်ပါ
+      admin:read:accounts: အကောင့်အားလုံး၏ အရေးကြီးသော သတင်းအချက်အလက်များကို ဖတ်ပါ
+      admin:read:canonical_email_blocks: ပိတ်ပင်ထားသော Canonical Email အားလုံး၏ အရေးကြီးသောသတင်းအချက်အလက်များကို ဖတ်ပါ
+      admin:read:domain_allows: ခွင့်ပြုထားသော ဒိုမိန်းအားလုံး၏ အရေးကြီးသော သတင်းအချက်အလက်များကို ဖတ်ပါ
+      admin:read:domain_blocks: ပိတ်ပင်ထားသော ဒိုမိန်းအားလုံး၏ အရေးကြီးသော သတင်းအချက်အလက်များကို ဖတ်ပါ
+      admin:read:email_domain_blocks: ပိတ်ပင်ထားသော အီးမေးလ်ဒိုမိန်းအားလုံး၏ အရေးကြီးသောသတင်းအချက်အလက်များကို ဖတ်ပါ
+      admin:read:ip_blocks: ပိတ်ပင်ထားသော IP အားလုံး၏ အရေးကြီးသောသတင်းအချက်အလက်များကို ဖတ်ပါ
+      admin:read:reports: မှတ်တမ်းများနှင့် တိုင်ကြားထားသောအကောင့်များအားလုံး၏ အရေးကြီးသော အချက်အလက်ကို ဖတ်ပါ။
+      admin:write: ဆာဗာပေါ်ရှိ အချက်အလက်အားလုံးကို ပြင်ဆင်ပါ
+      admin:write:accounts: အကောင့်များအား စိစစ်လုပ်ဆောင်မှုများ ဆောင်ရွက်ပါ
+      admin:write:canonical_email_blocks: Canonical Email ပိတ်ပင်ထားမှုများအား စိစစ်လုပ်ဆောင်မှုများ ဆောင်ရွက်ပါ
+      admin:write:domain_allows: ခွင့်ပြုထားသောဒိုမိန်းပေါ်တွင် စိစစ်လုပ်ဆောင်ချက်များ ဆောင်ရွက်ပါ
+      admin:write:domain_blocks: ပိတ်ပင်ထားသောဒိုမိန်းပေါ်တွင် စိစစ်လုပ်ဆောင်ချက်များ ဆောင်ရွက်ပါ
+      admin:write:email_domain_blocks: ပိတ်ပင်ထားသော အီးမေးလ်ဒိုမိန်းပေါ်တွင် စိစစ်လုပ်ဆောင်ချက်များ ဆောင်ရွက်ပါ
+      admin:write:ip_blocks: IP ပိတ်ပင်ခြင်းများအတွက် စိစစ်လုပ်ဆောင်မှုများ ဆောင်ရွက်ပါ
+      admin:write:reports: အစီရင်ခံစာများပေါ်တွင် စိစစ်လုပ်ဆောင်ချက်များ ဆောင်ရွက်ပါ
+      crypto: ပေးပို့သူနှင့် ရရှိသူများသာသိနိုင်သော လုံခြုံမှုနည်းလမ်းကို အသုံးပြုပါ
+      follow: အကောင့်ဆက်ဆံရေးများကို ပြင်ဆင်ပါ
+      push: သင်ရရှိအောင်ပေးပို့ထားသည့် အသိပေးချက်များကို လက်ခံပါ
+      read: သင့်အကောင့်အချက်အလက်အားလုံးကို ဖတ်ပါ
+      read:accounts: အကောင့်အချက်အလက်များကို ကြည့်ပါ
+      read:blocks: သင် ပိတ်ပင်ထားသည်များကို ကြည့်ပါ
+      read:bookmarks: သင် မှတ်ထားသည်များကို ကြည့်ပါ
+      read:favourites: သင့်အကြိုက်ဆုံးများကို ကြည့်ပါ
+      read:filters: သင် စစ်ထုတ်ထားမှုများကို ကြည့်ပါ
+      read:follows: သင့်အားစောင့်ကြည့်နေသူများကို ကြည့်ပါ
+      read:lists: သင့်စာရင်းများကို ကြည့်ပါ
+      read:mutes: သင်အသံပိတ်ထားမှုများကို ကြည့်ပါ
+      read:notifications: သင့်အသိပေးချက်များကို ကြည့်ပါ
+      read:reports: သင့်မှတ်တမ်းများကို ကြည့်ပါ
+      read:search: သင့်ကိုယ်စား ရှာဖွေပါ
+      read:statuses: ပို့စ်အားလုံးကို ကြည့်ပါ
+      write: သင့်အကောင့်၏ အချက်အလက်အားလုံးကို ပြင်ဆင်ပါ
+      write:accounts: သင့်ပရိုဖိုင်ကို ပြင်ဆင်ပါ
+      write:blocks: အကောင့်များနှင့် ဒိုမိန်းများကို ပိတ်ပင်ပါ
+      write:bookmarks: မှတ်ထားသောပို့စ်များ
+      write:conversations: စကားဝိုင်းများကို အသံပိတ်ပြီး ဖျက်ပါ
+      write:favourites: အကြိုက်ဆုံးပို့စ်များ
+      write:filters: စစ်ထုတ်ခြင်းအား ဖန်တီးပါ
+      write:follows: စောင့်ကြည့်ရန်
+      write:lists: စာရင်းများ ဖန်တီးရန်
+      write:media: မီဒီယာဖိုင်များကို အင်တာနက်ပေါ်တင်ပါ
+      write:mutes: လူများနှင့် စကားဝိုင်းများကို ပိတ်ထားပါ
+      write:notifications: သင့်အအသိပေးချက်များကို ရှင်းလင်းပါ
+      write:reports: အခြားလူများကို သတင်းပို့မည်
+      write:statuses: ပို့စ်များအား ရအောင်ပို့ခြင်း
diff --git a/config/locales/doorkeeper.nl.yml b/config/locales/doorkeeper.nl.yml
index 9c18e924a..f9e90e795 100644
--- a/config/locales/doorkeeper.nl.yml
+++ b/config/locales/doorkeeper.nl.yml
@@ -122,14 +122,14 @@ nl:
         admin/accounts: Accountbeheer
         admin/all: Alle beheerfuncties
         admin/reports: Rapportagebeheer
-        all: Alles
+        all: Volledige toegang tot je Mastodon-account
         blocks: Blokkeren
         bookmarks: Bladwijzers
         conversations: Gesprekken
         crypto: End-to-end-encryptie
         favourites: Favorieten
         filters: Filters
-        follow: Relaties
+        follow: Volgers, genegeerde en geblokkeerde gebruikers
         follows: Volgend
         lists: Lijsten
         media: Mediabijlagen
diff --git a/config/locales/doorkeeper.nn.yml b/config/locales/doorkeeper.nn.yml
index 10e7fbc1d..0582b53f8 100644
--- a/config/locales/doorkeeper.nn.yml
+++ b/config/locales/doorkeeper.nn.yml
@@ -122,14 +122,14 @@ nn:
         admin/accounts: Kontoadministrasjon
         admin/all: Alle administrative funksjonar
         admin/reports: Rapportadministrasjon
-        all: Alt
+        all: Full tilgang til Mastodon-kontoen din
         blocks: Blokkeringar
         bookmarks: Bokmerke
         conversations: Samtalar
         crypto: Ende-til-ende-kryptering
         favourites: Favorittar
         filters: Filter
-        follow: Forhold
+        follow: Dei du fylgjer, målbind og blokkerer
         follows: Fylgjer
         lists: Lister
         media: Mediavedlegg
diff --git a/config/locales/doorkeeper.no.yml b/config/locales/doorkeeper.no.yml
index 1f45808c4..c432f6645 100644
--- a/config/locales/doorkeeper.no.yml
+++ b/config/locales/doorkeeper.no.yml
@@ -122,14 +122,14 @@
         admin/accounts: Administrasjon av kontoer
         admin/all: All administrativ funksjonalitet
         admin/reports: Administrasjon av rapporteringer
-        all: Alt
+        all: Full tilgang til din Mastodon-konto
         blocks: Blokkeringer
         bookmarks: Bokmerker
         conversations: Samtaler
         crypto: Ende-til-ende-kryptering
         favourites: Favoritter
         filters: Filtre
-        follow: Relasjoner
+        follow: Hvem du følger, demper og blokkerer
         follows: Følger
         lists: Lister
         media: Mediavedlegg
diff --git a/config/locales/doorkeeper.oc.yml b/config/locales/doorkeeper.oc.yml
index d86fbe793..84935d49b 100644
--- a/config/locales/doorkeeper.oc.yml
+++ b/config/locales/doorkeeper.oc.yml
@@ -122,14 +122,14 @@ oc:
         admin/accounts: Administracion de comptes
         admin/all: Totas las foncions administrativas
         admin/reports: Administracion de senhalaments
-        all: Tot
+        all: Accès complèt a vòstre compte Mastodon
         blocks: Blocatges
         bookmarks: Marcadors
         conversations: Conversacions
         crypto: Chiframent del cap a la fin
         favourites: Favorits
         filters: Filtres
-        follow: Relacions
+        follow: Seguidors, Silenciats e blocats
         follows: Abonaments
         lists: Listas
         media: Fichièrs junts
@@ -149,6 +149,11 @@ oc:
     scopes:
       admin:read: lectura de totas las donadas del servidor
       admin:read:accounts: lectura de las informacions sensiblas dels comptes
+      admin:read:canonical_email_blocks: legir las informacions sensiblas de totes los blocs d’e-mails canonics
+      admin:read:domain_allows: legir las informacions sensiblas de totes los domenis autorizats
+      admin:read:domain_blocks: legir las informacions sensiblas de totes los domenis blocats
+      admin:read:email_domain_blocks: legir las informacions sensiblas de totes los blocs de domenis d’e-mail
+      admin:read:ip_blocks: legir las informacions sensiblas de totes los blocats d’IP
       admin:read:reports: lectura de las informacions sensiblas dels senhalaments e dels comptes senhalats
       admin:write: modificacion de las donadas del servidor
       admin:write:accounts: realizacion d’accions de moderacion suls comptes
diff --git a/config/locales/doorkeeper.pl.yml b/config/locales/doorkeeper.pl.yml
index 4a9c6ae36..1891ada15 100644
--- a/config/locales/doorkeeper.pl.yml
+++ b/config/locales/doorkeeper.pl.yml
@@ -48,7 +48,7 @@ pl:
         title: Nowa aplikacja
       show:
         actions: Akcje
-        application_id: ID Aplikacji
+        application_id: Klucz klienta
         callback_urls: Adresy wywołań zwrotnych
         scopes: Zakresy (scopes)
         secret: Sekret
@@ -122,14 +122,14 @@ pl:
         admin/accounts: Zarządzanie kontami użytkowników
         admin/all: Wszystkie opcje administratora
         admin/reports: Zarządzanie zgłoszeniami
-        all: Wszystko
+        all: Pełny dostęp do konta Mastodon
         blocks: Zablokowane
         bookmarks: Zakładki
         conversations: Konwersacje
         crypto: Szyfrowanie End-to-End
         favourites: Ulubione
         filters: Filtry
-        follow: Relacje
+        follow: Obserwatorzy, wyciszeni i zablokowani
         follows: Obserwowani
         lists: Listy
         media: Załączniki multimedialne
diff --git a/config/locales/doorkeeper.pt-BR.yml b/config/locales/doorkeeper.pt-BR.yml
index d097b954d..6b64badf7 100644
--- a/config/locales/doorkeeper.pt-BR.yml
+++ b/config/locales/doorkeeper.pt-BR.yml
@@ -122,14 +122,14 @@ pt-BR:
         admin/accounts: Administração de contas
         admin/all: Todas as funções administrativas
         admin/reports: Controle de denúncias
-        all: Tudo
+        all: Acesso total à sua conta Mastodon
         blocks: Bloqueios
         bookmarks: Salvos
         conversations: Conversas
         crypto: Criptografia de ponta a ponta
         favourites: Favoritos
         filters: Filtros
-        follow: Relacionamentos
+        follow: Seguidores, Silenciados e Bloqueados
         follows: Seguidores
         lists: Listas
         media: Mídias anexadas
@@ -149,9 +149,19 @@ pt-BR:
     scopes:
       admin:read: ler todos os dados no servidor
       admin:read:accounts: ler informações sensíveis de todas as contas
+      admin:read:canonical_email_blocks: ler informações sensíveis de todos os blocos de e-mail canônicos
+      admin:read:domain_allows: ler informações sensíveis de todos os domínios (URL) permitidos
+      admin:read:domain_blocks: ler informações sensíveis de todos os domínios (URL) bloqueados
+      admin:read:email_domain_blocks: ler informações sensíveis de todos os e-mails de domínios bloqueados
+      admin:read:ip_blocks: ler informações sensíveis de todos os endereços de IP bloqueados
       admin:read:reports: ler informações sensíveis de todas as denúncias e contas denunciadas
       admin:write: alterar todos os dados no servidor
       admin:write:accounts: executar ações de moderação em contas
+      admin:write:canonical_email_blocks: executar ações de moderação em blocos canônicos de e-mail
+      admin:write:domain_allows: executar ações de moderação em domínios (URL) permitidos
+      admin:write:domain_blocks: executar ações de moderação em domínios (URL) bloqueados
+      admin:write:email_domain_blocks: executar ações de moderação em blocos de e-mail bloqueados
+      admin:write:ip_blocks: executar ações de moderação em IPs bloqueados
       admin:write:reports: executar ações de moderação em denúncias
       crypto: usar criptografia de ponta-a-ponta
       follow: alterar o relacionamento das contas
diff --git a/config/locales/doorkeeper.pt-PT.yml b/config/locales/doorkeeper.pt-PT.yml
index 2648118d8..2dee34bd9 100644
--- a/config/locales/doorkeeper.pt-PT.yml
+++ b/config/locales/doorkeeper.pt-PT.yml
@@ -3,10 +3,10 @@ pt-PT:
   activerecord:
     attributes:
       doorkeeper/application:
-        name: Nome da Aplicação
-        redirect_uri: URL de redirecionamento
-        scopes: Autorizações
-        website: Site da Aplicação
+        name: Nome da aplicação
+        redirect_uri: URI de redireccionamento
+        scopes: Âmbitos
+        website: Página na teia da aplicação
     errors:
       models:
         doorkeeper/application:
@@ -15,7 +15,7 @@ pt-PT:
               fragment_present: não pode conter um fragmento.
               invalid_uri: tem de ser um URI válido.
               relative_uri: tem de ser um URI absoluto.
-              secured_uri: tem de ser um HTTPS/SSL URI.
+              secured_uri: tem de ser um URI HTTPS/SSL.
   doorkeeper:
     applications:
       buttons:
@@ -23,27 +23,27 @@ pt-PT:
         cancel: Cancelar
         destroy: Destruir
         edit: Editar
-        submit: Submeter
+        submit: Enviar
       confirmations:
-        destroy: Tens a certeza?
+        destroy: De certeza?
       edit:
         title: Editar aplicação
       form:
-        error: Oops! Verifica que o formulário não tem erros
+        error: Ups! Verifique que o formulário não tem erros
       help:
         native_redirect_uri: Usa %{native_redirect_uri} para testes locais
         redirect_uri: Utiliza uma linha por URI
-        scopes: Separa autorizações com espaços. Deixa em branco para usar autorizações predefinidas.
+        scopes: Separe as esferas de acção com espaços. Deixe em branco para usar autorizações predefinidas.
       index:
         application: Aplicações
         callback_url: URL de retorno
         delete: Eliminar
         empty: Não tem aplicações.
         name: Nome
-        new: Nova Aplicação
-        scopes: Autorizações
+        new: Nova aplicação
+        scopes: Âmbitos
         show: Mostrar
-        title: As tuas aplicações
+        title: As suas aplicações
       new:
         title: Nova aplicação
       show:
@@ -122,14 +122,14 @@ pt-PT:
         admin/accounts: Administração de contas
         admin/all: Todas as funções administrativas
         admin/reports: Administração de denúncias
-        all: Tudo
+        all: Acesso total à sua conta Mastodon
         blocks: Bloqueios
         bookmarks: Itens Salvos
         conversations: Conversas
         crypto: Encriptação ponta-a-ponta
         favourites: Favoritos
         filters: Filtros
-        follow: Relações
+        follow: A seguir, a silenciar, e a bloquear
         follows: Seguidores
         lists: Listas
         media: Anexos de media
diff --git a/config/locales/doorkeeper.ro.yml b/config/locales/doorkeeper.ro.yml
index 7ad11d13a..59e67aeb2 100644
--- a/config/locales/doorkeeper.ro.yml
+++ b/config/locales/doorkeeper.ro.yml
@@ -122,14 +122,12 @@ ro:
         admin/accounts: Administrarea conturilor
         admin/all: Toate funcțiile administrative
         admin/reports: Administrarea rapoartelor
-        all: Tot
         blocks: Blocuri
         bookmarks: Marcaje
         conversations: Conversații
         crypto: Criptare în ambele părți
         favourites: Favorite
         filters: Filtre
-        follow: Relații
         follows: Urmăriri
         lists: Liste
         media: Atașamente media
diff --git a/config/locales/doorkeeper.ru.yml b/config/locales/doorkeeper.ru.yml
index 97bf7fcc1..d8262dae9 100644
--- a/config/locales/doorkeeper.ru.yml
+++ b/config/locales/doorkeeper.ru.yml
@@ -122,14 +122,14 @@ ru:
         admin/accounts: Управление учётными записями
         admin/all: Все административные функции
         admin/reports: Управление отчётами
-        all: Все
+        all: Полный доступ к вашей учетной записи Mastodon
         blocks: Блокировки
         bookmarks: Закладки
         conversations: Диалоги
         crypto: Сквозное шифрование
         favourites: Избранное
         filters: Фильтры
-        follow: Взаимосвязи
+        follow: Подписки, заглушенные и заблокированные
         follows: Подписки
         lists: Списки
         media: Медиафайлы
@@ -151,9 +151,17 @@ ru:
       admin:read:accounts: читать конфиденциальную информацию всех учётных записей
       admin:read:canonical_email_blocks: чтение конфиденциальной информации всех канонических блоков электронной почты
       admin:read:domain_allows: чтение конфиденциальной информации для всего домена позволяет
+      admin:read:domain_blocks: чтение конфиденциальной информации для всего домена позволяет
+      admin:read:email_domain_blocks: читать конфиденциальную информацию обо всех блоках домена электронной почты
+      admin:read:ip_blocks: читать конфиденциальную информацию обо всех IP-блоках
       admin:read:reports: читать конфиденциальную информацию о всех жалобах и учётных записях с жалобами
       admin:write: модифицировать все данные на сервере
       admin:write:accounts: производить модерацию учётных записей
+      admin:write:canonical_email_blocks: выполнять действия по модерации канонических блоков электронной почты
+      admin:write:domain_allows: производить модерацию учётных записей
+      admin:write:domain_blocks: выполнять модерационные действия над блокировкой домена
+      admin:write:email_domain_blocks: выполнять действия по модерации блоков домена электронной почты
+      admin:write:ip_blocks: выполнять модерационные действия над блокировками IP
       admin:write:reports: производить модерацию жалоб
       crypto: использ. сквозное шифрование
       follow: управлять подписками и списком блокировок
diff --git a/config/locales/doorkeeper.sco.yml b/config/locales/doorkeeper.sco.yml
index 60453dc78..8b6ac8355 100644
--- a/config/locales/doorkeeper.sco.yml
+++ b/config/locales/doorkeeper.sco.yml
@@ -122,14 +122,12 @@ sco:
         admin/accounts: Administration o accoonts
         admin/all: Aw administrative functions
         admin/reports: Administration o clypes
-        all: Awthin
         blocks: Dingies
         bookmarks: Buikmairks
         conversations: Conversations
         crypto: En-tae-en encryption
         favourites: Favourites
         filters: Filters
-        follow: Relationships
         follows: Follaes
         lists: Lists
         media: Media attachments
diff --git a/config/locales/doorkeeper.si.yml b/config/locales/doorkeeper.si.yml
index ebb7f474f..3f5fcd5c0 100644
--- a/config/locales/doorkeeper.si.yml
+++ b/config/locales/doorkeeper.si.yml
@@ -122,14 +122,12 @@ si:
         admin/accounts: ගිණුම් පරිපාලනය
         admin/all: සියලුම පරිපාලන කාර්යයන්
         admin/reports: වාර්තා පරිපාලනය
-        all: සියල්ල
         blocks: කුට්ටි
         bookmarks: පිටු සලකුණු
         conversations: සංවාද
         crypto: අන්ත සංකේතනය
         favourites: ප්රියතම
         filters: පෙරහන්
-        follow: සබඳතා
         follows: පහත සඳහන්
         lists: ලැයිස්තු
         media: මාධ්ය ඇමුණුම්
diff --git a/config/locales/doorkeeper.sk.yml b/config/locales/doorkeeper.sk.yml
index 4260ad2ca..ed85ab1c9 100644
--- a/config/locales/doorkeeper.sk.yml
+++ b/config/locales/doorkeeper.sk.yml
@@ -122,14 +122,13 @@ sk:
         admin/accounts: Správa účtov
         admin/all: Všetky administratívne funkcie
         admin/reports: Správa reportov
-        all: Všetko
+        all: Plný prístup k tvojmu Mastodon účtu
         blocks: Blokovania
         bookmarks: Záložky
         conversations: Konverzácie
         crypto: Šifrovanie End-to-end
         favourites: Obľúbené
         filters: Filtre
-        follow: Vzťahy
         follows: Sledovania
         lists: Zoznamy
         media: Mediálne prílohy
diff --git a/config/locales/doorkeeper.sl.yml b/config/locales/doorkeeper.sl.yml
index ca689e8f1..8fe39bbf6 100644
--- a/config/locales/doorkeeper.sl.yml
+++ b/config/locales/doorkeeper.sl.yml
@@ -122,14 +122,14 @@ sl:
         admin/accounts: Upravljanje računov
         admin/all: Vse skrbniške funkcije
         admin/reports: Upravljanje prijav
-        all: Vse
+        all: Poln dostop do vašega računa Mastodon
         blocks: Blokira
         bookmarks: Zaznamki
         conversations: Pogovori
         crypto: Šifriranje od konca do konca
         favourites: Priljubljeni
         filters: Filtri
-        follow: Razmerja
+        follow: Sledi, utiša in blokira
         follows: Sledi
         lists: Seznami
         media: Predstavnostne priloge
diff --git a/config/locales/doorkeeper.sq.yml b/config/locales/doorkeeper.sq.yml
index c6c173187..afa91bc87 100644
--- a/config/locales/doorkeeper.sq.yml
+++ b/config/locales/doorkeeper.sq.yml
@@ -72,7 +72,7 @@ sq:
         revoke: A jeni i sigurt?
       index:
         authorized_at: Autorizuar më %{date}
-        description_html: Këto janë aplikacione që mund të hyjnë në llogarinë tuaj duke përdorur API-n. Nëse këtu ka aplikacione që nuk i njihni, ose një aplikacion po sillet ndryshe nga sa pritet, mund t’i shfuqiozoni hyrjen.
+        description_html: Këto janë aplikacione që mund të hyjnë në llogarinë tuaj duke përdorur API-n. Nëse këtu ka aplikacione që nuk i njihni, ose një aplikacion po sillet ndryshe nga sa pritet, mund t’i shfuqizoni hyrjen.
         last_used_at: Përdorur së fundi më %{date}
         never_used: I papërdorur ndonjëherë
         scopes: Leje
@@ -122,14 +122,14 @@ sq:
         admin/accounts: Administrim llogarish
         admin/all: Krejt funksionet administrative
         admin/reports: Administrim i raporteve
-        all: Gjithçka
+        all: Hyrje të plotë në llogarinë tuaj Mastodon
         blocks: Blloqe
         bookmarks: Faqerojtës
         conversations: Biseda
         crypto: Fshehtëzim skaj-më-skaj
         favourites: Të parapëlqyer
         filters: Filtra
-        follow: Marrëdhënie
+        follow: Ndjekje, Heshtime dhe Bllokime
         follows: Ndjekje
         lists: Lista
         media: Bashkëngjitje media
@@ -148,13 +148,13 @@ sq:
         title: Lypset autorizim OAuth
     scopes:
       admin:read: të lexojë krejt të dhënat te shërbyesi
-      admin:read:accounts: të lexojë krejt të dhënat rezervat të krejt llogarive
+      admin:read:accounts: të lexojë hollësi rezervat të krejt llogarive
       admin:read:canonical_email_blocks: të lexojë hollësi rezervat të krejt bllokimeve të zakonshëm të email-eve
       admin:read:domain_allows: të lexojë hollësi rezervat të krejt lejimeve të përkatësive
       admin:read:domain_blocks: të lexojë hollësi rezervat të krejt bllokimeve të përkatësive
       admin:read:email_domain_blocks: të lexojë hollësi rezervat të krejt bllokimeve të përkatësive të email-eve
-      admin:read:ip_blocks: të lexojë hollësi rezervat të krejt bllokimeve të IP-eve
-      admin:read:reports: të lexojë të dhëna rezervat të krejt raportimeve dhe të llogarive të raportuara
+      admin:read:ip_blocks: të lexojë hollësi rezervat të krejt bllokimeve të IP-ve
+      admin:read:reports: të lexojë hollësi rezervat të krejt raportimeve dhe të llogarive të raportuara
       admin:write: të përpunojë krejt të dhënat në shërbyes
       admin:write:accounts: të kryejë veprime moderimi në llogaritë
       admin:write:canonical_email_blocks: të kryejë veprime moderimi në bllokime të zakonshëm të email-eve
@@ -167,7 +167,7 @@ sq:
       follow: të ndryshojë marrëdhënie llogarish
       push: të marrë njoftime push për ju
       read: të lexojë krejt të dhënat e llogarisë tuaj
-      read:accounts: të shohë të dhëna llogarish
+      read:accounts: të shohë hollësi llogarish
       read:blocks: të shohë blloqet tuaja
       read:bookmarks: të shohë faqerojtësit tuaj
       read:favourites: të shohë të parapëlqyerit tuaj
diff --git a/config/locales/doorkeeper.sr-Latn.yml b/config/locales/doorkeeper.sr-Latn.yml
index 66ec102b1..3dca04d0a 100644
--- a/config/locales/doorkeeper.sr-Latn.yml
+++ b/config/locales/doorkeeper.sr-Latn.yml
@@ -122,18 +122,18 @@ sr-Latn:
         admin/accounts: Administracija naloga
         admin/all: Sve funkcije administracije
         admin/reports: Administracija prijava
-        all: Sve
+        all: Potpuni pristup vašem Mastodon nalogu
         blocks: Blokirani
         bookmarks: Obeleživači
         conversations: Razgovori
         crypto: End-to-end enkripcija
         favourites: Omiljeni
         filters: Filteri
-        follow: Veze
+        follow: Praćenja, ignorisanja i blokiranja
         follows: Praćeni
         lists: Liste
         media: Multimedijalni prilozi
-        mutes: Utišani
+        mutes: Ignorisani
         notifications: Obaveštenja
         push: Prosleđena obaveštenja
         reports: Prijave
@@ -149,17 +149,22 @@ sr-Latn:
     scopes:
       admin:read: čitanje svih podataka na serveru
       admin:read:accounts: čitanje osetljivih podataka za sve naloge
+      admin:read:canonical_email_blocks: pročitaj osetljive informacije i kanonske imejl blokove
       admin:read:domain_allows: pročitaj osetljive informacije za sve dozvole domena
       admin:read:domain_blocks: pročitaj osetljive informacije za sve blokove domena
+      admin:read:email_domain_blocks: pročitaj osetljive informacije svih blokova imejl domena
       admin:read:ip_blocks: pročitaj osetljive informacije za sve IP blokove
       admin:read:reports: čitanje osetljivih podataka svih izveštaja i prijavljenih naloga
       admin:write: menjanje svih podataka na serveru
       admin:write:accounts: vršenje moderatorskih aktivnosti nad nalozima
+      admin:write:canonical_email_blocks: izvrši moderacijske radnje nad kanonskim imejl blokovima
+      admin:write:domain_allows: izvrši moderacijske radnje nad dozvolama domena
       admin:write:domain_blocks: izvrši moderatorske aktivnosti na blokovima domena
+      admin:write:email_domain_blocks: izvrši moderacijske radnje nad blokovima imejl domena
       admin:write:ip_blocks: izvrši moderatorske aktivnosti na IP blokovima
       admin:write:reports: vršenje moderatorskih aktivnosti nad izveštajima
       crypto: korišćenje end-to-end enkripcije
-      follow: prati, blokira, odblokira i otprati naloge
+      follow: menja odnose naloga
       push: primanje prosleđenih obaveštenja
       read: čita podatke Vašeg naloga
       read:accounts: pogledaj informacije o nalozima
@@ -169,7 +174,7 @@ sr-Latn:
       read:filters: pogledaj svoje filtere
       read:follows: pogledaj koga pratiš
       read:lists: pogledaj svoje liste
-      read:mutes: pogledaj svoje utišane
+      read:mutes: pogledaj ignorisanja
       read:notifications: pogledaj svoja obaveštenja
       read:reports: pogledaj svoje prijave
       read:search: pretraži u svoje ime
@@ -178,13 +183,13 @@ sr-Latn:
       write:accounts: izmeni svoj profil
       write:blocks: blokiraj naloge i domene
       write:bookmarks: obeleži objave
-      write:conversations: utišaj i obriši razgovore
+      write:conversations: ignoriši i izbriši razgovore
       write:favourites: omiljene objave
       write:filters: kreiraj filtere
       write:follows: prati korisnike
       write:lists: kreiraj liste
       write:media: otpremi medijske datoteke
-      write:mutes: utišaj korinsike i razgovore
+      write:mutes: ignoriši korisnike i razgovore
       write:notifications: obriši svoja obaveštenja
       write:reports: prijavi druge korisnike
       write:statuses: objavi objave
diff --git a/config/locales/doorkeeper.sr.yml b/config/locales/doorkeeper.sr.yml
index 20e39e1c0..00287543f 100644
--- a/config/locales/doorkeeper.sr.yml
+++ b/config/locales/doorkeeper.sr.yml
@@ -122,18 +122,18 @@ sr:
         admin/accounts: Администрација налога
         admin/all: Све функције администрације
         admin/reports: Администрација пријава
-        all: Све
+        all: Потпуни приступ вашем Mastodon налогу
         blocks: Блокирани
         bookmarks: Обележивачи
         conversations: Разговори
         crypto: End-to-end енкрипција
         favourites: Омиљени
         filters: Филтери
-        follow: Везе
+        follow: Праћења, игнорисања и блокирања
         follows: Праћени
         lists: Листе
         media: Мултимедијални прилози
-        mutes: Утишани
+        mutes: Игнорисани
         notifications: Обавештења
         push: Прослеђена обавештења
         reports: Пријаве
@@ -149,17 +149,22 @@ sr:
     scopes:
       admin:read: читање свих података на серверу
       admin:read:accounts: читање осетљивих података за све налоге
+      admin:read:canonical_email_blocks: прочитај осетљиве информације и канонске имејл блокове
       admin:read:domain_allows: прочитај осетљиве информације за све дозволе домена
       admin:read:domain_blocks: прочитај осетљиве информације за све блокове домена
+      admin:read:email_domain_blocks: прочитај осетљиве информације свих блокова имејл домена
       admin:read:ip_blocks: прочитај осетљиве информације за све IP блокове
       admin:read:reports: читање осетљивих података свих извештаја и пријављених налога
       admin:write: мењање свих података на серверу
       admin:write:accounts: вршење модераторских активности над налозима
+      admin:write:canonical_email_blocks: изврши модерацијске радње над канонским имејл блоковима
+      admin:write:domain_allows: изврши модерацијске радње над дозволама домена
       admin:write:domain_blocks: изврши модераторске активности на блоковима домена
+      admin:write:email_domain_blocks: изврши модерацијске радње над блоковима имејл домена
       admin:write:ip_blocks: изврши модераторске активности на IP блоковима
       admin:write:reports: вршење модераторских активности над извештајима
       crypto: коришћење end-to-end енкрипције
-      follow: прати, блокира, одблокира и отпрати налоге
+      follow: мења односе налога
       push: примање прослеђених обавештења
       read: чита податке Вашег налога
       read:accounts: погледај информације о налозима
@@ -169,7 +174,7 @@ sr:
       read:filters: погледај своје филтере
       read:follows: погледај кога пратиш
       read:lists: погледај своје листе
-      read:mutes: погледај своје утишане
+      read:mutes: погледај игнорисања
       read:notifications: погледај своја обавештења
       read:reports: погледај своје пријаве
       read:search: претражи у своје име
@@ -178,13 +183,13 @@ sr:
       write:accounts: измени свој профил
       write:blocks: блокирај налоге и домене
       write:bookmarks: обележи објаве
-      write:conversations: утишај и обриши разговоре
+      write:conversations: игнориши и избриши разговоре
       write:favourites: омиљене објаве
       write:filters: креирај филтере
       write:follows: прати кориснике
       write:lists: креирај листе
       write:media: отпреми медијске датотеке
-      write:mutes: утишај коринсике и разговоре
+      write:mutes: игнориши кориснике и разговоре
       write:notifications: обриши своја обавештења
       write:reports: пријави друге кориснике
       write:statuses: објави објаве
diff --git a/config/locales/doorkeeper.sv.yml b/config/locales/doorkeeper.sv.yml
index 9a7b53bcc..b6c499896 100644
--- a/config/locales/doorkeeper.sv.yml
+++ b/config/locales/doorkeeper.sv.yml
@@ -122,14 +122,14 @@ sv:
         admin/accounts: Administrering av konton
         admin/all: Alla administrativa funktioner
         admin/reports: Administrering av rapporter
-        all: Allting
+        all: Full åtkomst till ditt Mastodon-konto
         blocks: Blockeringar
         bookmarks: Bokmärken
         conversations: Konversationer
         crypto: Ände-till-ände-kryptering
         favourites: Favoriter
         filters: Filter
-        follow: Relationer
+        follow: Följare, mjutade och blockerade
         follows: Följer
         lists: Listor
         media: Mediabilagor
diff --git a/config/locales/doorkeeper.th.yml b/config/locales/doorkeeper.th.yml
index e4ca5b58f..ceb81f656 100644
--- a/config/locales/doorkeeper.th.yml
+++ b/config/locales/doorkeeper.th.yml
@@ -82,7 +82,7 @@ th:
       messages:
         access_denied: เจ้าของทรัพยากรหรือเซิร์ฟเวอร์การอนุญาตปฏิเสธคำขอ
         credential_flow_not_configured: โฟลว์ข้อมูลประจำตัวรหัสผ่านเจ้าของทรัพยากรล้มเหลวเนื่องจากไม่ได้กำหนดค่า Doorkeeper.configure.resource_owner_from_credentials
-        invalid_client: การรับรองความถูกต้องไคลเอ็นต์ล้มเหลวเนื่องจากไคลเอ็นต์ที่ไม่รู้จัก ไม่มีการรับรองความถูกต้องไคลเอ็นต์ที่รวมอยู่ หรือวิธีการรับรองความถูกต้องที่ไม่รองรับ
+        invalid_client: การรับรองความถูกต้องไคลเอ็นต์ล้มเหลวเนื่องจากไคลเอ็นต์ที่ไม่รู้จัก ไม่ได้รวมการรับรองความถูกต้องไคลเอ็นต์ หรือวิธีการรับรองความถูกต้องที่ไม่รองรับ
         invalid_grant: การให้การรับรองความถูกต้องที่ให้มาไม่ถูกต้อง หมดอายุแล้ว เพิกถอนแล้ว ไม่ตรงกับ URI การเปลี่ยนเส้นทางที่ใช้ในคำขอการรับรองความถูกต้อง หรือออกให้ไคลเอ็นต์อื่น
         invalid_redirect_uri: URI การเปลี่ยนเส้นทางที่รวมอยู่ไม่ถูกต้อง
         invalid_request:
@@ -99,7 +99,7 @@ th:
         server_error: เซิร์ฟเวอร์การรับรองความถูกต้องพบเงื่อนไขที่ไม่คาดคิดซึ่งป้องกันไม่ให้เซิร์ฟเวอร์ดำเนินการตามคำขอ
         temporarily_unavailable: เซิร์ฟเวอร์การรับรองความถูกต้องไม่สามารถจัดการคำขอได้ในปัจจุบันเนื่องจากการทำงานเกินพิกัดชั่วคราวหรือการบำรุงรักษาเซิร์ฟเวอร์
         unauthorized_client: ไคลเอ็นต์ไม่ได้รับอนุญาตให้ทำคำขอนี้โดยใช้วิธีการนี้
-        unsupported_grant_type: ชนิดการให้การรับรองความถูกต้องไม่รองรับโดยเซิร์ฟเวอร์การรับรองความถูกต้อง
+        unsupported_grant_type: ไม่รองรับชนิดการให้การรับรองความถูกต้องโดยเซิร์ฟเวอร์การรับรองความถูกต้อง
         unsupported_response_type: เซิร์ฟเวอร์การอนุญาตไม่รองรับชนิดการตอบสนองนี้
     flash:
       applications:
@@ -122,14 +122,14 @@ th:
         admin/accounts: การดูแลบัญชี
         admin/all: ฟังก์ชันการดูแลทั้งหมด
         admin/reports: การดูแลรายงาน
-        all: ทุกอย่าง
+        all: การเข้าถึงบัญชี Mastodon ของคุณแบบเต็ม
         blocks: การปิดกั้น
         bookmarks: ที่คั่นหน้า
         conversations: การสนทนา
         crypto: การเข้ารหัสแบบต้นทางถึงปลายทาง
         favourites: รายการโปรด
         filters: ตัวกรอง
-        follow: ความสัมพันธ์
+        follow: ติดตาม ซ่อน และปิดกั้น
         follows: การติดตาม
         lists: รายการ
         media: ไฟล์แนบสื่อ
diff --git a/config/locales/doorkeeper.tr.yml b/config/locales/doorkeeper.tr.yml
index 704a5af07..46ab470ac 100644
--- a/config/locales/doorkeeper.tr.yml
+++ b/config/locales/doorkeeper.tr.yml
@@ -19,7 +19,7 @@ tr:
   doorkeeper:
     applications:
       buttons:
-        authorize: İzin Ver
+        authorize: Yetkilendir
         cancel: İptal Et
         destroy: Yok Et
         edit: Düzenle
@@ -29,19 +29,19 @@ tr:
       edit:
         title: Uygulamayı düzenle
       form:
-        error: Hata! Olası hatalar için formunuzu kontrol edin
+        error: Opps! Olası hatalar için formunuzu kontrol edin
       help:
         native_redirect_uri: Yerel testler için %{native_redirect_uri} kullanın
-        redirect_uri: URL başına bir satır kullanın
+        redirect_uri: URL başına tek satır kullanın
         scopes: Kapsamları boşluklarla ayırın. Varsayılan kapsamları kullanmak için boş bırakın.
       index:
         application: Uygulama
-        callback_url: Geri Dönüş URL
+        callback_url: Geri Dönüş bağlantısı
         delete: Sil
         empty: Hiç uygulamanız yok.
         name: İsim
         new: Yeni uygulama
-        scopes: Kapsam
+        scopes: Kapsamlar
         show: Göster
         title: Uygulamalarınız
       new:
@@ -49,13 +49,13 @@ tr:
       show:
         actions: Eylemler
         application_id: İstemci anahtarı
-        callback_urls: Callback URL
+        callback_urls: Geri Dönüş bağlantıları
         scopes: Kapsamlar
         secret: İstemci gizli anahtarı
         title: 'Uygulama: %{name}'
     authorizations:
       buttons:
-        authorize: İzin Ver
+        authorize: Yetkilendir
         deny: Reddet
       error:
         title: Bir hata oluştu
@@ -122,14 +122,14 @@ tr:
         admin/accounts: Hesapların yönetimi
         admin/all: Tüm yönetsel işlevler
         admin/reports: Şikayetlerin yönetimi
-        all: Her şey
+        all: Mastodon hesabınıza tam erişim
         blocks: Engeller
         bookmarks: Yer imleri
         conversations: Sohbetler
         crypto: Uçtan uca şifreleme
         favourites: Beğeniler
         filters: Filtreler
-        follow: İlişkiler
+        follow: Takipler, Sessizler ve Engeller
         follows: Takip edilenler
         lists: Listeler
         media: Medya ekleri
diff --git a/config/locales/doorkeeper.uk.yml b/config/locales/doorkeeper.uk.yml
index 0dd66cb37..4e06a3590 100644
--- a/config/locales/doorkeeper.uk.yml
+++ b/config/locales/doorkeeper.uk.yml
@@ -122,14 +122,14 @@ uk:
         admin/accounts: Адміністрація облікових записів
         admin/all: Усі адміністративні функції
         admin/reports: Адміністрація звітів
-        all: Усе
+        all: Повний доступ до вашого облікового запису Mastodon
         blocks: Блокування
         bookmarks: Закладки
         conversations: Бесіди
         crypto: Наскрізне шифрування
         favourites: Вподобане
         filters: Фільтри
-        follow: Взаємозв'язки
+        follow: Підписки, ігнорування і блокування
         follows: Підписки
         lists: Списки
         media: Мультимедійні вкладення
diff --git a/config/locales/doorkeeper.uz.yml b/config/locales/doorkeeper.uz.yml
new file mode 100644
index 000000000..3ed042df3
--- /dev/null
+++ b/config/locales/doorkeeper.uz.yml
@@ -0,0 +1 @@
+uz:
diff --git a/config/locales/doorkeeper.vi.yml b/config/locales/doorkeeper.vi.yml
index d4808984f..a3a0f158b 100644
--- a/config/locales/doorkeeper.vi.yml
+++ b/config/locales/doorkeeper.vi.yml
@@ -122,14 +122,14 @@ vi:
         admin/accounts: Quản trị tài khoản
         admin/all: Mọi chức năng quản trị
         admin/reports: Quản trị báo cáo
-        all: Tất cả
+        all: Toàn quyền truy cập vào tài khoản Mastodon của bạn
         blocks: Chặn
         bookmarks: Tút đã lưu
         conversations: Thảo luận
         crypto: Mã hóa đầu cuối
         favourites: Lượt thích
         filters: Bộ lọc
-        follow: Mối quan hệ
+        follow: Theo dõi, Ẩn và Chặn
         follows: Đang theo dõi
         lists: Danh sách
         media: Tập tin đính kèm
diff --git a/config/locales/doorkeeper.zh-CN.yml b/config/locales/doorkeeper.zh-CN.yml
index e74f46ad5..bcaa5d0b8 100644
--- a/config/locales/doorkeeper.zh-CN.yml
+++ b/config/locales/doorkeeper.zh-CN.yml
@@ -122,14 +122,14 @@ zh-CN:
         admin/accounts: 账号管理
         admin/all: 所有管理功能
         admin/reports: 举报管理
-        all: 所有
+        all: 完全访问您的Mastodon账户
         blocks: 屏蔽
         bookmarks: 书签
         conversations: 会话
         crypto: 端到端加密
         favourites: 喜欢
         filters: 过滤器
-        follow: 关系
+        follow: 关注者,隐藏与屏蔽
         follows: 关注
         lists: 列表
         media: 媒体文件
diff --git a/config/locales/doorkeeper.zh-HK.yml b/config/locales/doorkeeper.zh-HK.yml
index 632d51183..6b078d609 100644
--- a/config/locales/doorkeeper.zh-HK.yml
+++ b/config/locales/doorkeeper.zh-HK.yml
@@ -122,14 +122,14 @@ zh-HK:
         admin/accounts: 帳號管理
         admin/all: 所有管理功能
         admin/reports: 檢舉報告管理
-        all: 全部
+        all: 完整存取你的 Mastodon 帳號
         blocks: 封鎖
         bookmarks: 書籤
         conversations: 對話
         crypto: 端到端加密
         favourites: 最愛
         filters: 篩選器
-        follow: 關係
+        follow: 追蹤、靜音及封鎖
         follows: 追蹤
         lists: 名單
         media: 媒體附件
diff --git a/config/locales/doorkeeper.zh-TW.yml b/config/locales/doorkeeper.zh-TW.yml
index edafecf72..6d07717e1 100644
--- a/config/locales/doorkeeper.zh-TW.yml
+++ b/config/locales/doorkeeper.zh-TW.yml
@@ -122,16 +122,16 @@ zh-TW:
         admin/accounts: 帳號管理
         admin/all: 所有管理功能
         admin/reports: 檢舉報告管理
-        all: 全部
+        all: 完整存取您的 Mastodon 帳號
         blocks: 封鎖
         bookmarks: 書籤
         conversations: 對話
         crypto: 端到端加密
         favourites: 最愛
         filters: 過濾器
-        follow: 關係
+        follow: 跟隨、靜音與封鎖
         follows: 跟隨的使用者
-        lists: 名單
+        lists: 列表
         media: 多媒體附加檔案
         mutes: 靜音
         notifications: 通知
@@ -168,12 +168,12 @@ zh-TW:
       push: 接收帳號的推播通知
       read: 讀取您所有的帳號資料
       read:accounts: 檢視帳號資訊
-      read:blocks: 檢視您的封鎖名單
+      read:blocks: 檢視您的封鎖列表
       read:bookmarks: 檢視您的書籤
       read:favourites: 檢視您收藏的最愛
       read:filters: 檢視您的過濾條件
       read:follows: 檢視您跟隨的人
-      read:lists: 檢視您的名單
+      read:lists: 檢視您的列表
       read:mutes: 檢視您靜音的人
       read:notifications: 檢視您的通知
       read:reports: 檢視您的檢舉
@@ -192,4 +192,4 @@ zh-TW:
       write:mutes: 靜音使用者及對話
       write:notifications: 清除您的通知
       write:reports: 檢舉其他人
-      write:statuses: 發布嘟文
+      write:statuses: 發表嘟文
diff --git a/config/locales/el.yml b/config/locales/el.yml
index 00bcdfd64..e5dce13b0 100644
--- a/config/locales/el.yml
+++ b/config/locales/el.yml
@@ -11,67 +11,73 @@ el:
     followers:
       one: Ακόλουθος
       other: Ακόλουθοι
-    following: Ακολουθεί
+    following: Ακολουθείτε
+    instance_actor_flash: Αυτός ο λογαριασμός είναι εικονικός και χρησιμοποιείται για να αντιπροσωπεύει τον ίδιο τον διακομιστή και όχι κάποιον μεμονωμένο χρήστη. Χρησιμοποιείται για ομοσπονδιακούς σκοπούς και δεν πρέπει να ανασταλεί.
     last_active: τελευταία ενεργός/ή
-    link_verified_on: Η κυριότητα αυτού του συνδέσμου ελέγχθηκε στις %{date}
+    link_verified_on: Η ιδιοκτησία αυτού του συνδέσμου ελέγχθηκε στις %{date}
     nothing_here: Δεν υπάρχει τίποτα εδώ!
     pin_errors:
       following: Πρέπει ήδη να ακολουθείς το άτομο που θέλεις να επιδοκιμάσεις
     posts:
-      one: Τουτ
-      other: Τουτ
-    posts_tab_heading: Τουτ
+      one: Ανάρτηση
+      other: Αναρτήσεις
+    posts_tab_heading: Αναρτήσεις
   admin:
     account_actions:
       action: Εκτέλεση ενέργειας
-      title: Εκτέλεση ενέργειας διαχείρισης στο %{acct}
+      title: Εκτέλεση ενέργειας συντονισμού στον %{acct}
     account_moderation_notes:
       create: Άφησε σημείωση
-      created_msg: Επιτυχής δημιουργία σημειώματος μεσολάβησης!
-      destroyed_msg: Επιτυχής καταστροφή σημειώματος μεσολάβησης!
+      created_msg: Επιτυχής δημιουργία σημειώματος συντονισμού!
+      destroyed_msg: Η σημείωση συντονισμού καταστράφηκε επιτυχώς!
     accounts:
-      add_email_domain_block: Εγγραφή τομέα email σε μαύρη λίστα
+      add_email_domain_block: Αποκλεισμός τομέα email
       approve: Έγκριση
       approved_msg: Επιτυχής έγκριση αίτησης εγγραφής του/της %{username}
       are_you_sure: Σίγουρα;
-      avatar: Αβατάρ
+      avatar: Άβαταρ
       by_domain: Τομέας
       change_email:
+        changed_msg: Το email άλλαξε επιτυχώς!
         current_email: Τρέχον email
         label: Αλλαγή email
         new_email: Νέο email
         submit: Αλλαγή email
         title: Αλλαγή email για %{username}
       change_role:
+        changed_msg: Ο ρόλος άλλαξε επιτυχώς!
         label: Αλλαγή ρόλου
         no_role: Κανένας ρόλος
         title: Αλλαγή ρόλου για %{username}
       confirm: Επιβεβαίωση
       confirmed: Επιβεβαιώθηκε
       confirming: Προς επιβεβαίωση
+      custom: Προσαρμοσμένο
       delete: Διαγραφή δεδομένων
       deleted: Διαγραμμένοι
       demote: Υποβίβαση
       destroyed_msg: Τα δεδομένα του/της %{username} εκκρεμούν για άμεση διαγραφή
-      disable: Απενεργοποίηση
+      disable: Πάγωμα
+      disable_sign_in_token_auth: Απενεργοποίηση επαλήθευσης μέσω email
       disable_two_factor_authentication: Απενεργοποίηση 2FA
-      disabled: Απενεργοποιημένο
-      display_name: Όνομα εμφάνισης
+      disabled: Παγωμένος
+      display_name: Εμφανιζόμενο όνομα
       domain: Τομέας
       edit: Επεξεργασία
       email: Email
       email_status: Κατάσταση email
-      enable: Ενεργοποίηση
+      enable: Ξεπάγωμα
+      enable_sign_in_token_auth: Ενεργοποίηση ελέγχου ταυτότητας μέσω e-mail
       enabled: Ενεργοποιημένο
       enabled_msg: Επιτυχές ξεπάγωμα λογαριασμού του/της %{username}
       followers: Ακόλουθοι
       follows: Ακολουθεί
-      header: Επικεφαλίδα
+      header: Κεφαλίδα
       inbox_url: URL εισερχομένων
-      invite_request_text: Λόγοι για εγγραφή
+      invite_request_text: Λόγοι για συμμετοχή
       invited_by: Προσκλήθηκε από
       ip: IP
-      joined: Γράφτηκε
+      joined: Έγινε μέλος
       location:
         all: Όλες
         local: Τοπική
@@ -79,38 +85,43 @@ el:
         title: Τοποθεσία
       login_status: Κατάσταση σύνδεσης
       media_attachments: Συνημμένα πολυμέσα
-      memorialize: Μετατροπή σε νεκρολογία
-      memorialized: Μετατροπή σε αναμνηστικό
-      memorialized_msg: Επιτυχής μετατροπή λογαριασμού του/της %{username} σε αναμνηστικό
+      memorialize: Μετατροπή σε εις μνήμην
+      memorialized: Μετατράπηκε σε εις μνήμην
+      memorialized_msg: Επιτυχής μετατροπή λογαριασμού του/της %{username} σε εις μνήμην
       moderation:
-        active: Ενεργός/ή
-        all: Όλα
+        active: Ενεργός
+        all: Όλοι
+        disabled: Απενεργοποιημένο
         pending: Εκκρεμούν
-        silenced: Περιορισμένοι
+        silenced: Περιορισμένη
         suspended: Σε αναστολή
-        title: Μεσολάβηση
-      moderation_notes: Σημειώσεις μεσολάβησης
+        title: Συντονισμός
+      moderation_notes: Σημειώσεις συντονισμού
       most_recent_activity: Πιο πρόσφατη δραστηριότητα
       most_recent_ip: Πιο πρόσφατη IP
       no_account_selected: Κανείς λογαριασμός δεν ενημερώθηκε αφού κανείς δεν ήταν επιλεγμένος
       no_limits_imposed: Χωρίς όρια
       no_role_assigned: Δεν έχει ανατεθεί ρόλος
-      not_subscribed: Άνευ συνδρομής
-      pending: Εκκρεμεί έγκριση
+      not_subscribed: Δεν έγινε εγγραφή
+      pending: Εκκρεμεί αξιολόγηση
       perform_full_suspension: Αναστολή
+      previous_strikes: Προηγούμενα παραπτώματα
+      previous_strikes_description_html:
+        one: Αυτός ο λογαριασμός έχει <strong>ένα</strong> παράπτωμα.
+        other: Αυτός ο λογαριασμός έχει <strong>%{count}</strong> παραπτώματα.
       promote: Προβίβασε
       protocol: Πρωτόκολλο
       public: Δημόσιο
       push_subscription_expires: Η εγγραφή PuSH λήγει
-      redownload: Ανανέωση αβατάρ
-      redownloaded_msg: Επιτυχής ανανέωη προφίλ του/της %{username} από την πηγή
+      redownload: Ανανέωση άβαταρ
+      redownloaded_msg: Επιτυχής ανανέωση προφίλ του/της %{username} από την πηγή
       reject: Απόρριψη
       rejected_msg: Επιτυχής απόρριψη αίτησης εγγραφής του/της %{username}
       remote_suspension_irreversible: Τα δεδομένα αυτού του λογαριασμού έχουν διαγραφεί αμετάκλητα.
-      remote_suspension_reversible_hint_html: Ο λογαριασμός έχει ανασταλλεί στον server του και τα δεδομένα του θα διαγραφούν πλήρως στις %{date}. Μέχρι τότε, ο απομακρυσμένος server μπορεί να επαναφέρει τον λογαριασμό χωρίς επιπτώσεις. Αν θέλεις να διαγράψεις αμέσως όλα τα δεδομένα του λογαριασμού, μπορείς να το κάνεις παρακάτω.
-      remove_avatar: Απομακρυσμένο αβατάρ
-      remove_header: Αφαίρεση επικεφαλίδας
-      removed_avatar_msg: Επιτυχής αφαίρεση εικόνας προφίλ του/της%{username}
+      remote_suspension_reversible_hint_html: Ο λογαριασμός έχει ανασταλλεί στον διακομιστή του και τα δεδομένα του θα διαγραφούν πλήρως στις %{date}. Μέχρι τότε, ο απομακρυσμένος διακομιστής μπορεί να επαναφέρει τον λογαριασμό χωρίς επιπτώσεις. Αν θέλεις να διαγράψεις αμέσως όλα τα δεδομένα του λογαριασμού, μπορείς να το κάνεις παρακάτω.
+      remove_avatar: Αφαίρεση άβαταρ
+      remove_header: Αφαίρεση κεφαλίδας
+      removed_avatar_msg: Επιτυχής αφαίρεση εικόνας προφίλ του/της %{username}
       removed_header_msg: Επιτυχής αφαίρεση εικόνας κεφαλίδας του/της %{username}
       resend_confirmation:
         already_confirmed: Ήδη επιβεβαιωμένος χρήστης
@@ -123,35 +134,40 @@ el:
       search: Αναζήτηση
       search_same_email_domain: Άλλοι χρήστες με τον ίδιο τομέα e-mail
       search_same_ip: Υπόλοιποι χρήστες με την ίδια διεύθυνση IP
+      security: Ασφάλεια
+      security_measures:
+        only_password: Μόνο κωδικός πρόσβασης
+        password_and_2fa: Κωδικός πρόσβασης και 2FA
       sensitive: Ευαίσθητο
-      sensitized: σήμανση ως ευαίσθητο
+      sensitized: Επισημάνθηκε ως ευαίσθητος
       shared_inbox_url: URL κοινόχρηστων εισερχομένων
       show:
-        created_reports: Αναφορές από αυτόν το λογαριασμό
-        targeted_reports: Αναφορές για αυτόν το λογαριασμό
-      silence: Αποσιώπησε
-      silenced: Αποσιωπημένοι
-      statuses: Καταστάσεις
+        created_reports: Αναφορές από τον ίδιο
+        targeted_reports: Αναφορές από άλλους
+      silence: Περιορισμός
+      silenced: Περιορισμένος
+      statuses: Αναρτήσεις
+      strikes: Προηγούμενα παραπτώματα
       subscribe: Εγγραφή
       suspend: Αναστολή
       suspended: Σε αναστολή
       suspension_irreversible: Τα δεδομένα αυτού του λογαριασμού έχουν διαγραφεί οριστικά. Μπορείς να άρεις την αναστολή του λογαριασμού για να μπορέσει να χρησιμοποιηθεί αλλά αυτό δεν θα επαναφέρει όσα δεδομένα είχε προηγουμένως.
       suspension_reversible_hint_html: Ο λογαριασμός έχει ανασταλλεί και τα δεδομένα του θα διαγραφούν πλήρως στις %{date}. Μέχρι τότε ο λογαριασμός μπορεί να επανέλθει κανονικά. Αν θέλεις να διαγράψεις όλα τα δεδομένα του λογαριασμού, μπορείς να το κάνεις παρακάτω.
       title: Λογαριασμοί
-      unblock_email: Ξεμπλοκάρισμα διεύθυνσης email
-      unblocked_email_msg: Επιτυχής κατάργηση αποκλεισμού διεύθυνσης ηλεκτρονικού ταχυδρομείου %{username}
+      unblock_email: Άρση αποκλεισμού διεύθυνσης email
+      unblocked_email_msg: Επιτυχής άρση αποκλεισμού διεύθυνσης email %{username}
       unconfirmed_email: Ανεπιβεβαίωτο email
-      undo_sensitized: Αναίρεση ευαίσθητου
-      undo_silenced: Αναίρεση αποσιώπησης
+      undo_sensitized: Άρση ευαίσθητου
+      undo_silenced: Άρση περιορισμού
       undo_suspension: Αναίρεση παύσης
       unsilenced_msg: Επιτυχής άρση περιορισμών λογαριασμού του/της %{username}
       unsubscribe: Κατάργηση εγγραφής
       unsuspended_msg: Επιτυχής άρση αναστολής λογαριασμού του/της %{username}
       username: Όνομα χρήστη
-      view_domain: Προβολή περίληψης για τομέα
+      view_domain: Προβολή περίληψης τομέα
       warn: Προειδοποίηση
       web: Διαδίκτυο
-      whitelisted: Εγκεκριμένοι
+      whitelisted: Εγκεκριμένοι για συναλλαγές
     action_logs:
       action_types:
         approve_appeal: Έγκριση Έφεσης
@@ -162,66 +178,115 @@ el:
         confirm_user: Επιβεβαίωση Χρήστη
         create_account_warning: Δημιουργία Προειδοποίησης
         create_announcement: Δημιουργία Ανακοίνωσης
+        create_canonical_email_block: Δημιουργία αποκλεισμού e-mail
         create_custom_emoji: Δημιουργία Προσαρμοσμένου Emoji
-        create_domain_allow: Δημιουργία Επιτρεπτού Τομέα
-        create_domain_block: Δημιουργία Αποκλεισμένου Τομέα
-        create_email_domain_block: Δημουργία Αποκλεισμένου Τομέα email
+        create_domain_allow: Δημιουργία ΑποδεκτούΤομέα
+        create_domain_block: Δημιουργία Αποκλεισμού Τομέα
+        create_email_domain_block: Δημουργία Αποκλεισμού Τομέα email
         create_ip_block: Δημιουργία κανόνα IP
+        create_unavailable_domain: Δημιουργία Μη Διαθέσιμου Τομέα
         create_user_role: Δημιουργία ρόλου
         demote_user: Υποβιβασμός Χρήστη
         destroy_announcement: Διαγραφή Ανακοίνωσης
+        destroy_canonical_email_block: Διαγραφή Αποκλεισμού email
         destroy_custom_emoji: Διαγραφή Προσαρμοσμένου Emoji
-        destroy_domain_allow: Διαγραφή Επιτρεπτού Τομέα
+        destroy_domain_allow: Διαγραφή Αποδεκτού Τομέα
         destroy_domain_block: Διαγραφή Αποκλεισμού Τομέα
         destroy_email_domain_block: Διαγραφή Αποκλεισμένου Τομέα email
         destroy_instance: Εκκαθάριση Τομέα
         destroy_ip_block: Διαγραφή κανόνα IP
         destroy_status: Διαγραφή Κατάστασης
+        destroy_unavailable_domain: Διαγραφή Μη Διαθέσιμου Τομέα
+        destroy_user_role: Καταστροφή Ρόλου
         disable_2fa_user: Απενεργοποίηση 2FA
         disable_custom_emoji: Απενεργοποίηση Προσαρμοσμένων Emoji
+        disable_sign_in_token_auth_user: Απενεργοποίηση Ελέγχου Ταυτότητας Μέσω E-mail για το Χρήστη
         disable_user: Απενεργοποίηση Χρήστη
         enable_custom_emoji: Ενεργοποίηση Προσαρμοσμένων Emoji
+        enable_sign_in_token_auth_user: Ενεργοποίηση Ελέγχου Ταυτότητας Μέσω E-mail για το Χρήστη
         enable_user: Ενεργοποίηση Χρήστη
-        memorialize_account: Μετατροπή Λογαριασμού σε Αναμνηστικό
+        memorialize_account: Μετατροπή Λογαριασμού σε Εις Μνήμην
         promote_user: Προαγωγή Χρήστη
         reject_appeal: Απόρριψη Έφεσης
         reject_user: Απόρριψη Χρήστη
-        remove_avatar_user: Αφαίρεση Avatar
-        reopen_report: Ξανάνοιγμα Καταγγελίας
+        remove_avatar_user: Αφαίρεση Άβαταρ
+        reopen_report: Ξανάνοιγμα Ανααφοράς
         resend_user: Επαναποστολή του email επιβεβαίωσης
         reset_password_user: Επαναφορά Συνθηματικού
-        resolve_report: Επίλυση Καταγγελίας
-        sensitive_account: Σήμανση των πολυμέσων στον λογαριασμό σας ως ευαίσθητων
-        silence_account: Σίγαση Λογαριασμού
+        resolve_report: Επίλυση Αναφοράς
+        sensitive_account: Ευαίσθητος Λογιαριασμός
+        silence_account: Περιορισμός Λογαριασμού
         suspend_account: Αναστολή Λογαριασμού
-        unassigned_report: Αποδέσμευση Καταγγελίας
-        unblock_email_account: Ξεμπλοκάρισμα διεύθυνσης email
-        unsensitive_account: Αφαίρεση σήμανσης των πολυμέσων στον λογαριασμό σας ως ευαίσθητων
-        unsilence_account: Άρση Σίγασης Λογαριασμού
+        unassigned_report: Αποδέσμευση Αναφοράς
+        unblock_email_account: Άρση αποκλεισμού διεύθυνσης email
+        unsensitive_account: Άρση επισήμανσης Λογαριασμού ως Ευαίσθητο
+        unsilence_account: Άρση Περιορισμού Λογαριασμού
         unsuspend_account: Άρση Αναστολής Λογαριασμού
         update_announcement: Ενημέρωση Ανακοίνωσης
         update_custom_emoji: Ενημέρωση Προσαρμοσμένου Emoji
-        update_status: Ενημέρωση Κατάστασης
+        update_domain_block: Ενημέρωση Αποκλεισμού Τομέα
+        update_ip_block: Ενημέρωση κανόνα IP
+        update_status: Ενημέρωση Ανάρτησης
         update_user_role: Ενημέρωση ρόλου
       actions:
-        approve_user_html: "%{name} εγκρίθηκε εγγραφή από %{target}"
+        approve_appeal_html: Ο/Η %{name} ενέκρινε την ένσταση της απόφασης των συντονιστών από %{target}
+        approve_user_html: ο/η %{name} ενέκρινε την εγγραφή του %{target}
+        assigned_to_self_report_html: Ο/Η %{name} ανάθεσε την αναφορά %{target} στον εαυτό του/της
+        change_email_user_html: Ο/Η %{name} άλλαξε τη διεύθυνση email του χρήστη %{target}
+        change_role_user_html: Ο/Η %{name} άλλαξε ρόλο του/της %{target}
+        confirm_user_html: Ο/Η %{name} επιβεβαίωσε τη διεύθυνση email του χρήστη %{target}
+        create_account_warning_html: Ο/Η %{name} έστειλε προειδοποίηση προς %{target}
+        create_announcement_html: Ο/Η %{name} δημιούργησε νέα ανακοίνωση %{target}
+        create_canonical_email_block_html: Ο/Η %{name} απέκλεισε e-mail με το hash %{target}
+        create_custom_emoji_html: Ο/Η %{name} ανέβασε νέο emoji %{target}
+        create_domain_allow_html: Ο/Η %{name} επέτρεψε την συναλλαγή με τον τομέα %{target}
+        create_domain_block_html: Ο/Η %{name} απέκλεισε τον τομέα %{target}
+        create_email_domain_block_html: Ο/Η %{name} απέκλεισε τον τομέα email %{target}
+        create_ip_block_html: Ο/Η %{name} δημιούργησε κανόνα για την IP %{target}
+        create_unavailable_domain_html: Ο/Η %{name} σταμάτησε να τροφοδοτεί τον τομέα %{target}
+        create_user_role_html: Ο/Η %{name} δημιούργησε ρόλο %{target}
+        demote_user_html: Ο/Η %{name} υποβίβασε τον χρήστη %{target}
+        destroy_announcement_html: Ο/Η %{name} διέγραψε την ανακοίνωση %{target}
+        destroy_canonical_email_block_html: Ο/Η %{name} επέτρεψε email με το hash %{target}
+        destroy_custom_emoji_html: Ο/Η %{name} διέγραψε το emoji %{target}
+        destroy_domain_allow_html: Ο/Η %{name} αφαίρεσε τον τομέα %{target} από τη λίστα εγκρίσεων
+        destroy_domain_block_html: Ο/Η %{name} επέτρεψε τον τομέα %{target}
         destroy_email_domain_block_html: Ο/Η %{name} ξεμπλόκαρε το email domain %{target}
         destroy_instance_html: Ο/Η %{name} εκκαθάρισε τον τομέα %{target}
         destroy_ip_block_html: Ο/Η %{name} διέγραψε τον κανόνα για την IP %{target}
         destroy_status_html: Ο/Η %{name} αφαίρεσε την ανάρτηση του/της %{target}
         destroy_unavailable_domain_html: Ο/Η %{name} ξανάρχισε να τροφοδοτεί το domain %{target}
         destroy_user_role_html: Ο/Η %{name} διέγραψε τον ρόλο του %{target}
-        disable_2fa_user_html: Ο/Η %{name} απενεργοποίησε το two factor requirement για τον χρήστη %{target}
+        disable_2fa_user_html: Ο/Η %{name} απενεργοποίησε την απαίτηση για ταυτοποίηση δύο παραγόντων για τον χρήστη %{target}
         disable_custom_emoji_html: Ο/Η %{name} απενεργοποίησε το emoji %{target}
-        disable_sign_in_token_auth_user_html: Ο/Η %{name} απενεργοποίησε την πιστοποίηση e-mail token του %{target}
+        disable_sign_in_token_auth_user_html: Ο/Η %{name} απενεργοποίησε την ταυτοποίηση χαρακτηριστικού μέσω e-mail του %{target}
         disable_user_html: Ο/Η %{name} απενεργοποίησε τη σύνδεση για τον χρήστη %{target}
         enable_custom_emoji_html: Ο/Η %{name} ενεργοποίησε το emoji %{target}
-        enable_sign_in_token_auth_user_html: Ο/Η %{name} ενεργοποίησε την πιστοποίηση e-mail token του %{target}
+        enable_sign_in_token_auth_user_html: Ο/Η %{name} ενεργοποίησε την ταυτοποίηση χαρακτηριστικού μέσω e-mail του %{target}
         enable_user_html: Ο/Η %{name} ενεργοποίησε τη σύνδεση για τον χρήστη %{target}
-        memorialize_account_html: O/H %{name} μετέτρεψε τον λογαριασμό του %{target} σε σελίδα μνήμης
+        memorialize_account_html: O/H %{name} μετέτρεψε τον λογαριασμό του %{target} σε σελίδα εις μνήμην
         promote_user_html: Ο/Η %{name} προβίβασε το χρήστη %{target}
-        reject_user_html: "%{name} απορρίφθηκε εγγραφή από %{target}"
-        unblock_email_account_html: "%{name} ξεμπλόκαρε τη διεύθυνση ηλεκτρονικού ταχυδρομείου του %{target}"
+        reject_appeal_html: Ο/Η %{name} απέρριψε την ένσταση της απόφασης των συντονιστών από %{target}
+        reject_user_html: ο/η %{name} απέρριψε την εγγραφή από %{target}
+        remove_avatar_user_html: ο/η %{name} αφαίρεσε το άβαταρ του/της %{target}
+        reopen_report_html: Ο/Η %{name} ξανάνοιξε την αναφορά %{target}
+        resend_user_html: Ο/Η %{name} έστειλε ξανά e-mail επιβεβαίωσης για τον/την %{target}
+        reset_password_user_html: Ο/Η %{name} επανέφερε το συνθηματικό του χρήστη %{target}
+        resolve_report_html: Ο/Η %{name} επέλυσε την αναφορά %{target}
+        sensitive_account_html: Ο/Η %{name} επισήμανε τα πολυμέσα του/της %{target} ως ευαίσθητα
+        silence_account_html: Ο/Η %{name} περιόρισε τον λογαριασμό του/της %{target}
+        suspend_account_html: Ο/Η %{name} ανέστειλε τον λογαριασμό του/της %{target}
+        unassigned_report_html: Ο/Η %{name} αποδέσμευσε την αναφορά %{target}
+        unblock_email_account_html: "%{name} έκανε άρση αποκλεισμού στη διεύθυνση email του %{target}"
+        unsensitive_account_html: Ο/Η %{name} επισήμανε τα πολυμέσα του/της %{target} ως μη ευαίσθητα
+        unsilence_account_html: Ο/Η %{name} αφαίρεσε το περιορισμό του λογαριασμού του/της %{target}
+        unsuspend_account_html: Ο/Η %{name} επανέφερε τον λογαριασμό του/της %{target}
+        update_announcement_html: Ο/Η %{name} ενημέρωσε την ανακοίνωση %{target}
+        update_custom_emoji_html: Ο/Η %{name} ενημέρωσε το emoji %{target}
+        update_domain_block_html: Ο/Η %{name} ενημέρωσε τον αποκλεισμό τομέα για %{target}
+        update_ip_block_html: Ο/Η %{name} άλλαξε τον κανόνα για την IP %{target}
+        update_status_html: Ο/Η %{name} ενημέρωσε την ανάρτηση του/της %{target}
+        update_user_role_html: Ο/Η %{name} άλλαξε τον ρόλο %{target}
       deleted_account: διαγραμμένος λογαριασμός
       empty: Δεν βρέθηκαν αρχεία καταγραφής.
       filter_by_action: Φιλτράρισμα ανά ενέργεια
@@ -245,12 +310,12 @@ el:
       unpublished_msg: Επιτυχής ακύρωση δημοσίευσης ανακοίνωσης!
       updated_msg: Επιτυχής ενημέρωση ανακοίνωσης!
     custom_emojis:
-      assign_category: Κατηγορία
+      assign_category: Ανάθεση κατηγορίας
       by_domain: Τομέας
       copied_msg: Επιτυχής δημιουργία τοπικού αντίγραφου του emoji
       copy: Αντιγραφή
       copy_failed_msg: Αδυναμία δημιουργίας τοπικού αντίγραφου αυτού του emoji
-      create_new_category: Νέα κατηγορία
+      create_new_category: Δημιούργησε νέα κατηγορία
       created_msg: Επιτυχής δημιουργία του emoji!
       delete: Διαγραφή
       destroyed_msg: Επιτυχής καταστροφή του emojo!
@@ -262,110 +327,203 @@ el:
       enabled: Ενεργοποιημένα
       enabled_msg: Επιτυχής ενεργοποίηση αυτού του emoji
       image_hint: PNG ή GIF έως %{size}
-      list: Εμφάνιση
+      list: Παράθεση
       listed: Αναφερθέντα
       new:
         title: Προσθήκη νέου προσαρμοσμένου emoji
+      no_emoji_selected: Δεν άλλαξαν τα emoji καθώς δεν επιλέχθηκε κανένα
       not_permitted: Δεν επιτρέπεται να κάνετε αυτή την λειτουργία
       overwrite: Αντικατάσταση
       shortcode: Σύντομος κωδικός
       shortcode_hint: Τουλάχιστον 2 χαρακτήρες, μόνο αλφαριθμητικοί και κάτω παύλες
       title: Προσαρμοσμένα emoji
       uncategorized: Χωρίς κατηγορία
-      unlist: Απόκρυψη
+      unlist: Αφαίρεση από λίστα
       unlisted: Μη καταχωρημένα
       update_failed_msg: Αδυναμία ενημέρωσης του emoji
       updated_msg: Επιτυχής ενημέρωση του emoji!
-      upload: Ανέβασμα
+      upload: Μεταμόρφωση
     dashboard:
       active_users: ενεργοί χρήστες
+      interactions: αλληλεπιδράσεις
+      media_storage: Χώρος πολυμέσων
       new_users: νέοι χρήστες
+      opened_reports: ανοιγμένες αναφορές
+      pending_appeals_html:
+        one: "<strong>%{count}</strong> έφεση σε εκκρεμότητα"
+        other: "<strong>%{count}</strong> εφέσεις σε εκκρεμότητα"
+      pending_reports_html:
+        one: "<strong>%{count}</strong> εκκρεμής αναφορά"
+        other: "<strong>%{count}</strong> εκκρεμείς αναφορές"
+      pending_tags_html:
+        one: "<strong>%{count}</strong> εκκρεμής ετικέτα"
+        other: "<strong>%{count}</strong> εκκρεμείς ετικέτες"
+      pending_users_html:
+        one: "<strong>%{count}</strong> χρήστης σε εκκρεμότητα"
+        other: "<strong>%{count}</strong> χρήστες σε εκκρεμότητα"
+      resolved_reports: επιλυμμένες αναφορές
       software: Λογισμικό
+      sources: Πηγές εγγραφής
       space: Κατανάλωση χώρου
       title: Ταμπλό
+      top_languages: Κορυφαίες ενεργές γλώσσες
+      top_servers: Κορυφαίοι ενεργοί διακομιστές
+      website: Ιστοσελίδα
     disputes:
       appeals:
+        empty: Καμία ένσταση.
         title: Εφέσεις
     domain_allows:
       add_new: Έγκριση τομέα
-      created_msg: Ο τομέας εγκρίθηκε με επιτυχία
-      destroyed_msg: Ο τομέας δεν είναι πια εγκεκριμένος
+      created_msg: Ο τομέας εγκρίθηκε με επιτυχία για συναλλαγές
+      destroyed_msg: Ο τομέας δεν είναι πια εγκεκριμένος για συναλλαγές
       export: Εξαγωγή
       import: Εισαγωγή
-      undo: Αφαίρεση έγκρισης
+      undo: Αφαίρεση συναλλαγής με τον τομέα
     domain_blocks:
-      add_new: Προσθήκη νέου
+      add_new: Προσθήκη νέου αποκλεισμού τομέα
       created_msg: Ο αποκλεισμός τομέα είναι υπό επεξεργασία
-      destroyed_msg: Ο αποκλεισμός τομέα άρθηκε
+      destroyed_msg: Ο αποκλεισμός τομέα αναιρέθηκε
       domain: Τομέας
-      edit: Επεξεργασία αποκλεισμένου τομέα
+      edit: Επεξεργασία αποκλεισμού τομέα
       existing_domain_block: Έχετε ήδη επιβάλει αυστηρότερα όρια στο %{name}.
       existing_domain_block_html: Έχεις ήδη επιβάλλει αυστηρότερους περιορισμούς στο %{name}, πρώτα θα πρέπει να τους <a href="%{unblock_url}">αναιρέσεις</a>.
       export: Εξαγωγή
       import: Εισαγωγή
       new:
         create: Δημιουργία αποκλεισμού
-        hint: Ο αποκλεισμός τομέα δεν θα αποτρέψει νέες καταχωρίσεις λογαριασμών στην βάση δεδομένων, αλλά θα εφαρμόσει αναδρομικά και αυτόματα συγκεκριμένες πολιτικές μεσολάβησης σε αυτούς τους λογαριασμούς.
+        hint: Ο αποκλεισμός τομέα δεν θα αποτρέψει νέες καταχωρίσεις λογαριασμών στην βάση δεδομένων, αλλά θα εφαρμόσει αναδρομικά και αυτόματα συγκεκριμένες πολιτικές συντονισμού σε αυτούς τους λογαριασμούς.
         severity:
+          desc_html: Ο <strong>περιορισμός</strong> θα κάνει αόρατες τις ανάρτησεις ενός λογαριασμού σε όσους δεν τον ακολουθούν. Η <strong>αναστολή</strong> θα αφαιρέσει όλο το περιεχόμενο, τα πολυμέσα και τα στοιχεία προφίλ ενός λογαριασμού. Χρησιμοποίησε το <strong>κανένα</strong> αν θέλεις απλά να απορρίψεις τα αρχεία πολυμέσων.
           noop: Κανένα
           silence: Περιορισμός
           suspend: Αναστολή
         title: Αποκλεισμός νέου τομέα
+      no_domain_block_selected: Δεν άλλαξαν οι αποκλεισμοί τομέα καθώς δεν επιλέχθηκε κανένας
+      not_permitted: Δεν επιτρπέπεται να εκτελέσετε αυτήν την ενέργεια
+      obfuscate: Θόλωμα ονόματος τομέα
+      obfuscate_hint: Μερικό θόλωμα του ονόματος τομέα στη λίστα, εάν η ανακοίνωση της λίστας των περιορισμών τομέα είναι ενεργή
       private_comment: Ιδιωτικό σχόλιο
-      private_comment_hint: Σχόλιο για τον περιορισμό αυτού του τομέα για εσωτερική χρήση από τους διαχειριστές.
+      private_comment_hint: Σχόλιο για τον περιορισμό αυτού του τομέα για εσωτερική χρήση από τους συντονιστές.
       public_comment: Δημόσιο σχόλιο
-      public_comment_hint: Σχόλιο σχετικά με τον περιορισμό αυτού του τομέα προς το κοινό, αν η λειτουργία δημοσιοποίησης των περιορισμένων τομέων είναι ενεργοποιημένη.
-      reject_media: Απόρριψη πολυμέσων
+      public_comment_hint: Σχόλιο σχετικά με τον περιορισμό αυτού του τομέα προς το κοινό, αν η λειτουργία διαφήμισης των περιορισμένων τομέων είναι ενεργοποιημένη.
+      reject_media: Απόρριψη αρχείων πολυμέσων
       reject_media_hint: Αφαιρεί τα τοπικά αποθηκευμένα αρχεία πολυμέσων και αποτρέπει τη λήψη άλλων στο μέλλον. Δεν έχει σημασία για τις αναστολές
-      reject_reports: Απόρριψη καταγγελιών
-      reject_reports_hint: Αγνόηση όσων καταγγελιών προέρχονται από αυτό τον τομέα. Δεν σχετίζεται με τις παύσεις
-      undo: Αναίρεση
+      reject_reports: Απόρριψη αναφορών
+      reject_reports_hint: Αγνόησε όσων αναφορών που προέρχονται από αυτό τον τομέα. Δεν σχετίζεται με τις παύσεις
+      undo: Αναίρεση αποκλεισμού τομέα
       view: Εμφάνιση αποκλεισμού τομέα
     email_domain_blocks:
       add_new: Πρόσθεση νέου
       attempts_over_week:
         one: "%{count} προσπάθεια την τελευταία εβδομάδα"
         other: "%{count} προσπάθειες εγγραφής την τελευταία εβδομάδα"
-      created_msg: Επιτυχής πρόσθεση email τομέα σε μαύρη λίστα
+      created_msg: Επιτυχής αποκλεισμού email τομέα
       delete: Διαγραφή
       dns:
         types:
           mx: Εγγραφή MX
       domain: Τομέας
       new:
-        create: Πρόσθεση τομέα
-        title: Νέα εγγραφή email στη μαύρη λίστα
-      title: Μαύρη λίστα email
+        create: Προσθήκη τομέα
+        resolve: Επίλυση τομέα
+        title: Αποκλεισμός νέου τομέα email
+      no_email_domain_block_selected: Δεν άλλαξαν οι αποκλεισμοί τομέα καθώς δεν επιλέχθηκε κανένας
+      not_permitted: Δεν επιτρέπεται
+      resolved_dns_records_hint_html: Το όνομα τομέα επιλύεται στους ακόλουθους τομείς MX, οι οποίοι είναι τελικά υπεύθυνοι για την αποδοχή των e-mail. Αποκλείοντας έναν τομέα MX θα μπλοκάρει τις εγγραφές από οποιαδήποτε διεύθυνση email που χρησιμοποιεί τον ίδιο τομέα MX, ακόμη και αν το ορατό όνομα τομέα είναι διαφορετικό. <strong>Προσέξτε να μην αποκλείσετε τους μεγάλους παρόχους ηλεκτρονικού ταχυδρομείου.</strong>
+      resolved_through_html: Επιλύθηκε μέσω %{domain}
+      title: Αποκλεισμένοι τομείς email
     export_domain_allows:
+      new:
+        title: Εισαγωγή επιτρεπόμενων τομέων
       no_file: Δεν επιλέχθηκε αρχείο
     export_domain_blocks:
+      import:
+        description_html: Πρόκειται να εισαγάγετε μια λίστα με αποκλεισμένους τομείς. Παρακαλώ ελέγξτε τη λίστα πολύ προσεκτικά, ειδικά αν δεν έχετε συντάξει τη λίστα μόνοι σας.
+        existing_relationships_warning: Υπάρχουσες σχέσεις ακολούθησης
+        private_comment_description_html: 'Για να σας βοηθήσουμε να παρακολουθείτε από πού προέρχονται οι εισαγόμενοι αποκλεισμοί, οι εισαγόμενοι αποκλεισμοί θα δημιουργηθούν με το ακόλουθο ιδιωτικό σχόλιο: <q>%{comment}</q>'
+        private_comment_template: Εισήχθη από %{source} στις %{date}
+        title: Εισαγωγή αποκλεισμένων τομέων
+      invalid_domain_block: 'Ένας ή περισσότεροι αποκλεισμοί τομέα παραλείφθηκαν λόγω των ακόλουθων σφαλμάτων: %{error}'
+      new:
+        title: Εισαγωγή αποκλεισμένων τομέων
       no_file: Δεν επιλέχθηκε αρχείο
     follow_recommendations:
+      description_html: "<strong>Ακολουθώντας συστάσεις βοηθάει τους νέους χρήστες να βρουν γρήγορα ενδιαφέρον περιεχόμενο</strong>. Όταν ένας χρήστης δεν έχει αλληλεπιδράσει με άλλους αρκετά για να διαμορφώσει εξατομικευμένες συστάσεις, συνιστώνται αυτοί οι λογαριασμοί. Υπολογίζονται εκ νέου σε καθημερινή βάση από ένα σύνολο λογαριασμών με τις υψηλότερες πρόσφατες αλληλεπιδράσεις και μεγαλύτερο αριθμό τοπικών ακόλουθων για μια δεδομένη γλώσσα."
       language: Για τη γλώσσα
       status: Κατάσταση
+      suppress: Καταστολή της πρότασης ακολούθησης
+      suppressed: Κατασταλμένο
+      title: Ακολούθησε τις προτάσεις
+      unsuppress: Επαναφορά των συστάσεων ακολούθησης
     instances:
+      availability:
+        description_html:
+          one: Εάν η παράδοση στον τομέα αποτύχει για <strong>%{count} ημέρα</strong>, δεν θα γίνουν περαιτέρω προσπάθειες παράδοσης εκτός αν μια παράδοση <em>από</em> τον τομέα ληφθεί.
+          other: Εάν η παράδοση στον τομέα αποτύχει για <strong>%{count} διαφορετικές ημέρες</strong>, δεν θα γίνουν περαιτέρω προσπάθειες παράδοσης εκτός αν μια παράδοση <em>από</em> τον τομέα ληφθεί.
+        failure_threshold_reached: Το όριο αποτυχίας επετεύχθη στις %{date}.
+        failures_recorded:
+          one: Αποτυχία προσπάθειας την %{count} ημέρα.
+          other: Αποτυχημένες προσπάθειες σε %{count} διαφορετικές ημέρες.
+        no_failures_recorded: Καμία καταγεγραμμένη αποτυχία.
+        title: Διαθεσιμότητα
+        warning: Η τελευταία προσπάθεια σύνδεσης σε αυτόν τον διακομιστή απέτυχε
+      back_to_all: Όλα
+      back_to_limited: Περιορισμένα
+      back_to_warning: Προειδοποίηση
       by_domain: Τομέας
-      confirm_purge: Είστε βέβαιοι ότι θέλετε να διαγράψετε μόνιμα τα δεδομένα από αυτόν τον τομέα;
+      confirm_purge: Είσαι βέβαιος ότι θες να διαγράψεις μόνιμα τα δεδομένα από αυτόν τον τομέα;
+      content_policies:
+        comment: Εσωτερική σημείωση
+        description_html: Μπορείς να ορίσεις τις πολιτικές περιεχομένου που θα εφαρμόζονται σε όλους τους λογαριασμούς από αυτόν τον τομέα και οποιονδήποτε από τους υποτομείς του.
+        limited_federation_mode_description_html: Μπορείτε να επιλέξετε αν θα επιτρέψετε τις συναλλαγές με αυτόν τον τομέα.
+        policies:
+          reject_media: Απόρριψη πολυμέσων
+          reject_reports: Απόρριψη αναφορών
+          silence: Περιορισμός
+          suspend: Αναστολή
+        policy: Πολιτική
+        reason: Δημόσιος λόγος
+        title: Πολιτικές περιεχομένου
       dashboard:
+        instance_accounts_dimension: Λογαριασμοί με τους περισσότερους ακόλουθους
+        instance_accounts_measure: αποθηκευμένοι λογαριασμοί
+        instance_followers_measure: οι ακόλουθοί μας εκεί
+        instance_follows_measure: οι ακόλουθοί τους εδώ
         instance_languages_dimension: Κορυφαίες γλώσσες
+        instance_media_attachments_measure: αποθηκευμένα συνημμένα πολυμέσων
+        instance_reports_measure: αναφορές γι' αυτές
+        instance_statuses_measure: αποθηκευμένες αναρτήσεις
       delivery:
-        failing: Αποτυγχάνει
+        all: Όλες
+        clear: Εκκαθάριση σφαλμάτων παράδοσης
+        failing: Αποτυχημένες
+        restart: Επανεκκίνηση παράδοσης
+        stop: Διακοπή παράδοσης
+        unavailable: Μη διαθέσιμο
       delivery_available: Διαθέσιμη παράδοση
+      delivery_error_days: Ημέρες σφάλματος παράδοσης
+      delivery_error_hint: Εάν η παράδοση δεν είναι δυνατή για %{count} ημέρες, θα επισημανθεί αυτόματα ως μη παραδόσιμη.
       destroyed_msg: Τα δεδομένα από το %{domain} βρίσκονται σε αναμονή για επικείμενη διαγραφή.
       empty: Δεν βρέθηκαν τομείς.
+      known_accounts:
+        one: "%{count} γνωστός λογαριασμός"
+        other: "%{count} γνωστοί λογαριασμοί"
       moderation:
         all: Όλα
         limited: Περιορισμένα
-        title: Διαμεσολάβηση
+        title: Συντονισμός
       private_comment: Ιδιωτικό σχόλιο
       public_comment: Δημόσιο σχόλιο
       purge: Εκκαθάριση
-      title: Γνωστοί κόμβοι
-      total_blocked_by_us: Μπλοκάρονται από εμάς
+      purge_description_html: Εάν πιστεύεις ότι αυτός ο τομέας είναι πλήρως εκτός σύνδεσης, μπορείς να διαγράψεις όλες τις καταχωρήσεις λογαριασμών και τα σχετικά δεδομένα από αυτόν τον τομέα από τον αποθηκευτικό σου χώρο. Αυτό μπορεί να διαρκέσει λίγη ώρα.
+      title: Συναλλαγές
+      total_blocked_by_us: Αποκλεισμένοι από εμάς
       total_followed_by_them: Ακολουθούνται από εκείνους
       total_followed_by_us: Ακολουθούνται από εμάς
-      total_reported: Καταγγελίες προς εκείνους
+      total_reported: Αναφορές προς εκείνους
       total_storage: Συνημμένα πολυμέσα
+      totals_time_period_hint_html: Τα σύνολα που εμφανίζονται παρακάτω περιλαμβάνουν στοιχεία από την αρχή.
     invites:
       deactivate_all: Απενεργοποίηση όλων
       filter:
@@ -387,28 +545,29 @@ el:
         '94670856': 3 χρόνια
       new:
         title: Δημιουργία νέου κανόνα IP
+      no_ip_block_selected: Δεν άλλαξαν οι κανόνες IP καθώς κανένας δεν επιλέχθηκε
       title: Κανόνες IP
     relationships:
-      title: Σχέσεις %{acct}
+      title: Σχέσεις του %{acct}
     relays:
-      add_new: Πρόσθεσε νέο ανταποκριτή (relay)
+      add_new: Προσθήκη νέου ανταποκριτή
       delete: Διαγραφή
-      description_html: Ο <strong>ομοσπονδιακός ανταποκριτής</strong> είναι ένας ενδιάμεσος εξυπηρετητής (server) που ανταλλάσσει μεγάλους όγκους δημόσιων τουτ μεταξύ εξυπηρετητών που εγγράφονται και δημοσιεύουν σε αυτόν. <strong>Βοηθάει μικρούς και μεσαίους εξυπηρετητές να ανακαλύψουν περιεχόμενο στο fediverse</strong>, που υπό άλλες συνθήκες θα χρειαζόταν κάποιους τοπικούς χρήστες που να ακολουθούν χρήστες σε απομακρυσμένους εξυπηρετητές.
+      description_html: Ο <strong>ανταποκριτής συναλλαγών</strong> είναι ένας ενδιάμεσος διακομιστής που ανταλλάσσει μεγάλους όγκους δημόσιων αναρτήσεων μεταξύ διακομιστών που εγγράφονται και δημοσιεύουν σε αυτόν. <strong>Βοηθάει μικρούς και μεσαίους να ανακαλύψουν περιεχόμενο στο fediverse</strong>, που υπό άλλες συνθήκες θα χρειαζόταν κάποιους τοπικούς χρήστες που να ακολουθούν χρήστες σε απομακρυσμένους διακομιστές.
       disable: Απενεργοποίηση
       disabled: Απενεργοποιημένο
       enable: Ενεργοποίηση
-      enable_hint: Μόλις ενεργοποιηθεί, ο εξυπηρετητής (server) σου θα εγγραφεί σε όλα τα δημόσια τουτ αυτού του ανταποκριτή (relay) και θα αρχίσει να προωθεί τα δικά του δημόσια τουτ σε αυτόν.
+      enable_hint: Μόλις ενεργοποιηθεί, ο διακομιστής σου θα εγγραφεί σε όλα τις δημόσιες αναρτήσεις αυτού του ανταποκριτή και θα αρχίσει να προωθεί τις δικές του δημόσιες αναρτήσεις σε αυτόν.
       enabled: Ενεργοποιημένο
       inbox_url: URL ανταποκριτή
       pending: Περιμένοντας την έγκριση του ανταποκριτή
       save_and_enable: Αποθήκευση και ενεργοποίηση
       setup: Όρισε μια σύνδεση ανταπόκρισης
-      signatures_not_enabled: Οι ανταποκριτές δεν θα λειτουργούν σωστά όσο είναι ενεργοποιημένες οι επιλογές ασφαλούς λειτουργίας ή επιτρεπόμενων συνδέσεων
+      signatures_not_enabled: Οι ανταποκριτές δεν θα λειτουργούν σωστά όσο είναι ενεργοποιημένες οι επιλογές ασφαλούς λειτουργίας ή περιορισμένων συναλλαγών
       status: Κατάσταση
       title: Ανταποκριτές
     report_notes:
-      created_msg: Επιτυχής δημιουργία σημείωσης καταγγελίας!
-      destroyed_msg: Επιτυχής διαγραφή σημείωσης καταγγελίας!
+      created_msg: Επιτυχής δημιουργία σημείωσης αναφοράς!
+      destroyed_msg: Επιτυχής διαγραφή σημείωσης αναφοράς!
     reports:
       account:
         notes:
@@ -417,44 +576,78 @@ el:
       action_log: Αρχείο ελέγχου
       action_taken_by: Ενέργεια από τον/την
       actions:
+        delete_description_html: Οι δημοσιεύσεις με αναφορά θα διαγραφούν και θα καταγραφεί μια ποινή που θα σας βοηθήσει να αποφασίσετε σε μελλοντικές παραβάσεις από τον ίδιο λογαριασμό.
+        mark_as_sensitive_description_html: Τα πολυμέσα με αναφορά θα επισημανθούν ως ευαίσθητα και θα καταγραφεί μια ποινή που θα σας βοηθήσει να αποφασίσετε σε μελλοντικές παραβάσεις από τον ίδιο λογαριασμό.
         other_description_html: Δείτε περισσότερες επιλογές για τον έλεγχο της συμπεριφοράς του λογαριασμού και προσαρμόσετε την επικοινωνία στον αναφερόμενο λογαριασμό.
-        silence_description_html: Το προφίλ θα είναι ορατό μόνο σε όσους το ακολουθούν ή το αναζητούν με μη αυτόματο τρόπο, περιορίζοντας σημαντικά την εμβέλειά του. Μπορεί πάντα να επανέλθει.
-      add_to_report: Προσθέστε περισσότερα στην αναφορά
+        resolve_description_html: Δεν θα ληφθούν μέτρα κατά του αναφερόμενου λογαριασμού, δεν θα καταγραφεί κανένα παράπτωμα, και η αναφορά θα κλείσει.
+        silence_description_html: Ο λογαριασμός θα είναι ορατός μόνο σε εκείνους που ήδη τον ακολουθούν ή τον αναζητούν χειροκίνητα, περιορίζοντας κατά πολύ την εμβέλειά του. Η ενέργεια αυτή είναι αναστρέψιμη. Κλείνει όλες τις αναφορές εναντίον αυτού του λογαριασμού.
+        suspend_description_html: Ο λογαριασμός και όλο το περιεχόμενό του θα είναι απρόσιτα και τελικά θα διαγραφούν και η αλληλεπίδραση με αυτόν θα είναι αδύνατη. Αναστρέψιμη εντός 30 ημερών. Κλείνει όλες τις αναφορές εναντίον αυτού του λογαριασμού.
+      actions_description_html: Αποφάσισε ποια μέτρα θα ληφθούν για την επίλυση αυτής της αναφοράς. Εάν προβείς σε τιμωρητική ενέργεια κατά του αναφερόμενου λογαριασμού, θα αποσταλεί ειδοποίηση μέσω ηλεκτρονικού ταχυδρομείου σε αυτόν, εκτός όταν η κατηγορία <strong>Σπαμ</strong> είναι επιλεγμένη.
+      actions_description_remote_html: Αποφάσισε ποια μέτρα θα ληφθούν για την επίλυση αυτής της αναφοράς. Αυτό θα επηρεάσει μόνο το πώς <strong>ο δικός σας</strong> διακομιστής επικοινωνεί με αυτόν τον απομακρυσμένο λογαριασμό και χειρίζεται το περιεχόμενό του.
+      add_to_report: Πρόσθεσε περισσότερα στην αναφορά
       are_you_sure: Σίγουρα;
       assign_to_self: Ανάθεση σε μένα
       assigned: Αρμόδιος συντονιστής
-      by_target_domain: Κόμβος του λογαριασμού υπό καταγγελία
+      by_target_domain: Τομέας του αναφερόμενου λογαριασμού
+      cancel: Άκυρο
       category: Κατηγορία
+      category_description_html: Ο λόγος για τον οποίο αναφέρθηκε αυτός ο λογαριασμός και/ή το περιεχόμενο θα εσωκλείεται σε επικοινωνία με τον αναφερόμενο λογαριασμό
       comment:
         none: Κανένα
-      created_at: Καταγγέλθηκε
-      delete_and_resolve: Διαγραφή δημοσιεύσεων
+      comment_description_html: 'Για να δώσει περισσότερες πληροφορίες, ο/η %{name} έγραψε:'
+      confirm: Επιβεβαίωση
+      confirm_action: Επιβεβαίωση ενέργειας συντονισμού ενάντια στον λογαριασμό @%{acct}
+      created_at: Αναφέρθηκε
+      delete_and_resolve: Διαγραφή αναρτήσεων
       forwarded: Προωθημένα
       forwarded_to: Προώθημένα προς %{domain}
-      mark_as_resolved: Σημειωμένο ως επιλυμένο
+      mark_as_resolved: Σημείωση ως επιλυμένο
       mark_as_sensitive: Σήμανση ως ευαίσθητο
-      mark_as_unresolved: Σημειωμένο ως ανεπίλυτο
+      mark_as_unresolved: Σήμανση ως ανεπίλυτο
       no_one_assigned: Κανένας
       notes:
         create: Πρόσθεσε σημείωση
-        create_and_resolve: Επίλυσε με σημείωση
-        create_and_unresolve: Ξανάνοιξε με σημείωση
+        create_and_resolve: Επίλυσε μέ σημείωση
+        create_and_unresolve: Ξανάνοιξε μέ σημείωση
         delete: Διαγραφή
-        placeholder: Περιέγραψε τις ενέργειες που έγιναν, ή οποιαδήποτε άλλη ενημέρωση...
+        placeholder: Περιέγραψε τις ενέργειες που έγιναν, ή οποιαδήποτε άλλη σχετική ενημέρωση...
         title: Σημειώσεις
-      quick_actions_description_html: 'Κάντε μια γρήγορη ενέργεια ή μετακινηθείτε προς τα κάτω για να δείτε το αναφερόμενο περιεχόμενο:'
+      notes_description_html: Δες και άφησε σημειώσεις σε άλλους συντονιστές και τον μελλοντικό εαυτό σου
+      processed_msg: 'Η αναφορά #%{id} διεκπεραιώθηκε με επιτυχία'
+      quick_actions_description_html: 'Κάνε μια γρήγορη ενέργεια ή μετακινήσου προς τα κάτω για να δεις το αναφερόμενο περιεχόμενο:'
       remote_user_placeholder: ο απομακρυσμένος χρήστης από %{instance}
-      reopen: Ξανάνοιξε την καταγγελία
-      report: 'Καταγγελία #%{id}'
+      reopen: Ξανάνοιξε την αναφορά
+      report: 'Αναφορά #%{id}'
       reported_account: Αναφερόμενος λογαριασμός
       reported_by: Αναφέρθηκε από
       resolved: Επιλύθηκε
-      resolved_msg: Η καταγγελία επιλύθηκε επιτυχώς!
+      resolved_msg: Η αναφορά επιλύθηκε επιτυχώς!
       skip_to_actions: Μετάβαση στις ενέργειες
       status: Κατάσταση
       statuses: Αναφερόμενο περιεχόμενο
+      statuses_description_html: Το προσβλητικό περιεχόμενο θα εσωκλείεται στην επικοινωνία με τον αναφερόμενο λογαριασμό
+      summary:
+        action_preambles:
+          delete_html: 'Πρόκειται να <strong>αφαιρέσεις</strong> μερικές από τις αναρτήσεις του <strong>@%{acct}</strong>. Αυτό θα:'
+          mark_as_sensitive_html: 'Πρόκειται να <strong>επισημάνεις</strong> μερικές από τις αναρτήσεις του <strong>@%{acct}</strong> ως <strong>ευαίσθητες</strong>. Αυτό θα:'
+          silence_html: 'Πρόκειται να <strong>περιορίσεις</strong> τον λογαριασμό του <strong>@%{acct}</strong>. Αυτό θα:'
+          suspend_html: 'Πρόκειται να <strong>αναστείλεις</strong> τον λογαριασμό του <strong>@%{acct}</strong>. Αυτό θα:'
+        actions:
+          delete_html: Αφαίρεσε τις προσβλητικές αναρτήσεις
+          mark_as_sensitive_html: Σημειώστε τα πολυμέσα των προσβλητικών αναρτήσεων ως ευαίσθητα
+          silence_html: Περιορίσε σοβαρά την εμβέλεια του <strong>@%{acct}</strong> κάνοντας το προφίλ και το περιεχόμενό του ορατά μόνο σε άτομα που ήδη τον ακολουθούν ή που αναζητούν χειροκίνητα το προφίλ του
+          suspend_html: Αναστολή του <strong>@%{acct}</strong>, καθιστώντας το προφίλ και το περιεχόμενό του μη προσβάσιμα και αδύνατο να αλληλεπιδράσει κανείς με αυτά
+        close_report: 'Επισήμανση αναφοράς #%{id} ως επιλυμένη'
+        close_reports_html: Επισήμανε <strong>όλες </strong> τις αναφορές ενάντια στον λογαριασμό <strong>@%{acct}</strong> ως επιλυμένες
+        delete_data_html: Διάγραψε το προφίλ και το περιεχόμενο του <strong>@%{acct}</strong> σε 30 ημέρες από τώρα εκτός αν, εν τω μεταξύ, ανακληθεί η αναστολή
+        preview_preamble_html: 'Ο <strong>@%{acct}</strong> θα λάβει μια προειδοποίηση με τα ακόλουθο περιεχόμενο:'
+        record_strike_html: Κατάγραψε ένα παράπτωμα εναντίον του <strong>@%{acct}</strong> για να σε βοηθήσει να αποφασίσει; σε μελλοντικές παραβιάσεις από αυτόν τον λογαριασμό
+        send_email_html: Στείλε στον <strong>@%{acct}</strong> ένα προειδοποιητικό e-mail
+        warning_placeholder: Προαιρετικές επιπλέον εξηγήσεις για αυτή την ενέργεια από την ομάδα συντονισμού.
+      target_origin: Προέλευση του αναφερόμενου λογαριασμού
       title: Αναφορές
-      unassign: Αποσύνδεση
+      unassign: Αναίρεση ανάθεσης
+      unknown_action_msg: 'Άγνωστη ενέργεια: %{action}'
       unresolved: Άλυτη
       updated_at: Ενημερωμένη
       view_profile: Προβολή προφίλ
@@ -465,50 +658,100 @@ el:
         other: "%{count} χρήστες"
       categories:
         administration: Διαχείριση
-        devops: Devops
+        devops: DevOps
         invites: Προσκλήσεις
+        moderation: Συντονισμός
+        special: Ειδικός
       delete: Διαγραφή
+      description_html: Με τους <strong>ρόλους χρηστών</strong>, μπορείς να προσαρμόσεις σε ποιες λειτουργίες και περιοχές του Mastodon, οι χρήστες σας μπορούν να έχουν πρόσβαση.
       edit: Επεξεργασία ρόλου '%{name}'
       everyone: Προεπιλεγμένα δικαιώματα
+      everyone_full_description_html: Αυτός είναι ο <strong>βασικός ρόλος</strong> που επηρεάζει <strong>όλους τους χρήστες</strong>, ακόμη και εκείνους που δεν έχουν κάποιον ρόλο. Όλοι οι άλλοι ρόλοι κληρονομούν δικαιώματα από αυτόν.
       permissions_count:
         one: "%{count} δικαίωμα"
         other: "%{count} δικαιώματα"
       privileges:
         administrator: Διαχειριστής
-        invite_users: Πρόσκληση χρηστών
-        manage_announcements: Διαχείριση ανακοινώσεων
+        administrator_description: Οι χρήστες με αυτό το δικαίωμα θα παρακάμπτουν κάθε δικαίωμα
+        delete_user_data: Διαγραφή Δεδομένων Χρήστη
+        delete_user_data_description: Επιτρέπει στους χρήστες να διαγράφουν τα δεδομένα άλλων χρηστών χωρίς καθυστέρηση
+        invite_users: Πρόσκληση Χρηστών
+        invite_users_description: Επιτρέπει στους χρήστες να προσκαλούν νέα άτομα στον διακομιστή
+        manage_announcements: Διαχείριση Ανακοινώσεων
+        manage_announcements_description: Επιτρέπει στους χρήστες να διαχειρίζονται ανακοινώσεις στον διακομιστή
+        manage_appeals: Διαχείριση Εφέσεων
+        manage_appeals_description: Επιτρέπει στους χρήστες να εξετάζουν τις εφέσεις κατά των ενεργειών της ομάδας συντονισμού
+        manage_blocks: Διαχείριση Αποκλεισμών
+        manage_blocks_description: Επιτρέπει στους χρήστες να αποκλείουν παρόχους e-mail και διευθύνσεις IP
+        manage_custom_emojis: Διαχείριση Προσαρμοσμένων Emojis
+        manage_custom_emojis_description: Επιτρέπει στους χρήστες να διαχειρίζονται προσαρμοσμένα emojis στον διακομιστή
+        manage_federation: Διαχείριση Συναλλαγών
+        manage_federation_description: Επιτρέπει στους χρήστες να αποκλείουν ή να επιτρέπουν τις συναλλαγές με άλλους τομείς και να ελέγχουν την παράδοση
+        manage_invites: Διαχείριση Προσκλήσεων
+        manage_invites_description: Επιτρέπει στους χρήστες να περιηγούνται και να απενεργοποιούν τους συνδέσμους πρόσκλησης
+        manage_reports: Διαχείριση Αναφορών
+        manage_reports_description: Επιτρέπει στους χρήστες να εξετάζουν τις αναφορές και να εκτελούν ενέργειες συντονισμού εναντίον τους
         manage_roles: Διαχείριση ρόλων
+        manage_roles_description: Επιτρέπει στους χρήστες να διαχειρίζονται και να αναθέτουν ρόλους κατώτερων των δικών τους
+        manage_rules: Διαχείριση Κανόνων
+        manage_rules_description: Επιτρέπει στους χρήστες να αλλάξουν τους κανόνες του διακομιστή
         manage_settings: Διαχείριση ρυθμίσεων
+        manage_settings_description: Επιτρέπει στους χρήστες να αλλάξουν τις ρυθμίσεις του ιστοτόπου
+        manage_taxonomies: Διαχείριση Ταξινομιών
+        manage_taxonomies_description: Επιτρέπει στους χρήστες να εξετάζουν το δημοφιλές περιεχόμενο και να ενημερώνουν τις ρυθμίσεις ετικέτας
+        manage_user_access: Διαχείριση Πρόσβασης Χρήστη
+        manage_user_access_description: Επιτρέπει στους χρήστες να απενεργοποιούν την ταυτοποίηση δύο παραγόντων άλλων χρηστών, να αλλάξουν τη διεύθυνση ηλεκτρονικού ταχυδρομείου τους και να επαναφέρουν τον κωδικό πρόσβασής τους
         manage_users: Διαχείριση χρηστών
+        manage_users_description: Επιτρέπει στους χρήστες να βλέπουν τις λεπτομέρειες άλλων χρηστών και να εκτελούν ενέργειες συντονισμού εναντίον τους
+        manage_webhooks: Διαχείριση Webhooks
+        manage_webhooks_description: Επιτρέπει στους χρήστες να ορίζουν webhooks για συμβάντα διαχείρισης
+        view_audit_log: Προβολή Καταλόγου Καταγραφών
+        view_audit_log_description: Επιτρέπει στους χρήστες να βλέπουν ένα ιστορικό ενεργειών διαχείρισης στον διακομιστή
+        view_dashboard: Προβολή Ταμπλό
+        view_dashboard_description: Επιτρέπει στους χρήστες να έχουν πρόσβαση στον ταμπλό πληροφοριών και σε διάφορες μετρήσεις
         view_devops: DevOps
+        view_devops_description: Επιτρέπει στους χρήστες να έχουν πρόσβαση στα ταμπλό πληροφοριών Sidekiq και pgHero
       title: Ρόλοι
     rules:
       add_new: Προσθήκη κανόνα
       delete: Διαγραφή
-      description_html: Ενώ οι περισσότεροι ισχυρίζονται ότι έχουν διαβάσει και συμφωνούν με τους όρους της υπηρεσίας, συνήθως οι άνθρωποι δεν διαβάζουν μέχρι μετά την εμφάνιση ενός προβλήματος. <strong>Κάντε ευκολότερο να δουν τους κανόνες του διακομιστή σας με μια ματιά παρέχοντας τους σε μια λίστα σημείων.</strong> Προσπαθήστε να κρατήσετε μεμονωμένους κανόνες σύντομους και απλούς, αλλά προσπαθήστε να μην τους χωρίσετε σε πολλά ξεχωριστά αντικείμενα.
+      description_html: Ενώ οι περισσότεροι ισχυρίζονται ότι έχουν διαβάσει και συμφωνούν με τους όρους της υπηρεσίας, συνήθως οι άνθρωποι δεν διαβάζουν μέχρι μετά την εμφάνιση ενός προβλήματος. <strong>Κάνε ευκολότερο να δουν τους κανόνες του διακομιστή σας με μια ματιά παρέχοντας τους σε μια λίστα.</strong> Προσπάθησε να κρατήσεις τους μεμονωμένους κανόνες σύντομους και απλούς, αλλά προσπάθησε να μην τους χωρίσεις σε πολλά ξεχωριστά αντικείμενα.
       edit: Επεξεργασία κανόνα
       empty: Δεν έχουν οριστεί ακόμα κανόνες διακομιστή.
       title: Κανόνες διακομιστή
     settings:
       about:
         manage_rules: Διαχείριση κανόνων διακομιστή
+        preamble: Παρέχετε αναλυτικές πληροφορίες σχετικά με τη λειτουργία του διακομιστή, τον συντονισμό και τη χρηματοδότηση.
+        rules_hint: Υπάρχει μια ειδική περιοχή για τους κανόνες που αναμένεται να τηρούν οι χρήστες σας.
         title: Σχετικά με
       appearance:
+        preamble: Προσάρμοσε την ιστοσελίδα του Mastodon.
         title: Εμφάνιση
+      branding:
+        preamble: Η ταυτότητα του διακομιστή σου, τον διαφοροποιεί από άλλους διακομιστές του δικτύου. Αυτές οι πληροφορίες μπορεί να εμφανίζονται σε διάφορα περιβάλλοντα, όπως η ιστοσελίδα του Mastodon, εγγενείς εφαρμογές, σε προεπισκοπήσεις συνδέσμου σε άλλους ιστότοπους και εντός εφαρμογών μηνυμάτων και ούτω καθεξής. Γι' αυτό, είναι καλύτερο να διατηρούνται αυτές οι πληροφορίες σαφείς, σύντομες και συνοπτικές.
+        title: Ταυτότητα
       content_retention:
+        preamble: Έλεγξε πώς αποθηκεύεται το περιεχόμενο που δημιουργείται από τους χρήστες στο Mastodon.
         title: Διατήρηση περιεχομένου
       default_noindex:
-        desc_html: Επηρεάζει όσους χρήστες δεν έχουν αλλάξει αυτή τη ρύθμιση οι ίδιοι
+        desc_html: Επηρεάζει όσους χρήστες δεν έχουν αλλάξει οι ίδιοι αυτή τη ρύθμιση
         title: Εξαίρεση χρηστών από τις μηχανές αναζήτησης
       discovery:
+        follow_recommendations: Ακολούθησε τις προτάσεις
+        preamble: Δημοσιεύοντας ενδιαφέρον περιεχόμενο είναι καθοριστικό για την ενσωμάτωση νέων χρηστών που μπορεί να μη γνωρίζουν κανέναν στο Mastodon. Έλεγξε πώς λειτουργούν διάφορες δυνατότητες ανακάλυψης στον διακομιστή σας.
         profile_directory: Κατάλογος προφίλ
         public_timelines: Δημόσιες ροές
+        publish_discovered_servers: Δημοσίευση διακομιστών που έχουν ανακαλυφθεί
+        publish_statistics: Δημοσίευση στατιστικών
+        title: Ανακάλυψη
         trends: Τάσεις
       domain_blocks:
         all: Για όλους
         disabled: Για κανέναν
         users: Προς συνδεδεμένους τοπικούς χρήστες
       registrations:
+        preamble: Έλεγξε ποιος μπορεί να δημιουργήσει ένα λογαριασμό στον διακομιστή σας.
         title: Εγγραφές
       registrations_mode:
         modes:
@@ -523,6 +766,7 @@ el:
       account: Συντάκτης
       application: Εφαρμογή
       back_to_account: Επιστροφή στη σελίδα λογαριασμού
+      back_to_report: Πίσω στη σελίδα αναφοράς
       batch:
         remove_from_report: Αφαίρεση από την αναφορά
         report: Αναφορά
@@ -535,65 +779,181 @@ el:
         title: Πολυμέσα
       metadata: Μεταδεδομένα
       no_status_selected: Καμία δημοσίευση δεν άλλαξε αφού καμία δεν ήταν επιλεγμένη
-      open: Άνοιγμα δημοσίευσης
+      open: Άνοιγμα ανάρτησης
+      original_status: Αρχική ανάρτηση
       reblogs: Αναδημοσιεύσεις
       status_changed: Η ανάρτηση άλλαξε
       title: Καταστάσεις λογαριασμού
-      trending: Δημοφιλή
+      trending: Τάσεις
       visibility: Ορατότητα
       with_media: Με πολυμέσα
+    strikes:
+      actions:
+        delete_statuses: Ο/Η %{name} διέγραψε τις αναρτήσεις του/της %{target}
+        disable: Ο/Η %{name} πάγωσε τον λογαριασμό του/της %{target}
+        mark_statuses_as_sensitive: Ο/Η %{name} επισήμανε τα πολυμέσα του/της %{target} ως ευαίσθητα
+        none: Ο/Η %{name} έστειλε προειδοποίηση προς τον/την %{target}
+        sensitive: Ο/Η %{name} επισήμανε τα πολυμέσα του λογαριασμού %{target} ως ευαίσθητα
+        silence: Ο/Η %{name} περιόρισε τον λογαριασμό %{target}
+        suspend: ο/η %{name} ανέστειλε τον λογαριασμό %{target}
+      appeal_approved: Έγινε έφεση
+      appeal_pending: Έφεση σε εκκρεμότητα
+      appeal_rejected: Η έφεση απορρίφθηκε
     system_checks:
       database_schema_check:
-        message_html: Υπάρχουν μετακινήσεις βάσης δεδομένων που εκκρεμούν. Παρακαλώ εκτελέστε τις για να βεβαιωθείτε ότι η εφαρμογή συμπεριφέρεται όπως αναμένεται
+        message_html: Υπάρχουν μετακινήσεις βάσεων δεδομένων που εκκρεμούν. Παρακαλώ εκτέλεσέ τες για να βεβαιωθείς ότι η εφαρμογή συμπεριφέρεται όπως αναμένεται
+      elasticsearch_running_check:
+        message_html: Δεν ήταν δυνατή η σύνδεση στο Elasticsearch. Παρακαλώ έλεγξε ότι εκτελείται ή απενεργοποίησε την αναζήτηση πλήρους κειμένου
+      elasticsearch_version_check:
+        message_html: 'Μη συμβατή έκδοση Elasticsearch: %{value}'
+        version_comparison: Εκτελείται η έκδοση Elasticsearch %{running_version} ενώ απαιτείται %{required_version}
       rules_check:
         action: Διαχείριση κανόνων διακομιστή
-        message_html: Δεν έχετε ορίσει κανέναν κανόνα διακομιστή.
+        message_html: Δεν έχεις ορίσει κανέναν κανόνα διακομιστή.
+      sidekiq_process_check:
+        message_html: Καμία διεργασία Sidekiq δεν εκτελείται για την ουρά %{value}. Παρακαλώ έλεγξε τη διαμόρφωση του Sidekiq
+      upload_check_privacy_error:
+        action: Δες εδώ για περισσότερες πληροφορίες
+        message_html: "<strong>Ο διακομιστής σας δεν έχει ρυθμιστεί σωστά. Το απόρρητο των χρηστών σας κινδυνεύει.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Δες εδώ για περισσότερες πληροφορίες
+        message_html: "<strong>Ο χώρος αποθήκευσης αντικειμένων σας δεν έχει ρυθμιστεί σωστά. Το απόρρητο των χρηστών σας κινδυνεύει.</strong>"
     tags:
-      review: Κατάσταση έγκρισης
+      review: Κατάσταση αξιολόγησης
       updated_msg: Οι ρυθμίσεις των ετικετών ενημερώθηκαν επιτυχώς
     title: Διαχείριση
     trends:
+      allow: Επιτρέπεται
+      approved: Εγκρίθηκε
+      disallow: Να μην επιτρέπεται
+      links:
+        allow: Να επιτρέπεται σύνδεσμος
+        allow_provider: Να επιτρέπεται ο εκδότης
+        description_html: Αυτοί οι σύνδεσμοι μοιράζονται αρκετά από λογαριασμούς των οποίων τις δημοσιεύσεις, βλέπει ο διακομιστής σας. Μπορεί να βοηθήσει τους χρήστες σας να μάθουν τί συμβαίνει στον κόσμο. Οι σύνδεσμοι δεν εμφανίζονται δημόσια μέχρι να εγκρίνετε τον εκδότη. Μπορείτε επίσης να επιτρέψετε ή να απορρίψετε μεμονωμένους συνδέσμους.
+        disallow: Να μην επιτρέπεται ο σύνδεσμος
+        disallow_provider: Να μην επιτρέπεται ο εκδότης
+        no_link_selected: Κανένας σύνδεσμος δεν άλλαξε αφού κανείς δεν επιλέχθηκε
+        publishers:
+          no_publisher_selected: Κανένας εκδότης δεν άλλαξε καθώς κανένας δεν επιλέχθηκε
+        shared_by_over_week:
+          one: Κοινοποιήθηκε από ένα άτομο την τελευταία εβδομάδα
+          other: Κοινοποιήθηκε από %{count} άτομα την τελευταία εβδομάδα
+        title: Δημοφιλείς σύνδεσμοι
+        usage_comparison: Κοινοποιήθηκε %{today} φορές σήμερα, σε σύγκριση με %{yesterday} χθες
+      not_allowed_to_trend: Δεν επιτρέπεται να γίνει δημοφιλές
       only_allowed: Μόνο επιτρεπόμενα
+      pending_review: Εκκρεμεί αξιολόγηση
+      preview_card_providers:
+        allowed: Σύνδεσμοι από αυτόν τον εκδότη μπορούν να γίνουν δημοφιλείς
+        description_html: Αυτοί είναι τομείς από τους οποίους οι σύνδεσμοι συχνά μοιράζονται στον διακομιστή σας. Σύνδεσμοι δεν γίνουν δημοφιλείς δημοσίως εκτός και αν ο τομέας του συνδέσμου εγκριθεί. Η έγκρισή σας (ή απόρριψη) περιλαμβάνει και τους υποτομείς.
+        rejected: Σύνδεσμοι από αυτόν τον εκδότη δε θα γίνουν δημοφιλείς
+        title: Εκδότες
+      rejected: Απορρίφθηκε
+      statuses:
+        allow: Να επιτρέπεται η ανάρτηση
+        allow_account: Να επιτρέπεται ο συγγραφέας
+        description_html: Αυτές είναι αναρτήσεις για τις οποίες ο διακομιστής σας γνωρίζει ότι κοινοποιούνται και αρέσουν πολύ αυτή τη περίοδο. Μπορεί να βοηθήσει νέους και χρήστες που επιστρέφουν, να βρουν περισσότερα άτομα να ακολουθήσουν. Καμία ανάρτηση δεν εμφανίζεται δημόσια μέχρι να εγκρίνετε το συντάκτη και ο συντάκτης να επιτρέπει ο λογαριασμός του να προτείνεται και σε άλλους. Μπορείτε επίσης να επιτρέψετε ή να απορρίψετε μεμονωμένες δημοσιεύσεις.
+        disallow: Να μην επιτρέπεται η δημοσίευση
+        disallow_account: Να μην επιτρέπεται ο συντάκτης
+        no_status_selected: Καμία δημοφιλής ανάρτηση δεν άλλαξε αφού καμία δεν επιλέχθηκε
+        not_discoverable: Ο συντάκτης δεν έχει επιλέξει να είναι ανακαλύψιμος
+        shared_by:
+          one: Μοιράστηκε ή προστέθηκε στα αγαπημένα μία φορά
+          other: Μοιράστηκε και προστέθηκε στα αγαπημένα %{friendly_count} φορές
+        title: Δημοφιλείς αναρτήσεις
       tags:
+        current_score: Τρέχουσα βαθμολογία %{score}
         dashboard:
+          tag_accounts_measure: μοναδικές χρήσεις
           tag_languages_dimension: Κορυφαίες γλώσσες
           tag_servers_dimension: Κορυφαίοι διακομιστές
           tag_servers_measure: διαφορετικοί διακομιστές
-      trending: Δημοφιλή
+          tag_uses_measure: συνολικές χρήσεις
+        description_html: Αυτές είναι ετικέτες που εμφανίζονται αυτή τη στιγμή σε πολλές αναρτήσεις που βλέπει ο διακομιστής σας. Μπορεί να βοηθήσει τους χρήστες σας να μάθουν τί συζητείται αυτή τη στιγμή. Δεν εμφανίζονται ετικέτες δημοσίως μέχρι να τις εγκρίνετε.
+        listable: Μπορεί να προταθεί
+        no_tag_selected: Καμία ετικέτα δεν άλλαξε αφού καμία δεν ήταν επιλεγμένη
+        not_listable: Δεν θα προτείνεται
+        not_trendable: Δεν θα εμφανίζεται στις τάσεις
+        not_usable: Δεν μπορεί να χρησιμοποιηθεί
+        peaked_on_and_decaying: Κορυφαία θέση στις %{date}, τώρα φθίνει
+        title: Δημοφιλείς ετικέτες
+        trendable: Μπορεί να εμφανιστεί στις τάσεις
+        trending_rank: 'Δημοφιλές #%{rank}'
+        usable: Μπορεί να χρησιμοποιηθεί
+        usage_comparison: Χρησιμοποιήθηκε %{today} φορές σήμερα, σε σύγκριση με %{yesterday} χθες
+        used_by_over_week:
+          one: Χρησιμοποιήθηκε από ένα άτομο την τελευταία εβδομάδα
+          other: Χρησιμοποιήθηκε από %{count} άτομα την τελευταία εβδομάδα
+      title: Δημοφιλή
+      trending: Τάσεις
     warning_presets:
       add_new: Πρόσθεση νέου
       delete: Διαγραφή
       edit_preset: Ενημέρωση προκαθορισμένης προειδοποίησης
-      empty: Δεν έχετε ακόμη ορίσει κάποια προεπιλογή προειδοποίησης.
+      empty: Δεν έχετε ακόμη ορίσει κάποια προκαθορισμένη προειδοποίηση.
       title: Διαχείριση προκαθορισμένων προειδοποιήσεων
     webhooks:
+      add_new: Προσθήκη σημείου τερματισμού
       delete: Διαγραφή
+      description_html: Ένα <strong>webhook</strong> επιτρέπει στο Mastodon να στείλει <strong>ειδοποιήσεις πραγματικού χρόνου</strong> σχετικά με επιλεγμένα γεγονότα στη δική σας εφαρμογή, ώστε η εφαρμογή να σας μπορεί <strong>να προκαλέσει αντιδράσεις αυτόματα</strong>.
       disable: Απενεργοποίηση
       disabled: Απενεργοποιημένα
+      edit: Επεξεργασία σημείου τερματισμού
+      empty: Δεν έχετε ακόμα ρυθμισμένα σημεία τερματισμού webhook.
       enable: Ενεργοποίηση
+      enabled: Ενεργό
+      enabled_events:
+        one: 1 ενεργό συμβάν
+        other: "%{count} ενεργά συμβάντα"
+      events: Συμβάντα
+      new: Νέο webhook
+      rotate_secret: Περιστροφή μυστικού
+      secret: Υπογραφή μυστικού
       status: Κατάσταση
+      title: Webhooks
+      webhook: Webhook
   admin_mailer:
     new_appeal:
       actions:
+        delete_statuses: διαγραφή των αναρτήσεών του
+        disable: να παγώσει τον λογαριασμό του
+        mark_statuses_as_sensitive: να επισημάνουν τις δημοσιεύσεις του ως ευαίσθητες
         none: μια προειδοποίηση
+        sensitive: να επισημάνουν τον λογαριασμό του ως ευαίσθητο
+        silence: να περιορίσουν το λογαριασμό του
+        suspend: να αναστείλουν τον λογαριασμό του
+      body: 'Ο/Η %{target} κάνει έφεση στην απόφαση συντονισμού που έγινε από τον/την %{action_taken_by} στις %{date}, η οποία ήταν %{type}. Έγραψαν:'
+      next_steps: Μπορείτε να εγκρίνετε την έφεση για να αναιρέσετε την απόφαση της ομάδας συντονισμού ή να την αγνοήσετε.
+      subject: Ο/Η %{username} κάνει έφεση σε μια απόφαση της ομάδας συντονισμού στον %{instance}
     new_pending_account:
       body: Τα στοιχεία του νέου λογαριασμού είναι παρακάτω. Μπορείς να εγκρίνεις ή να απορρίψεις αυτή την αίτηση.
       subject: Νέος λογαριασμός προς έγκριση στο %{instance} (%{username})
     new_report:
-      body: Ο/Η %{reporter} κατήγγειλε τον/την %{target}
-      body_remote: Κάποιος/α από τον τομέα %{domain} κατήγγειλε τον/την %{target}
-      subject: Νέα καταγγελία για %{instance} (#%{id})
+      body: Ο/Η %{reporter} ανέφερε τον/την %{target}
+      body_remote: Κάποιος/α από τον τομέα %{domain} ανέφερε τον/την %{target}
+      subject: Νέα αναφορά για %{instance} (#%{id})
+    new_trends:
+      body: 'Τα ακόλουθα στοιχεία χρειάζονται αξιολόγηση για να μπορούν να προβληθούν δημόσια:'
+      new_trending_links:
+        title: Σύνδεσμοι σε τάση
+      new_trending_statuses:
+        title: Αναρτήσεις σε τάση
+      new_trending_tags:
+        no_approved_tags: Προς το παρόν δεν υπάρχουν εγκεκριμένες δημοφιλείς ετικέτες.
+        requirements: 'Οποιοσδήποτε από αυτούς τους υποψηφίους θα μπορούσε να ξεπεράσει την #%{rank} εγκεκριμένη δημοφιλή ετικέτα, που επί του παρόντος είναι #%{lowest_tag_name} με βαθμολογία %{lowest_tag_score}.'
+        title: Δημοφιλείς ετικέτες
+      subject: Νέες τάσεις προς αξιολόγηση στο %{instance}
   aliases:
     add_new: Δημιουργία ψευδώνυμου
     created_msg: Δημιουργήθηκε νέο ψευδώνυμο. Τώρα μπορείς να ξεκινήσεις τη μεταφορά από τον παλιό λογαριασμό.
     deleted_msg: Αφαιρέθηκε το ψευδώνυμο. Η μεταφορά από εκείνον τον λογαριασμό σε αυτόν εδώ δε θα είναι πλέον δυνατή.
     empty: Δεν έχεις ψευδώνυμα.
-    hint_html: Αν θέλεις να μετακομίσεις από έναν άλλο λογαριασμό σε αυτόν εδώ, μπορείς εδώ να δημιουργήσεις ένα ψευδώνυμο, πράγμα που απαιτείται πριν προχωρήσεις για να μεταφέρεις τους ακολούθους σου από τον παλιό λογαριασμό σε αυτόν εδώ. Η ενέργεια αυτή είναι <strong>ακίνδυνη και αναστρέψιμη</strong>.<strong>Η μετακόμιση του λογαριασμού ξεκινάει από τον παλιό λογαριασμό</strong>.
+    hint_html: Αν θέλεις να μετακομίσεις από έναν άλλο λογαριασμό σε αυτόν εδώ, εδώ μπορείς να δημιουργήσεις ένα ψευδώνυμο, πράγμα που απαιτείται πριν προχωρήσεις για να μεταφέρεις τους ακολούθους σου από τον παλιό λογαριασμό σε αυτόν εδώ. Η ενέργεια αυτή είναι <strong>ακίνδυνη και αναστρέψιμη</strong>.<strong>Η μετακόμιση του λογαριασμού ξεκινάει από τον παλιό λογαριασμό</strong>.
     remove: Αφαίρεση ψευδώνυμου
   appearance:
-    advanced_web_interface: Προηγμένη λειτουργία χρήσης
-    advanced_web_interface_hint: 'Αν θέλεις να χρησιμοποιήσεις ολόκληρο το πλάτος της οθόνης σου, η προηγμένη λειτουργία χρήσης σου επιτρέπει να ορίσεις πολλαπλές κολώνες ώστε να βλέπεις ταυτόχρονα όση πληροφορία θέλεις: Την αρχική ροή, τις ειδοποιήσεις, την ομοσπονδιακή ροή και όσες λίστες και ετικέτες θέλεις.'
-    animations_and_accessibility: Κίνηση και προσβασιμότητα
+    advanced_web_interface: Προηγμένη διεπαφή ιστού
+    advanced_web_interface_hint: 'Αν θέλεις να χρησιμοποιήσεις ολόκληρο το πλάτος της οθόνης σου, η προηγμένη λειτουργία χρήσης σου επιτρέπει να ορίσεις πολλαπλές στύλες ώστε να βλέπεις ταυτόχρονα όση πληροφορία θέλεις: Την αρχική ροή, τις ειδοποιήσεις, την ροή συναλλαγών και όσες λίστες και ετικέτες θέλεις.'
+    animations_and_accessibility: Εφέ κινήσεων και προσβασιμότητα
     confirmation_dialogs: Ερωτήσεις επιβεβαίωσης
     discovery: Εξερεύνηση
     localization:
@@ -601,29 +961,32 @@ el:
       guide_link: https://crowdin.com/project/mastodon
       guide_link_text: Μπορεί να συνεισφέρει ο οποιοσδήποτε.
     sensitive_content: Ευαίσθητο περιεχόμενο
-    toot_layout: Διαρρύθμιση τουτ
+    toot_layout: Διαρρύθμιση αναρτήσεων
   application_mailer:
     notification_preferences: Αλλαγή προτιμήσεων email
     salutation: "%{name},"
     settings: 'Άλλαξε τις προτιμήσεις email: %{link}'
     view: 'Προβολή:'
     view_profile: Προβολή προφίλ
-    view_status: Προβολή κατάστασης
+    view_status: Προβολή ανάρτησης
   applications:
     created: Η εφαρμογή δημιουργήθηκε επιτυχώς
     destroyed: Η εφαρμογή διαγράφηκε επιτυχώς
+    logout: Αποσύνδεση
     regenerate_token: Αναδημιουργία του διακριτικού πρόσβασης (access token)
-    token_regenerated: Το διακριτικό πρόσβασης (access token) αναδημιουργήθηκε επιτυχώς
-    warning: Μεγάλη προσοχή με αυτά τα στοιχεία. Μην τα μοιραστείς ποτέ με κανέναν!
-    your_token: Το διακριτικό πρόσβασής σου (access token)
+    token_regenerated: Το διακριτικό πρόσβασης αναδημιουργήθηκε επιτυχώς
+    warning: Να είσαι πολύ προσεκτικός με αυτά τα στοιχεία. Μην τα μοιραστείς ποτέ με κανέναν!
+    your_token: Το διακριτικό πρόσβασής σου
   auth:
     apply_for_account: Ζητήστε έναν λογαριασμό
     change_password: Συνθηματικό
+    confirmations:
+      wrong_email_hint: Εάν αυτή η διεύθυνση email δεν είναι σωστή, μπορείς να την αλλάξεις στις ρυθμίσεις λογαριασμού.
     delete_account: Διαγραφή λογαριασμού
     delete_account_html: Αν θέλεις να διαγράψεις το λογαριασμό σου, μπορείς <a href="%{path}">να συνεχίσεις εδώ</a>. Θα σου ζητηθεί επιβεβαίωση.
     description:
       prefix_invited_by_user: Ο/Η @%{name} σε προσκαλεί να συνδεθείς με αυτό τον διακομιστή του Mastodon!
-      prefix_sign_up: Άνοιξε λογαριασμό στο Mastodon σήμερα!
+      prefix_sign_up: Κάνε εγγραφή στο Mastodon σήμερα!
       suffix: Ανοίγοντας λογαριασμό θα μπορείς να ακολουθείς άλλους, να ανεβάζεις ενημερώσεις και να ανταλλάζεις μηνύματα με χρήστες σε οποιοδήποτε διακομιστή Mastodon, καθώς και άλλα!
     didnt_get_confirmation: Δεν έλαβες τις οδηγίες επιβεβαίωσης;
     dont_have_your_security_key: Δεν έχετε κλειδί ασφαλείας;
@@ -637,6 +1000,7 @@ el:
     migrate_account: Μετακόμιση σε διαφορετικό λογαριασμό
     migrate_account_html: Αν θέλεις να ανακατευθύνεις αυτό τον λογαριασμό σε έναν διαφορετικό, μπορείς να το <a href="%{path}">διαμορφώσεις εδώ</a>.
     or_log_in_with: Ή συνδέσου με
+    privacy_policy_agreement_html: Έχω διαβάσει και συμφωνώ με την <a href="%{privacy_policy_path}" target="_blank">πολιτική απορρήτου</a>
     providers:
       cas: Υπηρεσία Κεντρικής Πιστοποίησης (CAS)
       saml: Πρωτόκολλο SAML
@@ -645,6 +1009,9 @@ el:
     resend_confirmation: Στείλε ξανά τις οδηγίες επιβεβαίωσης
     reset_password: Επαναφορά συνθηματικού
     rules:
+      accept: Αποδοχή
+      back: Πίσω
+      preamble: Αυτά ορίζονται και επιβάλλονται από τους συντονιστές του%{domain}.
       title: Ορισμένοι βασικοί κανόνες.
     security: Ασφάλεια
     set_new_password: Ορισμός νέου συνθηματικού
@@ -653,14 +1020,18 @@ el:
       email_settings_hint_html: Το email επιβεβαίωσης στάλθηκε στο %{email}. Αν η διεύθυνση αυτή δεν είναι σωστή, μπορείτε να την ενημερώσετε στις ρυθμίσεις λογαριασμού.
       title: Ρυθμίσεις
     sign_in:
+      preamble_html: Συνδεθείτε με τα διαπιστευτήριά σας στον <strong>%{domain}</strong>. Αν ο λογαριασμός σας φιλοξενείται σε διαφορετικό διακομιστή, δε θα μπορείτε να συνδεθείτε εδώ.
       title: Συνδεθείτε στο %{domain}
     sign_up:
+      preamble: Με έναν λογαριασμό σ' αυτόν τον διακομιστή Mastodon, θα μπορείτε να ακολουθήσετε οποιοδήποτε άλλο άτομο στο δίκτυο, ανεξάρτητα από το πού φιλοξενείται ο λογαριασμός του.
       title: Ας ξεκινήσουμε τις ρυθμίσεις στο %{domain}.
     status:
       account_status: Κατάσταση λογαριασμού
       confirming: Αναμονή για ολοκλήρωση επιβεβαίωσης του email.
+      functional: Ο λογαριασμός σας είναι πλήρως λειτουργικός.
       pending: Η εφαρμογή σας εκκρεμεί έγκρισης, πιθανόν θα διαρκέσει κάποιο χρόνο. Θα λάβετε email αν εγκριθεί.
       redirecting_to: Ο λογαριασμός σου είναι ανενεργός γιατί επί του παρόντος ανακατευθύνει στον %{acct}.
+      view_strikes: Προβολή προηγούμενων ποινών εναντίον του λογαριασμού σας
     too_fast: Η φόρμα υποβλήθηκε πολύ γρήγορα, προσπαθήστε ξανά.
     use_security_key: Χρήση κλειδιού ασφαλείας
   authorize_follow:
@@ -684,6 +1055,10 @@ el:
     errors:
       invalid_key: δεν είναι έγκυρο κλειδί Ed25519 ή Curve25519
       invalid_signature: δεν είναι έγκυρη υπογραφή Ed25519
+  date:
+    formats:
+      default: "%b %d, %Y"
+      with_month_name: "%B %d, %Y"
   datetime:
     distance_in_words:
       about_x_hours: "%{count}ω"
@@ -717,8 +1092,34 @@ el:
       username_unavailable: Το όνομα χρήστη σου θα παραμείνει μη διαθέσιμο
   disputes:
     strikes:
+      action_taken: Ενέργεια που πραγματοποιήθηκε
+      appeal: Έφεση
+      appeal_approved: Αυτή η ποινή έχει αναιρεθεί επιτυχώς και δεν είναι πλέον έγκυρη
+      appeal_rejected: Η έφεση απορρίφθηκε
+      appeal_submitted_at: Η έφεση υποβλήθηκε
+      appealed_msg: Η έφεσή σας έχει υποβληθεί. Αν εγκριθεί, θα ειδοποιηθείτε.
+      appeals:
+        submit: Υποβολή έφεσης
       approve_appeal: Έγκριση έφεσης
+      associated_report: Συσχετισμένη αναφορά
+      created_at: Χρονολογείται
+      description_html: Αυτές είναι ενέργειες που λήφθηκαν κατά του λογαριασμού σας και προειδοποιήσεις που σας έχουν σταλεί από το προσωπικό του %{instance}.
+      recipient: Απευθύνεται σε
       reject_appeal: Απόρριψη έφεσης
+      status: 'Δημοσίευση #%{id}'
+      status_removed: Η δημοσιεύση έχει ήδη αφαιρεθεί από το σύστημα
+      title: "%{action} στις %{date}"
+      title_actions:
+        delete_statuses: Αφαίρεση δημοσίευσης
+        disable: Πάγωμα λογαριασμού
+        mark_statuses_as_sensitive: Σήμανση δημοσιεύσεων ως ευαίσθητες
+        none: Προειδοποίηση
+        sensitive: Σήμανση λογαριασμού ως ευαίσθητο
+        silence: Περιορισμός λογαριασμού
+        suspend: Αναστολή λογαριασμού
+      your_appeal_approved: Η έφεση σας έχει εγκριθεί
+      your_appeal_pending: Υποβάλατε έφεση
+      your_appeal_rejected: Η έφεση σας απορρίφθηκε
   domain_validator:
     invalid_domain: δεν είναι έγκυρο όνομα τομέα
   errors:
@@ -769,8 +1170,11 @@ el:
     edit:
       add_keyword: Προσθήκη λέξης-κλειδιού
       keywords: Λέξεις-κλειδιά
+      statuses: Μεμονωμένες δημοσιεύσεις
+      statuses_hint_html: Αυτό το φίλτρο εφαρμόζεται για την επιλογή μεμονωμένων δημοσιεύσεων, ανεξάρτητα από το αν ταιριάζουν με τις λέξεις - κλειδιά παρακάτω. <a href="%{path}">Επισκόπηση ή αφαίρεση δημοσιεύσεων από το φίλτρο</a>.
       title: Ενημέρωση φίλτρου
     errors:
+      deprecated_api_multiple_keywords: Αυτές οι παράμετροι δεν μπορούν να αλλάξουν από αυτήν την εφαρμογή επειδή ισχύουν για περισσότερες από μία λέξεις - κλειδιά φίλτρου. Χρησιμοποιήστε μια πιο πρόσφατη εφαρμογή ή την ιστοσελίδα.
       invalid_context: Έδωσες λάθος ή ανύπαρκτο πλαίσιο
     index:
       contexts: Φίλτρα σε %{contexts}
@@ -778,25 +1182,48 @@ el:
       empty: Δεν έχεις φίλτρα.
       expires_in: Λήγει σε %{distance}
       expires_on: Λήγει στις %{date}
+      keywords:
+        one: "%{count} λέξη - κλειδί"
+        other: "%{count} λέξεις - κλειδιά"
+      statuses:
+        one: "%{count} δημοσίευση"
+        other: "%{count} δημοσιεύσεις"
+      statuses_long:
+        one: "%{count} κρυμμένη μεμονωμένη δημοσίευση"
+        other: "%{count} κρυμμένες μεμονωμένες δημοσιεύσεις"
       title: Φίλτρα
     new:
       save: Αποθήκευση νέου φίλτρου
       title: Πρόσθεσε νέο φίλτρο
-  footer:
-    trending_now: Τάσεις
+    statuses:
+      back_to_filter: Πίσω στο φίλτρο
+      batch:
+        remove: Αφαίρεση από φίλτρο
+      index:
+        hint: Αυτό το φίλτρο ισχύει για την επιλογή μεμονωμένων δημοσιεύσεων ανεξάρτητα από άλλα κριτήρια. Μπορείτε να προσθέσετε περισσότερες δημοσιεύσεις σε αυτό το φίλτρο από την ιστοσελίδα.
+        title: Φιλτραρισμένες δημοσιεύσεις
   generic:
     all: Όλα
+    all_items_on_page_selected_html:
+      one: "<strong>%{count}</strong> στοιχείο σε αυτή τη σελίδα είναι επιλεγμένο."
+      other: Όλα τα <strong>%{count}</strong> 5 στοιχεία σε αυτή τη σελίδα είναι επιλεγμένα.
+    all_matching_items_selected_html:
+      one: "<strong>%{count}</strong> στοιχείο που ταιριάζει με την αναζήτησή σας είναι επιλεγμένο."
+      other: Όλα τα <strong>%{count}</strong> στοιχεία που ταιριάζουν στην αναζήτησή σας είναι επιλεγμένα.
     changes_saved_msg: Οι αλλαγές αποθηκεύτηκαν!
     copy: Αντιγραφή
     delete: Διαγραφή
+    deselect: Αποεπιλογή όλων
+    none: Κανένα
     order_by: Ταξινόμηση κατά
     save_changes: Αποθήκευση αλλαγών
+    select_all_matching_items:
+      one: Επιλέξτε %{count} στοιχείο που ταιριάζει με την αναζήτησή σας.
+      other: Επιλέξτε και τα %{count} αντικείμενα που ταιριάζουν στην αναζήτησή σας.
     today: σήμερα
     validation_errors:
       one: Κάτι δεν είναι εντάξει ακόμα! Για κοίταξε το παρακάτω σφάλμα
       other: Κάτι δεν είναι εντάξει ακόμα! Για κοίταξε τα παρακάτω %{count} σφάλματα
-  html_validator:
-    invalid_markup: 'περιέχει λάθος μορφοποίηση HTML: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Μη έγκυρο αρχείο CSV. Σφάλμα: %{error}'
@@ -839,7 +1266,18 @@ el:
     title: Προσκάλεσε κόσμο
   lists:
     errors:
-      limit: Έχεις φτάσει το μέγιστο πλήθος επιτρεπτών λιστών
+      limit: Έχεις φτάσει το μέγιστο αριθμό λιστών
+  login_activities:
+    authentication_methods:
+      otp: εφαρμογή ταυτοποίησης δύο παραγόντων
+      password: συνθηματικό
+      sign_in_token: κωδικός ασφαλείας e-mail
+      webauthn: κλειδιά ασφαλείας
+    description_html: Αν δείτε δραστηριότητα που δεν αναγνωρίζετε, σκεφτείτε να αλλάξετε τον κωδικό πρόσβασής σας και να ενεργοποιήσετε τον έλεγχο ταυτότητας δύο παραγόντων.
+    empty: Δεν υπάρχει διαθέσιμο ιστορικό ελέγχου ταυτότητας
+    failed_sign_in_html: Αποτυχημένη προσπάθεια σύνδεσης με %{method} από %{ip} (%{browser})
+    successful_sign_in_html: Επιτυχής σύνδεση με %{method} από %{ip} (%{browser})
+    title: Ιστορικό ελέγχου ταυτότητας
   media_attachments:
     validations:
       images_and_video: Δεν γίνεται να προσθέσεις βίντεο σε ενημέρωση που ήδη περιέχει εικόνες
@@ -908,6 +1346,8 @@ el:
       body: 'Αναφέρθηκες από τον/την %{name} στο:'
       subject: Αναφέρθηκες από τον/την %{name}
       title: Νέα αναφορά
+    poll:
+      subject: Μια δημοσκόπηση του %{name} έληξε
     reblog:
       body: 'Η κατάστασή σου προωθήθηκε από τον/την %{name}:'
       subject: Ο/Η %{name} προώθησε την κατάστασή σου
@@ -932,8 +1372,12 @@ el:
           trillion: τρις.
   otp_authentication:
     code_hint: Για να συνεχίσεις, γράψε τον κωδικό που δημιούργησε η εφαρμογή πιστοποίησης
+    description_html: Αν ενεργοποιήσεις την <strong>ταυτοποίηση δύο παραγόντων</strong> χρησιμοποιώντας εφαρμογή ταυτοποίησης, για να συνδεθείς θα πρέπει να έχεις το τηλέφωνό σου, που θα σού δημιουργήσει κλειδιά εισόδου.
     enable: Ενεργοποίηση
+    instructions_html: "<strong>Σάρωσε αυτόν τον κωδικό QR με την εφαρμογή Google Authenticator ή κάποια άλλη αντίστοιχη στο τηλέφωνό σου</strong>. Από εδώ και στο εξής, η εφαρμογή θα δημιουργεί κλειδιά που θα πρέπει να εισάγεις όταν συνδέεσαι."
+    manual_instructions: 'Αν δεν μπορείς να σαρώσεις τον κωδικό QR και χρειάζεσαι να τον εισάγεις χειροκίνητα, ορίστε η μυστική φράση σε μορφή κειμένου:'
     setup: Ρύθμιση
+    wrong_code: Ο κωδικός που έβαλες ήταν άκυρος! Η ώρα στον διακομιστή και τη συσκευή είναι σωστή;
   pagination:
     newer: Νεότερο
     next: Επόμενο
@@ -963,7 +1407,12 @@ el:
       unrecognized_emoji: δεν αναγνωρίζεται ως emoji
   relationships:
     activity: Δραστηριότητα λογαριασμού
+    confirm_follow_selected_followers: Είσαι βέβαιος ότι θες να ακολουθήσεις τους επιλεγμένους ακόλουθους;
+    confirm_remove_selected_followers: Είσαι βέβαιος ότι θες να αφαιρέσεις τους επιλεγμένους ακόλουθους;
+    confirm_remove_selected_follows: Είσαι βέβαιος ότι θες να αφαιρέσεις τα επιλεγμένα άτομα που ακολουθείς;
     dormant: Αδρανείς
+    follow_failure: Δεν ήταν δυνατή η παρακολούθηση ορισμένων από τους επιλεγμένους λογαριασμούς.
+    follow_selected_followers: Ακολουθήστε τους επιλεγμένους ακόλουθους
     followers: Σε ακολουθούν
     following: Ακολουθείς
     invited: Προσκεκλημένοι
@@ -984,6 +1433,9 @@ el:
       invalid_rules: δεν παραπέμπει σε έγκυρους κανόνες
   rss:
     content_warning: 'Προειδοποίηση περιεχομένου:'
+    descriptions:
+      account: Δημόσιες δημοσιεύσεις από @%{acct}
+      tag: 'Δημόσιες δημοσιεύσεις με ετικέτα #%{hashtag}'
   scheduled_statuses:
     over_daily_limit: Έχεις υπερβεί το όριο των %{limit} προγραμματισμένων τουτ για εκείνη τη μέρα
     over_total_limit: Έχεις υπερβεί το όριο των %{limit} προγραμματισμένων τουτ
@@ -992,33 +1444,47 @@ el:
     activity: Τελευταία δραστηριότητα
     browser: Φυλλομετρητής (Browser)
     browsers:
+      alipay: Alipay
       blackberry: BlackBerry
       chrome: Chrome
       edge: Microsoft Edge
+      electron: Electron
       firefox: Firefox
       generic: Άγνωστος φυλλομετρητής
+      huawei_browser: Huawei Browser
       ie: Internet Explorer
+      micro_messenger: MicroMessenger
+      nokia: Nokia S40 Ovi Browser
       opera: Opera
       otter: Otter
       phantom_js: PhantomJS
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Άγνωστος φυλλομετρητής
+      weibo: Weibo
     current_session: Τρέχουσα σύνδεση
     description: "%{browser} σε %{platform}"
     explanation: Αυτοί είναι οι φυλλομετρητές (browsers) που είναι συνδεδεμένοι στον λογαριασμό σου στο Mastodon αυτή τη στιγμή.
+    ip: IP
     platforms:
+      adobe_air: Adobe Air
       android: Android
       blackberry: BlackBerry
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: άγνωστη πλατφόρμα
+      unknown_platform: Άγνωστη Πλατφόρμα
+      windows: Windows
+      windows_mobile: Windows Mobile
+      windows_phone: Windows Phone
     revoke: Ανακάλεσε
     revoke_success: Η σύνδεση ανακλήθηκε επιτυχώς
     title: Σύνδεση
+    view_authentication_history: Δείτε το ιστορικό ταυτοποιήσεων του λογαριασμού σας
   settings:
     account: Λογαριασμός
     account_settings: Ρυθμίσεις λογαριασμού
@@ -1038,7 +1504,10 @@ el:
     preferences: Προτιμήσεις
     profile: Προφίλ
     relationships: Ακολουθείς και σε ακολουθούν
+    statuses_cleanup: Αυτοματοποιημένη διαγραφή δημοσιεύσεων
+    strikes: Ποινές από ομάδα συντονισμού
     two_factor_authentication: Πιστοποίηση 2 παραγόντων (2FA)
+    webauthn_authentication: Κλειδιά ασφαλείας
   statuses:
     attached:
       audio:
@@ -1080,7 +1549,9 @@ el:
     show_older: Εμφάνιση παλαιότερων
     show_thread: Εμφάνιση νήματος
     sign_in_to_participate: Συνδέσου για να συμμετάσχεις στη συζήτηση
+    title: '%{name}: "%{quote}"'
     visibilities:
+      direct: Άμεση
       private: Μόνο ακόλουθοι
       private_long: Εμφάνιση μόνο σε ακόλουθους
       public: Δημόσιο
@@ -1088,6 +1559,26 @@ el:
       unlisted: Μη καταχωρημένο
       unlisted_long: Βλέπει οποιοσδήποτε, αλλά δεν καταχωρείται στις δημόσιες ροές
   statuses_cleanup:
+    enabled: Αυτόματη διαγραφή παλιών δημοσιεύσεων
+    enabled_hint: Διαγράφει αυτόματα τις δημοσιεύσεις σας μόλις φτάσουν σε ένα καθορισμένο όριο ηλικίας, εκτός αν ταιριάζουν με μία από τις παρακάτω εξαιρέσεις
+    exceptions: Εξαιρέσεις
+    explanation: Επειδή η διαγραφή δημοσιέυσεων είναι μια κοστοβόρα διαδικασία, γίνεται σε αραιά τακτά διαστήματα, όταν ο διακομιστής δεν είναι ιδιαίτερα απασχολημένος. Γι' αυτό, οι δημοσιεύσεις σας μπορεί να διαγραφούν λίγο μετά το πέρας του ορίου ηλικίας.
+    ignore_favs: Αγνόηση αγαπημένων
+    ignore_reblogs: Αγνόηση ενισχύσεων
+    interaction_exceptions: Εξαιρέσεις βασισμένες σε αλληλεπιδράσεις
+    interaction_exceptions_explanation: Σημειώστε ότι δεν υπάρχει εγγύηση για πιθανή διαγραφή δημοσιεύσεων αν αυτά πέσουν κάτω από το όριο αγαπημένων ή ενισχύσεων ακόμα και αν κάποτε το είχαν ξεπεράσει.
+    keep_direct: Διατήρηση άμεσων μηνυμάτων
+    keep_direct_hint: Δεν διαγράφει κανένα από τα άμεσα μηνύματά σας
+    keep_media: Διατήρηση δημοσιεύσεων με συνημμένα πολυμέσων
+    keep_media_hint: Δεν διαγράφει καμία από τις δημοσιεύσεις σας που έχουν συνημμένα πολυμέσων
+    keep_pinned: Διατήρηση καρφιτσωμένων δημοσιεύσεων
+    keep_pinned_hint: Δεν διαγράφει καμία από τις καρφιτσωμένες δημοσιεύσεις σας
+    keep_polls: Διατήρηση δημοσκοπήσεων
+    keep_polls_hint: Δεν διαγράφει καμία από τις δημοσκοπήσεις σας
+    keep_self_bookmark: Διατήρηση δημοσιεύσεων που έχετε σελιδοδείκτη
+    keep_self_bookmark_hint: Δεν διαγράφει τις δημοσιεύσεις σας αν τις έχετε προσθέσει στους σελιδοδείκτες
+    keep_self_fav: Κρατήστε τις δημοσιεύσεις που έχετε στα αγαπημένες
+    keep_self_fav_hint: Δεν διαγράφει τις δημοσιεύσεις σας αν τις έχετε στα αγαπημένα
     min_age:
       '1209600': 2 εβδομάδες
       '15778476': 6 μήνες
@@ -1097,10 +1588,18 @@ el:
       '604800': 1 εβδομάδα
       '63113904': 2 χρόνια
       '7889238': 3 μήνες
+    min_age_label: Όριο ηλικίας
+    min_favs: Κρατήστε τις δημοσιεύσεις που έχουν γίνει αγαπημένες τουλάχιστον
+    min_favs_hint: Δεν διαγράφει καμία από τις δημοσιεύσεις σας που έχει λάβει τουλάχιστον αυτόν τον αριθμό αγαπημένων. Αφήστε κενό για να διαγράψετε δημοσιεύσεις ανεξάρτητα από τον αριθμό των αγαπημένων
+    min_reblogs: Διατήρηση δημοσιεύσεων που έχουν ενισχυθεί τουλάχιστον
+    min_reblogs_hint: Δεν διαγράφει καμία από τις δημοσιεύσεις σας που έχει λάβει τουλάχιστον αυτόν τον αριθμό ενισχύσεων. Αφήστε κενό για να διαγράψετε δημοσιεύσεις ανεξάρτητα από τον αριθμό των ενισχύσεων
   stream_entries:
     pinned: Καρφιτσωμένο τουτ
     reblogged: προωθημένο
     sensitive_content: Ευαίσθητο περιεχόμενο
+  strikes:
+    errors:
+      too_late: Είναι πολύ αργά για να κάνετε έφεση σε αυτήν την ποινή
   tags:
     does_not_match_previous_name: δεν ταιριάζει με το προηγούμενο όνομα
   themes:
@@ -1109,64 +1608,110 @@ el:
     mastodon-light: Mastodon (Ανοιχτόχρωμο)
   time:
     formats:
+      default: "%b %d, %Y, %H:%M"
+      month: "%b %Y"
       time: "%H:%M"
   two_factor_authentication:
     add: Προσθήκη
     disable: Απενεργοποίησε
+    disabled_success: Η ταυτοποίηση δύο παραγόντων απενεργοποιήθηκε επιτυχώς
     edit: Επεξεργασία
     enabled: Η πιστοποίηση 2 παραγόντων (2FA) είναι ενεργοποιημένη
     enabled_success: Η πιστοποίηση 2 παραγόντων (2FA) ενεργοποιήθηκε επιτυχώς
     generate_recovery_codes: Δημιούργησε κωδικούς ανάκτησης
     lost_recovery_codes: Οι κωδικοί ανάκτησης σου επιτρέπουν να ανακτήσεις ξανά πρόσβαση στον λογαριασμό σου αν χάσεις το τηλέφωνό σου. Αν έχεις χάσει τους κωδικούς ανάκτησης, μπορείς να τους δημιουργήσεις ξανά εδώ. Οι παλιοί κωδικοί σου θα ακυρωθούν.
+    methods: Μέθοδοι δύο παραγόντων
     otp: Εφαρμογή επαλήθευσης
     recovery_codes: Εφεδρικοί κωδικοί ανάκτησης
     recovery_codes_regenerated: Οι εφεδρικοί κωδικοί ανάκτησης δημιουργήθηκαν επιτυχώς
     recovery_instructions_html: Αν ποτέ δεν έχεις πρόσβαση στο κινητό σου, μπορείς να χρησιμοποιήσεις έναν από τους παρακάτω κωδικούς ανάκτησης για να αποκτήσεις πρόσβαση στο λογαριασμό σου. <strong>Διαφύλαξε τους κωδικούς ανάκτησης</strong>. Για παράδειγμα, μπορείς να τους εκτυπώσεις και να τους φυλάξεις μαζί με άλλα σημαντικά σου έγγραφα.
+    webauthn: Κλειδιά ασφαλείας
   user_mailer:
+    appeal_approved:
+      action: Μετάβαση στον λογαριασμό σας
+      explanation: Η έφεση της ποινής εναντίον του λογαριασμού σας στις %{strike_date}, που υποβάλλατε στις %{appeal_date}, έχει εγκριθεί. Ο λογαριασμός σας είναι και πάλι σε καλή κατάσταση.
+      subject: Η έφεση σας από τις %{date} έχει εγκριθεί
+      title: Έφεση εγκρίθηκε
+    appeal_rejected:
+      explanation: Η έφεση της ποινής εναντίον του λογαριασμού σας στις %{strike_date}, που υποβάλλατε στις %{appeal_date}, έχει απορριφθεί.
+      subject: Η έφεση σας από τις %{date} έχει απορριφθεί
+      title: Έφεση απορρίφθηκε
     backup_ready:
       explanation: Είχες ζητήσει εφεδρικό αντίγραφο του λογαριασμού σου στο Mastodon. Είναι έτοιμο για κατέβασμα!
       subject: Το εφεδρικό αντίγραφό σου είναι έτοιμο για κατέβασμα
       title: Λήψη εφεδρικού αρχείου
     suspicious_sign_in:
       change_password: αλλάξτε τον κωδικό πρόσβασής σας
+      details: 'Εδώ είναι οι λεπτομέρειες της σύνδεσης:'
+      explanation: Εντοπίσαμε μια σύνδεση στο λογαριασμό σας από μια νέα διεύθυνση IP.
+      further_actions_html: Αν δεν ήσασταν εσείς, σας συνιστούμε να %{action} αμέσως και να ενεργοποιήσετε τον έλεγχο ταυτοποίησης δύο παραγόντων για να διατηρήσετε τον λογαριασμό σας ασφαλή.
+      subject: Ο λογαριασμός σας έχει συνδεθεί από μια νέα διεύθυνση IP
+      title: Μια νέα σύνδεση
     warning:
+      appeal: Υποβολή έφεσης
+      appeal_description: Αν πιστεύετε ότι έγινε λάθος, μπορείτε να υποβάλετε μια αίτηση στο προσωπικό του %{instance}.
       categories:
         spam: Ανεπιθύμητο
+        violation: Το περιεχόμενο παραβιάζει τις ακόλουθες οδηγίες κοινότητας
+      explanation:
+        delete_statuses: Μερικές από τις δημοσιεύσεις σου έχουν βρεθεί να παραβιάζουν μία ή περισσότερες οδηγίες κοινότητας και έχουν συνεπώς αφαιρεθεί από τους συντονιστές του %{instance}.
+        disable: Δεν μπορείς πλέον να χρησιμοποιήσεις τον λογαριασμό σου, αλλά το προφίλ σου και άλλα δεδομένα παραμένουν άθικτα. Μπορείς να ζητήσετε ένα αντίγραφο ασφαλείας των δεδομένων σου, να αλλάξεις τις ρυθμίσεις του λογαριασμού σου ή να διαγράψεις τον λογαριασμό σου.
+        mark_statuses_as_sensitive: Μερικές από τις δημοσιεύσεις σου έχουν επισημανθεί ως ευαίσθητες από τους συντονιστές του %{instance}. Αυτό σημαίνει ότι οι άνθρωποι θα πρέπει να πατήσουν τα πολυμέσα στις δημοσιεύσεις πριν εμφανιστεί μια προεπισκόπηση. Μπορείς να επισημάνεις τα πολυμέσα ως ευαίσθητα όταν δημοσιεύεις στο μέλλον.
+        sensitive: Από δω και στο εξής, όλα τα μεταφορτωμένα αρχεία πολυμέσων σου θα επισημανθούν ως ευαίσθητα και κρυμμένα πίσω από μια προειδοποίηση πατήστε - για - εμφάνιση.
+        silence: Μπορείς ακόμα να χρησιμοποιείς το λογαριασμό σου, αλλά μόνο άτομα που σε ακολουθούν ήδη θα δουν τις δημοσιεύσεις σου σε αυτόν τον διακομιστή και μπορεί να αποκλειστείς από διάφορες δυνατότητες ανακάλυψης. Ωστόσο, οι άλλοι μπορούν ακόμα να σε ακολουθήσουν με μη αυτόματο τρόπο.
+        suspend: Δε μπορείς πλέον να χρησιμοποιήσεις τον λογαριασμό σου και το προφίλ σου και άλλα δεδομένα δεν είναι πλέον προσβάσιμα. Μπορείς ακόμα να συνδεθείς για να αιτηθείς αντίγραφο των δεδομένων σου μέχρι να αφαιρεθούν πλήρως σε περίπου 30 μέρες αλλά, θα διατηρήσουμε κάποια βασικά δεδομένα για να σε αποτρέψουμε να παρακάμψεις την αναστολή.
       reason: 'Αιτιολογία:'
+      statuses: 'Αναφερόμενες δημοσιεύσεις:'
       subject:
+        delete_statuses: Οι δημοσιεύσεις σου στον %{acct} έχουν αφαιρεθεί
         disable: Ο λογαριασμός σου %{acct} έχει παγώσει
+        mark_statuses_as_sensitive: Οι δημοσιεύσεις σου στον %{acct} έχουν επισημανθεί ως ευαίσθητες
         none: Προειδοποίηση προς %{acct}
+        sensitive: Οι δημοσιεύσεις σου στο %{acct} θα επισημανθούν ως ευαίσθητες από 'δω και στο εξής
         silence: Ο λογαριασμός σου %{acct} έχει περιοριστεί
         suspend: Ο λογαριασμός σου %{acct} έχει ανασταλεί
       title:
+        delete_statuses: Οι δημοσιεύσεις αφαιρέθηκαν
         disable: Παγωμένος λογαριασμός
+        mark_statuses_as_sensitive: Οι δημοσιέυσεις επισημάνθηκαν ως ευαίσθητες
         none: Προειδοποίηση
+        sensitive: Ο λογαριασμός επισημάνθηκε ως ευαίσθητος
         silence: Περιορισμένος λογαριασμός
         suspend: Λογαριασμός σε αναστολή
     welcome:
       edit_profile_action: Στήσιμο προφίλ
+      edit_profile_step: Μπορείς να προσαρμόσεις το προφίλ σου ανεβάζοντας μια εικόνα προφίλ, αλλάζοντας το εμφνιζόμενο όνομα και άλλα. Μπορείς να επιλέξεις να αξιολογείς νέους ακόλουθους πριν τους επιτραπεί να σε ακολουθήσουν.
       explanation: Μερικές συμβουλές για να ξεκινήσεις
       final_action: Ξεκίνα τις δημοσιεύσεις
+      final_step: 'Ξεκίνα να δημοσιεύεις! Ακόμα και χωρίς ακόλουθους τις δημόσιες δημοσιεύσεις σου μπορεί να τις δουν άλλοι, για παράδειγμα στην τοπική ροή ή στις ετικέτες. Ίσως να θέλεις να μας γνωρίσεις τον εαυτό σου με την ετικέτα #introductions.'
       full_handle: Το πλήρες όνομά σου
       full_handle_hint: Αυτό θα εδώ θα πεις στους φίλους σου για να σου μιλήσουν ή να σε ακολουθήσουν από άλλο κόμβο.
       subject: Καλώς ήρθες στο Mastodon
       title: Καλώς όρισες, %{name}!
   users:
     follow_limit_reached: Δεν μπορείς να ακολουθήσεις περισσότερα από %{limit} άτομα
+    go_to_sso_account_settings: Μεταβείτε στις ρυθμίσεις λογαριασμού του παρόχου ταυτότητας σας
     invalid_otp_token: Άκυρος κωδικός πιστοποίησης 2 παραγόντων (2FA)
     otp_lost_help_html: Αν χάσεις και τα δύο, μπορείς να επικοινωνήσεις με τον/την %{email}
     seamless_external_login: Επειδή έχεις συνδεθεί μέσω τρίτης υπηρεσίας, οι ρυθμίσεις συνθηματικού και email δεν είναι διαθέσιμες.
     signed_in_as: 'Έχεις συνδεθεί ως:'
   verification:
-    explanation_html: 'Μπορείς να <strong>πιστοποιήσεις τον εαυτό σου ως ιδιοκτήτη των συνδέσμων που εμφανίζεις στα μεταδεδομένα του προφίλ σου</strong>. Για να συμβεί αυτό, ο συνδεδεμένος ιστότοπος πρέπει να περιέχει ένα σύνδεσμο που να επιστρέφει προς το προφίλ σου στο Mastodon. Ο σύνδεσμος επιστροφής <strong>πρέπει</strong> περιέχει την ιδιότητα (attribute) <code>rel="me"</code>. Το περιεχόμενο του κειμένου δεν έχει σημασία. Για παράδειγμα:'
+    explanation_html: 'Μπορείς να <strong>επαληθεύσεις τον εαυτό σου ως ιδιοκτήτη των συνδέσμων που εμφανίζεις στα μεταδεδομένα του προφίλ σου</strong>. Για να συμβεί αυτό, ο συνδεδεμένος ιστότοπος πρέπει να περιέχει ένα σύνδεσμο που να επιστρέφει προς το προφίλ σου στο Mastodon. Ο σύνδεσμος επιστροφής <strong>πρέπει</strong> να περιέχει την ιδιότητα <code>rel="me"</code>. Το περιεχόμενο του κειμένου δεν έχει σημασία. Για παράδειγμα:'
     verification: Πιστοποίηση
   webauthn_credentials:
     add: Προσθήκη νέου κλειδιού ασφαλείας
     create:
+      error: Παρουσιάστηκε πρόβλημα κατά την προσθήκη του κλειδιού ασφαλείας. Παρακαλώ δοκίμασε ξανά.
       success: Το κλειδί ασφαλείας σας προστέθηκε με επιτυχία.
     delete: Διαγραφή
     delete_confirmation: Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το κλειδί ασφαλείας;
+    description_html: Αν ενεργοποιήσεις την <strong>ταυτοποίηση κλειδιού ασφαλείας</strong>, κατά τη σύνδεσή σου θα χρειαστεί να χρησιμοποιήσεις ένα από τα κλειδιά ασφαλείας σου.
     destroy:
+      error: Παρουσιάστηκε πρόβλημα κατά την διαγραφή του κλειδιού ασφαλείας. Παρακαλώ δοκίμασε ξανά.
       success: Το κλειδί ασφαλείας σας διαγράφηκε με επιτυχία.
     invalid_credential: Άκυρο κλειδί ασφαλείας
+    nickname_hint: Εισάγετε το ψευδώνυμο του νέου κλειδιού ασφαλείας σου
+    not_enabled: Δεν έχεις ενεργοποιήσει το WebAuthn ακόμα
+    not_supported: Αυτό το πρόγραμμα περιήγησης δεν υποστηρίζει κλειδιά ασφαλείας
+    otp_required: Για να χρησιμοποιήσεις κλειδιά ασφαλείας, ενεργοποιήσε πρώτα την ταυτοποίηση δύο παραγόντων.
     registered_on: Εγγραφή στις %{date}
diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml
index 4b0ee8773..db4f83b93 100644
--- a/config/locales/en-GB.yml
+++ b/config/locales/en-GB.yml
@@ -69,25 +69,1526 @@ en-GB:
       enable: Unfreeze
       enable_sign_in_token_auth: Enable e-mail token authentication
       enabled: Enabled
+      enabled_msg: Successfully unfroze %{username}'s account
+      followers: Followers
+      follows: Follows
+      header: Header
+      inbox_url: Inbox URL
+      invite_request_text: Reasons for joining
+      invited_by: Invited by
+      ip: IP
+      joined: Joined
+      location:
+        all: All
+        local: Local
+        remote: Remote
+        title: Location
+      login_status: Login status
+      media_attachments: Media attachments
+      memorialize: Turn into memoriam
+      memorialized: Memorialised
+      memorialized_msg: Successfully turned %{username} into a memorial account
+      moderation:
+        active: Active
+        all: All
+        disabled: Disabled
+        pending: Pending
+        silenced: Limited
+        suspended: Suspended
+        title: Moderation
+      moderation_notes: Moderation notes
+      most_recent_activity: Most recent activity
+      most_recent_ip: Most recent IP
+      no_account_selected: No accounts were changed as none were selected
+      no_limits_imposed: No limits imposed
+      no_role_assigned: No role assigned
+      not_subscribed: Not subscribed
+      pending: Pending review
+      perform_full_suspension: Suspend
+      previous_strikes: Previous strikes
+      previous_strikes_description_html:
+        one: This account has <strong>one</strong> strike.
+        other: This account has <strong>%{count}</strong> strikes.
+      promote: Promote
+      protocol: Protocol
+      public: Public
+      push_subscription_expires: PuSH subscription expires
+      redownload: Refresh profile
+      redownloaded_msg: Successfully refreshed %{username}'s profile from origin
+      reject: Reject
+      rejected_msg: Successfully rejected %{username}'s sign-up application
+      remote_suspension_irreversible: The data of this account has been irreversibly deleted.
+      remote_suspension_reversible_hint_html: The account has been suspended on their server, and the data will be fully removed on %{date}. Until then, the remote server can restore this account without any ill effects. If you wish to remove all of the account's data immediately, you can do so below.
+      remove_avatar: Remove avatar
+      remove_header: Remove header
+      removed_avatar_msg: Successfully removed %{username}'s avatar image
+      removed_header_msg: Successfully removed %{username}'s header image
+      resend_confirmation:
+        already_confirmed: This user is already confirmed
+        send: Resend confirmation email
+        success: Confirmation email successfully sent!
+      reset: Reset
+      reset_password: Reset password
+      resubscribe: Resubscribe
+      role: Role
+      search: Search
+      search_same_email_domain: Other users with the same e-mail domain
+      search_same_ip: Other users with the same IP
+      security: Security
+      security_measures:
+        only_password: Only password
+        password_and_2fa: Password and 2FA
+      sensitive: Force-sensitive
+      sensitized: Marked as sensitive
+      shared_inbox_url: Shared inbox URL
+      show:
+        created_reports: Made reports
+        targeted_reports: Reported by others
+      silence: Limit
+      silenced: Limited
+      statuses: Posts
+      strikes: Previous strikes
+      subscribe: Subscribe
+      suspend: Suspend
+      suspended: Suspended
+      suspension_irreversible: The data of this account has been irreversibly deleted. You can unsuspend the account to make it usable but it will not recover any data it previously had.
+      suspension_reversible_hint_html: The account has been suspended, and the data will be fully removed on %{date}. Until then, the account can be restored without any ill effects. If you wish to remove all of the account's data immediately, you can do so below.
+      title: Accounts
+      unblock_email: Unblock email address
+      unblocked_email_msg: Successfully unblocked %{username}'s email address
+      unconfirmed_email: Unconfirmed email
+      undo_sensitized: Undo force-sensitive
+      undo_silenced: Undo limit
+      undo_suspension: Undo suspension
+      unsilenced_msg: Successfully undid limit of %{username}'s account
+      unsubscribe: Unsubscribe
+      unsuspended_msg: Successfully unsuspended %{username}'s account
+      username: Username
+      view_domain: View summary for domain
+      warn: Warn
+      web: Web
+      whitelisted: Allowed for federation
+    action_logs:
+      action_types:
+        approve_appeal: Approve Appeal
+        approve_user: Approve User
+        assigned_to_self_report: Assign Report
+        change_email_user: Change E-mail for User
+        change_role_user: Change Role of User
+        confirm_user: Confirm User
+        create_account_warning: Create Warning
+        create_announcement: Create Announcement
+        create_canonical_email_block: Create E-mail Block
+        create_custom_emoji: Create Custom Emoji
+        create_domain_allow: Create Domain Allow
+        create_domain_block: Create Domain Block
+        create_email_domain_block: Create E-mail Domain Block
+        create_ip_block: Create IP rule
+        create_unavailable_domain: Create Unavailable Domain
+        create_user_role: Create Role
+        demote_user: Demote User
+        destroy_announcement: Delete Announcement
+        destroy_canonical_email_block: Delete E-mail Block
+        destroy_custom_emoji: Delete Custom Emoji
+        destroy_domain_allow: Delete Domain Allow
+        destroy_domain_block: Delete Domain Block
+        destroy_email_domain_block: Delete E-mail Domain Block
+        destroy_instance: Purge Domain
+        destroy_ip_block: Delete IP rule
+        destroy_status: Delete Post
+        destroy_unavailable_domain: Delete Unavailable Domain
+        destroy_user_role: Destroy Role
+        disable_2fa_user: Disable 2FA
+        disable_custom_emoji: Disable Custom Emoji
+        disable_sign_in_token_auth_user: Disable E-mail Token Authentication for User
+        disable_user: Disable User
+        enable_custom_emoji: Enable Custom Emoji
+        enable_sign_in_token_auth_user: Enable E-mail Token Authentication for User
+        enable_user: Enable User
+        memorialize_account: Memorialise Account
+        promote_user: Promote User
+        reject_appeal: Reject Appeal
+        reject_user: Reject User
+        remove_avatar_user: Remove Avatar
+        reopen_report: Reopen Report
+        resend_user: Resend Confirmation Mail
+        reset_password_user: Reset Password
+        resolve_report: Resolve Report
+        sensitive_account: Force-Sensitive Account
+        silence_account: Limit Account
+        suspend_account: Suspend Account
+        unassigned_report: Unassign Report
+        unblock_email_account: Unblock email address
+        unsensitive_account: Undo Force-Sensitive Account
+        unsilence_account: Undo Limit Account
+        unsuspend_account: Unsuspend Account
+        update_announcement: Update Announcement
+        update_custom_emoji: Update Custom Emoji
+        update_domain_block: Update Domain Block
+        update_ip_block: Update IP rule
+        update_status: Update Post
+        update_user_role: Update Role
+      actions:
+        approve_appeal_html: "%{name} approved moderation decision appeal from %{target}"
+        approve_user_html: "%{name} approved sign-up from %{target}"
+        assigned_to_self_report_html: "%{name} assigned report %{target} to themselves"
+        change_email_user_html: "%{name} changed the e-mail address of user %{target}"
+        change_role_user_html: "%{name} changed role of %{target}"
+        confirm_user_html: "%{name} confirmed e-mail address of user %{target}"
+        create_account_warning_html: "%{name} sent a warning to %{target}"
+        create_announcement_html: "%{name} created new announcement %{target}"
+        create_canonical_email_block_html: "%{name} blocked e-mail with the hash %{target}"
+        create_custom_emoji_html: "%{name} uploaded new emoji %{target}"
+        create_domain_allow_html: "%{name} allowed federation with domain %{target}"
+        create_domain_block_html: "%{name} blocked domain %{target}"
+        create_email_domain_block_html: "%{name} blocked e-mail domain %{target}"
+        create_ip_block_html: "%{name} created rule for IP %{target}"
+        create_unavailable_domain_html: "%{name} stopped delivery to domain %{target}"
+        create_user_role_html: "%{name} created %{target} role"
+        demote_user_html: "%{name} demoted user %{target}"
+        destroy_announcement_html: "%{name} deleted announcement %{target}"
+        destroy_canonical_email_block_html: "%{name} unblocked e-mail with the hash %{target}"
+        destroy_custom_emoji_html: "%{name} deleted emoji %{target}"
+        destroy_domain_allow_html: "%{name} disallowed federation with domain %{target}"
+        destroy_domain_block_html: "%{name} unblocked domain %{target}"
+        destroy_email_domain_block_html: "%{name} unblocked e-mail domain %{target}"
+        destroy_instance_html: "%{name} purged domain %{target}"
+        destroy_ip_block_html: "%{name} deleted rule for IP %{target}"
+        destroy_status_html: "%{name} removed post by %{target}"
+        destroy_unavailable_domain_html: "%{name} stopped delivery to domain %{target}"
+        destroy_user_role_html: "%{name} deleted %{target} role"
+        disable_2fa_user_html: "%{name} disabled two factor requirement for user %{target}"
+        disable_custom_emoji_html: "%{name} disabled emoji %{target}"
+        disable_sign_in_token_auth_user_html: "%{name} disabled e-mail token authentication for %{target}"
+        disable_user_html: "%{name} disabled login for user %{target}"
+        enable_custom_emoji_html: "%{name} enabled emoji %{target}"
+        enable_sign_in_token_auth_user_html: "%{name} enabled e-mail token authentication for %{target}"
+        enable_user_html: "%{name} enabled login for user %{target}"
+        memorialize_account_html: "%{name} turned %{target}'s account into a memoriam page"
+        promote_user_html: "%{name} promoted user %{target}"
+        reject_appeal_html: "%{name} rejected moderation decision appeal from %{target}"
+        reject_user_html: "%{name} rejected sign-up from %{target}"
+        remove_avatar_user_html: "%{name} removed %{target}'s avatar"
+        reopen_report_html: "%{name} reopened report %{target}"
+        resend_user_html: "%{name} resent confirmation e-mail for %{target}"
+        reset_password_user_html: "%{name} reset password of user %{target}"
+        resolve_report_html: "%{name} resolved report %{target}"
+        sensitive_account_html: "%{name} marked %{target}'s media as sensitive"
+        silence_account_html: "%{name} limited %{target}'s account"
+        suspend_account_html: "%{name} suspended %{target}'s account"
+        unassigned_report_html: "%{name} unassigned report %{target}"
+        unblock_email_account_html: "%{name} unblocked %{target}'s email address"
+        unsensitive_account_html: "%{name} unmarked %{target}'s media as sensitive"
+        unsilence_account_html: "%{name} undid limit of %{target}'s account"
+        unsuspend_account_html: "%{name} unsuspended %{target}'s account"
+        update_announcement_html: "%{name} updated announcement %{target}"
+        update_custom_emoji_html: "%{name} updated emoji %{target}"
+        update_domain_block_html: "%{name} updated domain block for %{target}"
+        update_ip_block_html: "%{name} changed rule for IP %{target}"
+        update_status_html: "%{name} updated post by %{target}"
+        update_user_role_html: "%{name} changed %{target} role"
+      deleted_account: deleted account
+      empty: No logs found.
+      filter_by_action: Filter by action
+      filter_by_user: Filter by user
+      title: Audit log
+    announcements:
+      destroyed_msg: Announcement successfully deleted!
+      edit:
+        title: Edit announcement
+      empty: No announcements found.
+      live: Live
+      new:
+        create: Create announcement
+        title: New announcement
+      publish: Publish
+      published_msg: Announcement successfully published!
+      scheduled_for: Scheduled for %{time}
+      scheduled_msg: Announcement scheduled for publication!
+      title: Announcements
+      unpublish: Unpublish
+      unpublished_msg: Announcement successfully unpublished!
+      updated_msg: Announcement successfully updated!
+    custom_emojis:
+      assign_category: Assign category
+      by_domain: Domain
+      copied_msg: Successfully created local copy of the emoji
+      copy: Copy
+      copy_failed_msg: Could not make a local copy of that emoji
+      create_new_category: Create new category
+      created_msg: Emoji successfully created!
+      delete: Delete
+      destroyed_msg: Emojo successfully destroyed!
+      disable: Disable
+      disabled: Disabled
+      disabled_msg: Successfully disabled that emoji
+      emoji: Emoji
+      enable: Enable
+      enabled: Enabled
+      enabled_msg: Successfully enabled that emoji
+      image_hint: PNG or GIF up to %{size}
+      list: List
+      listed: Listed
+      new:
+        title: Add new custom emoji
+      no_emoji_selected: No emojis were changed as none were selected
+      not_permitted: You are not permitted to perform this action
+      overwrite: Overwrite
+      shortcode: Shortcode
+      shortcode_hint: At least 2 characters, only alphanumeric characters and underscores
+      title: Custom emojis
+      uncategorized: Uncategorised
+      unlist: Unlist
+      unlisted: Unlisted
+      update_failed_msg: Could not update that emoji
+      updated_msg: Emoji successfully updated!
+      upload: Upload
+    dashboard:
+      active_users: active users
+      interactions: interactions
+      media_storage: Media storage
+      new_users: new users
+      opened_reports: reports opened
+      pending_appeals_html:
+        one: "<strong>%{count}</strong> pending appeal"
+        other: "<strong>%{count}</strong> pending appeals"
+      pending_reports_html:
+        one: "<strong>%{count}</strong> pending report"
+        other: "<strong>%{count}</strong> pending reports"
+      pending_tags_html:
+        one: "<strong>%{count}</strong> pending hashtag"
+        other: "<strong>%{count}</strong> pending hashtags"
+      pending_users_html:
+        one: "<strong>%{count}</strong> pending user"
+        other: "<strong>%{count}</strong> pending users"
+      resolved_reports: reports resolved
+      software: Software
+      sources: Sign-up sources
+      space: Space usage
+      title: Dashboard
+      top_languages: Top active languages
+      top_servers: Top active servers
+      website: Website
+    disputes:
+      appeals:
+        empty: No appeals found.
+        title: Appeals
+    domain_allows:
+      add_new: Allow federation with domain
+      created_msg: Domain has been successfully allowed for federation
+      destroyed_msg: Domain has been disallowed from federation
+      export: Export
+      import: Import
+      undo: Disallow federation with domain
+    domain_blocks:
+      add_new: Add new domain block
+      created_msg: Domain block is now being processed
+      destroyed_msg: Domain block has been undone
+      domain: Domain
+      edit: Edit domain block
+      existing_domain_block: You have already imposed stricter limits on %{name}.
+      existing_domain_block_html: You have already imposed stricter limits on %{name}, you need to <a href="%{unblock_url}">unblock it</a> first.
+      export: Export
+      import: Import
+      new:
+        create: Create block
+        hint: The domain block will not prevent creation of account entries in the database, but will retroactively and automatically apply specific moderation methods on those accounts.
+        severity:
+          desc_html: "<strong>Limit</strong> will make posts from accounts at this domain invisible to anyone who isn't following them. <strong>Suspend</strong> will remove all content, media, and profile data for this domain's accounts from your server. Use <strong>None</strong> if you just want to reject media files."
+          noop: None
+          silence: Limit
+          suspend: Suspend
+        title: New domain block
+      no_domain_block_selected: No domain blocks were changed as none were selected
+      not_permitted: You are not permitted to perform this action
+      obfuscate: Obfuscate domain name
+      obfuscate_hint: Partially obfuscate the domain name in the list if advertising the list of domain limitations is enabled
+      private_comment: Private comment
+      private_comment_hint: Comment about this domain limitation for internal use by the moderators.
+      public_comment: Public comment
+      public_comment_hint: Comment about this domain limitation for the general public, if advertising the list of domain limitations is enabled.
+      reject_media: Reject media files
+      reject_media_hint: Removes locally stored media files and refuses to download any in the future. Irrelevant for suspensions
+      reject_reports: Reject reports
+      reject_reports_hint: Ignore all reports coming from this domain. Irrelevant for suspensions
+      undo: Undo domain block
+      view: View domain block
+    email_domain_blocks:
+      add_new: Add new
+      attempts_over_week:
+        one: "%{count} attempt over the last week"
+        other: "%{count} sign-up attempts over the last week"
+      created_msg: Successfully blocked e-mail domain
+      delete: Delete
+      dns:
+        types:
+          mx: MX record
+      domain: Domain
+      new:
+        create: Add domain
+        resolve: Resolve domain
+        title: Block new e-mail domain
+      no_email_domain_block_selected: No e-mail domain blocks were changed as none were selected
+      not_permitted: Not permitted
+      resolved_dns_records_hint_html: The domain name resolves to the following MX domains, which are ultimately responsible for accepting e-mail. Blocking an MX domain will block sign-ups from any e-mail address which uses the same MX domain, even if the visible domain name is different. <strong>Be careful not to block major e-mail providers.</strong>
+      resolved_through_html: Resolved through %{domain}
+      title: Blocked e-mail domains
+    export_domain_allows:
+      new:
+        title: Import domain allows
+      no_file: No file selected
+    export_domain_blocks:
+      import:
+        description_html: You are about to import a list of domain blocks. Please review this list very carefully, especially if you have not authored this list yourself.
+        existing_relationships_warning: Existing follow relationships
+        private_comment_description_html: 'To help you track where imported blocks come from, imported blocks will be created with the following private comment: <q>%{comment}</q>'
+        private_comment_template: Imported from %{source} on %{date}
+        title: Import domain blocks
+      invalid_domain_block: 'One or more domain blocks were skipped because of the following error(s): %{error}'
+      new:
+        title: Import domain blocks
+      no_file: No file selected
+    follow_recommendations:
+      description_html: "<strong>Follow recommendations help new users quickly find interesting content</strong>. When a user has not interacted with others enough to form personalised follow recommendations, these accounts are recommended instead. They are re-calculated on a daily basis from a mix of accounts with the highest recent engagements and highest local follower counts for a given language."
+      language: For language
+      status: Status
+      suppress: Suppress follow recommendation
+      suppressed: Suppressed
+      title: Follow recommendations
+      unsuppress: Restore follow recommendation
+    instances:
+      availability:
+        description_html:
+          one: If delivering to the domain fails <strong>%{count} day</strong> without succeeding, no further delivery attempts will be made unless a delivery <em>from</em> the domain is received.
+          other: If delivering to the domain fails on <strong>%{count} different days</strong> without succeeding, no further delivery attempts will be made unless a delivery <em>from</em> the domain is received.
+        failure_threshold_reached: Failure threshold reached on %{date}.
+        failures_recorded:
+          one: Failed attempt on %{count} day.
+          other: Failed attempts on %{count} different days.
+        no_failures_recorded: No failures on record.
+        title: Availability
+        warning: The last attempt to connect to this server has been unsuccessful
+      back_to_all: All
+      back_to_limited: Limited
+      back_to_warning: Warning
+      by_domain: Domain
+      confirm_purge: Are you sure you want to permanently delete data from this domain?
+      content_policies:
+        comment: Internal note
+        description_html: You can define content policies that will be applied to all accounts from this domain and any of its subdomains.
+        limited_federation_mode_description_html: You can chose whether to allow federation with this domain.
+        policies:
+          reject_media: Reject media
+          reject_reports: Reject reports
+          silence: Limit
+          suspend: Suspend
+        policy: Policy
+        reason: Public reason
+        title: Content policies
+      dashboard:
+        instance_accounts_dimension: Most followed accounts
+        instance_accounts_measure: stored accounts
+        instance_followers_measure: our followers there
+        instance_follows_measure: their followers here
+        instance_languages_dimension: Top languages
+        instance_media_attachments_measure: stored media attachments
+        instance_reports_measure: reports about them
+        instance_statuses_measure: stored posts
+      delivery:
+        all: All
+        clear: Clear delivery errors
+        failing: Failing
+        restart: Restart delivery
+        stop: Stop delivery
+        unavailable: Unavailable
+      delivery_available: Delivery is available
+      delivery_error_days: Delivery error days
+      delivery_error_hint: If delivery is not possible for %{count} days, it will be automatically marked as undeliverable.
+      destroyed_msg: Data from %{domain} is now queued for imminent deletion.
+      empty: No domains found.
+      known_accounts:
+        one: "%{count} known account"
+        other: "%{count} known accounts"
+      moderation:
+        all: All
+        limited: Limited
+        title: Moderation
+      private_comment: Private comment
+      public_comment: Public comment
+      purge: Purge
+      purge_description_html: If you believe this domain is offline for good, you can delete all account records and associated data from this domain from your storage. This may take a while.
+      title: Federation
+      total_blocked_by_us: Blocked by us
+      total_followed_by_them: Followed by them
+      total_followed_by_us: Followed by us
+      total_reported: Reports about them
+      total_storage: Media attachments
+      totals_time_period_hint_html: The totals displayed below include data for all time.
+    invites:
+      deactivate_all: Deactivate all
+      filter:
+        all: All
+        available: Available
+        expired: Expired
+        title: Filter
+      title: Invites
+    ip_blocks:
+      add_new: Create rule
+      created_msg: Successfully added new IP rule
+      delete: Delete
+      expires_in:
+        '1209600': 2 weeks
+        '15778476': 6 months
+        '2629746': 1 month
+        '31556952': 1 year
+        '86400': 1 day
+        '94670856': 3 years
+      new:
+        title: Create new IP rule
+      no_ip_block_selected: No IP rules were changed as none were selected
+      title: IP rules
+    relationships:
+      title: "%{acct}'s relationships"
+    relays:
+      add_new: Add new relay
+      delete: Delete
+      description_html: A <strong>federation relay</strong> is an intermediary server that exchanges large volumes of public posts between servers that subscribe and publish to it. <strong>It can help small and medium servers discover content from the fediverse</strong>, which would otherwise require local users manually following other people on remote servers.
+      disable: Disable
+      disabled: Disabled
+      enable: Enable
+      enable_hint: Once enabled, your server will subscribe to all public posts from this relay, and will begin sending this server's public posts to it.
+      enabled: Enabled
+      inbox_url: Relay URL
+      pending: Waiting for relay's approval
+      save_and_enable: Save and enable
+      setup: Setup a relay connection
+      signatures_not_enabled: Relays may not work correctly while secure mode or limited federation mode is enabled
+      status: Status
+      title: Relays
+    report_notes:
+      created_msg: Report note successfully created!
+      destroyed_msg: Report note successfully deleted!
+    reports:
+      account:
+        notes:
+          one: "%{count} note"
+          other: "%{count} notes"
+      action_log: Audit log
+      action_taken_by: Action taken by
+      actions:
+        delete_description_html: The reported posts will be deleted and a strike will be recorded to help you escalate on future infractions by the same account.
+        mark_as_sensitive_description_html: The media in the reported posts will be marked as sensitive and a strike will be recorded to help you escalate on future infractions by the same account.
+        other_description_html: See more options for controlling the account's behaviour and customise communication to the reported account.
+        resolve_description_html: No action will be taken against the reported account, no strike recorded, and the report will be closed.
+        silence_description_html: The account will be visible only to those who already follow it or manually look it up, severely limiting its reach. Can always be reverted. Closes all reports against this account.
+        suspend_description_html: The account and all its contents will be inaccessible and eventually deleted, and interacting with it will be impossible. Reversible within 30 days. Closes all reports against this account.
+      actions_description_html: Decide which action to take to resolve this report. If you take a punitive action against the reported account, an e-mail notification will be sent to them, except when the <strong>Spam</strong> category is selected.
+      actions_description_remote_html: Decide which action to take to resolve this report. This will only affect how <strong>your</strong> server communicates with this remote account and handle its content.
+      add_to_report: Add more to report
+      are_you_sure: Are you sure?
+      assign_to_self: Assign to me
+      assigned: Assigned moderator
+      by_target_domain: Domain of reported account
+      cancel: Cancel
+      category: Category
+      category_description_html: The reason this account and/or content was reported will be cited in communication with the reported account
+      comment:
+        none: None
+      comment_description_html: 'To provide more information, %{name} wrote:'
+      confirm: Confirm
+      confirm_action: Confirm moderation action against @%{acct}
+      created_at: Reported
+      delete_and_resolve: Delete posts
+      forwarded: Forwarded
+      forwarded_to: Forwarded to %{domain}
+      mark_as_resolved: Mark as resolved
+      mark_as_sensitive: Mark as sensitive
+      mark_as_unresolved: Mark as unresolved
+      no_one_assigned: No one
+      notes:
+        create: Add note
+        create_and_resolve: Resolve with note
+        create_and_unresolve: Reopen with note
+        delete: Delete
+        placeholder: Describe what actions have been taken, or any other related updates...
+        title: Notes
+      notes_description_html: View and leave notes to other moderators and your future self
+      processed_msg: 'Report #%{id} successfully processed'
+      quick_actions_description_html: 'Take a quick action or scroll down to see reported content:'
+      remote_user_placeholder: the remote user from %{instance}
+      reopen: Reopen report
+      report: 'Report #%{id}'
+      reported_account: Reported account
+      reported_by: Reported by
+      resolved: Resolved
+      resolved_msg: Report successfully resolved!
+      skip_to_actions: Skip to actions
+      status: Status
+      statuses: Reported content
+      statuses_description_html: Offending content will be cited in communication with the reported account
+      summary:
+        action_preambles:
+          delete_html: 'You are about to <strong>remove</strong> some of <strong>@%{acct}</strong>''s posts. This will:'
+          mark_as_sensitive_html: 'You are about to <strong>mark</strong> some of <strong>@%{acct}</strong>''s posts as <strong>sensitive</strong>. This will:'
+          silence_html: 'You are about to <strong>limit</strong> <strong>@%{acct}</strong>''s account. This will:'
+          suspend_html: 'You are about to <strong>suspend</strong> <strong>@%{acct}</strong>''s account. This will:'
+        actions:
+          delete_html: Remove the offending posts
+          mark_as_sensitive_html: Mark the offending posts' media as sensitive
+          silence_html: Severely limit <strong>@%{acct}</strong>'s reach by making their profile and contents only visible to people already following them or manually looking it profile up
+          suspend_html: Suspend <strong>@%{acct}</strong>, making their profile and contents inaccessible and impossible to interact with
+        close_report: 'Mark report #%{id} as resolved'
+        close_reports_html: Mark <strong>all</strong> reports against <strong>@%{acct}</strong> as resolved
+        delete_data_html: Delete <strong>@%{acct}</strong>'s profile and contents 30 days from now unless they get unsuspended in the meantime
+        preview_preamble_html: "<strong>@%{acct}</strong> will receive a warning with the following contents:"
+        record_strike_html: Record a strike against <strong>@%{acct}</strong> to help you escalate on future violations from this account
+        send_email_html: Send <strong>@%{acct}</strong> a warning e-mail
+        warning_placeholder: Optional additional reasoning for the moderation action.
+      target_origin: Origin of reported account
+      title: Reports
+      unassign: Unassign
+      unknown_action_msg: 'Unknown action: %{action}'
+      unresolved: Unresolved
+      updated_at: Updated
+      view_profile: View profile
     roles:
+      add_new: Add role
+      assigned_users:
+        one: "%{count} user"
+        other: "%{count} users"
       categories:
+        administration: Administration
         devops: DevOps
+        invites: Invites
+        moderation: Moderation
+        special: Special
+      delete: Delete
+      description_html: With <strong>user roles</strong>, you can customize which functions and areas of Mastodon your users can access.
+      edit: Edit '%{name}' role
+      everyone: Default permissions
+      everyone_full_description_html: This is the <strong>base role</strong> affecting <strong>all users</strong>, even those without an assigned role. All other roles inherit permissions from it.
+      permissions_count:
+        one: "%{count} permission"
+        other: "%{count} permissions"
       privileges:
+        administrator: Administrator
+        administrator_description: Users with this permission will bypass every permission
+        delete_user_data: Delete User Data
+        delete_user_data_description: Allows users to delete other users' data without delay
+        invite_users: Invite Users
+        invite_users_description: Allows users to invite new people to the server
+        manage_announcements: Manage Announcements
+        manage_announcements_description: Allows users to manage announcements on the server
+        manage_appeals: Manage Appeals
+        manage_appeals_description: Allows users to review appeals against moderation actions
+        manage_blocks: Manage Blocks
+        manage_blocks_description: Allows users to block e-mail providers and IP addresses
+        manage_custom_emojis: Manage Custom Emojis
+        manage_custom_emojis_description: Allows users to manage custom emojis on the server
+        manage_federation: Manage Federation
+        manage_federation_description: Allows users to block or allow federation with other domains, and control deliverability
+        manage_invites: Manage Invites
+        manage_invites_description: Allows users to browse and deactivate invite links
+        manage_reports: Manage Reports
+        manage_reports_description: Allows users to review reports and perform moderation actions against them
+        manage_roles: Manage Roles
+        manage_roles_description: Allows users to manage and assign roles below theirs
+        manage_rules: Manage Rules
+        manage_rules_description: Allows users to change server rules
+        manage_settings: Manage Settings
+        manage_settings_description: Allows users to change site settings
+        manage_taxonomies: Manage Taxonomies
+        manage_taxonomies_description: Allows users to review trending content and update hashtag settings
+        manage_user_access: Manage User Access
+        manage_user_access_description: Allows users to disable other users' two-factor authentication, change their e-mail address, and reset their password
+        manage_users: Manage Users
+        manage_users_description: Allows users to view other users' details and perform moderation actions against them
+        manage_webhooks: Manage Webhooks
+        manage_webhooks_description: Allows users to set up webhooks for administrative events
+        view_audit_log: View Audit Log
+        view_audit_log_description: Allows users to see a history of administrative actions on the server
+        view_dashboard: View Dashboard
+        view_dashboard_description: Allows users to access the dashboard and various metrics
         view_devops: DevOps
+        view_devops_description: Allows users to access Sidekiq and pgHero dashboards
+      title: Roles
+    rules:
+      add_new: Add rule
+      delete: Delete
+      description_html: While most claim to have read and agree to the terms of service, usually people do not read through until after a problem arises. <strong>Make it easier to see your server's rules at a glance by providing them in a flat bullet point list.</strong> Try to keep individual rules short and simple, but try not to split them up into many separate items either.
+      edit: Edit rule
+      empty: No server rules have been defined yet.
+      title: Server rules
+    settings:
+      about:
+        manage_rules: Manage server rules
+      registrations:
+        title: Registrations
+      registrations_mode:
+        modes:
+          approved: Approval required for sign up
+          none: Nobody can sign up
+          open: Anyone can sign up
+      title: Server Settings
+    site_uploads:
+      delete: Delete uploaded file
+      destroyed_msg: Site upload successfully deleted!
+    statuses:
+      account: Author
+      application: Application
+      back_to_account: Back to account page
+      back_to_report: Back to report page
+      batch:
+        remove_from_report: Remove from report
+        report: Report
+      deleted: Deleted
+      favourites: Favourites
+      history: Version history
+      in_reply_to: Replying to
+    trends:
+      tags:
+        listable: Can be suggested
+        no_tag_selected: No tags were changed as none were selected
+        not_listable: Won't be suggested
+        not_trendable: Won't appear under trends
+        not_usable: Cannot be used
+        peaked_on_and_decaying: Peaked on %{date}, now decaying
+        title: Trending hashtags
+        trendable: Can appear under trends
+        trending_rank: 'Trending #%{rank}'
+        usable: Can be used
+        usage_comparison: Used %{today} times today, compared to %{yesterday} yesterday
+        used_by_over_week:
+          one: Used by one person over the last week
+          other: Used by %{count} people over the last week
+      title: Trends
+      trending: Trending
+    warning_presets:
+      add_new: Add new
+      delete: Delete
+      edit_preset: Edit warning preset
+      empty: You haven't defined any warning presets yet.
+      title: Manage warning presets
+    webhooks:
+      add_new: Add endpoint
+      delete: Delete
+      description_html: A <strong>webhook</strong> enables Mastodon to push <strong>real-time notifications</strong> about chosen events to your own application, so your application can <strong>automatically trigger reactions</strong>.
+      disable: Disable
+      disabled: Disabled
+      edit: Edit endpoint
+      empty: You don't have any webhook endpoints configured yet.
+      enable: Enable
+      enabled: Active
+      enabled_events:
+        one: 1 enabled event
+        other: "%{count} enabled events"
+      events: Events
+      new: New webhook
+      rotate_secret: Rotate secret
+      secret: Signing secret
+      status: Status
+      title: Webhooks
+      webhook: Webhook
+  admin_mailer:
+    new_appeal:
+      actions:
+        delete_statuses: to delete their posts
+        disable: to freeze their account
+        mark_statuses_as_sensitive: to mark their posts as sensitive
+        none: a warning
+        sensitive: to mark their account as sensitive
+        silence: to limit their account
+        suspend: to suspend their account
+      body: "%{target} is appealing a moderation decision by %{action_taken_by} from %{date}, which was %{type}. They wrote:"
+      next_steps: You can approve the appeal to undo the moderation decision, or ignore it.
+      subject: "%{username} is appealing a moderation decision on %{instance}"
+    new_pending_account:
+      body: The details of the new account are below. You can approve or reject this application.
+      subject: New account up for review on %{instance} (%{username})
+    new_report:
+      body: "%{reporter} has reported %{target}"
+      body_remote: Someone from %{domain} has reported %{target}
+      subject: New report for %{instance} (#%{id})
+    new_trends:
+      body: 'The following items need a review before they can be displayed publicly:'
+      new_trending_links:
+        title: Trending links
+      new_trending_statuses:
+        title: Trending posts
+      new_trending_tags:
+        no_approved_tags: There are currently no approved trending hashtags.
+        requirements: 'Any of these candidates could surpass the #%{rank} approved trending hashtag, which is currently #%{lowest_tag_name} with a score of %{lowest_tag_score}.'
+        title: Trending hashtags
+      subject: New trends up for review on %{instance}
+  aliases:
+    add_new: Create alias
+    created_msg: Successfully created a new alias. You can now initiate the move from the old account.
+    deleted_msg: Successfully removed the alias. Moving from that account to this one will no longer be possible.
+    empty: You have no aliases.
+    hint_html: If you want to move from another account to this one, here you can create an alias, which is required before you can proceed with moving followers from the old account to this one. This action by itself is <strong>harmless and reversible</strong>. <strong>The account migration is initiated from the old account</strong>.
+    remove: Unlink alias
+  appearance:
+    advanced_web_interface: Advanced web interface
+    advanced_web_interface_hint: 'If you want to make use of your entire screen width, the advanced web interface allows you to configure many different columns to see as much information at the same time as you want: Home, notifications, federated timeline, any number of lists and hashtags.'
+    animations_and_accessibility: Animations and accessibility
+    confirmation_dialogs: Confirmation dialogues
+    discovery: Discovery
+    localization:
+      body: Mastodon is translated by volunteers.
+      guide_link: https://crowdin.com/project/mastodon
+      guide_link_text: Everyone can contribute.
+    sensitive_content: Sensitive content
+    toot_layout: Post layout
+  application_mailer:
+    notification_preferences: Change e-mail preferences
+    salutation: "%{name},"
+    settings: 'Change e-mail preferences: %{link}'
+    view: 'View:'
+    view_profile: View profile
+    view_status: View post
+  applications:
+    created: Application successfully created
+    destroyed: Application successfully deleted
+    logout: Logout
+    regenerate_token: Regenerate access token
+    token_regenerated: Access token successfully regenerated
+    warning: Be very careful with this data. Never share it with anyone!
+    your_token: Your access token
+  auth:
+    apply_for_account: Request an account
+    change_password: Password
+    confirmations:
+      wrong_email_hint: If that e-mail address is not correct, you can change it in account settings.
+    delete_account: Delete account
+    delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation.
+    description:
+      prefix_invited_by_user: "@%{name} invites you to join this server of Mastodon!"
+      prefix_sign_up: Sign up on Mastodon today!
+      suffix: With an account, you will be able to follow people, post updates and exchange messages with users from any Mastodon server and more!
+    didnt_get_confirmation: Didn't receive confirmation instructions?
+    dont_have_your_security_key: Don't have your security key?
+    forgot_password: Forgot your password?
+    invalid_reset_password_token: Password reset token is invalid or expired. Please request a new one.
+    link_to_otp: Enter a two-factor code from your phone or a recovery code
+    link_to_webauth: Use your security key device
+    log_in_with: Log in with
+    login: Log in
+    logout: Logout
+    migrate_account: Move to a different account
+    migrate_account_html: If you wish to redirect this account to a different one, you can <a href="%{path}">configure it here</a>.
+    or_log_in_with: Or log in with
+    privacy_policy_agreement_html: I have read and agree to the <a href="%{privacy_policy_path}" target="_blank">privacy policy</a>
+    providers:
+      cas: CAS
+      saml: SAML
+    register: Sign up
+    registration_closed: "%{instance} is not accepting new members"
+    resend_confirmation: Resend confirmation instructions
+    reset_password: Reset password
+    rules:
+      accept: Accept
+      back: Back
+      preamble: These are set and enforced by the %{domain} moderators.
+      title: Some ground rules.
+    security: Security
+    set_new_password: Set new password
+    setup:
+      email_below_hint_html: If the below e-mail address is incorrect, you can change it here and receive a new confirmation e-mail.
+      email_settings_hint_html: The confirmation e-mail was sent to %{email}. If that e-mail address is not correct, you can change it in account settings.
+      title: Setup
+    sign_in:
+      preamble_html: Sign in with your <strong>%{domain}</strong> credentials. If your account is hosted on a different server, you will not be able to log in here.
+      title: Sign in to %{domain}
+    sign_up:
+      preamble: With an account on this Mastodon server, you'll be able to follow any other person on the network, regardless of where their account is hosted.
+      title: Let's get you set up on %{domain}.
+    status:
+      account_status: Account status
+      confirming: Waiting for e-mail confirmation to be completed.
+      functional: Your account is fully operational.
+      pending: Your application is pending review by our staff. This may take some time. You will receive an e-mail if your application is approved.
+      redirecting_to: Your account is inactive because it is currently redirecting to %{acct}.
+      view_strikes: View past strikes against your account
+    too_fast: Form submitted too fast, try again.
+    use_security_key: Use security key
+  authorize_follow:
+    already_following: You are already following this account
+    already_requested: You have already sent a follow request to that account
+    error: Unfortunately, there was an error looking up the remote account
+    follow: Follow
+    follow_request: 'You have sent a follow request to:'
+    following: 'Success! You are now following:'
+    post_follow:
+      close: Or, you can just close this window.
+      return: Show the user's profile
+      web: Go to web
+    title: Follow %{acct}
+  challenge:
+    confirm: Continue
+    hint_html: "<strong>Tip:</strong> We won't ask you for your password again for the next hour."
+    invalid_password: Invalid password
+    prompt: Confirm password to continue
+  crypto:
+    errors:
+      invalid_key: is not a valid Ed25519 or Curve25519 key
+      invalid_signature: is not a valid Ed25519 signature
+  date:
+    formats:
+      default: "%b %d, %Y"
+      with_month_name: "%B %d, %Y"
+  datetime:
+    distance_in_words:
+      about_x_hours: "%{count}h"
+      about_x_months: "%{count}mo"
+      about_x_years: "%{count}y"
+      almost_x_years: "%{count}y"
+      half_a_minute: Just now
+      less_than_x_minutes: "%{count}m"
+      less_than_x_seconds: Just now
+      over_x_years: "%{count}y"
+      x_days: "%{count}d"
+      x_minutes: "%{count}m"
+      x_months: "%{count}mo"
+      x_seconds: "%{count}s"
+  deletes:
+    challenge_not_passed: The information you entered was not correct
+    confirm_password: Enter your current password to verify your identity
+    confirm_username: Enter your username to confirm the procedure
+    proceed: Delete account
+    success_msg: Your account was successfully deleted
+    warning:
+      before: 'Before proceeding, please read these notes carefully:'
+      caches: Content that has been cached by other servers may persist
+      data_removal: Your posts and other data will be permanently removed
+      email_change_html: You can <a href="%{path}">change your e-mail address</a> without deleting your account
+      email_contact_html: If it still doesn't arrive, you can e-mail <a href="mailto:%{email}">%{email}</a> for help
+      email_reconfirmation_html: If you are not receiving the confirmation e-mail, you can <a href="%{path}">request it again</a>
+      irreversible: You will not be able to restore or reactivate your account
+      more_details_html: For more details, see the <a href="%{terms_path}">privacy policy</a>.
+      username_available: Your username will become available again
+      username_unavailable: Your username will remain unavailable
+  disputes:
+    strikes:
+      action_taken: Action taken
+      appeal: Appeal
+      appeal_approved: This strike has been successfully appealed and is no longer valid
+      appeal_rejected: The appeal has been rejected
+      appeal_submitted_at: Appeal submitted
+      appealed_msg: Your appeal has been submitted. If it is approved, you will be notified.
+      appeals:
+        submit: Submit appeal
+      approve_appeal: Approve appeal
+      associated_report: Associated report
+      created_at: Dated
+      description_html: These are actions taken against your account and warnings that have been sent to you by the staff of %{instance}.
+      recipient: Addressed to
+      reject_appeal: Reject appeal
+      status: 'Post #%{id}'
+      status_removed: Post already removed from system
+      title: "%{action} from %{date}"
+      title_actions:
+        delete_statuses: Post removal
+        disable: Freezing of account
+        mark_statuses_as_sensitive: Marking of posts as sensitive
+        none: Warning
+        sensitive: Marking of account as sensitive
+        silence: Limitation of account
+        suspend: Suspension of account
+      your_appeal_approved: Your appeal has been approved
+      your_appeal_pending: You have submitted an appeal
+      your_appeal_rejected: Your appeal has been rejected
+  domain_validator:
+    invalid_domain: is not a valid domain name
   errors:
     '400': The request you submitted was invalid or malformed.
     '403': You don't have permission to view this page.
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
+    '422':
+      content: Security verification failed. Are you blocking cookies?
+      title: Security verification failed
     '429': Too many requests
-    '500': 
+    '500':
+      content: We're sorry, but something went wrong on our end.
+      title: This page is not correct
     '503': The page could not be served due to a temporary server failure.
+    noscript_html: To use the Mastodon web application, please enable JavaScript. Alternatively, try one of the <a href="%{apps_path}">native apps</a> for Mastodon for your platform.
+  existing_username_validator:
+    not_found: could not find a local user with that username
+    not_found_multiple: could not find %{usernames}
+  exports:
+    archive_takeout:
+      date: Date
+      download: Download your archive
+      hint_html: You can request an archive of your <strong>posts and uploaded media</strong>. The exported data will be in the ActivityPub format, readable by any compliant software. You can request an archive every 7 days.
+      in_progress: Compiling your archive...
+      request: Request your archive
+      size: Size
+    blocks: You block
+    bookmarks: Bookmarks
+    csv: CSV
+    domain_blocks: Domain blocks
+    lists: Lists
+    mutes: You mute
+    storage: Media storage
+  featured_tags:
+    add_new: Add new
+    errors:
+      limit: You have already featured the maximum number of hashtags
+    hint_html: "<strong>What are featured hashtags?</strong> They are displayed prominently on your public profile and allow people to browse your public posts specifically under those hashtags. They are a great tool for keeping track of creative works or long-term projects."
+  filters:
+    contexts:
+      account: Profiles
+      home: Home and lists
+      notifications: Notifications
+      public: Public timelines
+      thread: Conversations
+    edit:
+      add_keyword: Add keyword
+      keywords: Keywords
+      statuses: Individual posts
+      statuses_hint_html: This filter applies to select individual posts regardless of whether they match the keywords below. <a href="%{path}">Review or remove posts from the filter</a>.
+      title: Edit filter
+    errors:
+      deprecated_api_multiple_keywords: These parameters cannot be changed from this application because they apply to more than one filter keyword. Use a more recent application or the web interface.
+      invalid_context: None or invalid context supplied
+    index:
+      contexts: Filters in %{contexts}
+      delete: Delete
+      empty: You have no filters.
+      expires_in: Expires in %{distance}
+      expires_on: Expires on %{date}
+      keywords:
+        one: "%{count} keyword"
+        other: "%{count} keywords"
+      statuses:
+        one: "%{count} post"
+        other: "%{count} posts"
+      statuses_long:
+        one: "%{count} individual post hidden"
+        other: "%{count} individual posts hidden"
+      title: Filters
+    new:
+      save: Save new filter
+      title: Add new filter
+    statuses:
+      back_to_filter: Back to filter
+      batch:
+        remove: Remove from filter
+      index:
+        hint: This filter applies to select individual posts regardless of other criteria. You can add more posts to this filter from the web interface.
+        title: Filtered posts
+  generic:
+    all: All
+    all_items_on_page_selected_html:
+      one: "<strong>%{count}</strong> item on this page is selected."
+      other: All <strong>%{count}</strong> items on this page are selected.
+    all_matching_items_selected_html:
+      one: "<strong>%{count}</strong> item matching your search is selected."
+      other: All <strong>%{count}</strong> items matching your search are selected.
+    changes_saved_msg: Changes successfully saved!
+    copy: Copy
+    delete: Delete
+    deselect: Deselect all
+    none: None
+    order_by: Order by
+    save_changes: Save changes
+    select_all_matching_items:
+      one: Select %{count} item matching your search.
+      other: Select all %{count} items matching your search.
+    today: today
+    validation_errors:
+      one: Something isn't quite right yet! Please review the error below
+      other: Something isn't quite right yet! Please review %{count} errors below
+  imports:
+    errors:
+      invalid_csv_file: 'Invalid CSV file. Error: %{error}'
+      over_rows_processing_limit: contains more than %{count} rows
+    modes:
+      merge: Merge
+      merge_long: Keep existing records and add new ones
+      overwrite: Overwrite
+      overwrite_long: Replace current records with the new ones
+    preface: You can import data that you have exported from another server, such as a list of the people you are following or blocking.
+    success: Your data was successfully uploaded and will be processed in due time
+    types:
+      blocking: Blocking list
+      bookmarks: Bookmarks
+      domain_blocking: Domain blocking list
+      following: Following list
+      muting: Muting list
+    upload: Upload
+  invites:
+    delete: Deactivate
+    expired: Expired
+    expires_in:
+      '1800': 30 minutes
+      '21600': 6 hours
+      '3600': 1 hour
+      '43200': 12 hours
+      '604800': 1 week
+      '86400': 1 day
+    expires_in_prompt: Never
+    generate: Generate invite link
+    invited_by: 'You were invited by:'
+    max_uses:
+      one: 1 use
+      other: "%{count} uses"
+    max_uses_prompt: No limit
+    prompt: Generate and share links with others to grant access to this server
+    table:
+      expires_at: Expires
+      uses: Uses
+    title: Invite people
+  lists:
+    errors:
+      limit: You have reached the maximum number of lists
+  login_activities:
+    authentication_methods:
+      otp: two-factor authentication app
+      password: password
+      sign_in_token: e-mail security code
+      webauthn: security keys
+    description_html: If you see activity that you don't recognise, consider changing your password and enabling two-factor authentication.
+    empty: No authentication history available
+    failed_sign_in_html: Failed sign-in attempt with %{method} from %{ip} (%{browser})
+    successful_sign_in_html: Successful sign-in with %{method} from %{ip} (%{browser})
+    title: Authentication history
+  media_attachments:
+    validations:
+      images_and_video: Cannot attach a video to a post that already contains images
+      not_ready: Cannot attach files that have not finished processing. Try again in a moment!
+      too_many: Cannot attach more than 4 files
+  migrations:
+    acct: Moved to
+    cancel: Cancel redirect
+    cancel_explanation: Cancelling the redirect will re-activate your current account, but will not bring back followers that have been moved to that account.
+    cancelled_msg: Successfully cancelled the redirect.
+    errors:
+      already_moved: is the same account you have already moved to
+      missing_also_known_as: is not an alias of this account
+      move_to_self: cannot be current account
+      not_found: could not be found
+      on_cooldown: You are on cooldown
+    followers_count: Followers at time of move
+    incoming_migrations: Moving from a different account
+    incoming_migrations_html: To move from another account to this one, first you need to <a href="%{path}">create an account alias</a>.
+    moved_msg: Your account is now redirecting to %{acct} and your followers are being moved over.
+    not_redirecting: Your account is not redirecting to any other account currently.
+    on_cooldown: You have recently migrated your account. This function will become available again in %{count} days.
+    past_migrations: Past migrations
+    proceed_with_move: Move followers
+    redirected_msg: Your account is now redirecting to %{acct}.
+    redirecting_to: Your account is redirecting to %{acct}.
+    set_redirect: Set redirect
+    warning:
+      backreference_required: The new account must first be configured to back-reference this one
+      before: 'Before proceeding, please read these notes carefully:'
+      cooldown: After moving there is a waiting period during which you will not be able to move again
+      disabled_account: Your current account will not be fully usable afterwards. However, you will have access to data export as well as re-activation.
+      followers: This action will move all followers from the current account to the new account
+      only_redirect_html: Alternatively, you can <a href="%{path}">only put up a redirect on your profile</a>.
+      other_data: No other data will be moved automatically
+      redirect: Your current account's profile will be updated with a redirect notice and be excluded from searches
+  moderation:
+    title: Moderation
+  move_handler:
+    carry_blocks_over_text: This user moved from %{acct}, which you had blocked.
+    carry_mutes_over_text: This user moved from %{acct}, which you had muted.
+    copy_account_note_text: 'This user moved from %{acct}, here were your previous notes about them:'
+  navigation:
+    toggle_menu: Toggle menu
+  notification_mailer:
+    admin:
+      report:
+        subject: "%{name} submitted a report"
+      sign_up:
+        subject: "%{name} signed up"
+    favourite:
+      body: 'Your post was favourited by %{name}:'
+      subject: "%{name} favourited your post"
+      title: New favourite
+    follow:
+      body: "%{name} is now following you!"
+      subject: "%{name} is now following you"
+      title: New follower
+    follow_request:
+      action: Manage follow requests
+      body: "%{name} has requested to follow you"
+      subject: 'Pending follower: %{name}'
+      title: New follow request
+    mention:
+      action: Reply
+      body: 'You were mentioned by %{name} in:'
+      subject: You were mentioned by %{name}
+      title: New mention
+    poll:
+      subject: A poll by %{name} has ended
+    reblog:
+      body: 'Your post was boosted by %{name}:'
+      subject: "%{name} boosted your post"
+      title: New boost
+    status:
+      subject: "%{name} just posted"
+    update:
+      subject: "%{name} edited a post"
+  notifications:
+    email_events: Events for e-mail notifications
+    email_events_hint: 'Select events that you want to receive notifications for:'
+    other_settings: Other notifications settings
+  number:
+    human:
+      decimal_units:
+        format: "%n%u"
+        units:
+          billion: B
+          million: M
+          quadrillion: Q
+          thousand: K
+          trillion: T
+  otp_authentication:
+    code_hint: Enter the code generated by your authenticator app to confirm
+    description_html: If you enable <strong>two-factor authentication</strong> using an authenticator app, logging in will require you to be in possession of your phone, which will generate tokens for you to enter.
+    enable: Enable
+    instructions_html: "<strong>Scan this QR code into Google Authenticator or a similar TOTP app on your phone</strong>. From now on, that app will generate tokens that you will have to enter when logging in."
+    manual_instructions: 'If you can''t scan the QR code and need to enter it manually, here is the plain-text secret:'
+    setup: Set up
+    wrong_code: The entered code was invalid! Are server time and device time correct?
+  pagination:
+    newer: Newer
+    next: Next
+    older: Older
+    prev: Prev
+    truncate: "&hellip;"
+  polls:
+    errors:
+      already_voted: You have already voted on this poll
+      duplicate_options: contain duplicate items
+      duration_too_long: is too far into the future
+      duration_too_short: is too soon
+      expired: The poll has already ended
+      invalid_choice: The chosen vote option does not exist
+      over_character_limit: cannot be longer than %{max} characters each
+      too_few_options: must have more than one item
+      too_many_options: can't contain more than %{max} items
+  preferences:
+    other: Other
+    posting_defaults: Posting defaults
+    public_timelines: Public timelines
+  privacy_policy:
+    title: Privacy Policy
+  reactions:
+    errors:
+      limit_reached: Limit of different reactions reached
+      unrecognized_emoji: is not a recognised emoji
+  relationships:
+    activity: Account activity
+    confirm_follow_selected_followers: Are you sure you want to follow selected followers?
+    confirm_remove_selected_followers: Are you sure you want to remove selected followers?
+    confirm_remove_selected_follows: Are you sure you want to remove selected follows?
+    dormant: Dormant
+    follow_failure: Could not follow some of the selected accounts.
+    follow_selected_followers: Follow selected followers
+    followers: Followers
+    following: Following
+    invited: Invited
+    last_active: Last active
+    most_recent: Most recent
+    moved: Moved
+    mutual: Mutual
+    primary: Primary
+    relationship: Relationship
+    remove_selected_domains: Remove all followers from the selected domains
+    remove_selected_followers: Remove selected followers
+    remove_selected_follows: Unfollow selected users
+    status: Account status
+  remote_follow:
+    missing_resource: Could not find the required redirect URL for your account
+  reports:
+    errors:
+      invalid_rules: does not reference valid rules
+  rss:
+    content_warning: 'Content warning:'
+    descriptions:
+      account: Public posts from @%{acct}
+      tag: 'Public posts tagged #%{hashtag}'
+  scheduled_statuses:
+    over_daily_limit: You have exceeded the limit of %{limit} scheduled posts for today
+    over_total_limit: You have exceeded the limit of %{limit} scheduled posts
+    too_soon: The scheduled date must be in the future
   sessions:
+    activity: Last activity
+    browser: Browser
     browsers:
+      alipay: Alipay
       blackberry: BlackBerry
+      chrome: Chrome
+      edge: Microsoft Edge
+      electron: Electron
+      firefox: Firefox
+      generic: Unknown browser
+      huawei_browser: Huawei Browser
+      ie: Internet Explorer
+      micro_messenger: MicroMessenger
+      nokia: Nokia S40 Ovi Browser
+      opera: Opera
+      otter: Otter
+      phantom_js: PhantomJS
+      qq: QQ Browser
+      safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Unknown Browser
+      weibo: Weibo
+    current_session: Current session
+    description: "%{browser} on %{platform}"
+    explanation: These are the web browsers currently logged in to your Mastodon account.
+    ip: IP
     platforms:
+      adobe_air: Adobe Air
+      android: Android
       blackberry: BlackBerry
       chrome_os: ChromeOS
+      firefox_os: Firefox OS
+      ios: iOS
+      kai_os: KaiOS
+      linux: Linux
+      mac: macOS
+      unknown_platform: Unknown Platform
+      windows: Windows
+      windows_mobile: Windows Mobile
+      windows_phone: Windows Phone
+    revoke: Revoke
+    revoke_success: Session successfully revoked
+    title: Sessions
+    view_authentication_history: View authentication history of your account
+  settings:
+    account: Account
+    account_settings: Account settings
+    aliases: Account aliases
+    appearance: Appearance
+    authorized_apps: Authorised apps
+    back: Back to Mastodon
+    delete: Account deletion
+    development: Development
+    edit_profile: Edit profile
+    export: Data export
+    featured_tags: Featured hashtags
+    import: Import
+    import_and_export: Import and export
+    migrate: Account migration
+    notifications: Notifications
+    preferences: Preferences
+    profile: Profile
+    relationships: Follows and followers
+    statuses_cleanup: Automated post deletion
+    strikes: Moderation strikes
+    two_factor_authentication: Two-factor Auth
+    webauthn_authentication: Security keys
+  statuses:
+    attached:
+      audio:
+        one: "%{count} audio"
+        other: "%{count} audio"
+      description: 'Attached: %{attached}'
+      image:
+        one: "%{count} image"
+        other: "%{count} images"
+      video:
+        one: "%{count} video"
+        other: "%{count} videos"
+    boosted_from_html: Boosted from %{acct_link}
+    content_warning: 'Content warning: %{warning}'
+    default_language: Same as interface language
+    disallowed_hashtags:
+      one: 'contained a disallowed hashtag: %{tags}'
+      other: 'contained the disallowed hashtags: %{tags}'
+    edited_at_html: Edited %{date}
+    errors:
+      in_reply_not_found: The post you are trying to reply to does not appear to exist.
+    open_in_web: Open in web
+    over_character_limit: character limit of %{max} exceeded
+    pin_errors:
+      direct: Posts that are only visible to mentioned users cannot be pinned
+      limit: You have already pinned the maximum number of posts
+      ownership: Someone else's post cannot be pinned
+      reblog: A boost cannot be pinned
+    poll:
+      total_people:
+        one: "%{count} person"
+        other: "%{count} people"
+      total_votes:
+        one: "%{count} vote"
+        other: "%{count} votes"
+      vote: Vote
+    show_more: Show more
+    show_newer: Show newer
+    show_older: Show older
+    show_thread: Show thread
+    sign_in_to_participate: Sign in to participate in the conversation
+    title: '%{name}: "%{quote}"'
+    visibilities:
+      direct: Direct
+      private: Followers-only
+      private_long: Only show to followers
+      public: Public
+      public_long: Everyone can see
+      unlisted: Unlisted
+      unlisted_long: Everyone can see, but not listed on public timelines
+  statuses_cleanup:
+    enabled: Automatically delete old posts
+    enabled_hint: Automatically deletes your posts once they reach a specified age threshold, unless they match one of the exceptions below
+    exceptions: Exceptions
+    explanation: Because deleting posts is an expensive operation, this is done slowly over time when the server is not otherwise busy. For this reason, your posts may be deleted a while after they reach the age threshold.
+    ignore_favs: Ignore favourites
+    ignore_reblogs: Ignore boosts
+    interaction_exceptions: Exceptions based on interactions
+    interaction_exceptions_explanation: Note that there is no guarantee for posts to be deleted if they go below the favourite or boost threshold after having once gone over them.
+    keep_direct: Keep direct messages
+    keep_direct_hint: Doesn't delete any of your direct messages
+    keep_media: Keep posts with media attachments
+    keep_media_hint: Doesn't delete any of your posts that have media attachments
+    keep_pinned: Keep pinned posts
+    keep_pinned_hint: Doesn't delete any of your pinned posts
+    keep_polls: Keep polls
+    keep_polls_hint: Doesn't delete any of your polls
+    keep_self_bookmark: Keep posts you bookmarked
+    keep_self_bookmark_hint: Doesn't delete your own posts if you have bookmarked them
+    keep_self_fav: Keep posts you favourited
+    keep_self_fav_hint: Doesn't delete your own posts if you have favourited them
+    min_age:
+      '1209600': 2 weeks
+      '15778476': 6 months
+      '2629746': 1 month
+      '31556952': 1 year
+      '5259492': 2 months
+      '604800': 1 week
+      '63113904': 2 years
+      '7889238': 3 months
+    min_age_label: Age threshold
+    min_favs: Keep posts favourited at least
+    min_favs_hint: Doesn't delete any of your posts that has received at least this number of favourites. Leave blank to delete posts regardless of their number of favourites
+    min_reblogs: Keep posts boosted at least
+    min_reblogs_hint: Doesn't delete any of your posts that has been boosted at least this number of times. Leave blank to delete posts regardless of their number of boosts
+  stream_entries:
+    pinned: Pinned post
+    reblogged: boosted
+    sensitive_content: Sensitive content
+  strikes:
+    errors:
+      too_late: It is too late to appeal this strike
+  tags:
+    does_not_match_previous_name: does not match the previous name
+  themes:
+    contrast: Mastodon (High contrast)
+    default: Mastodon (Dark)
+    mastodon-light: Mastodon (Light)
+  time:
+    formats:
+      default: "%b %d, %Y, %H:%M"
+      month: "%b %Y"
+      time: "%H:%M"
+  two_factor_authentication:
+    add: Add
+    disable: Disable 2FA
+    disabled_success: Two-factor authentication successfully disabled
+    edit: Edit
+    enabled: Two-factor authentication is enabled
+    enabled_success: Two-factor authentication successfully enabled
+    generate_recovery_codes: Generate recovery codes
+    lost_recovery_codes: Recovery codes allow you to regain access to your account if you lose your phone. If you've lost your recovery codes, you can regenerate them here. Your old recovery codes will be invalidated.
+    methods: Two-factor methods
+    otp: Authenticator app
+    recovery_codes: Backup recovery codes
+    recovery_codes_regenerated: Recovery codes successfully regenerated
+    recovery_instructions_html: If you ever lose access to your phone, you can use one of the recovery codes below to regain access to your account. <strong>Keep the recovery codes safe</strong>. For example, you may print them and store them with other important documents.
+    webauthn: Security keys
+  user_mailer:
+    appeal_approved:
+      action: Go to your account
+      explanation: The appeal of the strike against your account on %{strike_date} that you submitted on %{appeal_date} has been approved. Your account is once again in good standing.
+      subject: Your appeal from %{date} has been approved
+      title: Appeal approved
+    appeal_rejected:
+      explanation: The appeal of the strike against your account on %{strike_date} that you submitted on %{appeal_date} has been rejected.
+      subject: Your appeal from %{date} has been rejected
+      title: Appeal rejected
+    backup_ready:
+      explanation: You requested a full backup of your Mastodon account. It's now ready for download!
+      subject: Your archive is ready for download
+      title: Archive takeout
+    suspicious_sign_in:
+      change_password: change your password
+      details: 'Here are details of the sign-in:'
+      explanation: We've detected a sign-in to your account from a new IP address.
+      further_actions_html: If this wasn't you, we recommend that you %{action} immediately and enable two-factor authentication to keep your account secure.
+      subject: Your account has been accessed from a new IP address
+      title: A new sign-in
+    warning:
+      appeal: Submit an appeal
+      appeal_description: If you believe this is an error, you can submit an appeal to the staff of %{instance}.
+      categories:
+        spam: Spam
+        violation: Content violates the following community guidelines
+      explanation:
+        delete_statuses: Some of your posts have been found to violate one or more community guidelines and have been subsequently removed by the moderators of %{instance}.
+        disable: You can no longer use your account, but your profile and other data remains intact. You can request a backup of your data, change account settings or delete your account.
+        mark_statuses_as_sensitive: Some of your posts have been marked as sensitive by the moderators of %{instance}. This means that people will need to tap the media in the posts before a preview is displayed. You can mark media as sensitive yourself when posting in the future.
+        sensitive: From now on, all your uploaded media files will be marked as sensitive and hidden behind a click-through warning.
+        silence: You can still use your account but only people who are already following you will see your posts on this server, and you may be excluded from various discovery features. However, others may still manually follow you.
+        suspend: You can no longer use your account, and your profile and other data are no longer accessible. You can still login to request a backup of your data until the data is fully removed in about 30 days, but we will retain some basic data to prevent you from evading the suspension.
+      reason: 'Reason:'
+      statuses: 'Posts cited:'
+      subject:
+        delete_statuses: Your posts on %{acct} have been removed
+        disable: Your account %{acct} has been frozen
+        mark_statuses_as_sensitive: Your posts on %{acct} have been marked as sensitive
+        none: Warning for %{acct}
+        sensitive: Your posts on %{acct} will be marked as sensitive from now on
+        silence: Your account %{acct} has been limited
+        suspend: Your account %{acct} has been suspended
+      title:
+        delete_statuses: Posts removed
+        disable: Account frozen
+        mark_statuses_as_sensitive: Posts marked as sensitive
+        none: Warning
+        sensitive: Account marked as sensitive
+        silence: Account limited
+        suspend: Account suspended
+    welcome:
+      edit_profile_action: Setup profile
+      edit_profile_step: You can customise your profile by uploading a profile picture, changing your display name and more. You can opt-in to review new followers before they’re allowed to follow you.
+      explanation: Here are some tips to get you started
+      final_action: Start posting
+      final_step: 'Start posting! Even without followers, your public posts may be seen by others, for example on the local timeline or in hashtags. You may want to introduce yourself on the #introductions hashtag.'
+      full_handle: Your full handle
+      full_handle_hint: This is what you would tell your friends so they can message or follow you from another server.
+      subject: Welcome to Mastodon
+      title: Welcome aboard, %{name}!
+  users:
+    follow_limit_reached: You cannot follow more than %{limit} people
+    go_to_sso_account_settings: Go to your identity provider's account settings
+    invalid_otp_token: Invalid two-factor code
+    otp_lost_help_html: If you lost access to both, you may get in touch with %{email}
+    seamless_external_login: You are logged in via an external service, so password and e-mail settings are not available.
+    signed_in_as: 'Signed in as:'
+  verification:
+    explanation_html: 'You can <strong>verify yourself as the owner of the links in your profile metadata</strong>. For that, the linked website must contain a link back to your Mastodon profile. After adding the link you may need to come back here and re-save your profile for the verification to take effect. The link back <strong>must</strong> have a <code>rel="me"</code> attribute. The text content of the link does not matter. Here is an example:'
+    verification: Verification
+  webauthn_credentials:
+    add: Add new security key
+    create:
+      error: There was a problem adding your security key. Please try again.
+      success: Your security key was successfully added.
+    delete: Delete
+    delete_confirmation: Are you sure you want to delete this security key?
+    description_html: If you enable <strong>security key authentication</strong>, logging in will require you to use one of your security keys.
+    destroy:
+      error: There was a problem deleting you security key. Please try again.
+      success: Your security key was successfully deleted.
+    invalid_credential: Invalid security key
+    nickname_hint: Enter the nickname of your new security key
+    not_enabled: You haven't enabled WebAuthn yet
+    not_supported: This browser doesn't support security keys
+    otp_required: To use security keys please enable two-factor authentication first.
+    registered_on: Registered on %{date}
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 763110c77..fa7fb6be0 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -91,6 +91,7 @@ en:
       moderation:
         active: Active
         all: All
+        disabled: Disabled
         pending: Pending
         silenced: Limited
         suspended: Suspended
@@ -133,6 +134,7 @@ en:
       search: Search
       search_same_email_domain: Other users with the same e-mail domain
       search_same_ip: Other users with the same IP
+      security: Security
       security_measures:
         only_password: Only password
         password_and_2fa: Password and 2FA
@@ -427,6 +429,7 @@ en:
         resolve: Resolve domain
         title: Block new e-mail domain
       no_email_domain_block_selected: No e-mail domain blocks were changed as none were selected
+      not_permitted: Not permitted
       resolved_dns_records_hint_html: The domain name resolves to the following MX domains, which are ultimately responsible for accepting e-mail. Blocking an MX domain will block sign-ups from any e-mail address which uses the same MX domain, even if the visible domain name is different. <strong>Be careful not to block major e-mail providers.</strong>
       resolved_through_html: Resolved through %{domain}
       title: Blocked e-mail domains
@@ -441,6 +444,7 @@ en:
         private_comment_description_html: 'To help you track where imported blocks come from, imported blocks will be created with the following private comment: <q>%{comment}</q>'
         private_comment_template: Imported from %{source} on %{date}
         title: Import domain blocks
+      invalid_domain_block: 'One or more domain blocks were skipped because of the following error(s): %{error}'
       new:
         title: Import domain blocks
       no_file: No file selected
@@ -472,6 +476,7 @@ en:
       content_policies:
         comment: Internal note
         description_html: You can define content policies that will be applied to all accounts from this domain and any of its subdomains.
+        limited_federation_mode_description_html: You can chose whether to allow federation with this domain.
         policies:
           reject_media: Reject media
           reject_reports: Reject reports
@@ -584,11 +589,14 @@ en:
       assign_to_self: Assign to me
       assigned: Assigned moderator
       by_target_domain: Domain of reported account
+      cancel: Cancel
       category: Category
       category_description_html: The reason this account and/or content was reported will be cited in communication with the reported account
       comment:
         none: None
       comment_description_html: 'To provide more information, %{name} wrote:'
+      confirm: Confirm
+      confirm_action: Confirm moderation action against @%{acct}
       created_at: Reported
       delete_and_resolve: Delete posts
       forwarded: Forwarded
@@ -605,6 +613,7 @@ en:
         placeholder: Describe what actions have been taken, or any other related updates...
         title: Notes
       notes_description_html: View and leave notes to other moderators and your future self
+      processed_msg: 'Report #%{id} successfully processed'
       quick_actions_description_html: 'Take a quick action or scroll down to see reported content:'
       remote_user_placeholder: the remote user from %{instance}
       reopen: Reopen report
@@ -617,9 +626,28 @@ en:
       status: Status
       statuses: Reported content
       statuses_description_html: Offending content will be cited in communication with the reported account
+      summary:
+        action_preambles:
+          delete_html: 'You are about to <strong>remove</strong> some of <strong>@%{acct}</strong>''s posts. This will:'
+          mark_as_sensitive_html: 'You are about to <strong>mark</strong> some of <strong>@%{acct}</strong>''s posts as <strong>sensitive</strong>. This will:'
+          silence_html: 'You are about to <strong>limit</strong> <strong>@%{acct}</strong>''s account. This will:'
+          suspend_html: 'You are about to <strong>suspend</strong> <strong>@%{acct}</strong>''s account. This will:'
+        actions:
+          delete_html: Remove the offending posts
+          mark_as_sensitive_html: Mark the offending posts' media as sensitive
+          silence_html: Severely limit <strong>@%{acct}</strong>'s reach by making their profile and contents only visible to people already following them or manually looking it profile up
+          suspend_html: Suspend <strong>@%{acct}</strong>, making their profile and contents inaccessible and impossible to interact with
+        close_report: 'Mark report #%{id} as resolved'
+        close_reports_html: Mark <strong>all</strong> reports against <strong>@%{acct}</strong> as resolved
+        delete_data_html: Delete <strong>@%{acct}</strong>'s profile and contents 30 days from now unless they get unsuspended in the meantime
+        preview_preamble_html: "<strong>@%{acct}</strong> will receive a warning with the following contents:"
+        record_strike_html: Record a strike against <strong>@%{acct}</strong> to help you escalate on future violations from this account
+        send_email_html: Send <strong>@%{acct}</strong> a warning e-mail
+        warning_placeholder: Optional additional reasoning for the moderation action.
       target_origin: Origin of reported account
       title: Reports
       unassign: Unassign
+      unknown_action_msg: 'Unknown action: %{action}'
       unresolved: Unresolved
       updated_at: Updated
       view_profile: View profile
@@ -770,6 +798,7 @@ en:
         suspend: "%{name} suspended %{target}'s account"
       appeal_approved: Appealed
       appeal_pending: Appeal pending
+      appeal_rejected: Appeal rejected
     system_checks:
       database_schema_check:
         message_html: There are pending database migrations. Please run them to ensure the application behaves as expected
@@ -783,6 +812,12 @@ en:
         message_html: You haven't defined any server rules.
       sidekiq_process_check:
         message_html: No Sidekiq process running for the %{value} queue(s). Please review your Sidekiq configuration
+      upload_check_privacy_error:
+        action: Check here for more information
+        message_html: "<strong>Your web server is misconfigured. The privacy of your users is at risk.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Check here for more information
+        message_html: "<strong>Your object storage is misconfigured. The privacy of your users is at risk.</strong>"
     tags:
       review: Review status
       updated_msg: Hashtag settings updated successfully
@@ -805,6 +840,7 @@ en:
           other: Shared by %{count} people over the last week
         title: Trending links
         usage_comparison: Shared %{today} times today, compared to %{yesterday} yesterday
+      not_allowed_to_trend: Not allowed to trend
       only_allowed: Only allowed
       pending_review: Pending review
       preview_card_providers:
@@ -936,6 +972,7 @@ en:
   applications:
     created: Application successfully created
     destroyed: Application successfully deleted
+    logout: Logout
     regenerate_token: Regenerate access token
     token_regenerated: Access token successfully regenerated
     warning: Be very careful with this data. Never share it with anyone!
@@ -943,6 +980,8 @@ en:
   auth:
     apply_for_account: Request an account
     change_password: Password
+    confirmations:
+      wrong_email_hint: If that e-mail address is not correct, you can change it in account settings.
     delete_account: Delete account
     delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation.
     description:
@@ -970,6 +1009,8 @@ en:
     resend_confirmation: Resend confirmation instructions
     reset_password: Reset password
     rules:
+      accept: Accept
+      back: Back
       preamble: These are set and enforced by the %{domain} moderators.
       title: Some ground rules.
     security: Security
@@ -1117,7 +1158,7 @@ en:
   featured_tags:
     add_new: Add new
     errors:
-      limit: You have already featured the maximum amount of hashtags
+      limit: You have already featured the maximum number of hashtags
     hint_html: "<strong>What are featured hashtags?</strong> They are displayed prominently on your public profile and allow people to browse your public posts specifically under those hashtags. They are a great tool for keeping track of creative works or long-term projects."
   filters:
     contexts:
@@ -1161,8 +1202,6 @@ en:
       index:
         hint: This filter applies to select individual posts regardless of other criteria. You can add more posts to this filter from the web interface.
         title: Filtered posts
-  footer:
-    trending_now: Trending now
   generic:
     all: All
     all_items_on_page_selected_html:
@@ -1185,8 +1224,6 @@ en:
     validation_errors:
       one: Something isn't quite right yet! Please review the error below
       other: Something isn't quite right yet! Please review %{count} errors below
-  html_validator:
-    invalid_markup: 'contains invalid HTML markup: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Invalid CSV file. Error: %{error}'
@@ -1229,7 +1266,7 @@ en:
     title: Invite people
   lists:
     errors:
-      limit: You have reached the maximum amount of lists
+      limit: You have reached the maximum number of lists
   login_activities:
     authentication_methods:
       otp: two-factor authentication app
@@ -1375,6 +1412,7 @@ en:
     confirm_remove_selected_followers: Are you sure you want to remove selected followers?
     confirm_remove_selected_follows: Are you sure you want to remove selected follows?
     dormant: Dormant
+    follow_failure: Could not follow some of the selected accounts.
     follow_selected_followers: Follow selected followers
     followers: Followers
     following: Following
@@ -1414,6 +1452,7 @@ en:
       electron: Electron
       firefox: Firefox
       generic: Unknown browser
+      huawei_browser: Huawei Browser
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1423,6 +1462,7 @@ en:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Unknown Browser
       weibo: Weibo
     current_session: Current session
     description: "%{browser} on %{platform}"
@@ -1435,9 +1475,10 @@ en:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: unknown platform
+      unknown_platform: Unknown Platform
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1550,7 +1591,7 @@ en:
       '7889238': 3 months
     min_age_label: Age threshold
     min_favs: Keep posts favourited at least
-    min_favs_hint: Doesn't delete any of your posts that has received at least this amount of favourites. Leave blank to delete posts regardless of their number of favourites
+    min_favs_hint: Doesn't delete any of your posts that has received at least this number of favourites. Leave blank to delete posts regardless of their number of favourites
     min_reblogs: Keep posts boosted at least
     min_reblogs_hint: Doesn't delete any of your posts that has been boosted at least this number of times. Leave blank to delete posts regardless of their number of boosts
   stream_entries:
@@ -1650,12 +1691,13 @@ en:
       title: Welcome aboard, %{name}!
   users:
     follow_limit_reached: You cannot follow more than %{limit} people
+    go_to_sso_account_settings: Go to your identity provider's account settings
     invalid_otp_token: Invalid two-factor code
     otp_lost_help_html: If you lost access to both, you may get in touch with %{email}
     seamless_external_login: You are logged in via an external service, so password and e-mail settings are not available.
     signed_in_as: 'Signed in as:'
   verification:
-    explanation_html: 'You can <strong>verify yourself as the owner of the links in your profile metadata</strong>. For that, the linked website must contain a link back to your Mastodon profile. The link back <strong>must</strong> have a <code>rel="me"</code> attribute. The text content of the link does not matter. Here is an example:'
+    explanation_html: 'You can <strong>verify yourself as the owner of the links in your profile metadata</strong>. For that, the linked website must contain a link back to your Mastodon profile. After adding the link you may need to come back here and re-save your profile for the verification to take effect. The link back <strong>must</strong> have a <code>rel="me"</code> attribute. The text content of the link does not matter. Here is an example:'
     verification: Verification
   webauthn_credentials:
     add: Add new security key
diff --git a/config/locales/en_GB.yml b/config/locales/en_GB.yml
deleted file mode 100644
index 2cba40da0..000000000
--- a/config/locales/en_GB.yml
+++ /dev/null
@@ -1,1043 +0,0 @@
----
-en_GB:
-  about:
-    about_hashtag_html: These are public toots tagged with <strong>#%{hashtag}</strong>. You can interact with them if you have an account anywhere in the fediverse.
-    about_mastodon_html: Mastodon is a social network based on open web protocols and free, open-source software. It is decentralized like e-mail.
-    about_this: About
-    active_count_after: active
-    active_footnote: Monthly Active Users (MAU)
-    administered_by: 'Administered by:'
-    api: API
-    apps: Mobile apps
-    apps_platforms: Use Mastodon from iOS, Android and other platforms
-    browse_directory: Browse a profile directory and filter by interests
-    browse_public_posts: Browse a live stream of public posts on Mastodon
-    contact: Contact
-    contact_missing: Not set
-    contact_unavailable: N/A
-    discover_users: Discover users
-    documentation: Documentation
-    extended_description_html: |
-      <h3>1A good place for rules</h3>2
-      <p>3The extended description has not been set up yet.</p>4
-    federation_hint_html: With an account on %{instance} you'll be able to follow people on any Mastodon server and beyond.
-    generic_description: "%{domain} is one server in the network"
-    get_apps: Try a mobile app
-    hosted_on: Mastodon hosted on %{domain}
-    learn_more: Learn more
-    privacy_policy: Privacy policy
-    see_whats_happening: See what's happening
-    server_stats: 'Server stats:'
-    source_code: Source code
-    status_count_after:
-      one: status
-      other: statuses
-    status_count_before: Who authored
-    tagline: Follow friends and discover new ones
-    terms: Terms of service
-    user_count_after:
-      one: user
-      other: users
-    user_count_before: Home to
-    what_is_mastodon: What is Mastodon?
-  accounts:
-    choices_html: "%{name}'s choices:"
-    follow: Follow
-    followers:
-      one: Follower
-      other: Follower
-    following: Following
-    joined: Joined %{date}
-    last_active: last active
-    link_verified_on: Ownership of this link was checked on %{date}
-    media: Media
-    moved_html: "%{name} has moved to %{new_profile_link}:"
-    network_hidden: This information is not available
-    nothing_here: There is nothing here!
-    people_followed_by: People whom %{name} follows
-    people_who_follow: People who follow %{name}
-    pin_errors:
-      following: You must be already following the person you want to endorse
-    posts:
-      one: Toot
-      other: Toots
-    posts_tab_heading: Toots
-    posts_with_replies: Toots and replies
-    reserved_username: The username is reserved
-    roles:
-      admin: Admin
-      bot: Bot
-      moderator: Mod
-    unfollow: Unfollow
-  admin:
-    account_actions:
-      action: Perform action
-      title: Perform moderation action on %{acct}
-    account_moderation_notes:
-      create: Leave note
-      created_msg: Moderation note successfully created!
-      delete: Delete
-      destroyed_msg: Moderation note successfully destroyed!
-    accounts:
-      approve: Approve
-      are_you_sure: Are you sure?
-      avatar: Avatar
-      by_domain: Domain
-      change_email:
-        changed_msg: Account email successfully changed!
-        current_email: Current email
-        label: Change email
-        new_email: New email
-        submit: Change email
-        title: Change email for %{username}
-      confirm: Confirm
-      confirmed: Confirmed
-      confirming: Confirming
-      deleted: Deleted
-      demote: Demote
-      disable: Disable
-      disable_two_factor_authentication: Disable 2FA
-      disabled: Disabled
-      display_name: Display name
-      domain: Domain
-      edit: Edit
-      email: Email
-      email_status: Email status
-      enable: Enable
-      enabled: Enabled
-      followers: Followers
-      follows: Follows
-      header: Header
-      inbox_url: Inbox URL
-      invited_by: Invited by
-      ip: IP
-      joined: Joined
-      location:
-        all: All
-        local: Local
-        remote: Remote
-        title: Location
-      login_status: Login status
-      media_attachments: Media attachments
-      memorialize: Turn into memoriam
-      moderation:
-        active: Active
-        all: All
-        pending: Pending
-        silenced: Silenced
-        suspended: Suspended
-        title: Moderation
-      moderation_notes: Moderation notes
-      most_recent_activity: Most recent activity
-      most_recent_ip: Most recent IP
-      no_limits_imposed: No limits imposed
-      not_subscribed: Not subscribed
-      pending: Pending review
-      perform_full_suspension: Suspend
-      promote: Promote
-      protocol: Protocol
-      public: Public
-      push_subscription_expires: PuSH subscription expires
-      redownload: Refresh profile
-      reject: Reject
-      remove_avatar: Remove avatar
-      remove_header: Remove header
-      resend_confirmation:
-        already_confirmed: This user is already confirmed
-        send: Resend confirmation email
-        success: Confirmation email successfully sent!
-      reset: Reset
-      reset_password: Reset password
-      resubscribe: Resubscribe
-      role: Permissions
-      roles:
-        admin: Administrator
-        moderator: Moderator
-        staff: Staff
-        user: User
-      search: Search
-      shared_inbox_url: Shared inbox URL
-      show:
-        created_reports: Made reports
-        targeted_reports: Reported by others
-      silence: Silence
-      silenced: Silenced
-      statuses: Statuses
-      subscribe: Subscribe
-      suspended: Suspended
-      title: Accounts
-      unconfirmed_email: Unconfirmed email
-      undo_silenced: Undo silence
-      undo_suspension: Undo suspension
-      unsubscribe: Unsubscribe
-      username: Username
-      warn: Warn
-      web: Web
-    action_logs:
-      actions:
-        assigned_to_self_report: "%{name} assigned report %{target} to themselves"
-        change_email_user: "%{name} changed the e-mail address of user %{target}"
-        confirm_user: "%{name} confirmed e-mail address of user %{target}"
-        create_account_warning: "%{name} sent a warning to %{target}"
-        create_custom_emoji: "%{name} uploaded new emoji %{target}"
-        create_domain_block: "%{name} blocked domain %{target}"
-        create_email_domain_block: "%{name} blacklisted e-mail domain %{target}"
-        demote_user: "%{name} demoted user %{target}"
-        destroy_custom_emoji: "%{name} destroyed emoji %{target}"
-        destroy_domain_block: "%{name} unblocked domain %{target}"
-        destroy_email_domain_block: "%{name} whitelisted e-mail domain %{target}"
-        destroy_status: "%{name} removed status by %{target}"
-        disable_2fa_user: "%{name} disabled two factor requirement for user %{target}"
-        disable_custom_emoji: "%{name} disabled emoji %{target}"
-        disable_user: "%{name} disabled login for user %{target}"
-        enable_custom_emoji: "%{name} enabled emoji %{target}"
-        enable_user: "%{name} enabled login for user %{target}"
-        memorialize_account: "%{name} turned %{target}'s account into a memoriam page"
-        promote_user: "%{name} promoted user %{target}"
-        remove_avatar_user: "%{name} removed %{target}'s avatar"
-        reopen_report: "%{name} reopened report %{target}"
-        reset_password_user: "%{name} reset password of user %{target}"
-        resolve_report: "%{name} resolved report %{target}"
-        silence_account: "%{name} silenced %{target}'s account"
-        suspend_account: "%{name} suspended %{target}'s account"
-        unassigned_report: "%{name} unassigned report %{target}"
-        unsilence_account: "%{name} unsilenced %{target}'s account"
-        unsuspend_account: "%{name} unsuspended %{target}'s account"
-        update_custom_emoji: "%{name} updated emoji %{target}"
-        update_status: "%{name} updated status by %{target}"
-      deleted_status: "(deleted status)"
-      title: Audit log
-    custom_emojis:
-      by_domain: Domain
-      copied_msg: Successfully created local copy of the emoji
-      copy: Copy
-      copy_failed_msg: Could not make a local copy of that emoji
-      created_msg: Emoji successfully created!
-      delete: Delete
-      destroyed_msg: Emojo successfully destroyed!
-      disable: Disable
-      disabled_msg: Successfully disabled that emoji
-      emoji: Emoji
-      enable: Enable
-      enabled_msg: Successfully enabled that emoji
-      listed: Listed
-      new:
-        title: Add new custom emoji
-      overwrite: Overwrite
-      shortcode: Shortcode
-      shortcode_hint: At least 2 characters, only alphanumeric characters and underscores
-      title: Custom emojis
-      unlisted: Unlisted
-      update_failed_msg: Could not update that emoji
-      updated_msg: Emoji successfully updated!
-      upload: Upload
-    dashboard:
-      backlog: backlogged jobs
-      config: Configuration
-      feature_deletions: Account deletions
-      feature_invites: Invite links
-      feature_profile_directory: Profile directory
-      feature_registrations: Registrations
-      feature_relay: Federation relay
-      features: Features
-      hidden_service: Federation with hidden services
-      open_reports: open reports
-      recent_users: Recent users
-      search: Full-text search
-      single_user_mode: Single user mode
-      software: Software
-      space: Space usage
-      title: Dashboard
-      total_users: users in total
-      trends: Trends
-      week_interactions: interactions this week
-      week_users_active: active this week
-      week_users_new: users this week
-    domain_blocks:
-      add_new: Add new domain block
-      created_msg: Domain block is now being processed
-      destroyed_msg: Domain block has been undone
-      domain: Domain
-      new:
-        create: Create block
-        hint: The domain block will not prevent creation of account entries in the database, but will retroactively and automatically apply specific moderation methods on those accounts.
-        severity:
-          desc_html: "<strong>Silence</strong> will make the account's posts invisible to anyone who isn't following them. <strong>Suspend</strong> will remove all of the account's content, media, and profile data. Use <strong>None</strong> if you just want to reject media files."
-          noop: None
-          silence: Silence
-          suspend: Suspend
-        title: New domain block
-      reject_media: Reject media files
-      reject_media_hint: Removes locally stored media files and refuses to download any in the future. Irrelevant for suspensions
-      reject_reports: Reject reports
-      reject_reports_hint: Ignore all reports coming from this domain. Irrelevant for suspensions
-      rejecting_media: rejecting media files
-      rejecting_reports: rejecting reports
-      severity:
-        silence: silenced
-        suspend: suspended
-      show:
-        affected_accounts:
-          one: One account in the database affected
-          other: "%{count} accounts in the database affected"
-        retroactive:
-          silence: Unsilence all existing accounts from this domain
-          suspend: Unsuspend all existing accounts from this domain
-        title: Undo domain block for %{domain}
-        undo: Undo
-      undo: Undo domain block
-    email_domain_blocks:
-      add_new: Add new
-      created_msg: Successfully added e-mail domain to blacklist
-      delete: Delete
-      destroyed_msg: Successfully deleted e-mail domain from blacklist
-      domain: Domain
-      new:
-        create: Add domain
-        title: New e-mail blacklist entry
-      title: E-mail blacklist
-    followers:
-      back_to_account: Back To Account
-      title: "%{acct}'s Followers"
-    instances:
-      by_domain: Domain
-      delivery_available: Delivery is available
-      known_accounts:
-        one: "%{count} known account"
-        other: "%{count} known accounts"
-      moderation:
-        all: All
-        limited: Limited
-        title: Moderation
-      title: Federation
-      total_blocked_by_us: Blocked by us
-      total_followed_by_them: Followed by them
-      total_followed_by_us: Followed by us
-      total_reported: Reports about them
-      total_storage: Media attachments
-    invites:
-      deactivate_all: Deactivate all
-      filter:
-        all: All
-        available: Available
-        expired: Expired
-        title: Filter
-      title: Invites
-    relays:
-      add_new: Add new relay
-      delete: Delete
-      description_html: A <strong>federation relay</strong> is an intermediary server that exchanges large volumes of public toots between servers that subscribe and publish to it. <strong>It can help small and medium servers discover content from the fediverse</strong>, which would otherwise require local users manually following other people on remote servers.
-      disable: Disable
-      disabled: Disabled
-      enable: Enable
-      enable_hint: Once enabled, your server will subscribe to all public toots from this relay, and will begin sending this server's public toots to it.
-      enabled: Enabled
-      inbox_url: Relay URL
-      pending: Waiting for relay's approval
-      save_and_enable: Save and enable
-      setup: Setup a relay connection
-      status: Status
-      title: Relays
-    report_notes:
-      created_msg: Report note successfully created!
-      destroyed_msg: Report note successfully deleted!
-    reports:
-      account:
-        note: note
-        report: report
-      action_taken_by: Action taken by
-      are_you_sure: Are you sure?
-      assign_to_self: Assign to me
-      assigned: Assigned moderator
-      comment:
-        none: None
-      created_at: Reported
-      mark_as_resolved: Mark as resolved
-      mark_as_unresolved: Mark as unresolved
-      notes:
-        create: Add note
-        create_and_resolve: Resolve with note
-        create_and_unresolve: Reopen with note
-        delete: Delete
-        placeholder: Describe what actions have been taken, or any other related updates...
-      reopen: Reopen report
-      report: 'Report #%{id}'
-      reported_account: Reported account
-      reported_by: Reported by
-      resolved: Resolved
-      resolved_msg: Report successfully resolved!
-      status: Status
-      title: Reports
-      unassign: Unassign
-      unresolved: Unresolved
-      updated_at: Updated
-    settings:
-      activity_api_enabled:
-        desc_html: Counts of locally posted statuses, active users, and new registrations in weekly buckets
-        title: Publish aggregate statistics about user activity
-      bootstrap_timeline_accounts:
-        desc_html: Separate multiple usernames by comma. Only local and unlocked accounts will work. Default when empty is all local admins.
-        title: Default follows for new users
-      contact_information:
-        email: Business e-mail
-        username: Contact username
-      custom_css:
-        desc_html: Modify the look with CSS loaded on every page
-        title: Custom CSS
-      hero:
-        desc_html: Displayed on the frontpage. At least 600x100px recommended. When not set, falls back to server thumbnail
-        title: Hero image
-      mascot:
-        desc_html: Displayed on multiple pages. At least 293×205px recommended. When not set, falls back to default mascot
-        title: Mascot image
-      peers_api_enabled:
-        desc_html: Domain names this server has encountered in the fediverse
-        title: Publish list of discovered servers
-      preview_sensitive_media:
-        desc_html: Link previews on other websites will display a thumbnail even if the media is marked as sensitive
-        title: Show sensitive media in OpenGraph previews
-      profile_directory:
-        desc_html: Allow users to be discoverable
-        title: Enable profile directory
-      registrations:
-        closed_message:
-          desc_html: Displayed on frontpage when registrations are closed. You can use HTML tags
-          title: Closed registration message
-        deletion:
-          desc_html: Allow anyone to delete their account
-          title: Open account deletion
-        min_invite_role:
-          disabled: No one
-          title: Allow invitations by
-      registrations_mode:
-        modes:
-          approved: Approval required for sign up
-          none: Nobody can sign up
-          open: Anyone can sign up
-        title: Registrations mode
-      show_known_fediverse_at_about_page:
-        desc_html: When toggled, it will show toots from all the known fediverse on preview. Otherwise it will only show local toots.
-        title: Show known fediverse on timeline preview
-      show_staff_badge:
-        desc_html: Show a staff badge on a user page
-        title: Show staff badge
-      site_description:
-        desc_html: Introductory paragraph on the frontpage. Describe what makes this Mastodon server special and anything else important. You can use HTML tags, in particular <code>&lt;a&gt;</code> and <code>&lt;em&gt;</code>.
-        title: Server description
-      site_description_extended:
-        desc_html: A good place for your code of conduct, rules, guidelines and other things that set your server apart. You can use HTML tags
-        title: Custom extended information
-      site_short_description:
-        desc_html: Displayed in sidebar and meta tags. Describe what Mastodon is and what makes this server special in a single paragraph. If empty, defaults to server description.
-        title: Short server description
-      site_terms:
-        desc_html: You can write your own privacy policy, terms of service or other legalese. You can use HTML tags
-        title: Custom terms of service
-      site_title: Server name
-      thumbnail:
-        desc_html: Used for previews via OpenGraph and API. 1200x630px recommended
-        title: Server thumbnail
-      timeline_preview:
-        desc_html: Display public timeline on landing page
-        title: Timeline preview
-      title: Site settings
-    statuses:
-      back_to_account: Back to account page
-      batch:
-        delete: Delete
-        nsfw_off: Mark as not sensitive
-        nsfw_on: Mark as sensitive
-      failed_to_execute: Failed to execute
-      media:
-        title: Media
-      no_media: No media
-      no_status_selected: No statuses were changed as none were selected
-      title: Account statuses
-      with_media: With media
-    subscriptions:
-      callback_url: Callback URL
-      confirmed: Confirmed
-      expires_in: Expires in
-      last_delivery: Last delivery
-      title: WebSub
-      topic: Topic
-    tags:
-      accounts: Accounts
-      hidden: Hidden
-      hide: Hide from directory
-      name: Hashtag
-      title: Hashtags
-      unhide: Show in directory
-      visible: Visible
-    title: Administration
-    warning_presets:
-      add_new: Add new
-      delete: Delete
-      edit_preset: Edit warning preset
-      title: Manage warning presets
-  admin_mailer:
-    new_pending_account:
-      body: The details of the new account are below. You can approve or reject this application.
-      subject: New account up for review on %{instance} (%{username})
-    new_report:
-      body: "%{reporter} has reported %{target}"
-      body_remote: Someone from %{domain} has reported %{target}
-      subject: New report for %{instance} (#%{id})
-  application_mailer:
-    notification_preferences: Change e-mail preferences
-    salutation: "%{name},"
-    settings: 'Change e-mail preferences: %{link}'
-    view: 'View:'
-    view_profile: View Profile
-    view_status: View status
-  applications:
-    created: Application successfully created
-    destroyed: Application successfully deleted
-    invalid_url: The provided URL is invalid
-    regenerate_token: Regenerate access token
-    token_regenerated: Access token successfully regenerated
-    warning: Be very careful with this data. Never share it with anyone!
-    your_token: Your access token
-  auth:
-    apply_for_account: Request an invite
-    change_password: Password
-    checkbox_agreement_html: I agree to the <a href="%{rules_path}" target="_blank">server rules</a> and <a href="%{terms_path}" target="_blank">terms of service</a>
-    confirm_email: Confirm email
-    delete_account: Delete account
-    delete_account_html: If you wish to delete your account, you can <a href="%{path}">proceed here</a>. You will be asked for confirmation.
-    didnt_get_confirmation: Didn't receive confirmation instructions?
-    forgot_password: Forgot your password?
-    invalid_reset_password_token: Password reset token is invalid or expired. Please request a new one.
-    login: Log in
-    logout: Logout
-    migrate_account: Move to a different account
-    migrate_account_html: If you wish to redirect this account to a different one, you can <a href="%{path}">configure it here</a>.
-    or_log_in_with: Or log in with
-    providers:
-      cas: CAS
-      saml: SAML
-    register: Sign up
-    registration_closed: "%{instance} is not accepting new members"
-    resend_confirmation: Resend confirmation instructions
-    reset_password: Reset password
-    security: Security
-    set_new_password: Set new password
-    trouble_logging_in: Trouble logging in?
-  authorize_follow:
-    already_following: You are already following this account
-    error: Unfortunately, there was an error looking up the remote account
-    follow: Follow
-    follow_request: 'You have sent a follow request to:'
-    following: 'Success! You are now following:'
-    post_follow:
-      close: Or, you can just close this window.
-      return: Show the user's profile
-      web: Go to web
-    title: Follow %{acct}
-  datetime:
-    distance_in_words:
-      about_x_hours: "%{count}h"
-      about_x_months: "%{count}mo"
-      about_x_years: "%{count}y"
-      almost_x_years: "%{count}y"
-      half_a_minute: Just now
-      less_than_x_minutes: "%{count}m"
-      less_than_x_seconds: Just now
-      over_x_years: "%{count}y"
-      x_days: "%{count}d"
-      x_minutes: "%{count}m"
-      x_months: "%{count}mo"
-      x_seconds: "%{count}s"
-  deletes:
-    bad_password_msg: Nice try, hackers! Incorrect password
-    confirm_password: Enter your current password to verify your identity
-    description_html: This will <strong>permanently, irreversibly</strong> remove content from your account and deactivate it. Your username will remain reserved to prevent future impersonations.
-    proceed: Delete account
-    success_msg: Your account was successfully deleted
-    warning_html: Only deletion of content from this particular server is guaranteed. Content that has been widely shared is likely to leave traces. Offline servers and servers that have unsubscribed from your updates will not update their databases.
-    warning_title: Disseminated content availability
-  directories:
-    directory: Profile directory
-    enabled: You are currently listed in the directory.
-    enabled_but_waiting: You have opted-in to be listed in the directory, but you do not have the minimum number of followers (%{min_followers}) to be listed yet.
-    explanation: Discover users based on their interests
-    explore_mastodon: Explore %{title}
-    how_to_enable: You are not currently opted-in to the directory. You can opt-in below. Use hashtags in your bio text to be listed under specific hashtags!
-    people:
-      one: "%{count} person"
-      other: "%{count} people"
-  errors:
-    '403': You don't have permission to view this page.
-    '404': The page you are looking for isn't here.
-    '410': The page you were looking for doesn't exist here anymore.
-    '422':
-      content: Security verification failed. Are you blocking cookies?
-      title: Security verification failed
-    '429': Throttled
-    '500':
-      content: We're sorry, but something went wrong on our end.
-      title: This page is not correct
-    noscript_html: To use the Mastodon web application, please enable JavaScript. Alternatively, try one of the <a href="%{apps_path}">native apps</a> for Mastodon for your platform.
-  exports:
-    archive_takeout:
-      date: Date
-      download: Download your archive
-      hint_html: You can request an archive of your <strong>toots and uploaded media</strong>. The exported data will be in the ActivityPub format, readable by any compliant software. You can request an archive every 7 days.
-      in_progress: Compiling your archive...
-      request: Request your archive
-      size: Size
-    blocks: You block
-    csv: CSV
-    domain_blocks: Domain blocks
-    follows: You follow
-    lists: Lists
-    mutes: You mute
-    storage: Media storage
-  featured_tags:
-    add_new: Add new
-    errors:
-      limit: You have already featured the maximum amount of hashtags
-  filters:
-    contexts:
-      home: Home and lists
-      notifications: Notifications
-      public: Public timelines
-      thread: Conversations
-    edit:
-      title: Edit filter
-    errors:
-      invalid_context: None or invalid context supplied
-      invalid_irreversible: Irreversible filtering only works with home or notifications context
-    index:
-      delete: Delete
-      title: Filters
-    new:
-      title: Add new filter
-  footer:
-    developers: Developers
-    more: More…
-    resources: Resources
-  generic:
-    all: All
-    changes_saved_msg: Changes successfully saved!
-    copy: Copy
-    save_changes: Save changes
-    validation_errors:
-      one: Something isn't quite right yet! Please review the error below
-      other: Something isn't quite right yet! Please review %{count} errors below
-  identity_proofs:
-    active: Active
-    authorize: Yes, authorize
-    authorize_connection_prompt: Authorize this cryptographic connection?
-    errors:
-      failed: The cryptographic connection failed. Please try again from %{provider}.
-      keybase:
-        invalid_token: Keybase tokens are hashes of signatures and must be 66 hex characters
-        verification_failed: Keybase does not recognize this token as a signature of Keybase user %{kb_username}. Please retry from Keybase.
-    explanation_html: Here you can cryptographically connect your other identities, such as a Keybase profile. This lets other people send you encrypted messages and trust content you send them.
-    i_am_html: I am %{username} on %{service}.
-    identity: Identity
-    inactive: Inactive
-    status: Verification status
-    view_proof: View proof
-  imports:
-    modes:
-      merge: Merge
-      merge_long: Keep existing records and add new ones
-      overwrite: Overwrite
-      overwrite_long: Replace current records with the new ones
-    preface: You can import data that you have exported from another server, such as a list of the people you are following or blocking.
-    success: Your data was successfully uploaded and will now be processed in due time
-    types:
-      blocking: Blocking list
-      domain_blocking: Domain blocking list
-      following: Following list
-      muting: Muting list
-    upload: Upload
-  in_memoriam_html: In Memoriam.
-  invites:
-    delete: Deactivate
-    expired: Expired
-    expires_in:
-      '1800': 30 minutes
-      '21600': 6 hours
-      '3600': 1 hour
-      '43200': 12 hours
-      '604800': 1 week
-      '86400': 1 day
-    expires_in_prompt: Never
-    generate: Generate
-    invited_by: 'You were invited by:'
-    max_uses:
-      one: 1 use
-      other: "%{count} uses"
-    max_uses_prompt: No limit
-    prompt: Generate and share links with others to grant access to this server
-    table:
-      expires_at: Expires
-      uses: Uses
-    title: Invite people
-  lists:
-    errors:
-      limit: You have reached the maximum amount of lists
-  media_attachments:
-    validations:
-      images_and_video: Cannot attach a video to a status that already contains images
-      too_many: Cannot attach more than 4 files
-  migrations:
-    acct: username@domain of the new account
-    currently_redirecting: 'Your profile is set to redirect to:'
-    proceed: Save
-    updated_msg: Your account migration setting successfully updated!
-  moderation:
-    title: Moderation
-  notification_mailer:
-    digest:
-      action: View all notifications
-      body: Here is a brief summary of the messages you missed since your last visit on %{since}
-      mention: "%{name} mentioned you in:"
-      new_followers_summary:
-        one: Also, you have acquired one new follower while being away! Yay!
-        other: Also, you have acquired %{count} new followers while being away! Amazing!
-      subject:
-        one: "1 new notification since your last visit 🐘"
-        other: "%{count} new notifications since your last visit 🐘"
-      title: In your absence...
-    favourite:
-      body: 'Your status was favourited by %{name}:'
-      subject: "%{name} favourited your status"
-      title: New favourite
-    follow:
-      body: "%{name} is now following you!"
-      subject: "%{name} is now following you"
-      title: New follower
-    follow_request:
-      action: Manage follow requests
-      body: "%{name} has requested to follow you"
-      subject: 'Pending follower: %{name}'
-      title: New follow request
-    mention:
-      action: Reply
-      body: 'You were mentioned by %{name} in:'
-      subject: You were mentioned by %{name}
-      title: New mention
-    reblog:
-      body: 'Your status was boosted by %{name}:'
-      subject: "%{name} boosted your status"
-      title: New boost
-  number:
-    human:
-      decimal_units:
-        format: "%n%u"
-        units:
-          billion: B
-          million: M
-          quadrillion: Q
-          thousand: K
-          trillion: T
-  pagination:
-    newer: Newer
-    next: Next
-    older: Older
-    prev: Prev
-    truncate: "&hellip;"
-  polls:
-    errors:
-      already_voted: You have already voted on this poll
-      duplicate_options: contain duplicate items
-      duration_too_long: is too far into the future
-      duration_too_short: is too soon
-      expired: The poll has already ended
-      over_character_limit: cannot be longer than %{max} characters each
-      too_few_options: must have more than one item
-      too_many_options: can't contain more than %{max} items
-  preferences:
-    other: Other
-  relationships:
-    activity: Account activity
-    dormant: Dormant
-    moved: Moved
-    mutual: Mutual
-    primary: Primary
-    relationship: Relationship
-    remove_selected_domains: Remove all followers from the selected domains
-    remove_selected_followers: Remove selected followers
-    remove_selected_follows: Unfollow selected users
-    status: Account status
-  remote_follow:
-    acct: Enter your username@domain you want to act from
-    missing_resource: Could not find the required redirect URL for your account
-    no_account_html: Don't have an account? You can <a href='%{sign_up_path}' target='_blank'>sign up here</a>
-    proceed: Proceed to follow
-    prompt: 'You are going to follow:'
-    reason_html: "<strong>Why is this step necessary?</strong> <code>%{instance}</code> might not be the server where you are registered, so we need to redirect you to your home server first."
-  remote_interaction:
-    favourite:
-      proceed: Proceed to favourite
-      prompt: 'You want to favourite this toot:'
-    reblog:
-      proceed: Proceed to boost
-      prompt: 'You want to boost this toot:'
-    reply:
-      proceed: Proceed to reply
-      prompt: 'You want to reply to this toot:'
-  remote_unfollow:
-    error: Error
-    title: Title
-    unfollowed: Unfollowed
-  scheduled_statuses:
-    over_daily_limit: You have exceeded the limit of %{limit} scheduled toots for that day
-    over_total_limit: You have exceeded the limit of %{limit} scheduled toots
-    too_soon: The scheduled date must be in the future
-  sessions:
-    activity: Last activity
-    browser: Browser
-    browsers:
-      alipay: Alipay
-      blackberry: Blackberry
-      chrome: Chrome
-      edge: Microsoft Edge
-      electron: Electron
-      firefox: Firefox
-      generic: Unknown browser
-      ie: Internet Explorer
-      micro_messenger: MicroMessenger
-      nokia: Nokia S40 Ovi Browser
-      opera: Opera
-      otter: Otter
-      phantom_js: PhantomJS
-      qq: QQ Browser
-      safari: Safari
-      uc_browser: UCBrowser
-      weibo: Weibo
-    current_session: Current session
-    description: "%{browser} on %{platform}"
-    explanation: These are the web browsers currently logged in to your Mastodon account.
-    ip: IP
-    platforms:
-      adobe_air: Adobe Air
-      android: Android
-      blackberry: Blackberry
-      chrome_os: ChromeOS
-      firefox_os: Firefox OS
-      ios: iOS
-      linux: Linux
-      mac: Mac
-      other: unknown platform
-      windows: Windows
-      windows_mobile: Windows Mobile
-      windows_phone: Windows Phone
-    revoke: Revoke
-    revoke_success: Session successfully revoked
-    title: Sessions
-  settings:
-    authorized_apps: Authorized apps
-    back: Back to Mastodon
-    delete: Account deletion
-    development: Development
-    edit_profile: Edit profile
-    export: Data export
-    featured_tags: Featured hashtags
-    identity_proofs: Identity proofs
-    import: Import
-    migrate: Account migration
-    notifications: Notifications
-    preferences: Preferences
-    relationships: Follows and followers
-    two_factor_authentication: Two-factor Auth
-  statuses:
-    attached:
-      description: 'Attached: %{attached}'
-      image:
-        one: "%{count} image"
-        other: "%{count} images"
-      video:
-        one: "%{count} video"
-        other: "%{count} videos"
-    boosted_from_html: Boosted from %{acct_link}
-    content_warning: 'Content warning: %{warning}'
-    disallowed_hashtags:
-      one: 'contained a disallowed hashtag: %{tags}'
-      other: 'contained the disallowed hashtags: %{tags}'
-    language_detection: Automatically detect language
-    open_in_web: Open in web
-    over_character_limit: character limit of %{max} exceeded
-    pin_errors:
-      limit: You have already pinned the maximum number of toots
-      ownership: Someone else's toot cannot be pinned
-      private: Non-public toot cannot be pinned
-      reblog: A boost cannot be pinned
-    poll:
-      total_votes:
-        one: "%{count} vote"
-        other: "%{count} votes"
-      vote: Vote
-    show_more: Show more
-    sign_in_to_participate: Sign in to participate in the conversation
-    title: '%{name}: "%{quote}"'
-    visibilities:
-      private: Followers-only
-      private_long: Only show to followers
-      public: Public
-      public_long: Everyone can see
-      unlisted: Unlisted
-      unlisted_long: Everyone can see, but not listed on public timelines
-  stream_entries:
-    pinned: Pinned toot
-    reblogged: boosted
-    sensitive_content: Sensitive content
-  terms:
-    body_html: |
-      <h2>Privacy Policy</h2>
-      <h3 id="collect">What information do we collect?</h3>
-
-      <ul>
-        <li><em>Basic account information</em>: If you register on this server, you may be asked to enter a username, an e-mail address and a password. You may also enter additional profile information such as a display name and biography, and upload a profile picture and header image. The username, display name, biography, profile picture and header image are always listed publicly.</li>
-        <li><em>Posts, following and other public information</em>: The list of people you follow is listed publicly, the same is true for your followers. When you submit a message, the date and time is stored as well as the application you submitted the message from. Messages may contain media attachments, such as pictures and videos. Public and unlisted posts are available publicly. When you feature a post on your profile, that is also publicly available information. Your posts are delivered to your followers, in some cases it means they are delivered to different servers and copies are stored there. When you delete posts, this is likewise delivered to your followers. The action of reblogging or favouriting another post is always public.</li>
-        <li><em>Direct and followers-only posts</em>: All posts are stored and processed on the server. Followers-only posts are delivered to your followers and users who are mentioned in them, and direct posts are delivered only to users mentioned in them. In some cases it means they are delivered to different servers and copies are stored there. We make a good faith effort to limit the access to those posts only to authorized persons, but other servers may fail to do so. Therefore it's important to review servers your followers belong to. You may toggle an option to approve and reject new followers manually in the settings. <em>Please keep in mind that the operators of the server and any receiving server may view such messages</em>, and that recipients may screenshot, copy or otherwise re-share them. <em>Do not share any dangerous information over Mastodon.</em></li>
-        <li><em>IPs and other metadata</em>: When you log in, we record the IP address you log in from, as well as the name of your browser application. All the logged in sessions are available for your review and revocation in the settings. The latest IP address used is stored for up to 12 months. We also may retain server logs which include the IP address of every request to our server.</li>
-      </ul>
-
-      <hr class="spacer" />
-
-      <h3 id="use">What do we use your information for?</h3>
-
-      <p>Any of the information we collect from you may be used in the following ways:</p>
-
-      <ul>
-        <li>To provide the core functionality of Mastodon. You can only interact with other people's content and post your own content when you are logged in. For example, you may follow other people to view their combined posts in your own personalized home timeline.</li>
-        <li>To aid moderation of the community, for example comparing your IP address with other known ones to determine ban evasion or other violations.</li>
-        <li>The email address you provide may be used to send you information, notifications about other people interacting with your content or sending you messages, and to respond to inquiries, and/or other requests or questions.</li>
-      </ul>
-
-      <hr class="spacer" />
-
-      <h3 id="protect">How do we protect your information?</h3>
-
-      <p>We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information. Among other things, your browser session, as well as the traffic between your applications and the API, are secured with SSL, and your password is hashed using a strong one-way algorithm. You may enable two-factor authentication to further secure access to your account.</p>
-
-      <hr class="spacer" />
-
-      <h3 id="data-retention">What is our data retention policy?</h3>
-
-      <p>We will make a good faith effort to:</p>
-
-      <ul>
-        <li>Retain server logs containing the IP address of all requests to this server, in so far as such logs are kept, no more than 90 days.</li>
-        <li>Retain the IP addresses associated with registered users no more than 12 months.</li>
-      </ul>
-
-      <p>You can request and download an archive of your content, including your posts, media attachments, profile picture, and header image.</p>
-
-      <p>You may irreversibly delete your account at any time.</p>
-
-      <hr class="spacer"/>
-
-      <h3 id="cookies">Do we use cookies?</h3>
-
-      <p>Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow). These cookies enable the site to recognize your browser and, if you have a registered account, associate it with your registered account.</p>
-
-      <p>We use cookies to understand and save your preferences for future visits.</p>
-
-      <hr class="spacer" />
-
-      <h3 id="disclose">Do we disclose any information to outside parties?</h3>
-
-      <p>We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety.</p>
-
-      <p>Your public content may be downloaded by other servers in the network. Your public and followers-only posts are delivered to the servers where your followers reside, and direct messages are delivered to the servers of the recipients, in so far as those followers or recipients reside on a different server than this.</p>
-
-      <p>When you authorize an application to use your account, depending on the scope of permissions you approve, it may access your public profile information, your following list, your followers, your lists, all your posts, and your favourites. Applications can never access your e-mail address or password.</p>
-
-      <hr class="spacer" />
-
-      <h3 id="children">Site usage by children</h3>
-
-      <p>If this server is in the EU or the EEA: Our site, products and services are all directed to people who are at least 16 years old. If you are under the age of 16, per the requirements of the GDPR (<a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation">General Data Protection Regulation</a>) do not use this site.</p>
-
-      <p>If this server is in the USA: Our site, products and services are all directed to people who are at least 13 years old. If you are under the age of 13, per the requirements of COPPA (<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a>) do not use this site.</p>
-
-      <p>Law requirements can be different if this server is in another jurisdiction.</p>
-
-      <hr class="spacer" />
-
-      <h3 id="changes">Changes to our Privacy Policy</h3>
-
-      <p>If we decide to change our privacy policy, we will post those changes on this page.</p>
-
-      <p>This document is CC-BY-SA. It was last updated March 7, 2018.</p>
-
-      <p>Originally adapted from the <a href="https://github.com/discourse/discourse">Discourse privacy policy</a>.</p>
-    title: "%{instance} Terms of Service and Privacy Policy"
-  themes:
-    contrast: Mastodon (High contrast)
-    default: Mastodon (Dark)
-    mastodon-light: Mastodon (Light)
-  time:
-    formats:
-      default: "%b %d, %Y, %H:%M"
-      month: "%b %Y"
-  two_factor_authentication:
-    code_hint: Enter the code generated by your authenticator app to confirm
-    description_html: If you enable <strong>two-factor authentication</strong>, logging in will require you to be in possession of your phone, which will generate tokens for you to enter.
-    disable: Disable
-    enable: Enable
-    enabled: Two-factor authentication is enabled
-    enabled_success: Two-factor authentication successfully enabled
-    generate_recovery_codes: Generate recovery codes
-    instructions_html: "<strong>Scan this QR code into Google Authenticator or a similar TOTP app on your phone</strong>. From now on, that app will generate tokens that you will have to enter when logging in."
-    lost_recovery_codes: Recovery codes allow you to regain access to your account if you lose your phone. If you've lost your recovery codes, you can regenerate them here. Your old recovery codes will be invalidated.
-    manual_instructions: 'If you can''t scan the QR code and need to enter it manually, here is the plain-text secret:'
-    recovery_codes: Backup recovery codes
-    recovery_codes_regenerated: Recovery codes successfully regenerated
-    recovery_instructions_html: If you ever lose access to your phone, you can use one of the recovery codes below to regain access to your account. <strong>Keep the recovery codes safe</strong>. For example, you may print them and store them with other important documents.
-    setup: Set up
-    wrong_code: The entered code was invalid! Are server time and device time correct?
-  user_mailer:
-    backup_ready:
-      explanation: You requested a full backup of your Mastodon account. It's now ready for download!
-      subject: Your archive is ready for download
-      title: Archive takeout
-    warning:
-      explanation:
-        disable: While your account is frozen, your account data remains intact, but you cannot perform any actions until it is unlocked.
-        silence: While your account is limited, only people who are already following you will see your toots on this server, and you may be excluded from various public listings. However, others may still manually follow you.
-        suspend: Your account has been suspended, and all of your toots and your uploaded media files have been irreversibly removed from this server, and servers where you had followers.
-      review_server_policies: Review server policies
-      subject:
-        disable: Your account %{acct} has been frozen
-        none: Warning for %{acct}
-        silence: Your account %{acct} has been limited
-        suspend: Your account %{acct} has been suspended
-      title:
-        disable: Account frozen
-        none: Warning
-        silence: Account limited
-        suspend: Account suspended
-    welcome:
-      edit_profile_action: Setup profile
-      edit_profile_step: You can customize your profile by uploading an avatar, header, changing your display name and more. If you’d like to review new followers before they’re allowed to follow you, you can lock your account.
-      explanation: Here are some tips to get you started
-      final_action: Start posting
-      final_step: 'Start posting! Even without followers your public messages may be seen by others, for example on the local timeline and in hashtags. You may want to introduce yourself on the #introductions hashtag.'
-      full_handle: Your full handle
-      full_handle_hint: This is what you would tell your friends so they can message or follow you from another server.
-      review_preferences_action: Change preferences
-      review_preferences_step: Make sure to set your preferences, such as which emails you'd like to receive, or what privacy level you’d like your posts to default to. If you don’t have motion sickness, you could choose to enable GIF autoplay.
-      subject: Welcome to Mastodon
-      tip_federated_timeline: The federated timeline is a firehose view of the Mastodon network. But it only includes people your neighbours are subscribed to, so it's not complete.
-      tip_following: You follow your server's admin(s) by default. To find more interesting people, check the local and federated timelines.
-      tip_local_timeline: The local timeline is a firehose view of people on %{instance}. These are your immediate neighbours!
-      tip_mobile_webapp: If your mobile browser offers you to add Mastodon to your homescreen, you can receive push notifications. It acts like a native app in many ways!
-      tips: Tips
-      title: Welcome aboard, %{name}!
-  users:
-    follow_limit_reached: You cannot follow more than %{limit} people
-    invalid_email: The e-mail address is invalid
-    invalid_otp_token: Invalid two-factor code
-    otp_lost_help_html: If you lost access to both, you may get in touch with %{email}
-    seamless_external_login: You are logged in via an external service, so password and e-mail settings are not available.
-    signed_in_as: 'Signed in as:'
-  verification:
-    explanation_html: 'You can <strong>verify yourself as the owner of the links in your profile metadata</strong>. For that, the linked website must contain a link back to your Mastodon profile. The link back <strong>must</strong> have a <code>rel="me"</code> attribute. The text content of the link does not matter. Here is an example:'
-    verification: Verification
diff --git a/config/locales/eo.yml b/config/locales/eo.yml
index 031e9e6f9..b381805a1 100644
--- a/config/locales/eo.yml
+++ b/config/locales/eo.yml
@@ -1,7 +1,7 @@
 ---
 eo:
   about:
-    about_mastodon_html: 'La socia retejo de la estonteco: sen reklamo, sen kompania observado, etika desegno, kaj malcentrigo! Regu viajn datumojn per Mastodon!'
+    about_mastodon_html: 'La socia retejo de la estonteco: sen reklamo, sen observado por firmao, etika desegno, kaj malcentrigo! Regu viajn informojn per Mastodon!'
     contact_missing: Ne elektita
     contact_unavailable: Ne disponebla
     hosted_on: "%{domain} estas nodo de Mastodon"
@@ -38,14 +38,14 @@ eo:
       avatar: Profilbildo
       by_domain: Domajno
       change_email:
-        changed_msg: Retpoŝta adreso estis sukcese ŝanĝita!
+        changed_msg: Retpoŝta adreso sukcese ŝanĝita!
         current_email: Nuna retadreso
         label: Ŝanĝi retadreson
         new_email: Nova retadreso
         submit: Ŝanĝi retadreson
         title: Ŝanĝi retadreson por %{username}
       change_role:
-        changed_msg: Rolo estis sukcese ŝanĝita!
+        changed_msg: Rolo sukcese ŝanĝita!
         label: Ŝanĝi rolon
         no_role: Neniu rolo
         title: Ŝanĝi rolon por %{username}
@@ -58,7 +58,7 @@ eo:
       demote: Degradi
       destroyed_msg: Datumoj de %{username} nun enviciĝis por esti forigita baldaǔ
       disable: Frostigi
-      disable_sign_in_token_auth: Malŝalti aŭtentigon de retpoŝta ĵetono
+      disable_sign_in_token_auth: Malŝalti aŭtentigon per retpoŝta ĵetono
       disable_two_factor_authentication: Malŝalti 2FA-n
       disabled: Frostigita
       display_name: Montrata nomo
@@ -67,7 +67,7 @@ eo:
       email: Retpoŝto
       email_status: Stato de retpoŝto
       enable: Malfrostigi
-      enable_sign_in_token_auth: Ŝalti aŭtentigon de retpoŝta ĵetono por uzanto
+      enable_sign_in_token_auth: Ŝalti aŭtentigon per retpoŝta ĵetono
       enabled: Ebligita
       enabled_msg: Sukcese malfrostigis konton de %{username}
       followers: Sekvantoj
@@ -84,13 +84,14 @@ eo:
         remote: Foraj
         title: Loko
       login_status: Ensaluta stato
-      media_attachments: Ligitaj aŭdovidaĵoj
+      media_attachments: Plurmediaj aldonaĵoj
       memorialize: Ŝanĝi al memoro
       memorialized: Memorita
       memorialized_msg: Sukcese ŝanĝis %{username} al memorkonto
       moderation:
         active: Aktivaj
         all: Ĉio
+        disabled: Neebligita
         pending: Pritraktata
         silenced: Limigita
         suspended: Suspendita
@@ -113,24 +114,27 @@ eo:
       public: Publika
       push_subscription_expires: Eksvalidiĝo de la abono al PuSH
       redownload: Aktualigi profilon
-      redownloaded_msg: Sukcese refreŝis profilon de %{username} de origino
+      redownloaded_msg: Sukcese aktualigis profilon de %{username} el origino
       reject: Malakcepti
       rejected_msg: Sukcese malaprobis aliĝ-peton de %{username}
+      remote_suspension_irreversible: La informoj de ĉi tiu konto estis neinversigeble forigitaj.
+      remote_suspension_reversible_hint_html: La konto estas suspendita, kaj la datumoj estos komplete forigitaj je %{date}. Ĝis tiam, la konto povas esti malsuspendita sen flankefiko. Se vi deziras tuj forigi ĉiujn datumojn de la konto, vi povas fari tion sube.
       remove_avatar: Forigi la profilbildon
       remove_header: Forigi kapan bildon
       removed_avatar_msg: La rolfiguro de %{username} estas sukcese forigita
-      removed_header_msg: Sukcese forigis kapbildon de %{username}
+      removed_header_msg: Kapbildo de %{username} suksece forigita
       resend_confirmation:
         already_confirmed: Ĉi tiu uzanto jam estas konfirmita
         send: Resendi konfirman retpoŝton
-        success: Konfirma retpoŝto estis sukcese sendita!
+        success: Konfirma retmesaĝo sukcese sendita!
       reset: Restarigi
       reset_password: Restarigi pasvorton
       resubscribe: Reaboni
       role: Rolo
       search: Serĉi
-      search_same_email_domain: Aliaj uzantoj kun la sama retpoŝta domajno
+      search_same_email_domain: Aliaj uzantoj kun la sama domajno de retpoŝto
       search_same_ip: Aliaj uzantoj kun la sama IP
+      security: Sekureco
       security_measures:
         only_password: Nur pasvorto
         password_and_2fa: Pasvorto kaj 2FA
@@ -139,7 +143,7 @@ eo:
       shared_inbox_url: URL de kunhavigita leterkesto
       show:
         created_reports: Faritaj raportoj
-        targeted_reports: Raporitaj de la aliaj
+        targeted_reports: Raporitaj de aliaj
       silence: Limigi
       silenced: Silentigita
       statuses: Afiŝoj
@@ -147,7 +151,7 @@ eo:
       subscribe: Aboni
       suspend: Haltigu
       suspended: Suspendita
-      suspension_irreversible: La datumoj de ĉi tiu konto neinverseble forigitas.
+      suspension_irreversible: La datumoj de ĉi tiu konto estas porĉiame forigitaj. Vi povas malsuspendi tiun konton por igi ĝin uzebla, sed ĝi ne rehavos ajnan datumon kiun ĝi antaŭe havis.
       suspension_reversible_hint_html: La konto estas suspendita, kaj la datumoj estos komplete forigitaj je %{date}. Ĝis tiam, la konto povas esti malsuspendita sen flankefiko. Se vi deziras tuj forigi ĉiujn datumojn de la konto, vi povas fari tion sube.
       title: Kontoj
       unblock_email: Malbloki retpoŝtadresojn
@@ -196,10 +200,10 @@ eo:
         destroy_user_role: Detrui Rolon
         disable_2fa_user: Malebligi 2FA
         disable_custom_emoji: Malebligi proprajn emoĝiojn
-        disable_sign_in_token_auth_user: Malŝalti aŭtentigon de retpoŝta ĵetono por la uzanto
+        disable_sign_in_token_auth_user: Malŝalti aŭtentigon per retpoŝta ĵetono por la uzanto
         disable_user: Neebligi la uzanton
         enable_custom_emoji: Ebligi Propran Emoĝion
-        enable_sign_in_token_auth_user: Ŝalti aŭtentigon de retpoŝta ĵetono por la uzanto
+        enable_sign_in_token_auth_user: Ŝalti aŭtentigon per retpoŝta ĵetono por la uzanto
         enable_user: Ebligi uzanton
         memorialize_account: Memorigu Konton
         promote_user: Promocii Uzanton
@@ -222,10 +226,10 @@ eo:
         update_custom_emoji: Ĝisdatigi proprajn emoĝiojn
         update_domain_block: Ĝigdatigi domajnan blokadon
         update_ip_block: Krei IP-regulon
-        update_status: Ĝisdatigi staton
-        update_user_role: Ĝisdatigi Rolon
+        update_status: Ĝisdatigi afiŝon
+        update_user_role: Ĝisdatigi rolon
       actions:
-        approve_appeal_html: "%{name} aprobis kontroldecidapelacion de %{target}"
+        approve_appeal_html: "%{name} aprobis apelacion kontraŭ moderiga decido de %{target}"
         approve_user_html: "%{name} aprobis registriĝon de %{target}"
         assigned_to_self_report_html: "%{name} asignis signalon %{target} al si mem"
         change_email_user_html: "%{name} ŝanĝis retadreson de uzanto %{target}"
@@ -251,7 +255,7 @@ eo:
         destroy_instance_html: "%{name} forigis domajnon %{target}"
         destroy_ip_block_html: "%{name} forigis regulon por IP %{target}"
         destroy_status_html: "%{name} forigis mesaĝojn de %{target}"
-        destroy_unavailable_domain_html: "%{name} daurigis sendon al domajno %{target}"
+        destroy_unavailable_domain_html: "%{name} restartigis sendon al domajno %{target}"
         destroy_user_role_html: "%{name} forigis rolon de %{target}"
         disable_2fa_user_html: "%{name} malebligis dufaktoran aŭtentigon por uzanto %{target}"
         disable_custom_emoji_html: "%{name} malebligis la emoĝion %{target}"
@@ -262,27 +266,27 @@ eo:
         enable_user_html: "%{name} ebligis ensaluton por uzanto %{target}"
         memorialize_account_html: "%{name} ŝanĝis la konton de %{target} al memora paĝo"
         promote_user_html: "%{name} plirangigis uzanton %{target}"
-        reject_appeal_html: "%{name} malakceptis kontroldecidapelacion de %{target}"
+        reject_appeal_html: "%{name} malakceptis apelacion kontraŭ moderiga decido de %{target}"
         reject_user_html: "%{name} malakceptis registriĝon de %{target}"
         remove_avatar_user_html: "%{name} forigis la profilbildon de %{target}"
         reopen_report_html: "%{name} remalfermis signalon %{target}"
         resend_user_html: "%{name} resendis konfirman retmesaĝon por %{target}"
         reset_password_user_html: "%{name} restarigis la pasvorton de la uzanto %{target}"
         resolve_report_html: "%{name} solvis raporton %{target}"
-        sensitive_account_html: "%{name} markis audovidaĵojn de %{target} kiel sentemaj"
+        sensitive_account_html: "%{name} markis audovidaĵon de %{target} kiel tiklan"
         silence_account_html: "%{name} limigis la konton de %{target}"
         suspend_account_html: "%{name} suspendis la konton de %{target}"
         unassigned_report_html: "%{name} malasignis raporton %{target}"
         unblock_email_account_html: "%{name} malblokis retpoŝtoadreson de %{target}"
-        unsensitive_account_html: "%{name} malmarkis audovidaĵojn de %{target} kiel sentemaj"
-        unsilence_account_html: "%{name} malfaris limon de konto de %{target}"
+        unsensitive_account_html: "%{name} malmarkis audovidaĵon de %{target} kiel tiklan"
+        unsilence_account_html: "%{name} malfaris limon al konto de %{target}"
         unsuspend_account_html: "%{name} malsuspendis la konton de %{target}"
-        update_announcement_html: "%{name} ĝisdatigis anoncon %{target}"
-        update_custom_emoji_html: "%{name} ĝisdatigis emoĝion %{target}"
+        update_announcement_html: "%{name} ĝisdatigis la anoncon %{target}"
+        update_custom_emoji_html: "%{name} ĝisdatigis la emoĝion %{target}"
         update_domain_block_html: "%{name} ĝisdatigis domajnblokon por %{target}"
         update_ip_block_html: "%{name} ŝanĝis regulon por IP %{target}"
         update_status_html: "%{name} ĝisdatigis mesaĝon de %{target}"
-        update_user_role_html: "%{name} ŝanĝis rolon %{target}"
+        update_user_role_html: "%{name} ŝanĝis la rolon %{target}"
       deleted_account: forigita konto
       empty: Neniu ĵurnalo trovita.
       filter_by_action: Filtri per ago
@@ -321,13 +325,13 @@ eo:
       emoji: Emoĝio
       enable: Ebligi
       enabled: Ebligita
-      enabled_msg: Tiu emoĝio estis sukcese ebligita
+      enabled_msg: Emoĝio sukcese ebligita
       image_hint: PNG aŭ GIF malpli granda ol %{size}
       list: Listo
       listed: Listigita
       new:
         title: Aldoni novan propran emoĝion
-      no_emoji_selected: Neniuj emoĝioj ŝanĝitas ĉar nenio elektitas
+      no_emoji_selected: Neniu emoĝio estis ŝanĝita ĉar neniu estis elektita
       not_permitted: Vi ne rajtas plenumi ĉi tiun agon
       overwrite: Anstataŭigi
       shortcode: Mallonga kodo
@@ -342,21 +346,21 @@ eo:
     dashboard:
       active_users: aktivaj uzantoj
       interactions: interago
-      media_storage: Konservo de aŭdovidaĵoj
+      media_storage: Konservo de plurmedioj
       new_users: novaj uzantoj
       opened_reports: raportoj malfermitaj
       pending_appeals_html:
-        one: "<strong>%{count}</strong> restanta apelacio"
-        other: "<strong>%{count}</strong> restantaj aplecioj"
+        one: "<strong>%{count}</strong> apelacio atendas kontrolon"
+        other: "<strong>%{count}</strong> apelacioj atendas kontrolon"
       pending_reports_html:
-        one: "<strong>%{count}</strong> restanta raporto"
-        other: "<strong>%{count}</strong> restantaj raportoj"
+        one: "<strong>%{count}</strong> raporto atendas kontrolon"
+        other: "<strong>%{count}</strong> raportoj atendas kontrolon"
       pending_tags_html:
         one: "<strong>%{count}</strong> pritraktota kradvorto"
         other: "<strong>%{count}</strong> pritraktotaj kradvortoj"
       pending_users_html:
-        one: "<strong>%{count}</strong> restanta uzanto"
-        other: "<strong>%{count}</strong> restantaj uzantoj"
+        one: "<strong>%{count}</strong> uzanto atendas kontrolon"
+        other: "<strong>%{count}</strong> uzantoj atendas kontrolon"
       resolved_reports: raportoj solvitaj
       software: Programo
       sources: Fontoj de konto-kreado
@@ -371,10 +375,10 @@ eo:
         title: Apelacioj
     domain_allows:
       add_new: Aldoni domajnon al la blanka listo
-      created_msg: Domajno estis sukcese aldonita al la blanka listo
+      created_msg: Domajno sukcese permesita al federacii
       destroyed_msg: Domajno estis forigita el la blanka listo
       export: Eksporti
-      import: Importi
+      import: Enporti
       undo: Forigi el la blanka listo
     domain_blocks:
       add_new: Aldoni novan blokadon de domajno
@@ -385,7 +389,7 @@ eo:
       existing_domain_block: Vi jam metis pli striktajn limojn al %{name}.
       existing_domain_block_html: Vi jam trudis pli striktajn limojn al %{name}, vi devas <a href="%{unblock_url}">malbloki ĝin</a> unue.
       export: Eksporti
-      import: Importi
+      import: Enporti
       new:
         create: Krei blokadon
         hint: La domajna blokado ne evitigos kreadon de novaj kontoj en la datumbazo, sed aplikos specifajn kontrolajn agojn sur ĉi tiujn kontojn aŭtomate kaj retroaktive.
@@ -425,6 +429,7 @@ eo:
         resolve: Solvi domajnon
         title: Nova blokado de retadresa domajno
       no_email_domain_block_selected: Neniuj retpoŝtoadresodomajnblokoj ŝanĝitas ĉar nenio elektitas
+      not_permitted: Ne permesita
       resolved_dns_records_hint_html: La domajnnomo referencas al la MX-domajnoj kiuj akceptas retpoŝton. <strong>Ne bloku grandajn retpoŝtoservilojn.</strong>
       resolved_through_html: Solvis tra %{domain}
       title: Nigra listo de retadresaj domajnoj
@@ -437,8 +442,9 @@ eo:
         description_html: Vi importos liston de domajnblokoj.
         existing_relationships_warning: Ekzistantaj sekvorilatoj
         private_comment_description_html: 'Por helpi vin, importitaj blokoj kreitas kun la privata komento: <q>%{comment}</q>'
-        private_comment_template: Importita de %{source} je %{date}
+        private_comment_template: Enportita el %{source} je %{date}
         title: Importi domajnblokojn
+      invalid_domain_block: '1 au pli da domajnblokadoj ignoritas ĉar la eraro: %{error}'
       new:
         title: Importi domajnblokojn
       no_file: Neniu dosiero elektita
@@ -470,8 +476,9 @@ eo:
       content_policies:
         comment: Interna noto
         description_html: Vi povas difini enhavopolitikojn al la ĉiuj kontoj.
+        limited_federation_mode_description_html: Vi povas elekti, ĉu permesi federacion kun tiu domajno.
         policies:
-          reject_media: Malakcepti aŭdovidaĵojn
+          reject_media: Malakcepti plurmediojn
           reject_reports: Malakcepti raportojn
           silence: Kaŝu
           suspend: Suspendi
@@ -484,7 +491,7 @@ eo:
         instance_followers_measure: niaj sekvantoj tie
         instance_follows_measure: iliaj sekvantoj ĉi tie
         instance_languages_dimension: Ĉefaj lingvoj
-        instance_media_attachments_measure: konservitaj ligitaj aŭdovidaĵoj
+        instance_media_attachments_measure: konservitaj plurmediaj aldonaĵoj
         instance_reports_measure: raportoj pri ili
         instance_statuses_measure: konservitaj afiŝoj
       delivery:
@@ -555,7 +562,7 @@ eo:
       pending: Atendante aprobon de la ripetilo
       save_and_enable: Konservi kaj ebligi
       setup: Agordi konekton al ripetilo
-      signatures_not_enabled: Ripetiloj ne ĝuste funkcias dum sekura reĝimo au limigita federacio reĝimo ebligitas
+      signatures_not_enabled: Relajsoj eble ne funkcias ĝuste dum sekura reĝimo aŭ reĝimo de limigita federado estas ŝaltita
       status: Stato
       title: Ripetiloj
     report_notes:
@@ -573,19 +580,23 @@ eo:
         mark_as_sensitive_description_html: La audovidaĵo en la raportita mesaĝo markotas kiel sentema kaj admono rekorditas.
         other_description_html: Vidu pli da ebloj por regi la sintenon de la konto kaj por personigi la komunikadon kun la raportita konto.
         resolve_description_html: Nenio okazotas al la raportita konto kaj la raporto fermotas.
-        silence_description_html: La profilo estas videbla nur de persono kiu jam sekvis au serĉis.
-        suspend_description_html: La profilo kaj ĉiuj ĝiaj enhavoj fariĝos nealirebla kaj ĝi fine forigotas.
+        silence_description_html: La konto estos videbla al nur personoj kiu jam sekvis ĝin au permane serĉo ĝin, ege limigante ĝian atingon. Malfermi ciujn raportojn kontra ĉi tiun konton.
+        suspend_description_html: La konto kaj ciuj ĝiaj enhavoj estos neatingebla kaj poŝte forigitas, kaj interagi per ĝi estos neebla. Malfermi ciujn raportojn kontra ĉi tiu konto.
       actions_description_html: Decidu kiu ago por solvi ĉi tiuj raporto. <strong>Spamo</strong> kategorio elektitas.
+      actions_description_remote_html: Decidu kiun klopodon por solvi ĉi tiun raporton. Ĉi tiu efikas kiel nur <strong>via</strong> servilo komuniki per ĉi tiu fora konto kaj trakti ĝian enhavon.
       add_to_report: Aldoni pli al raporto
       are_you_sure: Ĉu vi certas?
       assign_to_self: Asigni al mi
       assigned: Asignita kontrolanto
       by_target_domain: Domajno de la signalita konto
+      cancel: Nuligi
       category: Kategorio
       category_description_html: La kialo pri ĉi tiuj konto kaj enhavo raportitaj sendotas al la raportita konto
       comment:
         none: Nenio
       comment_description_html: 'Por doni pli da informo, %{name} skribis:'
+      confirm: Konfirmi
+      confirm_action: Konfirmi moderigadagon kontra @%{acct}
       created_at: Signalita
       delete_and_resolve: Forigi afiŝojn
       forwarded: Plusendita
@@ -602,6 +613,7 @@ eo:
         placeholder: Priskribu faritajn agojn, aŭ ajnan novan informon pri tiu signalo…
         title: Notoj
       notes_description_html: Vidi kaj lasi notojn por aliaj kontrolantoj kaj estonta vi
+      processed_msg: 'La raporto #%{id} sukcese prilaborita'
       quick_actions_description_html: 'Agu au movu malsupre por vidi raportitajn enhavojn:'
       remote_user_placeholder: la ekstera uzanto de %{instance}
       reopen: Remalfermi signalon
@@ -614,9 +626,28 @@ eo:
       status: Mesaĝoj
       statuses: Raportita enhavo
       statuses_description_html: Sentema enhavo referencitas kun la raportita konto
+      summary:
+        action_preambles:
+          delete_html: 'Vi ja tuj <strong>forigos</strong> kelke da afiŝoj de <strong>@%{acct}</strong>. Tio faros kiel jene:'
+          mark_as_sensitive_html: 'Vi ja tuj <strong>markos</strong> kelke da afiŝoj de <strong>@%{acct}</strong> kiel <strong>tiklaj</strong>. Tio faros kiel:'
+          silence_html: 'Vi ja tuj <strong>limigos</strong> kelke da afiŝoj de <strong>@%{acct}</strong>. Tio faros kiel:'
+          suspend_html: 'Vi ja tuj <strong>suspendos</strong> la konton de <strong>@%{acct}</strong>. Tio faros kiel:'
+        actions:
+          delete_html: Forigi la sentemajn afiŝojn
+          mark_as_sensitive_html: Markigi la audovidaĵojn de sentemaj afiŝoj kiel sentemaj
+          silence_html: Ege limigi atingon de <strong>@%{acct}</strong> per kauzi ilia profilo kaj enhavoj esti videbla nur al personoj kiu jam sekvis ilin au permane serĉi ĝin
+          suspend_html: Suspendi <strong>@%{acct}</strong>, fari ties profilon kaj enhavojn neatingeblaj kaj maleblaj interagi kun
+        close_report: 'Marki la raporto #%{id} kiel solvita'
+        close_reports_html: Marki <strong>ĉiuj</strong> raportoj kontraŭ <strong>@%{acct}</strong> kiel solvitaj
+        delete_data_html: Forigi profilon kaj enhavojn de <strong>@%{acct}</strong> post 30 tagoj se ili ne malsuspenditas dum la dauro
+        preview_preamble_html: "<strong>@%{acct}</strong> akiros averton kun ĉi tiuj enhavoj:"
+        record_strike_html: Rekordu admonon kontra <strong>@%{acct}</strong> por helpi vi plikontroli estontajn malobservojn de ĉi tiu konto
+        send_email_html: Sendi al <strong>@%{acct}</strong> retpoŝtaĵon de averto
+        warning_placeholder: Nedeviga aldona kialo por la moderigadago.
       target_origin: Origino de raportita konto
       title: Signaloj
       unassign: Malasigni
+      unknown_action_msg: 'Nekonata ago: %{action}'
       unresolved: Nesolvitaj
       updated_at: Ĝisdatigita
       view_profile: Vidi profilon
@@ -711,6 +742,8 @@ eo:
         preamble: Interesa enhavo estas grava por novaj uzantoj kiuj eble ne konas ajn iun.
         profile_directory: Profilujo
         public_timelines: Publikaj templinioj
+        publish_discovered_servers: Publikigi la malkovritajn servilojn
+        publish_statistics: Publikigi statistikojn
         title: Eltrovado
         trends: Tendencoj
       domain_blocks:
@@ -765,6 +798,7 @@ eo:
         suspend: "%{name} suspendis la konton de %{target}"
       appeal_approved: Apelaciita
       appeal_pending: Apelacio pritraktiĝos
+      appeal_rejected: Apelacio malakceptita
     system_checks:
       database_schema_check:
         message_html: Estas pritraktataj datumbazaj migradoj. Bonvolu ekzekuti ilin por certigi, ke la apliko kondutas kiel atendite
@@ -800,6 +834,7 @@ eo:
           other: Diskonita de %{count} personoj ekde lasta semajno
         title: Tendencantaj ligiloj
         usage_comparison: Diskonita %{today}-foje hodiau, sed estas %{yesterday} hierau
+      not_allowed_to_trend: Ne rajtas fariĝi furoraĵo
       only_allowed: Nur permesitas
       pending_review: Atendante revizion
       preview_card_providers:
@@ -931,13 +966,16 @@ eo:
   applications:
     created: Aplikaĵo sukcese kreita
     destroyed: Aplikaĵo sukcese forigita
-    regenerate_token: Rekrei aliran ĵetonon
-    token_regenerated: Alira ĵetono sukcese rekreita
+    logout: Elsaluti
+    regenerate_token: Regeneri aliran ĵetonon
+    token_regenerated: Alira ĵetono sukcese regeneria
     warning: Estu tre atenta kun ĉi tiu datumo. Neniam diskonigu ĝin al iu ajn!
     your_token: Via alira ĵetono
   auth:
     apply_for_account: Peti konton
     change_password: Pasvorto
+    confirmations:
+      wrong_email_hint: Se tiu retpoŝtadreso ne estas ĝusta, vi povas ŝanĝi ĝin en la agordoj pri la konto.
     delete_account: Forigi konton
     delete_account_html: Se vi deziras forigi vian konton, vi povas <a href="%{path}">fari tion ĉi tie</a>. Vi bezonos konfirmi vian peton.
     description:
@@ -947,15 +985,15 @@ eo:
     didnt_get_confirmation: Ĉu vi ne ricevis la instrukciojn por konfirmi?
     dont_have_your_security_key: Ne havas vi vian sekurecan ŝlosilon?
     forgot_password: Pasvorto forgesita?
-    invalid_reset_password_token: Ĵetono por restarigi pasvorton nevalida aŭ eksvalida. Bonvolu peti novan.
+    invalid_reset_password_token: La ĵetono por restarigi la pasvorton estas nevalida aŭ eksvalida. Bonvolu peti novon.
     link_to_otp: Enigu 2-faktorkodo de via telefono au regajnkodo
     link_to_webauth: Uzi vian sekurecan ŝlosilon
     log_in_with: Ensaluti per
-    login: Saluti
+    login: Ensaluti
     logout: Adiaŭi
     migrate_account: Movi al alia konto
     migrate_account_html: Se vi deziras alidirekti ĉi tiun konton al alia, vi povas <a href="%{path}">agordi ĝin ĉi tie</a>.
-    or_log_in_with: Aŭ ensaluti per
+    or_log_in_with: Aŭ saluti per
     privacy_policy_agreement_html: Mi legis kaj konsentis pri <a href="%{privacy_policy_path}" target="_blank">privatpolitiko</a>
     providers:
       cas: CAS
@@ -965,6 +1003,8 @@ eo:
     resend_confirmation: Resendi la instrukciojn por konfirmi
     reset_password: Restarigi pasvorton
     rules:
+      accept: Akcepti
+      back: Reen
       preamble: Ĉi tioj fiksitas kaj devigitas de kontrolantoj de %{domain}.
       title: Bazaj reguloj.
     security: Sekureco
@@ -973,6 +1013,9 @@ eo:
       email_below_hint_html: Se malsupra retpoŝtoadreso estas neĝusta, vi povas ŝanĝi ĉi tie kaj ricevi novan konfirmretpoŝton.
       email_settings_hint_html: La konfirmretpoŝto senditas al %{email}.
       title: Agordi
+    sign_in:
+      preamble_html: Ensalutu per via detaloj de <strong>%{domain}</strong>. Se via konto gastigantigas sur malsama servilo, vi ne povas ensaluti ĉi tie.
+      title: Saluti en %{domain}
     sign_up:
       preamble: Per konto ĉe ĉi tiu Mastodon-servilo, vi povas sekvi ajn personojn en la reto.
       title: Ni pretigu vin ĉe %{domain}.
@@ -980,7 +1023,7 @@ eo:
       account_status: Statuso de la konto
       confirming: Retpoŝtokonfirmo bezonas kompletigitis.
       functional: Via konto estas tute funkcia.
-      pending: Via apliko ankarou bezonas kontrolon de nia teamo. Vi ricevos retpoŝto malantau ol aprobo.
+      pending: Via aliĝo estos kontrolita de nia teamo. Tio povas bezoni iom da tempo. Vi ricevos retpoŝtmesaĝon se via aliĝo estas aprobita.
       redirecting_to: Via konto estas neaktiva ĉar ĝi nun alidirektas al %{acct}.
       view_strikes: Vidi antauaj admonoj kontra via konto
     too_fast: Formularo sendita tro rapide, klopodu denove.
@@ -1033,7 +1076,7 @@ eo:
     warning:
       before: 'Antau ol dauri, legu ĉi tiujn notojn zorgeme:'
       caches: Enhavo kiu kaŝmemorigitas de aliaj serviloj eble restas
-      data_removal: Viaj mesaĝoj kaj aliaj datumoj permanente forigotas
+      data_removal: Viaj afiŝoj kaj aliaj informoj estos forigita por eterne
       email_change_html: Vi povas <a href="%{path}">ŝanĝi vian retadreson</a> sen forigi vian konton
       email_contact_html: Se ĝi ankorau ne venas, vi povas retpoŝti al <a href="mailto:%{email}">%{email}</a> por ricevi helpon
       email_reconfirmation_html: Se vi ne ricevas la konfirmretpoŝton, vi povas <a href="%{path}">denove peti</a>
@@ -1155,8 +1198,6 @@ eo:
       index:
         hint: Ĉi tiu filtrilo kongruas kelkaj mesaĝoj sendepende de aliaj kriterioj.
         title: Filtritaj mesaĝoj
-  footer:
-    trending_now: Nunaj furoraĵoj
   generic:
     all: Ĉio
     all_items_on_page_selected_html:
@@ -1179,8 +1220,6 @@ eo:
     validation_errors:
       one: Io mise okazis! Bonvolu konsulti la suban erar-raporton
       other: Io mise okazis! Bonvolu konsulti la subajn %{count} erar-raportojn
-  html_validator:
-    invalid_markup: 'havas nevalidan HTML-markadon: %{error}'
   imports:
     errors:
       invalid_csv_file: Nevalida CSV-dosiero. %{error}
@@ -1216,7 +1255,7 @@ eo:
       one: 1 uzo
       other: "%{count} uzoj"
     max_uses_prompt: Neniu limo
-    prompt: Generi kaj kundividi ligilojn kun la aliaj por doni aliron al ĉi tiu servilo
+    prompt: Generi kaj kundividi ligilojn kun aliaj por doni aliron al ĉi tiu servilo
     table:
       expires_at: Eksvalidiĝas je
       uses: Uzoj
@@ -1286,12 +1325,12 @@ eo:
       sign_up:
         subject: "%{name} registriĝis"
     favourite:
-      body: "%{name} stelumis vian mesaĝon:"
-      subject: "%{name} stelumis vian mesaĝon"
+      body: "%{name} stelumis vian afiŝon:"
+      subject: "%{name} stelumis vian afiŝon"
       title: Nova stelumo
     follow:
-      body: "%{name} eksekvis vin!"
-      subject: "%{name} eksekvis vin"
+      body: "%{name} nun sekvas vin!"
+      subject: "%{name} nun sekvas vin"
       title: Nova sekvanto
     follow_request:
       action: Administri petojn de sekvado
@@ -1307,10 +1346,10 @@ eo:
       subject: Enketo de %{name} finitas
     reblog:
       body: 'Via mesaĝo estis diskonigita de %{name}:'
-      subject: "%{name} diskonigis vian mesaĝon"
+      subject: "%{name} diskonigis vian afiŝon"
       title: Nova diskonigo
     status:
-      subject: "%{name} ĵus afiŝita"
+      subject: "%{name} ĵus afiŝis"
     update:
       subject: "%{name} redaktis afiŝon"
   notifications:
@@ -1364,7 +1403,11 @@ eo:
       unrecognized_emoji: ne estas rekonita emoĝio
   relationships:
     activity: Konta aktiveco
+    confirm_follow_selected_followers: Ĉu vi certas ke vi volas sekvi la elektitajn sekvantojn?
+    confirm_remove_selected_followers: Ĉu vi certas, ke vi volas forigi la elektitajn sekvantojn?
+    confirm_remove_selected_follows: Ĉu vi certas, ke vi volas forigi la elektitajn sekvatojn?
     dormant: Dormanta
+    follow_failure: Ne eblis sekvi iom da la elektitaj kontoj.
     follow_selected_followers: Sekvi selektitajn sekvantojn
     followers: Sekvantoj
     following: Sekvatoj
@@ -1404,6 +1447,7 @@ eo:
       electron: Electron
       firefox: Firefox
       generic: Nekonata retumilo
+      huawei_browser: Retumilo Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1413,6 +1457,7 @@ eo:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Nekonata retumilo
       weibo: Weibo
     current_session: Nuna seanco
     description: "%{browser} en %{platform}"
@@ -1425,9 +1470,10 @@ eo:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: nekonata platformo
+      unknown_platform: Nekonata platformo
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1447,7 +1493,7 @@ eo:
     edit_profile: Redakti profilon
     export: Eksporti datumojn
     featured_tags: Elstarigitaj kradvortoj
-    import: Importi
+    import: Enporti
     import_and_export: Importi kaj eksporti
     migrate: Konta migrado
     notifications: Sciigoj
@@ -1540,7 +1586,7 @@ eo:
       '7889238': 3 monatoj
     min_age_label: Aĝlimo
     min_favs: Konservi mesaĝojn stelumitajn almenaŭ
-    min_favs_hint: Ne forigas mesaĝojn de vi kiuj ricevis almenaŭ tiom da stelumoj. Lasu malplena por forigi mesaĝojn sendepende de la nombro de stelumoj
+    min_favs_hint: Oni ne forigas viajn afiŝojn, kiuj estas diskonigitaj almenaŭ ĉi tiun nombron da fojoj. Lasu malplena por forigi afiŝojn sendepende de iliaj nombroj da diskonigoj
     min_reblogs: Konservi diskonitajn mesaĝojn almenau
     min_reblogs_hint: Oni ne forigas viajn afiŝojn kiuj estas diskonigitaj almenaŭ ĉi tiun nombron da fojoj. Lasu malplena por forigi afiŝojn sendepende de iliaj nombroj da diskonigoj
   stream_entries:
@@ -1592,11 +1638,11 @@ eo:
       title: Arkiva elŝuto
     suspicious_sign_in:
       change_password: ŝanĝi vian pasvorton
-      details: 'Detaloj pri ensaluto:'
-      explanation: Ni detektas ensaluton al via konto de nova IP-adreso.
+      details: 'Ĉi-sube estas detaloj pri la saluto:'
+      explanation: Ni detektis saluton en via konto de nova IP-adreso.
       further_actions_html: Se ne estas vi, ni rekomendas ke vi %{action} tuj por sekurigi vian konton.
       subject: Via konto estas alirita de nova IP-adreso
-      title: Nova ensaluto
+      title: Nova saluto
     warning:
       appeal: Sendi apelacion
       appeal_description: Se vi pensas ke ĉi tio estas eraro, vi povas sendi apelacion al la teamo de %{instance}.
@@ -1608,7 +1654,7 @@ eo:
         disable: Vi ne povas uzi vian konton plu sed via profilo kaj alia datumo restas bone.
         mark_statuses_as_sensitive: Kelkaj viaj mesaĝoj markitas kiel sentemaj de kontrolantoj de %{instance}. Vi povas marki mem kiam mesaĝi dum la estonteco.
         sensitive: Malantau ol nun, ĉiuj viaj alŝutitaj audovidaĵoj markitas kiel sentemaj kaj kaŝitas.
-        silence: Vi ankorau povas uzi vian konton sed nur personoj kiu jam sekvis vin vidos viaj mesaĝoj ĉe ĉi tiu servilo.
+        silence: Vi ankorau povas uzi vian konton, sed nur personoj kiuj jam sekvas vin vidos viajn mesaĝojn en tiu ĉi servilo, kaj vi eble estos ekskluzive el diversaj malkovrigaj funkcioj. Tamen, aliaj ankoraŭ povas permane eksekvi vin.
         suspend: Vi ne povas uzi vian konton plu, kaj via profilo kaj aliaj datumoj ne estas disponeblaj plu.
       reason: 'Kialo:'
       statuses: 'Mesaĝoj ripetitaj:'
@@ -1660,7 +1706,7 @@ eo:
       success: Via sekureca ŝlosilo estis sukcese forigita.
     invalid_credential: Nevalida sekureca ŝlosilo
     nickname_hint: Enigu alinomon de via nova sekurecŝlosilo
-    not_enabled: Vu ne ebligas WebAuthn ĝis nun
+    not_enabled: Vi ankoraŭ ne ŝaltis WebAuth
     not_supported: Ĉi tiu legilo ne povas uzi sekurecŝlosilojn
     otp_required: Por uzi sekurecŝlosilojn, ebligu 2-faktoran autentigon unue.
     registered_on: Registrigita je %{date}
diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml
index 3f5ef4ede..fce42cf96 100644
--- a/config/locales/es-AR.yml
+++ b/config/locales/es-AR.yml
@@ -91,6 +91,7 @@ es-AR:
       moderation:
         active: Activas
         all: Todas
+        disabled: Deshabilitadas
         pending: Pendientes
         silenced: Limitada
         suspended: Suspendidas
@@ -133,6 +134,7 @@ es-AR:
       search: Buscar
       search_same_email_domain: Otros usuarios con el mismo dominio de correo electrónico
       search_same_ip: Otros usuarios con la misma dirección IP
+      security: Seguridad
       security_measures:
         only_password: Sólo contraseña
         password_and_2fa: Contraseña y 2FA
@@ -427,6 +429,7 @@ es-AR:
         resolve: Resolver dominio
         title: Bloquear nuevo dominio de correo electrónico
       no_email_domain_block_selected: No se cambiaron bloques de dominio ya que no se seleccionó ninguno
+      not_permitted: No permitidos
       resolved_dns_records_hint_html: El nombre de dominio resuelve los siguientes dominios MX, los cuales son responsables en última instancia de aceptar el correo electrónico. Bloquear un dominio MX bloqueará los registros de cualquier dirección de correo electrónico que utilice el mismo dominio MX, incluso si el nombre de dominio visible es diferente. <strong>Tené cuidado de no bloquear los principales proveedores de correo electrónico.</strong>
       resolved_through_html: Resuelto a través de %{domain}
       title: Dominios bloqueados de correo electrónico
@@ -441,6 +444,7 @@ es-AR:
         private_comment_description_html: 'Para ayudarte a rastrear de dónde vienen los bloques importados, se crearán los mismos con el siguiente comentario privado: <q>%{comment}</q>'
         private_comment_template: Importado desde %{source} el %{date}
         title: Importar bloques de dominio
+      invalid_domain_block: 'Uno o más bloqueos de dominio fueron omitidos debido al/los siguiente/s error/es: %{error}'
       new:
         title: Importar bloques de dominio
       no_file: No hay ningún archivo seleccionado
@@ -472,6 +476,7 @@ es-AR:
       content_policies:
         comment: Nota interna
         description_html: Podés definir políticas de contenido que se aplicarán a todas las cuentas de este dominio y a cualquiera de sus subdominios.
+        limited_federation_mode_description_html: Podés elegir si permit´s la federación con este dominio.
         policies:
           reject_media: Rechazar medios
           reject_reports: Rechazar denuncias
@@ -575,19 +580,23 @@ es-AR:
         mark_as_sensitive_description_html: Los archivos de medios en los mensajes denunciados se marcarán como sensibles y se registrará un incumplimiento para ayudarte a escalar las futuras infracciones de la misma cuenta.
         other_description_html: Ver más opciones para controlar el comportamiento de la cuenta y personalizar la comunicación de la cuenta denunciada.
         resolve_description_html: No se tomarán medidas contra la cuenta denunciada, no se registrará el incumplimiento, y se cerrará la denuncia.
-        silence_description_html: El perfil será visible sólo para aquellos que ya sigan esta cuenta o que la busquen manualmente, limitando seriamente su alcance. Siempre puede ser revertido.
-        suspend_description_html: El perfil y todos sus contenidos serán inaccesibles hasta que sean finalmente eliminados. No será posible interactuar con la cuenta. Reversible dentro de los 30 días.
+        silence_description_html: La cuenta será visible sólo para quienes ya la siguen o la busquen manualmente, limitando severamente su alcance. Siempre puede ser revertido. Esto cierra todas las denuncias contra esta cuenta.
+        suspend_description_html: La cuenta y todos sus contenidos serán inaccesibles y finalmente eliminados, e interactuar con ella será imposible. Revertible en 30 días. Esto cierra todas las denuncias contra esta cuenta.
       actions_description_html: Decidí qué medidas tomar para resolver esta denuncia. Si tomás una acción punitiva contra la cuenta denunciada, se le enviará a dicha cuenta una notificación por correo electrónico, excepto cuando se seleccione la categoría <strong>Spam</strong>.
+      actions_description_remote_html: Decidí qué medidas tomar para resolver esta denuncia. Esto sólo afectará la forma en que <strong>tu servidor</strong> se comunica con esta cuenta remota y maneja su contenido.
       add_to_report: Agregar más a la denuncia
       are_you_sure: "¿Estás seguro?"
       assign_to_self: Asignármela a mí
       assigned: Moderador asignado
       by_target_domain: Dominio de la cuenta denunciada
+      cancel: Cancelar
       category: Categoría
       category_description_html: El motivo por el que se denunció esta cuenta o contenido será citado en las comunicaciones con la cuenta denunciada
       comment:
         none: Ninguno
       comment_description_html: 'Para proporcionar más información, %{name} escribió:'
+      confirm: Confirmar
+      confirm_action: Confirmar acción de moderación contra @%{acct}
       created_at: Denunciado
       delete_and_resolve: Eliminar mensajes
       forwarded: Reenviado
@@ -604,6 +613,7 @@ es-AR:
         placeholder: Describí qué acciones se tomaron, o cualquier otra actualización relacionada...
         title: Notas
       notes_description_html: Ver y dejar notas para otros moderadores y como referencia futura
+      processed_msg: 'Denuncia #%{id}, procesada exitosamente'
       quick_actions_description_html: 'Tomá una acción rápida o desplazate hacia abajo para ver el contenido denunciado:'
       remote_user_placeholder: el usuario remoto de %{instance}
       reopen: Reabrir denuncia
@@ -616,9 +626,28 @@ es-AR:
       status: Estado
       statuses: Contenido denunciado
       statuses_description_html: El contenido ofensivo se citará en la comunicación con la cuenta denunciada
+      summary:
+        action_preambles:
+          delete_html: 'Estás a punto de <strong>eliminar</strong> algunos de los mensajes de <strong>@%{acct}</strong>. Esto hará lo siguiente:'
+          mark_as_sensitive_html: 'Estás a punto de <strong>marcar</strong> algunos de los mensajes de <strong>@%{acct}</strong>como <strong>sensibles</strong>. Esto hará lo siguiente:'
+          silence_html: 'Estás a punto de <strong>limitar</strong> la cuenta de <strong>@%{acct}</strong>. Esto hará lo siguiente:'
+          suspend_html: 'Estás a punto de <strong>suspender</strong> la cuenta de <strong>@%{acct}</strong>. Esto hará lo siguiente:'
+        actions:
+          delete_html: Eliminar los mensajes ofensivos
+          mark_as_sensitive_html: Marcar los mensajes ofensivos como sensibles
+          silence_html: Limitar severamente el alcance de <strong>@%{acct}</strong> haciendo que su perfil y contenido sólo sean visibles para las personas que ya lo siguen o que busquen manualmente su perfil
+          suspend_html: Suspender <strong>@%{acct}</strong>, haciendo su perfil y contenido inaccesibles, e imposibilitando la interacción con la cuenta
+        close_report: 'Marcar denuncia #%{id} como resuelta'
+        close_reports_html: Marcar <strong>todas</strong> las denuncias contra <strong>@%{acct}</strong> como resueltas
+        delete_data_html: Eliminar el perfil y contenido de <strong>@%{acct}</strong> en 30 días a partir de ahora, a menos que se revierta la suspensión en ese tiempo
+        preview_preamble_html: "<strong>@%{acct}</strong> recibirá una advertencia con el siguiente contenido:"
+        record_strike_html: Registrá una amonestación contra <strong>@%{acct}</strong> para ayudarte a escalar futuras violaciones de esta cuenta
+        send_email_html: Enviar a <strong>@%{acct}</strong> un correo electrónico de advertencia
+        warning_placeholder: Razones adicionales opcionales para la acción de moderación.
       target_origin: Origen de la cuenta denunciada
       title: Denuncias
       unassign: Desasignar
+      unknown_action_msg: 'Acción desconocida: %{action}'
       unresolved: No resueltas
       updated_at: Actualizadas
       view_profile: Ver perfil
@@ -713,6 +742,8 @@ es-AR:
         preamble: Exponer contenido interesante a la superficie es fundamental para incorporar nuevos usuarios que pueden no conocer a nadie Mastodon. Controlá cómo funcionan varias opciones de descubrimiento en tu servidor.
         profile_directory: Directorio de perfiles
         public_timelines: Líneas temporales públicas
+        publish_discovered_servers: Publicar servidores descubiertos
+        publish_statistics: Publicar estadísticas
         title: Descubrí
         trends: Tendencias
       domain_blocks:
@@ -767,6 +798,7 @@ es-AR:
         suspend: "%{name} suspendió la cuenta de %{target}"
       appeal_approved: Apelado
       appeal_pending: Apelación pendiente
+      appeal_rejected: Apelación rechazada
     system_checks:
       database_schema_check:
         message_html: Hay migraciones pendientes de la base de datos. Por favor, ejecutalas para asegurarte de que la aplicación funciona según lo esperado
@@ -780,6 +812,12 @@ es-AR:
         message_html: No definiste ninguna regla del servidor.
       sidekiq_process_check:
         message_html: No hay ningún proceso Sidekiq en ejecución para la/s cola/s %{value}. Por favor, revisá tu configuración de Sidekiq
+      upload_check_privacy_error:
+        action: Revisá acá para más información
+        message_html: "<strong>Tu servidor web está mal configurado. La privacidad de tus usuarios está en riesgo.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Revisá acá para más información
+        message_html: "<strong>El almacenamiento de tu objeto está mal configurado. La privacidad de tus usuarios está en riesgo.</strong>"
     tags:
       review: Estado de revisión
       updated_msg: La configuración de la etiqueta se actualizó exitosamente
@@ -802,6 +840,7 @@ es-AR:
           other: Compartido por %{count} personas durante la última semana
         title: Enlaces en tendencia
         usage_comparison: Compartido %{today} veces hoy, comparado con la/s %{yesterday} vez/veces de ayer
+      not_allowed_to_trend: No se permite la tendencia
       only_allowed: Sólo permitidas
       pending_review: Revisión pendiente
       preview_card_providers:
@@ -933,6 +972,7 @@ es-AR:
   applications:
     created: Aplicación creada exitosamente
     destroyed: Aplicación eliminada exitosamente
+    logout: Cerrar sesión
     regenerate_token: Regenerar clave de acceso
     token_regenerated: Clave de acceso regenerada exitosamente
     warning: Ojo con estos datos. ¡Nunca los compartas con nadie!
@@ -940,6 +980,8 @@ es-AR:
   auth:
     apply_for_account: Solicitar una cuenta
     change_password: Contraseña
+    confirmations:
+      wrong_email_hint: Si esa dirección de correo electrónico no es correcta, podés cambiarla en la configuración de la cuenta.
     delete_account: Eliminar cuenta
     delete_account_html: Si querés eliminar tu cuenta, podés <a href="%{path}">seguir por acá</a>. Se te va a pedir una confirmación.
     description:
@@ -967,6 +1009,8 @@ es-AR:
     resend_confirmation: Reenviar correo electrónico de confirmación
     reset_password: Cambiar contraseña
     rules:
+      accept: Aceptar
+      back: Volver
       preamble: Estas reglas son establecidas y aplicadas por los moderadores de %{domain}.
       title: Algunas reglas básicas.
     security: Seguridad
@@ -1013,22 +1057,22 @@ es-AR:
       invalid_signature: no es una firma Ed25519 válida
   date:
     formats:
-      default: "%Y.%b.%d"
-      with_month_name: "%Y.%B.%d"
+      default: "%d de %b de %Y"
+      with_month_name: "%d de %B de %Y"
   datetime:
     distance_in_words:
-      about_x_hours: "%{count}h"
-      about_x_months: "%{count}M"
-      about_x_years: "%{count}A"
-      almost_x_years: "%{count}A"
+      about_x_hours: "%{count}hrs."
+      about_x_months: "%{count}mes."
+      about_x_years: "%{count}año."
+      almost_x_years: "%{count}año."
       half_a_minute: Recién
-      less_than_x_minutes: "%{count}min"
+      less_than_x_minutes: "%{count}min."
       less_than_x_seconds: Recién
-      over_x_years: "%{count}A"
-      x_days: "%{count}D"
-      x_minutes: "%{count}min"
-      x_months: "%{count}M"
-      x_seconds: "%{count}s"
+      over_x_years: "%{count}año."
+      x_days: "%{count}día."
+      x_minutes: "%{count}min."
+      x_months: "%{count}mes."
+      x_seconds: "%{count}seg."
   deletes:
     challenge_not_passed: La información que ingresaste no es correcta
     confirm_password: Ingresá tu contraseña actual para verificar tu identidad
@@ -1114,7 +1158,7 @@ es-AR:
   featured_tags:
     add_new: Agregar nueva
     errors:
-      limit: Alcanzaste el máximo de etiquetas destacadas
+      limit: Ya estableciste el número máximo de etiquetas
     hint_html: "<strong>¿Qué son las etiquetas destacadas?</strong> Son etiquetas que se muestran de forma permanente en tu perfil público y permiten a los usuarios navegar por tus mensajes públicos que contengan esas etiquetas. Las etiquetas destacadas son una gran herramienta para hacer un seguimiento de trabajos creativos o proyectos a largo plazo."
   filters:
     contexts:
@@ -1158,8 +1202,6 @@ es-AR:
       index:
         hint: Este filtro se aplica a la selección de mensajes individuales, independientemente de otros criterios. Podés agregar más mensajes a este filtro desde la interface web.
         title: Mensajes filtrados
-  footer:
-    trending_now: Tendencia ahora
   generic:
     all: Todas
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ es-AR:
     validation_errors:
       one: "¡Falta algo! Por favor, revisá el error abajo"
       other: "¡Falta algo! Por favor, revisá los %{count} errores abajo"
-  html_validator:
-    invalid_markup: 'contiene markup HTML no válido: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Archivo CSV no válido. Error: %{error}'
@@ -1226,7 +1266,7 @@ es-AR:
     title: Invitar a gente
   lists:
     errors:
-      limit: Alcanzaste el máximo de listas
+      limit: Alcanzaste el número máximo de listas
   login_activities:
     authentication_methods:
       otp: aplicación de autenticación de dos factores
@@ -1367,7 +1407,11 @@ es-AR:
       unrecognized_emoji: no es un emoji conocido
   relationships:
     activity: Actividad de la cuenta
+    confirm_follow_selected_followers: "¿Estás seguro que querés seguir a los seguidores seleccionados?"
+    confirm_remove_selected_followers: "¿Estás seguro que querés quitar a los seguidores seleccionados?"
+    confirm_remove_selected_follows: "¿Estás seguro que querés quitar a las cuentas seguidas seleccionadas?"
     dormant: Inactivas
+    follow_failure: No se pudieron seguir algunas de las cuentas seleccionadas.
     follow_selected_followers: Seguir a los seguidores seleccionados
     followers: Seguidores
     following: Siguiendo
@@ -1407,6 +1451,7 @@ es-AR:
       electron: Electron
       firefox: Mozilla Firefox
       generic: "[Navegador web desconocido]"
+      huawei_browser: Navegador Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Navegador web de Nokia S40 Ovi
@@ -1416,6 +1461,7 @@ es-AR:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: "[Navegador web desconocido]"
       weibo: Weibo
     current_session: Sesión actual
     description: "%{browser} en %{platform}"
@@ -1428,9 +1474,10 @@ es-AR:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: GNU/Linux
       mac: macOS
-      other: "[Plataforma desconocida]"
+      unknown_platform: "[Plataforma desconocida]"
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1543,7 +1590,7 @@ es-AR:
       '7889238': 3 meses
     min_age_label: Umbral de edad
     min_favs: Conservar mensajes marcados como favoritos de por lo menos
-    min_favs_hint: No elimina ninguno de tus mensajes que haya recibido más de esta cantidad de favoritos. Dejá en blanco para eliminar mensajes independientemente de su número de favoritos.
+    min_favs_hint: No elimina ninguno de tus mensajes que haya recibido al menos esta cantidad de favoritos. Dejá en blanco para eliminar mensajes independientemente de su número de favoritos.
     min_reblogs: Conservar adhesiones de por lo menos
     min_reblogs_hint: No elimina ninguno de tus mensajes que haya recibido más de esta cantidad de adhesiones. Dejá en blanco para eliminar mensajes independientemente de su número de adhesiones.
   stream_entries:
@@ -1643,12 +1690,13 @@ es-AR:
       title: "¡Bienvenido a bordo, %{name}!"
   users:
     follow_limit_reached: No podés seguir a más de %{limit} cuentas
+    go_to_sso_account_settings: Andá a la configuración de cuenta de tu proveedor de identidad
     invalid_otp_token: Código de dos factores no válido
     otp_lost_help_html: Si perdiste al acceso a ambos, podés ponerte en contacto con %{email}
     seamless_external_login: Iniciaste sesión desde un servicio externo, así que la configuración de contraseña y correo electrónico no están disponibles.
     signed_in_as: 'Iniciaste sesión como:'
   verification:
-    explanation_html: 'Podés <strong>verificarte a vos mismo como el propietario de los enlaces en los metadatos de tu perfil</strong>. Para eso, el sitio web del enlace debe contener un enlace de vuelta a tu perfil de Mastodon. El enlace en tu sitio <strong>debe</strong> tener un atributo <code>rel="me"</code>. El contenido del texto del enlace no importa. Acá tenés un ejemplo:'
+    explanation_html: 'Podés <strong>verificarte a vos mismo como el propietario de los enlaces en los metadatos de tu perfil</strong>. Para eso, el sitio web del enlace debe contener un enlace de vuelta a tu perfil de Mastodon. Después de agregar el enlace, puede que tengás que volver acá y volver a guardar los cambios en tu perfil para que la verificación surta efecto. El enlace en tu sitio <strong>debe</strong> tener un atributo <code>rel="me"</code>. El contenido del texto del enlace no importa. Acá tenés un ejemplo:'
     verification: Verificación
   webauthn_credentials:
     add: Agregar nueva llave de seguridad
diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml
index 132105848..077a0192d 100644
--- a/config/locales/es-MX.yml
+++ b/config/locales/es-MX.yml
@@ -91,6 +91,7 @@ es-MX:
       moderation:
         active: Activo
         all: Todos
+        disabled: Deshabilitado
         pending: Pendiente
         silenced: Limitado
         suspended: Suspendidos
@@ -133,6 +134,7 @@ es-MX:
       search: Buscar
       search_same_email_domain: Otros usuarios con el mismo dominio de correo
       search_same_ip: Otros usuarios con la misma IP
+      security: Seguridad
       security_measures:
         only_password: Sólo contraseña
         password_and_2fa: Contraseña y 2FA
@@ -427,6 +429,7 @@ es-MX:
         resolve: Resolver dominio
         title: Nueva entrada en la lista negra de correo
       no_email_domain_block_selected: No se han cambiado bloqueos de dominio ya que ninguno ha sido seleccionado
+      not_permitted: No permitido
       resolved_dns_records_hint_html: El nombre de dominio resuelve los siguientes dominios MX, los cuales son responsables en última instancia de aceptar el correo electrónico. Bloquear un dominio MX bloqueará los registros de cualquier dirección de correo electrónico que utilice el mismo dominio MX, incluso si el nombre de dominio visible es diferente. <strong>Tenga cuidado de no bloquear los principales proveedores de correo electrónico.</strong>
       resolved_through_html: Resuelto a través de %{domain}
       title: Lista negra de correo
@@ -441,6 +444,7 @@ es-MX:
         private_comment_description_html: 'Para ayudarle a rastrear de dónde proceden los bloqueos importados, los bloqueos importados se crearán con el siguiente comentario privado: <q>%{comment}</q>'
         private_comment_template: Importado desde %{source} el %{date}
         title: Importar bloqueos de dominio
+      invalid_domain_block: 'Uno o más bloques de dominio fueron omitidos debido a el/los siguiente(s) error(es): %{error}'
       new:
         title: Importar bloqueos de dominio
       no_file: No hay archivo seleccionado
@@ -472,6 +476,7 @@ es-MX:
       content_policies:
         comment: Nota interna
         description_html: Puedes definir políticas de contenido que se aplicarán a todas las cuentas de este dominio y a cualquiera de sus subdominios.
+        limited_federation_mode_description_html: Puedes elegir si permites la federación con este dominio.
         policies:
           reject_media: Rechazar multimedia
           reject_reports: Rechazar informes
@@ -575,19 +580,23 @@ es-MX:
         mark_as_sensitive_description_html: Los archivos multimedia en los mensajes informados se marcarán como sensibles y se aplicará una amonestación para ayudarte a escalar las futuras infracciones de la misma cuenta.
         other_description_html: Ver más opciones para controlar el comportamiento de la cuenta y personalizar la comunicación de la cuenta reportada.
         resolve_description_html: No se tomarán medidas contra la cuenta denunciada, no se registrará la amonestación, y se cerrará el informe.
-        silence_description_html: El perfil será visible solo para aquellos que ya lo sigan o lo busquen manualmente, limitando seriamente su alcance. Siempre puede ser revertido.
-        suspend_description_html: El perfil y todos sus contenidos serán inaccesibles hasta que sean finalmente eliminados. La interacción con la cuenta será imposible. Reversible durante un plazo de 30 días.
+        silence_description_html: La cuenta será visible sólo para aquellos que ya la sigan o la busquen manualmente, limitando severamente su visibilidad. Siempre puede ser revertido. Cierra todos los reportes contra esta cuenta.
+        suspend_description_html: La cuenta y todos sus contenidos serán inaccesibles y eventualmente eliminados, e interactuar con ella será imposible. Reversible durante 30 días. Cierra todos los reportes contra esta cuenta.
       actions_description_html: Decide qué medidas tomar para resolver esta denuncia. Si tomas una acción punitiva contra la cuenta denunciada, se le enviará a dicha cuenta una notificación por correo electrónico, excepto cuando se seleccione la categoría <strong>Spam</strong>.
+      actions_description_remote_html: Decide qué medidas tomar para resolver este reporte. Esto solo afectará a la forma en que <strong>tu servidor</strong> se comunica con esta cuenta remota y gestiona su contenido.
       add_to_report: Añadir más al reporte
       are_you_sure: "¿Estás seguro?"
       assign_to_self: Asignármela a mí
       assigned: Moderador asignado
       by_target_domain: Dominio de la cuenta reportada
+      cancel: Cancelar
       category: Categoría
       category_description_html: La razón por la que se reportó esta cuenta o contenido será citada en las comunicaciones con la cuenta reportada
       comment:
         none: Ninguno
       comment_description_html: 'Para proporcionar más información, %{name} escribió:'
+      confirm: Confirmar
+      confirm_action: Confirmar acción de moderación contra @%{acct}
       created_at: Denunciado
       delete_and_resolve: Eliminar publicaciones
       forwarded: Reenviado
@@ -604,6 +613,7 @@ es-MX:
         placeholder: Especificar qué acciones se han tomado o cualquier otra novedad respecto a esta denuncia…
         title: Notas
       notes_description_html: Ver y dejar notas a otros moderadores y a tu yo futuro
+      processed_msg: 'La denuncia #%{id} ha sido procesada con éxito'
       quick_actions_description_html: 'Toma una acción rápida o desplázate hacia abajo para ver el contenido denunciado:'
       remote_user_placeholder: el usuario remoto de %{instance}
       reopen: Reabrir denuncia
@@ -616,9 +626,28 @@ es-MX:
       status: Estado
       statuses: Contenido reportado
       statuses_description_html: El contenido ofensivo se citará en comunicación con la cuenta reportada
+      summary:
+        action_preambles:
+          delete_html: 'Estás a punto de <strong>eliminar</strong> algunas de la publicaciones de <strong>@%{acct}</strong>. Esto hará:'
+          mark_as_sensitive_html: 'Estás a punto de <strong>marcar</strong> algunas de las publicaciones de <strong>@%{acct}</strong>como <strong>sensibles</strong>. Esto hará:'
+          silence_html: 'Estás a punto de <strong>limitar</strong> la cuenta de <strong>@%{acct}</strong>. Esto hará:'
+          suspend_html: 'Estás a punto de <strong>suspender</strong> la cuenta de <strong>@%{acct}</strong>. Esto hará:'
+        actions:
+          delete_html: Eliminar las publicaciones ofensivas
+          mark_as_sensitive_html: Marcar las publicaciones ofensivas como sensibles
+          silence_html: Limitar severamente el alcance de <strong>@%{acct}</strong> haciendo que su perfil y contenido sólo sean visibles para las personas que ya lo siguen o que consulten manualmente su perfil
+          suspend_html: Suspender a <strong>@%{acct}</strong>, haciendo su perfil y contenido inaccesibles e imposible la interacción
+        close_report: 'Marcar denuncia #%{id} como resuelta'
+        close_reports_html: Marcar <strong>todas</strong> las denuncias contra <strong>@%{acct}</strong> como resueltas
+        delete_data_html: Eliminar el perfil y contenido de <strong>@%{acct}</strong> en 30 días a partir de ahora a menos que se revierta la suspensión en ese tiempo
+        preview_preamble_html: "<strong>@%{acct}</strong> recibirá una advertencia con el siguiente contenido:"
+        record_strike_html: Registra una amonestación contra <strong>@%{acct}</strong> para ayudarte a escalar futuras violaciones de esta cuenta
+        send_email_html: Enviar a <strong>@%{acct}</strong> un correo electrónico de advertencia
+        warning_placeholder: Razones adicionales opcionales para la acción de moderación.
       target_origin: Origen de la cuenta reportada
       title: Reportes
       unassign: Desasignar
+      unknown_action_msg: 'Acción desconocida: %{action}'
       unresolved: No resuelto
       updated_at: Actualizado
       view_profile: Ver perfil
@@ -713,6 +742,8 @@ es-MX:
         preamble: Exponer contenido interesante a la superficie es fundamental para incorporar nuevos usuarios que pueden no conocer a nadie Mastodon. Controla cómo funcionan varias opciones de descubrimiento en tu servidor.
         profile_directory: Directorio de perfiles
         public_timelines: Lineas de tiempo públicas
+        publish_discovered_servers: Publicar servidores descubiertos
+        publish_statistics: Publicar estadísticas
         title: Descubrimiento
         trends: Tendencias
       domain_blocks:
@@ -767,6 +798,7 @@ es-MX:
         suspend: "%{name} suspendio la cuenta de %{target}"
       appeal_approved: Apelado
       appeal_pending: Apelación pendiente
+      appeal_rejected: Apelación rechazada
     system_checks:
       database_schema_check:
         message_html: Hay migraciones pendientes de la base de datos. Por favor, ejecútalas para asegurarte de que la aplicación funciona como debería
@@ -780,6 +812,12 @@ es-MX:
         message_html: No ha definido ninguna regla del servidor.
       sidekiq_process_check:
         message_html: No hay ningún proceso Sidekiq en ejecución para la(s) cola(s) %{value}. Por favor, revise su configuración de Sidekiq
+      upload_check_privacy_error:
+        action: Para más información aquí
+        message_html: "<strong>Su servidor web no está configurado. Está en riesgo la privacidad de sus usuarios.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Para más información aquí
+        message_html: "<strong>Su almacenamiento no está configurado. Está en riesgo la privacidad de sus usuarios.</strong>"
     tags:
       review: Estado de revisión
       updated_msg: Hashtags actualizados exitosamente
@@ -802,6 +840,7 @@ es-MX:
           other: Compartido por %{count} personas durante la última semana
         title: Enlaces en tendencia
         usage_comparison: Compartido %{today} veces hoy, comparado a %{yesterday} ayer
+      not_allowed_to_trend: No permitido para tendencia
       only_allowed: Sólo las permitidas
       pending_review: Revisión pendiente
       preview_card_providers:
@@ -933,6 +972,7 @@ es-MX:
   applications:
     created: Aplicación creada exitosamente
     destroyed: Apicación eliminada exitosamente
+    logout: Cerrar sesión
     regenerate_token: Regenerar token de acceso
     token_regenerated: Token de acceso regenerado exitosamente
     warning: Ten mucho cuidado con estos datos. ¡No los compartas con nadie!
@@ -940,6 +980,8 @@ es-MX:
   auth:
     apply_for_account: Solicitar una cuenta
     change_password: Contraseña
+    confirmations:
+      wrong_email_hint: Si esa dirección de correo electrónico no es correcta, puedes cambiarla en la configuración de la cuenta.
     delete_account: Borrar cuenta
     delete_account_html: Si desea eliminar su cuenta, puede <a href="%{path}">proceder aquí</a>. Será pedido de una confirmación.
     description:
@@ -967,6 +1009,8 @@ es-MX:
     resend_confirmation: Volver a enviar el correo de confirmación
     reset_password: Restablecer contraseña
     rules:
+      accept: Aceptar
+      back: Atrás
       preamble: Estas son establecidas y aplicadas por los moderadores de %{domain}.
       title: Algunas reglas básicas.
     security: Cambiar contraseña
@@ -1158,8 +1202,6 @@ es-MX:
       index:
         hint: Este filtro se aplica a la selección de publicaciones individuales independientemente de otros criterios. Puede añadir más publicaciones a este filtro desde la interfaz web.
         title: Publicaciones filtradas
-  footer:
-    trending_now: Tendencia ahora
   generic:
     all: Todos
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ es-MX:
     validation_errors:
       one: "¡Algo no está bien! Por favor, revisa el error"
       other: "¡Algo no está bien! Por favor, revise %{count} errores más abajo"
-  html_validator:
-    invalid_markup: 'contiene código HTML no válido: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Archivo CSV no válido. Error: %{error}'
@@ -1367,7 +1407,11 @@ es-MX:
       unrecognized_emoji: no es un emoji conocido
   relationships:
     activity: Actividad de la cuenta
+    confirm_follow_selected_followers: "¿Estás seguro de que quieres seguir a las cuentas seleccionadas?"
+    confirm_remove_selected_followers: "¿Estás seguro de que quieres eliminar a los seguidores seleccionados?"
+    confirm_remove_selected_follows: "¿Estás seguro de que quieres eliminar los seguidos seleccionados?"
     dormant: Inactivo
+    follow_failure: No se pudo seguir algunas de las cuentas seleccionadas.
     follow_selected_followers: Seguir a los seguidores seleccionados
     followers: Seguidores
     following: Siguiendo
@@ -1407,6 +1451,7 @@ es-MX:
       electron: Electron
       firefox: Firefox
       generic: Desconocido
+      huawei_browser: Navegador Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Navegador de Nokia S40 Ovi
@@ -1416,6 +1461,7 @@ es-MX:
       qq: Navegador QQ
       safari: Safari
       uc_browser: Navegador UC
+      unknown_browser: Navegador desconocido
       weibo: Weibo
     current_session: Sesión actual
     description: "%{browser} en %{platform}"
@@ -1428,9 +1474,10 @@ es-MX:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: GNU Linux
       mac: Mac
-      other: Desconocido
+      unknown_platform: Plataforma desconocida
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1543,7 +1590,7 @@ es-MX:
       '7889238': 3 meses
     min_age_label: Umbral de tiempo
     min_favs: Mantener mensajes con un número de favoritos mayor que
-    min_favs_hint: No borra ninguna de las publicaciones que hayan recibido más de esta cantidad de favoritos. Deja en blanco para eliminar publicaciones sin importar el número de favoritos
+    min_favs_hint: No borra ninguna de las publicaciones que hayan recibido al menos esta cantidad de favoritos. Deja en blanco para eliminar publicaciones sin importar el número de favoritos
     min_reblogs: Mantener publicaciones reblogueadas más de
     min_reblogs_hint: No borra ninguna de las publicaciones que hayan sido reblogueadas más de este número de veces. Deja en blanco para eliminar publicaciones sin importar el número de reblogueos
   stream_entries:
@@ -1643,12 +1690,13 @@ es-MX:
       title: Te damos la bienvenida a bordo, %{name}!
   users:
     follow_limit_reached: No puedes seguir a más de %{limit} personas
+    go_to_sso_account_settings: Diríjete a la configuración de la cuenta de su proveedor de identidad
     invalid_otp_token: Código de dos factores incorrecto
     otp_lost_help_html: Si perdiste al acceso a ambos, puedes ponerte en contancto con %{email}
     seamless_external_login: Has iniciado sesión desde un servicio externo, así que los ajustes de contraseña y correo no están disponibles.
     signed_in_as: 'Sesión iniciada como:'
   verification:
-    explanation_html: 'Puedes <strong> verificarte a ti mismo como el dueño de los links en los metadatos de tu perfil </strong>. Para eso, el sitio vinculado debe contener un vínculo a tu perfil de Mastodon. El vínculo en tu sitio <strong> debe </strong> tener un atributo <code> rel="me"</code>. El texto del vínculo no importa. Aquí un ejemplo:'
+    explanation_html: 'Puedes <strong>verificarte a ti mismo como el dueño de los links en los metadatos de tu perfil</strong>. Para eso, el sitio enlazado debe contener un enlace a tu perfil de Mastodon. Después de añadir el enlace, necesitarás regresar aquí y volver a guardar tu perfil para que la verificación tenga efecto. El enlace en tu sitio <strong>debe</strong> tener un atributo <code>rel="me"</code>. El texto del contenido del enlace no importa. Aquí un ejemplo:'
     verification: Verificación
   webauthn_credentials:
     add: Agregar nueva clave de seguridad
diff --git a/config/locales/es.yml b/config/locales/es.yml
index ad836bd4e..22b760fec 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -91,6 +91,7 @@ es:
       moderation:
         active: Activo
         all: Todos
+        disabled: Deshabilitado
         pending: Pendiente
         silenced: Limitado
         suspended: Suspendidos
@@ -133,6 +134,7 @@ es:
       search: Buscar
       search_same_email_domain: Otros usuarios con el mismo dominio de correo
       search_same_ip: Otros usuarios con la misma IP
+      security: Seguridad
       security_measures:
         only_password: Sólo contraseña
         password_and_2fa: Contraseña y 2FA
@@ -427,6 +429,7 @@ es:
         resolve: Resolver dominio
         title: Nueva entrada en la lista negra de correo
       no_email_domain_block_selected: No se han cambiado bloqueos de dominio ya que ninguno ha sido seleccionado
+      not_permitted: No permitido
       resolved_dns_records_hint_html: El nombre de dominio resuelve los siguientes dominios MX, los cuales son responsables en última instancia de aceptar el correo electrónico. Bloquear un dominio MX bloqueará los registros de cualquier dirección de correo electrónico que utilice el mismo dominio MX, incluso si el nombre de dominio visible es diferente. <strong>Tenga cuidado de no bloquear los principales proveedores de correo electrónico.</strong>
       resolved_through_html: Resuelto a través de %{domain}
       title: Lista negra de correo
@@ -441,6 +444,7 @@ es:
         private_comment_description_html: 'Para ayudarle a rastrear de dónde proceden los bloqueos importados, los bloqueos importados se crearán con el siguiente comentario privado: <q>%{comment}</q>'
         private_comment_template: Importado desde %{source} el %{date}
         title: Importar bloqueos de dominio
+      invalid_domain_block: 'Uno o más bloqueos de dominio fueron omitidos debido a los siguientes errores: %{error}'
       new:
         title: Importar bloqueos de dominio
       no_file: Ningún archivo seleccionado
@@ -472,6 +476,7 @@ es:
       content_policies:
         comment: Nota interna
         description_html: Puedes definir políticas de contenido que se aplicarán a todas las cuentas de este dominio y a cualquiera de sus subdominios.
+        limited_federation_mode_description_html: Puedes elegir si permites la federación con este dominio.
         policies:
           reject_media: Rechazar multimedia
           reject_reports: Rechazar informes
@@ -575,19 +580,23 @@ es:
         mark_as_sensitive_description_html: Los archivos multimedia en los mensajes informados se marcarán como sensibles y se aplicará una amonestación para ayudarte a escalar las futuras infracciones de la misma cuenta.
         other_description_html: Ver más opciones para controlar el comportamiento de la cuenta y personalizar la comunicación de la cuenta reportada.
         resolve_description_html: No se tomarán medidas contra la cuenta denunciada, no se registrará la amonestación, y se cerrará el informe.
-        silence_description_html: El perfil será visible solo para aquellos que ya lo sigan o lo busquen manualmente, limitando seriamente su alcance. Siempre puede ser revertido.
-        suspend_description_html: El perfil y todos sus contenidos serán inaccesibles hasta que sean finalmente eliminados. La interacción con la cuenta será imposible. Reversible durante un plazo de 30 días.
+        silence_description_html: La cuenta será visible sólo para aquellos que ya la sigan o la busquen manualmente, limitando severamente su visibilidad. Siempre puede ser revertido. Cierra todos los informes contra esta cuenta.
+        suspend_description_html: La cuenta y todos sus contenidos serán inaccesibles y finalmente eliminados, e interactuar con ella será imposible. Reversible durante 30 días. Cierra todos los informes contra esta cuenta.
       actions_description_html: Decide qué medidas tomar para resolver esta denuncia. Si tomas una acción punitiva contra la cuenta denunciada, se le enviará a dicha cuenta una notificación por correo electrónico, excepto cuando se seleccione la categoría <strong>Spam</strong>.
+      actions_description_remote_html: Decide qué medidas tomar para resolver este informe. Esto solo afectará a la forma en que <strong>tu servidor</strong> se comunica con esta cuenta remota y gestiona su contenido.
       add_to_report: Añadir más al reporte
       are_you_sure: "¿Estás seguro?"
       assign_to_self: Asignármela a mí
       assigned: Moderador asignado
       by_target_domain: Dominio de la cuenta reportada
+      cancel: Cancelar
       category: Categoría
       category_description_html: La razón por la que se reportó esta cuenta o contenido será citada en las comunicaciones con la cuenta reportada
       comment:
         none: Ninguno
       comment_description_html: 'Para proporcionar más información, %{name} escribió:'
+      confirm: Confirmar
+      confirm_action: Confirmar acción de moderación contra @%{acct}
       created_at: Denunciado
       delete_and_resolve: Eliminar publicaciones
       forwarded: Reenviado
@@ -604,6 +613,7 @@ es:
         placeholder: Especificar qué acciones se han tomado o cualquier otra novedad respecto a esta denuncia…
         title: Notas
       notes_description_html: Ver y dejar notas a otros moderadores y a tu yo futuro
+      processed_msg: 'El informe #%{id} ha sido procesado con éxito'
       quick_actions_description_html: 'Toma una acción rápida o desplázate hacia abajo para ver el contenido denunciado:'
       remote_user_placeholder: el usuario remoto de %{instance}
       reopen: Reabrir denuncia
@@ -616,9 +626,28 @@ es:
       status: Estado
       statuses: Contenido reportado
       statuses_description_html: El contenido ofensivo se citará en la comunicación con la cuenta reportada
+      summary:
+        action_preambles:
+          delete_html: 'Estás a punto de <strong>eliminar</strong> algunos de los mensajes de <strong>@%{acct}</strong>. Esto hará:'
+          mark_as_sensitive_html: 'Estás a punto de <strong>marcar</strong> algunas de las publicaciones de <strong>@%{acct}</strong>como <strong>sensibles</strong>. Esto hará:'
+          silence_html: 'Estás a punto de <strong>limitar</strong> la cuenta de <strong>@%{acct}</strong>. Esto hará:'
+          suspend_html: 'Estás a punto de <strong>suspender</strong> la cuenta de <strong>@%{acct}</strong>. Esto hará:'
+        actions:
+          delete_html: Eliminar los mensajes ofensivos
+          mark_as_sensitive_html: Marcar los mensajes ofensivos como sensibles
+          silence_html: Limitar severamente el alcance de <strong>@%{acct}</strong> haciendo que su perfil y contenido sólo sean visibles para las personas que ya lo siguen o que consulten manualmente su perfil
+          suspend_html: Suspender <strong>@%{acct}</strong>, haciendo su perfil y contenido inaccesibles y la interacción con la cuenta imposible
+        close_report: 'Marcar informe #%{id} como resuelto'
+        close_reports_html: Marcar <strong>todos los</strong> informes contra <strong>@%{acct}</strong> como resueltos
+        delete_data_html: Eliminar el perfil y contenido de <strong>@%{acct}</strong> en 30 días a partir de ahora a menos que se revierta la suspensión en ese tiempo
+        preview_preamble_html: "<strong>@%{acct}</strong> recibirá una advertencia con el siguiente contenido:"
+        record_strike_html: Registra una amonestación contra <strong>@%{acct}</strong> para ayudarte a escalar futuras violaciones de esta cuenta
+        send_email_html: Enviar a <strong>@%{acct}</strong> un correo electrónico de advertencia
+        warning_placeholder: Razones adicionales opcionales para la acción de moderación.
       target_origin: Origen de la cuenta reportada
       title: Reportes
       unassign: Desasignar
+      unknown_action_msg: 'Acción desconocida: %{action}'
       unresolved: No resuelto
       updated_at: Actualizado
       view_profile: Ver perfil
@@ -713,6 +742,8 @@ es:
         preamble: Exponer contenido interesante a la superficie es fundamental para incorporar nuevos usuarios que pueden no conocer a nadie Mastodon. Controla cómo funcionan varias opciones de descubrimiento en tu servidor.
         profile_directory: Directorio de perfiles
         public_timelines: Lineas de tiempo públicas
+        publish_discovered_servers: Publicar servidores descubiertos
+        publish_statistics: Publicar estadísticas
         title: Descubrimiento
         trends: Tendencias
       domain_blocks:
@@ -767,6 +798,7 @@ es:
         suspend: "%{name} suspendió la cuenta de %{target}"
       appeal_approved: Apelado
       appeal_pending: Apelación pendiente
+      appeal_rejected: Apelación rechazada
     system_checks:
       database_schema_check:
         message_html: Hay migraciones pendientes de la base de datos. Por favor, ejecútalas para asegurarte de que la aplicación funciona como debería
@@ -780,6 +812,12 @@ es:
         message_html: No ha definido ninguna regla del servidor.
       sidekiq_process_check:
         message_html: No hay ningún proceso Sidekiq en ejecución para la(s) cola(s) %{value}. Por favor, revise su configuración de Sidekiq
+      upload_check_privacy_error:
+        action: Haga clic aquí para obtener más información
+        message_html: "<strong>Su servidor web está mal configurado. La privacidad de sus usuarios está en riesgo.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Haga clic aquí para obtener más información
+        message_html: "<strong>El almacenamiento de su objeto está mal configurado. La privacidad de sus usuarios está en riesgo.</strong>"
     tags:
       review: Estado de revisión
       updated_msg: Hashtags actualizados exitosamente
@@ -802,6 +840,7 @@ es:
           other: Compartido por %{count} personas durante la última semana
         title: Enlaces en tendencia
         usage_comparison: Compartido %{today} veces hoy, comparado con %{yesterday} ayer
+      not_allowed_to_trend: No permitido para tendencia
       only_allowed: Sólo las permitidas
       pending_review: Revisión pendiente
       preview_card_providers:
@@ -892,7 +931,7 @@ es:
     new_report:
       body: "%{reporter} ha reportado a %{target}"
       body_remote: Alguien de %{domain} a reportado a %{target}
-      subject: Nuevo reporte para la %{instance} (#%{id})
+      subject: Nuevo informe para %{instance} (#%{id})
     new_trends:
       body: 'Los siguientes elementos necesitan una revisión antes de que se puedan mostrar públicamente:'
       new_trending_links:
@@ -933,6 +972,7 @@ es:
   applications:
     created: Aplicación creada exitosamente
     destroyed: Apicación eliminada exitosamente
+    logout: Cerrar sesión
     regenerate_token: Regenerar token de acceso
     token_regenerated: Token de acceso regenerado exitosamente
     warning: Ten mucho cuidado con estos datos. ¡No los compartas con nadie!
@@ -940,6 +980,8 @@ es:
   auth:
     apply_for_account: Solicitar una cuenta
     change_password: Contraseña
+    confirmations:
+      wrong_email_hint: Si esa dirección de correo electrónico no es correcta, puedes cambiarla en la configuración de la cuenta.
     delete_account: Borrar cuenta
     delete_account_html: Si desea eliminar su cuenta, puede <a href="%{path}">proceder aquí</a>. Será pedido de una confirmación.
     description:
@@ -967,6 +1009,8 @@ es:
     resend_confirmation: Volver a enviar el correo de confirmación
     reset_password: Restablecer contraseña
     rules:
+      accept: Aceptar
+      back: Atrás
       preamble: Estas son establecidas y aplicadas por los moderadores de %{domain}.
       title: Algunas reglas básicas.
     security: Cambiar contraseña
@@ -1158,8 +1202,6 @@ es:
       index:
         hint: Este filtro se aplica a la selección de publicaciones individuales independientemente de otros criterios. Puede añadir más publicaciones a este filtro desde la interfaz web.
         title: Publicaciones filtradas
-  footer:
-    trending_now: Tendencia ahora
   generic:
     all: Todos
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ es:
     validation_errors:
       one: "¡Algo no está bien! Por favor, revisa el error"
       other: "¡Algo no está bien! Por favor, revise %{count} errores más abajo"
-  html_validator:
-    invalid_markup: 'contiene código HTML no válido: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Archivo CSV no válido. Error: %{error}'
@@ -1367,7 +1407,11 @@ es:
       unrecognized_emoji: no es un emoji conocido
   relationships:
     activity: Actividad de la cuenta
+    confirm_follow_selected_followers: "¿Estás seguro de que quieres seguir a las cuentas seleccionadas?"
+    confirm_remove_selected_followers: "¿Estás seguro de que quieres eliminar los seguidores seleccionados?"
+    confirm_remove_selected_follows: "¿Estás seguro de que quieres eliminar los seguidos seleccionados?"
     dormant: Inactivo
+    follow_failure: No se pudieron seguir algunas de las cuentas seleccionadas.
     follow_selected_followers: Seguir a los seguidores seleccionados
     followers: Seguidores
     following: Siguiendo
@@ -1407,6 +1451,7 @@ es:
       electron: Electron
       firefox: Firefox
       generic: Desconocido
+      huawei_browser: Navegador Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Navegador de Nokia S40 Ovi
@@ -1416,6 +1461,7 @@ es:
       qq: Navegador QQ
       safari: Safari
       uc_browser: Navegador UC
+      unknown_browser: Navegador Desconocido
       weibo: Weibo
     current_session: Sesión actual
     description: "%{browser} en %{platform}"
@@ -1428,9 +1474,10 @@ es:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: GNU Linux
       mac: Mac
-      other: Desconocido
+      unknown_platform: Plataforma Desconocida
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1543,7 +1590,7 @@ es:
       '7889238': 3 meses
     min_age_label: Umbral de tiempo
     min_favs: Mantener mensajes con un número de favoritos mayor que
-    min_favs_hint: No borra ninguna de las publicaciones que hayan recibido más de esta cantidad de favoritos. Deja en blanco para eliminar publicaciones sin importar el número de favoritos
+    min_favs_hint: No borra ninguna de las publicaciones que hayan recibido al menos esta cantidad de favoritos. Deja en blanco para eliminar publicaciones sin importar el número de favoritos
     min_reblogs: Mantener publicaciones reblogueadas más de
     min_reblogs_hint: No borra ninguna de las publicaciones que hayan sido reblogueadas más de este número de veces. Deja en blanco para eliminar publicaciones sin importar el número de reblogueos
   stream_entries:
@@ -1643,12 +1690,13 @@ es:
       title: Te damos la bienvenida a bordo, %{name}!
   users:
     follow_limit_reached: No puedes seguir a más de %{limit} personas
+    go_to_sso_account_settings: Diríjase a la configuración de la cuenta de su proveedor de identidad
     invalid_otp_token: Código de dos factores incorrecto
     otp_lost_help_html: Si perdiste al acceso a ambos, puedes ponerte en contancto con %{email}
     seamless_external_login: Has iniciado sesión desde un servicio externo, así que los ajustes de contraseña y correo no están disponibles.
     signed_in_as: 'Sesión iniciada como:'
   verification:
-    explanation_html: 'Puedes <strong> verificarte a ti mismo como el dueño de los links en los metadatos de tu perfil </strong>. Para eso, el sitio vinculado debe contener un vínculo a tu perfil de Mastodon. El vínculo en tu sitio <strong> debe </strong> tener un atributo <code> rel="me"</code>. El texto del vínculo no importa. Aquí un ejemplo:'
+    explanation_html: 'Puedes <strong>verificarte a ti mismo como propietario de los enlaces en los metadatos de tu perfil</strong>. Para ello, el sitio web vinculado debe contener un enlace a tu perfil de Mastodon. Después de añadir el enlace, es posible que debas volver aquí y volver a guardar tu perfil para que la verificación surta efecto. El enlace de tu sitio <strong>debe</strong> tener un atributo <code>rel="me"</code>. El contenido textual del enlace no tiene relevancia. Aquí un ejemplo:'
     verification: Verificación
   webauthn_credentials:
     add: Agregar nueva clave de seguridad
diff --git a/config/locales/et.yml b/config/locales/et.yml
index 61aefe6bd..ad3af525b 100644
--- a/config/locales/et.yml
+++ b/config/locales/et.yml
@@ -91,6 +91,7 @@ et:
       moderation:
         active: Aktiivne
         all: Kõik
+        disabled: Keelatud
         pending: Ootel
         silenced: Vaigistatud
         suspended: Kustutatud
@@ -133,6 +134,7 @@ et:
       search: Otsi
       search_same_email_domain: Muud kasutajad sama e-posti domeeniga
       search_same_ip: Teised kasutajad, kellel on sama IP
+      security: Turvalisus
       security_measures:
         only_password: Ainult salasõna
         password_and_2fa: Salasõna ja 2-etapine autentimine (2FA)
@@ -179,8 +181,8 @@ et:
         create_canonical_email_block: E-posti blokeeringu lisamine
         create_custom_emoji: Lisas kohandatud emotikoni
         create_domain_allow: Lisas lubatud domeeni
-        create_domain_block: Lisas domeeniblokeeringu
-        create_email_domain_block: Lisas e-posti domeeni blokeeringu
+        create_domain_block: Domeeni blokeerimine
+        create_email_domain_block: E-postidomeeni blokeerimine
         create_ip_block: IP-reegli lisamine
         create_unavailable_domain: Kättesaamatu domeeni lisamine
         create_user_role: Loo roll
@@ -189,8 +191,8 @@ et:
         destroy_canonical_email_block: E-posti blokeeringu kustutamine
         destroy_custom_emoji: Eemaldas kohandatud emotikoni
         destroy_domain_allow: Eemaldas lubatud domeeni
-        destroy_domain_block: Eemaldas domeeniblokeeringu
-        destroy_email_domain_block: Eemaldas e-posti domeeni blokeeringu
+        destroy_domain_block: Domeeniblokeeringu eemaldamine
+        destroy_email_domain_block: E-postidomeeni blokeeringu eemaldamine
         destroy_instance: Domeeni kustutamine
         destroy_ip_block: IP-reegli kustutamine
         destroy_status: Kustuta postitus
@@ -390,7 +392,7 @@ et:
       import: Import
       new:
         create: Loo blokeering
-        hint: Domeeniblokeering ei takista kontode lisamist andmebaasi, aga lisab nendele kontodele tagasiulatuvalt ja automaatselt erinevaid moderatsioonimeetodeid.
+        hint: Domeeniblokeering ei takista kontode lisamist andmebaasi, aga lisab automaatselt nendele kontodele tagasiulatuvalt teatud moderatsioonimeetodeid.
         severity:
           desc_html: "<strong>Piira</strong> muudab domeeni kasutajate postitused mittejälgijatele nähtamatuks.<strong>Kustuta</strong> eemaldab serverist kõik selle domeeni kontode sisu, meedia ja kasutajaandmed. <strong>Ei midagi</strong> lihtsalt lükkab meediafailid tagasi."
           noop: Ei midagi
@@ -402,21 +404,21 @@ et:
       obfuscate: Domeeninime varjamine
       obfuscate_hint: Varja osaliselt domeeni nimi nimekirjas, kui domeenipiirangute nimekirja avaldamine on sisse lülitatud
       private_comment: Privaatne kommentaar
-      private_comment_hint: Kommenteeri selle domeeni limiteerimise kohta moderaatoritele.
+      private_comment_hint: Kommentaar selle domeenipiirangu kohta moderaatorite sisekasutuseks.
       public_comment: Avalik kommentaar
-      public_comment_hint: Kommenteeri selle domeeni limiteerimise kohta avalikkusele, kui domeenilimitatsioonide loend on kõigile nähtav.
+      public_comment_hint: Avalik kommentaar selle domeenipiirangu kohta, juhuks kui domeenipiirangute loetelu avaldamine on lubatud.
       reject_media: Keela meediafailid
-      reject_media_hint: Kustutab kohalikult salvestatud meediafailid ja keeldub tulevikus rohkem allalaadimast. Ei puuduta peatamisi
+      reject_media_hint: Kustutab kohalikult salvestatud meediafailid ja keeldub tulevikus allalaadimisest. Ei puuduta peatamisi
       reject_reports: Lükka teavitused tagasi
-      reject_reports_hint: Eira kõik teavitused sellelt domeenilt. Ei puuduta peatamisi
-      undo: Võta tagasi domeeniblokeering
+      reject_reports_hint: Eira kõiki teavitusi sellelt domeenilt. Ei puuduta peatamisi
+      undo: Võta domeeniblokeering tagasi
       view: Vaata domeeniblokeeringut
     email_domain_blocks:
       add_new: Lisa uus
       attempts_over_week:
         one: "%{count} katse viimase nädala kestel"
         other: "%{count} liitumiskatset viimase nädala kestel"
-      created_msg: E-posti aadressi keelunimekirja lisamine õnnestus
+      created_msg: Edukalt blokeeritud e-posti domeen
       delete: Kustuta
       dns:
         types:
@@ -425,8 +427,9 @@ et:
       new:
         create: Lisa domeen
         resolve: Domeeni lahendamine
-        title: Uus e-posti keelunimekirja sisend
+        title: Blokeeri uus e-posti domeen
       no_email_domain_block_selected: Ühtegi e-posti domeeni keeldu ei muudetud, kuna midagi polnud valitud
+      not_permitted: Ei ole lubatud
       resolved_dns_records_hint_html: Domeeninimi lahendub järgnevateks MX-domeenideks, mis vastutavad e-kirjade vastuvõtmise eest. MX-domeeni blokeerimine blokeerib liitumistaotlused kõigilt e-postiaadressidelt, mis kasutavad sama MX-domeeni, kuigi kasutatav domeeninimi on erinev. <strong>Ole tähelepanelik, et mitte blokeerida suuremaid e-postiteenuse pakkujaid.</strong>
       resolved_through_html: Lahendatud %{domain} kaudu
       title: E-posti keelunimekiri
@@ -441,6 +444,7 @@ et:
         private_comment_description_html: 'Pidamaks järge, kust imporditud keelud pärinevad, luuakse need järneva privaatse kommentaariga: <q>%{comment}</q>'
         private_comment_template: Imporditud allikast %{source} kuupäeval %{date}
         title: Domeenikeeldude import
+      invalid_domain_block: 'Üks või mitu domeeniblokeeringut jäeti vahele järgnevate vigade tõttu: %{error}'
       new:
         title: Domeenikeeldude import
       no_file: Faili pole valitud
@@ -472,6 +476,7 @@ et:
       content_policies:
         comment: Sisemine märge
         description_html: On võimalik kirjeldada sisureeglid kõigile kontodele sellelt domeenilt ja alamdomeenidelt.
+        limited_federation_mode_description_html: Saad valida, kas selle domeeniga on födereerumine lubatud.
         policies:
           reject_media: Meedia hülgamine
           reject_reports: Lükka raportid tagasi
@@ -575,23 +580,27 @@ et:
         mark_as_sensitive_description_html: Raporteeritud meedia märgitakse kui tundlik sisu ja juhtum talletatakse hoiatusena tulevaste eksimuste ärahoidmiseks.
         other_description_html: On rohkem valikuid konto käitumise juhtimiseks ja suhtluse kohandamiseks raporteeritud kontoga.
         resolve_description_html: Raporteeritud konto suhtes ei võeta midagi ette, juhtumit ei registreerita ja raport suletakse.
-        silence_description_html: Profiil on nähtav ainult neile, kes seda juba jälgivad või otsivad seda käsitsi, piirates sellega tõsiselt selle leitavust. Saab alati tagasi võtta.
-        suspend_description_html: Konto ja kogu sisu muutub lõpliku kustutamiseni kättesaamatuks. Kontoga suhtlemine pole võimalik. Tagasivõetav 30 päeva jooksul.
+        silence_description_html: Konto saab olema nähtav ainult senistele jälgijatele või otsestele pöördujatele, mõjutates avalikku levi. On tagasipööratav. Sulgeb kõik konto suhtes esitatud raportid.
+        suspend_description_html: See konto ja kogu selle sisu muutub kättesaamatuks ning kustub lõpuks ja igasugune suhtlus sellega muutub võimatuks. Tagasipööratav 30 päeva jooksul. Lõpetab kõik selle konto kohta esitatud kaebused.
       actions_description_html: Otsustus, mida raporti lahendamiseks ette võtta. Karistava tegevuse korral saadetakse e-postiga teade, välja arvatud kategooria <strong>rämpspost</strong> puhul.
+      actions_description_remote_html: Otsusta, mida teha selle raporti lahendamiseks. See mõjutab ainult seda, kuidas <strong>Sinu</strong> server selle kaugkontoga suhtleb ning selle sisu käsitleb.
       add_to_report: Lisa raportile juurde
       are_you_sure: Oled kindel?
       assign_to_self: Määra mulle
       assigned: Määratud moderaator
       by_target_domain: Teavitatud konto domeen
+      cancel: Tühista
       category: Kategooria
       category_description_html: Põhjus, miks sellest kontost ja/või sisust teatati, kaasatakse raporteeritud kontoga suhtlemisel
       comment:
         none: Pole
       comment_description_html: 'Täiendava infona kirjutas %{name}:'
+      confirm: Kinnita
+      confirm_action: Kinnita @%{acct} modereering
       created_at: Teavitatud
       delete_and_resolve: Kustuta postitused
       forwarded: Edastatud
-      forwarded_to: Edastatud domeenile %{domain}
+      forwarded_to: Edastatud %{domain} domeeni
       mark_as_resolved: Märgi lahendatuks
       mark_as_sensitive: Märgi kui tundlik sisu
       mark_as_unresolved: Märgi lahendamata
@@ -604,6 +613,7 @@ et:
         placeholder: Kirjelda, mis on ette võetud või muid seotud uuendusi...
         title: Märkmed
       notes_description_html: Märkmete vaatamine ja jätmine teistele moderaatoritele ja tulevikuks endale
+      processed_msg: 'Raport #%{id} edukalt käsitletud'
       quick_actions_description_html: 'Võimalus teha kiire otsus või näha raporteeritud sisu allpool:'
       remote_user_placeholder: kaugkasutaja domeenist %{instance}
       reopen: Taasava teavitus
@@ -616,9 +626,28 @@ et:
       status: Olek
       statuses: Raporteeritud sisu
       statuses_description_html: Sobimatu sisu kaasatakse suhtlusse raporteeritud kontoga
+      summary:
+        action_preambles:
+          delete_html: 'Oled <strong>eemaldamas</strong> mõnesid <strong>@%{acct}</strong> postitusi. Selle tulemusena:'
+          mark_as_sensitive_html: 'Oled <strong>märkimas</strong> mõnesid <strong>@%{acct}</strong> postitusi <strong>tundlikuks</strong>. Selle tulemusena:'
+          silence_html: 'Oled <strong>määramas piirangut</strong> kontole <strong>@%{acct}</strong>. Selle tulemusena:'
+          suspend_html: 'Oled <strong>peatamas</strong> kontot <strong>@%{acct}</strong>. Selle tulemusena:'
+        actions:
+          delete_html: Solvava postituse eemaldamine
+          mark_as_sensitive_html: Solvava postituse meedia märkimine tundlikuks
+          silence_html: "<strong>@%{acct}</strong> ulatuse tugev piiramine muutes tema profiili ja sisu nähtavaks ainult neile, kes teda juba jälgivad, või neile, kes just tema profiili üles otsivad"
+          suspend_html: "<strong>@%{acct}</strong> peatamine, muutes tema profiili ja sisu mitteligipääsetavaks ning mitteinterakteerutavaks"
+        close_report: 'Raporti #%{id} lahendatuks märkimine'
+        close_reports_html: Märgi <strong>kõik</strong> raportid <strong>@%{acct}</strong> vastu lahendatuks
+        delete_data_html: Kustuta tänasest 30 päeva pärast kasutaja <strong>@%{acct}</strong> profiil ja sisu, kui vahepeal tema kontot ei taastata
+        preview_preamble_html: "<strong>@%{acct}</strong> saab järgmise sisuga hoiatuse:"
+        record_strike_html: Salvesta <strong>@%{acct}</strong> kohta juhtum, et aidata selle konto tulevaste rikkumiste puhul reageerida
+        send_email_html: Saada kasutajale <strong>@%{acct}</strong> hoiatus-e-kiri
+        warning_placeholder: Valikuline täiendav põhjendus modereerimisele.
       target_origin: Raporteeritud konto päritolu
       title: Teavitused
       unassign: Eemalda määramine
+      unknown_action_msg: 'Tundmatu tegevus: %{action}'
       unresolved: Lahendamata
       updated_at: Uuendatud
       view_profile: Vaata profiili
@@ -713,6 +742,8 @@ et:
         preamble: Huvitava sisu esiletoomine on oluline uute kasutajate kaasamisel, kes ei pruugi Mastodonist kedagi tunda. Kontrolli, kuidas erinevad avastamisfunktsioonid serveris töötavad.
         profile_directory: Kasutajate kataloog
         public_timelines: Avalikud ajajooned
+        publish_discovered_servers: Avalda avastatud serverid
+        publish_statistics: Avalda statistika
         title: Avastamine
         trends: Trendid
       domain_blocks:
@@ -767,6 +798,7 @@ et:
         suspend: "%{name} kustutas %{target} konto"
       appeal_approved: Vaidlustatud
       appeal_pending: Vaidlustus on ootel
+      appeal_rejected: Vaidlustus tagasi lükatud
     system_checks:
       database_schema_check:
         message_html: On ootel andmebaasi migreerimisi. Rakenduse ootuspäraseks toimimiseks palun käivita need
@@ -780,6 +812,12 @@ et:
         message_html: Serverireegleid pole defineeritud.
       sidekiq_process_check:
         message_html: Ühtegi Sidekiq protsessi pole %{value} järjekorra jaoks. Sidekiq seadistus vajab üle vaatamist
+      upload_check_privacy_error:
+        action: Klõpsa lisainfo saamiseks siia
+        message_html: "<strong>Veebiserver on valesti seadistatud. Kasutajate privaatsus on ohustatud.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Klõpsa lisainfo saamiseks siia
+        message_html: "<strong>Objektihoidla on valesti seadistatud. Kasutajate privaatsus on ohustatud.</strong>"
     tags:
       review: Vaata olek üle
       updated_msg: Sildi sätted edukalt uuendatud
@@ -802,6 +840,7 @@ et:
           other: Jagatud %{count} kasutaja poolt viimase nädala jooksul
         title: Trendikad viited
         usage_comparison: Jagatud %{today} korda täna, eile %{yesterday} korda
+      not_allowed_to_trend: Trend ei ole lubatud
       only_allowed: Ainult lubatud
       pending_review: Ootab ülevaatust
       preview_card_providers:
@@ -933,6 +972,7 @@ et:
   applications:
     created: Rakenduse loomine õnnestus
     destroyed: Rakenduse kustutamine õnnestus
+    logout: Logi välja
     regenerate_token: Loo uus access token
     token_regenerated: Access tokeni loomine õnnestus
     warning: Ole nende andmetega ettevaatlikud. Ära jaga neid kellegagi!
@@ -940,10 +980,12 @@ et:
   auth:
     apply_for_account: Konto taotluse esitamine
     change_password: Salasõna
+    confirmations:
+      wrong_email_hint: Kui see e-postiaadress pole korrektne, saad seda kontosätetes muuta.
     delete_account: Konto kustutamine
     delete_account_html: Kui soovid oma konto kustutada, siis <a href="%{path}">jätka siit</a>. Pead kustutamise eraldi kinnitama.
     description:
-      prefix_invited_by_user: "@%{name} kutsub Teid liituma selle Mastodoni serveriga!"
+      prefix_invited_by_user: "@%{name} kutsub sind liituma selle Mastodoni serveriga!"
       prefix_sign_up: Loo Mastodoni konto juba täna!
       suffix: Kasutajakontoga saad jälgida inimesi, postitada uudiseid ning pidada kirjavahetust ükskõik millise Mastodoni serveri kasutajatega ja muudki!
     didnt_get_confirmation: Ei saanud kinnituse juhendeid?
@@ -967,6 +1009,8 @@ et:
     resend_confirmation: Saada kinnitusjuhendid uuesti
     reset_password: Salasõna lähtestamine
     rules:
+      accept: Nõus
+      back: Tagasi
       preamble: Neist kinnipidamist jälgivad %{domain} moderaatorid.
       title: Mõned põhireeglid.
     security: Turvalisus
@@ -1158,8 +1202,6 @@ et:
       index:
         hint: See filter kehtib üksikute postituste valimisel, sõltumata muudest kriteeriumidest. Sellesse filtrisse saab veebiliidese kaudu rohkem postitusi lisada.
         title: Filtreeritud postitused
-  footer:
-    trending_now: Praegu trendikad
   generic:
     all: Kõik
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ et:
     validation_errors:
       one: Midagi pole ikka õigesti! Palun vaata allolev veateade üle
       other: Midagi pole ikka õigesti! Palun vaata all olevad %{count} veateadet üle
-  html_validator:
-    invalid_markup: 'sisaldab valet HTMLi süntaksi: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Vigane CSV-fail. Tõrge: %{error}'
@@ -1214,7 +1254,7 @@ et:
       '86400': 1 päev
     expires_in_prompt: Mitte kunagi
     generate: Loo
-    invited_by: 'Teid kutsus:'
+    invited_by: 'Sind kutsus:'
     max_uses:
       one: 1 kasutus
       other: "%{count} kasutust"
@@ -1298,13 +1338,13 @@ et:
       title: Uus jälgija
     follow_request:
       action: Halda jälgimistaotlusi
-      body: "%{name} soovib Teid jälgida"
+      body: "%{name} soovib sind jälgida"
       subject: 'Ootav jälgija: %{name}'
       title: Uus jälgimistaotlus
     mention:
       action: Vasta
-      body: "%{name} mainis Teid:"
-      subject: "%{name} mainis Teid"
+      body: "%{name} mainis sind:"
+      subject: "%{name} mainis sind"
       title: Uus mainimine
     poll:
       subject: "%{name} küsitlus lõppes"
@@ -1367,7 +1407,11 @@ et:
       unrecognized_emoji: ei ole tuntud emotikoon
   relationships:
     activity: Konto tegevus
+    confirm_follow_selected_followers: Oled kindel, et soovid jälgida valitud jälgijaid?
+    confirm_remove_selected_followers: Oled kindel, et soovid eemaldada valitud jälgijad?
+    confirm_remove_selected_follows: Oled kindel, et soovid eemaldada valitud jälgitavad?
     dormant: Seisev
+    follow_failure: Mõne valitud konto jälgimine polnud võimalik.
     follow_selected_followers: Valitud jälgijate jälgimine
     followers: Jälgijad
     following: Jälgib
@@ -1407,6 +1451,7 @@ et:
       electron: Electron
       firefox: Firefox
       generic: Tundmatu veebilehitseja
+      huawei_browser: Huawei veebilehitseja
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1416,6 +1461,7 @@ et:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Tundmatu veebilehitseja
       weibo: Weibo
     current_session: Praegune seanss
     description: "%{browser} platvormil %{platform}"
@@ -1428,9 +1474,10 @@ et:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: tundmatu platvorm
+      unknown_platform: Tundmatu platvorm
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1543,7 +1590,9 @@ et:
       '7889238': 3 kuud
     min_age_label: Ajalimiit
     min_favs: Säilita postitused, meeldimistega vähemalt
-    min_favs_hint: Ei kustuta ühtegi postitust, mis on saanud vähemalt nii palju lemmikuks märkimist. Postituste kustutamiseks olenemata nende lemmikuks märkimise arvust, jäta tühjaks
+    min_favs_hint: |-
+      Ei kustuta ühtegi postitust, mis on saanud vähemalt nii palju lemmikuks märkimist. Postituste kustutamiseks olenemata nende lemmikuks märkimise arvust,
+      jäta tühjaks
     min_reblogs: Säilita postitused jagatud vähemalt
     min_reblogs_hint: Ei kustuta postitusi, kui need on jagatud vähemalt nii mitu korda. Tühjaks jättes kustuvad postitused olenemata jagamistest
   stream_entries:
@@ -1646,12 +1695,13 @@ et:
       title: Tere tulemast, %{name}!
   users:
     follow_limit_reached: Ei saa jälgida rohkem kui %{limit} inimest
+    go_to_sso_account_settings: Mine oma idenditeedipakkuja kontosätetesse
     invalid_otp_token: Vale kaheastmeline võti
     otp_lost_help_html: Kui kaotasid ligipääsu mõlemale, saad võtta ühendust %{email}-iga
     seamless_external_login: Välise teenuse kaudu sisse logides pole salasõna ja e-posti sätted saadaval.
     signed_in_as: 'Sisse logitud kasutajana:'
   verification:
-    explanation_html: 'Saad <strong>kinnitada ennast oma profiili veebiviidete omanikuna</strong>. Selleks peab viidatud veebilehel olema link tagasi su Mastodoni profiilile. Tagasi saatval lingil <strong>peab</strong> olema <code>rel="me"</code> atribuut. Lingi tekstiline sisu ei ole oluline. Siin on üks näide:'
+    explanation_html: 'Saad <strong>kinnitada ennast oma profiili metaandmete veebiviidete omanikuna.</strong> Selleks peab lingitud veebilehel olema viide tagasi sinu Mastodoni profiilile. Pärast lingi lisamist pead võib-olla siia tagasi tulema ja oma profiili uuesti salvestama, et kinnitus jõustuks. Tagasiviide <strong>peab</strong> sisaldama <code>rel="me"</code> atribuuti. Lingi tekstiline sisu ei ole oluline. Siin on näide:'
     verification: Kinnitamine
   webauthn_credentials:
     add: Uue turvavõtme lisamine
diff --git a/config/locales/eu.yml b/config/locales/eu.yml
index b94f621dc..b2e87cc8a 100644
--- a/config/locales/eu.yml
+++ b/config/locales/eu.yml
@@ -91,6 +91,7 @@ eu:
       moderation:
         active: Aktiboa
         all: Denak
+        disabled: Desgaituta
         pending: Zain
         silenced: Mugatua
         suspended: Kanporatua
@@ -116,6 +117,8 @@ eu:
       redownloaded_msg: "%{username} erabiltzailearen profila behar bezala freskatu da jatorritik"
       reject: Ukatu
       rejected_msg: "%{username} erabiltzailearen izen emate eskaera behar bezala ukatu da"
+      remote_suspension_irreversible: Kontu honetako datuak betiko ezabatuak izan dira.
+      remote_suspension_reversible_hint_html: Kontu hau kanporatua izan da bere zerbitzarian eta bere datuak %{date}(e)an behin betiko ezabatuko dira. Ordura arte urruneko zerbitzariak kontua kalterik gabe leheneratu dezake. Kontuaren datu guztiak oraintxe bertan ezabatu nahi badituzu, jarraian egin dezakezu.
       remove_avatar: Kendu abatarra
       remove_header: Kendu goiburua
       removed_avatar_msg: "%{username} erabiltzailearen avatarra behar bezala kendu da"
@@ -131,6 +134,7 @@ eu:
       search: Bilatu
       search_same_email_domain: E-mail domeinu bera duten beste erabiltzailean
       search_same_ip: IP bera duten beste erabiltzaileak
+      security: Segurtasuna
       security_measures:
         only_password: Soilik pasahitza
         password_and_2fa: Pasahitza eta 2FA
@@ -390,10 +394,15 @@ eu:
         create: Sortu blokeoa
         hint: Domeinuaren blokeoak ez du eragotziko kontuen sarrerak sortzea datu-basean, baina automatikoki ezarriko zaizkie moderazio metodo bereziak iraganeko mezuetan ere.
         severity:
+          desc_html: |-
+            <strong>Mugatu</strong> aukerak, domeinu hontako kontu guztien bidalketak ikusezinak bihurtuko dira jarraitzen ez dituztenentzat.
+            <strong>Egotzi</strong> aukerak, domeinu hontako kontu guztien, edukiak, media eta profilen datuak ezabatuak izango dira zure zerbitzaritik. Erabili <strong>Bat ere ez</strong> aukera soilik media-fitxategiak ukatu nahi badituzu.
           noop: Bat ere ez
           silence: Isilarazi
           suspend: Kanporatu
         title: Domeinuaren blokeo berria
+      no_domain_block_selected: Ez da domeinu berririk blokeatu, bat ere ez delako aukeratu
+      not_permitted: Ekintza hau egiteko baimenik ez duzu
       obfuscate: Lausotu domeinu-izena
       obfuscate_hint: Domeinuaren izena partzialki lausotu zerrendan, domeinuen zerrenda iragartzea mugatzea gaituta badago
       private_comment: Iruzkin pribatua
@@ -422,12 +431,24 @@ eu:
         resolve: Ebatzi domeinua
         title: Sarrera berria e-mail zerrenda beltzean
       no_email_domain_block_selected: Ez da eposta domeinu blokeorik aldatu ez delako bat ere hautatu
+      not_permitted: Baimendu gabea
       resolved_dns_records_hint_html: Domeinu-izena ondorengo MX domeinuetara ebazten da, zeinek eposta onartzeko ardura duten. MX domeinu bat blokeatzeak MX domeinu hori erabiltzen duen edozein helbide elektronikotatik izena-ematea blokeatzen du, baita ikusgai dagoen domeinu-izena beste bat bada ere. <strong>Kontuz ibili eposta hornitzaile nagusiak blokeatu gabe.</strong>
       resolved_through_html: "%{domain} domeinuaren bidez ebatzia"
       title: E-mail zerrenda beltza
     export_domain_allows:
+      new:
+        title: Baimendutako domeinuak inportatu
       no_file: Ez da fitxategirik hautatu
     export_domain_blocks:
+      import:
+        description_html: Blokeatutako domeinuen zerrenda bat inportatzera zoaz. Mesedez, berrikusi zerrenda kontu handiz, batez ere, zuk ez baduzu zerrenda hau egin.
+        existing_relationships_warning: Oraingo jarraipen-loturak
+        private_comment_description_html: 'Inportatutako blokeoak nondik datozen zuri jakiten laguntzeko, hauek hurrengo iruzkin pribatuarekin sortuko dira: <q>%{comment}</q>'
+        private_comment_template: "%{date} %{source}-(e)tik inportatua"
+        title: Domeinu-blokeoak inportatu
+      invalid_domain_block: 'Domeinu-blokeatze bat edo gehiago ekidin dira ondorengo errorearen edo erroreengatik: %{error}'
+      new:
+        title: Domeinu-blokeoak inportatu
       no_file: Ez da fitxategirik hautatu
     follow_recommendations:
       description_html: "<strong>Jarraitzeko gomendioek erabiltzaile berriei eduki interesgarria azkar aurkitzen laguntzen diete</strong>. Erabiltzaile batek jarraitzeko gomendio pertsonalizatuak jasotzeko adina interakzio izan ez duenean, kontu hauek gomendatzen zaizkio. Egunero birkalkulatzen dira hizkuntza bakoitzerako, azken aldian parte-hartze handiena izan duten eta jarraitzaile lokal gehien dituzten kontuak nahasiz."
@@ -457,6 +478,7 @@ eu:
       content_policies:
         comment: Barne-oharra
         description_html: Domeinu honetako eta bere azpi-domeinuetako kontu guztiei aplikatuko zaizkien eduki-politikak definitu ditzakezu.
+        limited_federation_mode_description_html: Domeinu honekin federatu ahal izatea baimendu dezakezu.
         policies:
           reject_media: Errefusatu multimediak
           reject_reports: Errefusatu salaketak
@@ -560,19 +582,23 @@ eu:
         mark_as_sensitive_description_html: Salatutako bidalketetako multimedia edukia hunkigarri bezala eta neurria gordeko da, etorkizunean kontu honek arau-hausterik egiten badu kontuan izan dezazun.
         other_description_html: Ikusi kontuaren portaera kontrolatzeko eta salatutako kontuarekin komunikazioa pertsonalizatzeko aukera gehiago.
         resolve_description_html: Ez da ekintzarik hartuko salatutako kontuaren aurka, ez da neurria gordeko eta salaketa itxiko da.
-        silence_description_html: Profila dagoeneko jarraitzen dutenei edo eskuz bilatzen dutenei bakarrik agertuko zaie, bere irismena asko mugatuz. Beti bota daiteke atzera.
-        suspend_description_html: Profila eta bere eduki guztiak iritsiezinak bihurtuko dira, ezabatzen den arte. Kontuarekin ezin da interakziorik eduki. Atzera bota daiteke 30 eguneko epean.
+        silence_description_html: Kontua soilik honen jarraitzaile edo espresuki bilatzen dutenentzat izango da ikusgarri, kontuaren irisgarritasuna gogorki mugatzen delarik.
+        suspend_description_html: Kontua bera eta honen edukiak eskuraezinak izango dira, eta azkenean, ezabatuak. Kontu honekin erlazionatzea ezinezkoa izango da. Prozesua 30 egunez itzulgarria izango da. Kontu honen aurkako txosten guztiak baztertuko lirateke.
       actions_description_html: Erabaki txosten hau konpontzeko ze ekintza hartu. Salatutako kontuaren aurka zigor ekintza bat hartzen baduzu, eposta jakinarazpen bat bidaliko zaie, <strong>Spam</strong> kategoria hautatzean ezik.
+      actions_description_remote_html: Txosten honi konponbidea aurkitzeko zein ekintza egin hautatu. Hau soilik <strong>zure</strong> zerbitzaria kontu honekin nola komunikatu eta bere edukia nola maneiatzeko da.
       add_to_report: Gehitu gehiago txostenera
       are_you_sure: Ziur zaude?
       assign_to_self: Esleitu niri
       assigned: Esleitutako moderatzailea
       by_target_domain: Jakinarazitako kontuaren domeinua
+      cancel: Utzi
       category: Kategoria
       category_description_html: Kontu edo/eta eduki hau salatu izanaren arrazoia salatutako kontuarekiko komunikazioan aipatuko da
       comment:
         none: Bat ere ez
       comment_description_html: 'Informazio gehiago emateko, %{name} idatzi:'
+      confirm: Berretsi
+      confirm_action: "@%{acct} kontuaren aurkako moderazio-ekintza baieztatu"
       created_at: Salatua
       delete_and_resolve: Ezabatu bidalketak
       forwarded: Birbidalia
@@ -589,6 +615,7 @@ eu:
         placeholder: Azaldu hartutako neurriak, edo erlazioa duten bestelako berriak...
         title: Oharrak
       notes_description_html: Ikusi eta idatzi oharrak beste moderatzaileentzat eta zuretzat etorkizunerako
+      processed_msg: "#%{id} txostena ongi prozesatu da"
       quick_actions_description_html: 'Hartu ekintza azkar bat edo korritu behera salatutako edukia ikusteko:'
       remote_user_placeholder: "%{instance} instantziako urruneko erabiltzailea"
       reopen: Berrireki salaketa
@@ -601,9 +628,28 @@ eu:
       status: Mezua
       statuses: Salatutako edukia
       statuses_description_html: Salatutako edukia salatutako kontuarekiko komunikazioan aipatuko da
+      summary:
+        action_preambles:
+          delete_html: "<strong>@%{acct}</strong> kontuaren bidalketa guztiak <strong>ezabatzera</strong> zoaz. Honek hau suposatuko du:"
+          mark_as_sensitive_html: "<strong>%{acct}</strong> kontuaren bidalketa guztiak <strong>hunkigarri</strong> gisa <strong>markatzera</strong> zoaz. Honek hau suposatuko du:"
+          silence_html: "<strong>@%{acct}</strong> kontua <strong>mugatzera</strong> zoaz. Honek hau suposatuko du:"
+          suspend_html: "<strong>@%{acct}</strong> kontua <strong>bertan behera uztera</strong> zoaz. Honek hau suposatuko du:"
+        actions:
+          delete_html: Bidalketa iraingarriak ezabatu
+          mark_as_sensitive_html: Bidalketa iraingarrien media hunkigarri gisa markatu
+          silence_html: "<strong>@%{acct}</strong> kontuaren irismena gogorki mugatu, beraren profila eta edukia soilik bere jarraitzaileentzat eta espresuki profila aurkitzen dutenentzat ikusgarri egiten direlarik"
+          suspend_html: "<strong>@%{acct}</strong> kontua bertan behera utzi, bere profila eta edukia iritsezin eta elkarreragin ezina izango direlarik"
+        close_report: "#%{id} txostena ebatzitako gisa markatu"
+        close_reports_html: "<strong>@%{acct}</strong> kontuaren txosten <strong>guztiak</strong> ebatzitako gisa markatu"
+        delete_data_html: "<strong>@%{acct}</strong> kontuaren profila eta edukia, gaurtik hasita, 30 egunez ezabatu, ez bada bitartean kontua berraktibatzen"
+        preview_preamble_html: "<strong>@%{acct}</strong> kontuak ondorengo edukia duen abisu bat jasoko du:"
+        record_strike_html: "<strong>@%{acct}</strong> kontuak eginiko eraso bat erregistratu, kontu honek etorkizunean egin ditzakeen erasoen aurrean erabakiak hartzen laguntzeko"
+        send_email_html: "<strong>@%{acct}</strong> kontuari abisu bat posta-elektronikoz bidali"
+        warning_placeholder: Moderazio-ekintzarako aukerazkoak diren arrazoiketa gehigarriak.
       target_origin: Salatutako kontuaren jatorria
       title: Salaketak
       unassign: Kendu esleipena
+      unknown_action_msg: 'Ekintza ezezaguna: %{action}'
       unresolved: Konpondu gabea
       updated_at: Eguneratua
       view_profile: Ikusi profila
@@ -690,11 +736,16 @@ eu:
       content_retention:
         preamble: Kontrolatu erabiltzaileek sortutako edukia nola biltegiratzen den Mastodonen.
         title: Edukia atxikitzea
+      default_noindex:
+        desc_html: Ezarpen hau aldatu ez duten erabiltzaile guztiei eragingo die
+        title: Erabiltzaileei bilaketa-motorraren indexaziotik at egoteko aukera ematen die lehenetsitako aukera modura
       discovery:
         follow_recommendations: Jarraitzeko gomendioak
         preamble: Eduki interesgarria aurkitzea garrantzitsua da Mastodoneko erabiltzaile berrientzat, behar bada inor ez dutelako ezagutuko. Kontrolatu zure zerbitzariko aurkikuntza-ezaugarriek nola funtzionatzen duten.
         profile_directory: Profil-direktorioa
         public_timelines: Denbora-lerro publikoak
+        publish_discovered_servers: Deskubritutako zerbitzariak argitaratu
+        publish_statistics: Estatistikak argitaratu
         title: Aurkitzea
         trends: Joerak
       domain_blocks:
@@ -749,6 +800,7 @@ eu:
         suspend: "%{name} erabiltzaileak %{target} kontua kanporatu du"
       appeal_approved: Apelatua
       appeal_pending: Apelazioa zain
+      appeal_rejected: Apelazioa baztertuta
     system_checks:
       database_schema_check:
         message_html: Aplikatu gabeko datu-basearen migrazioak daude. Exekutatu aplikazioak esperotako portaera izan dezan
@@ -762,6 +814,12 @@ eu:
         message_html: Ez duzu zerbitzariaren araurik definitu.
       sidekiq_process_check:
         message_html: Ez da ari Sidekiq prozesurik exekutatzen %{value} ilad(et)an. Egiaztatu Sidekiq konfigurazioa
+      upload_check_privacy_error:
+        action: Ikus hemen informazio gehiagorako
+        message_html: "<strong>Zure zerbitzaria ez dago ongi konfiguratua. Zure erabiltzaileen pribatutasuna arriskuan dago.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Ikus hemen informazio gehiagorako
+        message_html: "<strong>Zure objektuen biltegiratzea ez dago ongi konfiguratua. Zure erabiltzaileen pribatutasuna arriskuan dago.</strong>"
     tags:
       review: Berrikusketaren egoera
       updated_msg: Traola-ezarpenak ongi eguneratu dira
@@ -784,6 +842,7 @@ eu:
           other: "%{count} pertsonak partekatua azken astean"
         title: Esteken joerak
         usage_comparison: "%{today} aldiz partekatua gaur, atzo %{yesterday} aldiz"
+      not_allowed_to_trend: Joeretan agertzeko baimenik gabe
       only_allowed: Soilik onartutakoak
       pending_review: Berrikusketaren zain
       preview_card_providers:
@@ -915,12 +974,16 @@ eu:
   applications:
     created: Aplikazioa ongi sortu da
     destroyed: Aplikazioa ongi ezabatu da
+    logout: Amaitu saioa
     regenerate_token: Birsortu sarbide token-a
     token_regenerated: Sarbide token-a ongi birsortu da
     warning: Kontuz datu hauekin, ez partekatu inoiz inorekin!
     your_token: Zure sarbide token-a
   auth:
+    apply_for_account: Kontu bat eskatu
     change_password: Pasahitza
+    confirmations:
+      wrong_email_hint: Helbide-elektroniko hori zuzena ez bada, kontuaren ezarpenetan alda dezakezu.
     delete_account: Ezabatu kontua
     delete_account_html: Kontua ezabatu nahi baduzu, <a href="%{path}">jarraitu hemen</a>. Berrestea eskatuko zaizu.
     description:
@@ -948,6 +1011,8 @@ eu:
     resend_confirmation: Birbidali berresteko argibideak
     reset_password: Berrezarri pasahitza
     rules:
+      accept: Onartu
+      back: Atzera
       preamble: Hauek %{domain} instantziako moderatzaileek ezarriak eta betearaziak dira.
       title: Oinarrizko arau batzuk.
     security: Segurtasuna
@@ -956,6 +1021,9 @@ eu:
       email_below_hint_html: Beheko e-mail helbidea okerra bada, hemen aldatu dezakezu eta baieztapen e-mail berria jaso.
       email_settings_hint_html: Baieztamen e-maila %{email} helbidera bidali da. E-mail helbide hori zuzena ez bada, kontuaren ezarpenetan aldatu dezakezu.
       title: Ezarpena
+    sign_in:
+      preamble_html: Zure <strong>%{domain}-(e)ko</strong> egiaztagiriekin saioa hasi. Zure kontua beste zerbitzari batean badago, ezin izango duzu hemen saioa hasi.
+      title: "%{domain}-(e)an saioa hasi"
     sign_up:
       preamble: Mastodon zerbitzari honetako kontu batekin, aukera izango duzu sareko edozein pertsona jarraitzeko, ez dio axola kontua non ostatatua dagoen.
       title: "%{domain} zerbitzariko kontua prestatuko dizugu."
@@ -1092,7 +1160,7 @@ eu:
   featured_tags:
     add_new: Gehitu berria
     errors:
-      limit: Gehienezko traola kopurua nabarmendu duzu jada
+      limit: Gehienezko traola kopurua erakutsi duzu jada
     hint_html: "<strong>Zer dira nabarmendutako traolak?</strong> Zure profilean toki nabarmendu batean agertzen dira eta jendeari traola hau daukaten bidalketa publikoak arakatzea ahalbidetzen diote. Sormen lana edo epe luzerako proiektuak jarraitzeko primerakoak dira."
   filters:
     contexts:
@@ -1136,8 +1204,6 @@ eu:
       index:
         hint: Iragazki honek banako bidalketei eragiten die, beste kriterioak badaude ere. Bidalketa gehiago gehitu ditzakezu iragazkira web interfazetik.
         title: Iragazitako bidalketak
-  footer:
-    trending_now: Joera orain
   generic:
     all: Denak
     all_items_on_page_selected_html:
@@ -1160,8 +1226,6 @@ eu:
     validation_errors:
       one: Zerbait ez dabil ongi! Egiaztatu beheko errorea mesedez
       other: Zerbait ez dabil ongi! Egiaztatu beheko %{count}  erroreak mesedez
-  html_validator:
-    invalid_markup: 'HTML markaketa baliogabea du: %{error}'
   imports:
     errors:
       invalid_csv_file: 'CSV fitxategi baliogabea. Errorea: %{error}'
@@ -1204,7 +1268,7 @@ eu:
     title: Gonbidatu jendea
   lists:
     errors:
-      limit: Gehieneko zerrenda kopurura heldu zara
+      limit: Gehienezko zerrenda kopurura iritsi zara
   login_activities:
     authentication_methods:
       otp: bi faktoreko autentifikazio aplikazioa
@@ -1345,7 +1409,11 @@ eu:
       unrecognized_emoji: ez da emoji ezaguna
   relationships:
     activity: Kontuaren aktibitatea
+    confirm_follow_selected_followers: Ziur al zaude hautatutako jarraitzaileak jarraitu nahi dituzula?
+    confirm_remove_selected_followers: Ziur al zaude hautatutako jarraitzaileak ezabatu nahi dituzula?
+    confirm_remove_selected_follows: Ziur al zaude hautatutako jarraipenak ezabatu nahi dituzula?
     dormant: Ez aktiboa
+    follow_failure: Ezin izan dira aukeratutako kontu batzuk jarraitu.
     follow_selected_followers: Jarraitu hautatutako jarraitzaileak
     followers: Jarraitzaileak
     following: Jarraitzen
@@ -1385,6 +1453,7 @@ eu:
       electron: Electron
       firefox: Firefox
       generic: Nabigatzaile ezezaguna
+      huawei_browser: Huawei nabigatzailea
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi nabigatzailea
@@ -1394,6 +1463,7 @@ eu:
       qq: QQ nabigatzailea
       safari: Safari
       uc_browser: UC nabigatzailea
+      unknown_browser: Nabigatzaile ezezaguna
       weibo: Weibo
     current_session: Uneko saioa
     description: "%{browser} - %{platform}"
@@ -1406,9 +1476,10 @@ eu:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: plataforma ezezaguna
+      unknown_platform: Plataforma ezezaguna
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1521,7 +1592,7 @@ eu:
       '7889238': 3 hilabete
     min_age_label: Denbora muga
     min_favs: Mantendu gogoko kopuru hau duten bidalketak
-    min_favs_hint: Gutxienez gogoko kopuru hau jaso duten zure bidalketak ez dira ezabatuko. Hutsik utziz gero gogoko kopurua ez da kontuan hartuko bidalketak ezabatzean
+    min_favs_hint: Gogoko dutenen kopuru hau gutxienez duten bidalketak ez dira ezabatuko. Hutsik utzi, gogoko dutenen kopurua aintzat hartu gabe bidalketak ezabatzeko
     min_reblogs: Mantendu bultzada kopuru hau duten bidalketak
     min_reblogs_hint: Gutxienez bultzada kopuru hau jaso duten zure bidalketak ez dira ezabatuko. Hutsik utziz gero bultzada kopurua ez da kontuan hartuko bidalketak ezabatzean
   stream_entries:
@@ -1621,12 +1692,13 @@ eu:
       title: Ongi etorri, %{name}!
   users:
     follow_limit_reached: Ezin dituzu %{limit} pertsona baino gehiago jarraitu
+    go_to_sso_account_settings: Jo zure identitate-hornitzaileko kontuaren ezarpenetara
     invalid_otp_token: Bi faktoreetako kode baliogabea
     otp_lost_help_html: 'Bietara sarbidea galdu baduzu, jarri kontaktuan hemen: %{email}'
     seamless_external_login: Kanpo zerbitzu baten bidez hasi duzu saioa, beraz pasahitza eta e-mail ezarpenak ez daude eskuragarri.
     signed_in_as: 'Saioa honela hasita:'
   verification:
-    explanation_html: 'Ezin duzu <strong>zure burua zure profileko metadatuen esteken jabe gisa egiaztatu</strong>. Horretarako, estekatutako webgunean zure Mastodon profilera daraman esteka bat egon behar du. Mastodonera daraman esteka horrek<strong>derrigorrez</strong> <code>rel="me"</code> artibutua izan behar du . Estekaren testuak ez du axola. Hona adibide bat:'
+    explanation_html: '<strong>Zure profileko metadatuetako esteken jabe zarela egiazta</strong> dezakezu. Horretarako, webguneak zure Mastodoneko profilaren esteka eduki behar du. Esteka webgunean erantsi ondoren, aldaketak berriro gorde beharko dituzu egiaztapena burutu ahal izateko. Estekak <code>rel="me"</code> atributua eduki <strong>behar</strong> du. Estekaren testu-edukia ez da aintzat hartzen. Hemen duzu adibide bat:'
     verification: Egiaztaketa
   webauthn_credentials:
     add: Gehitu segurtasun gako berria
diff --git a/config/locales/fa.yml b/config/locales/fa.yml
index 0260e2e63..4fc9ef5ab 100644
--- a/config/locales/fa.yml
+++ b/config/locales/fa.yml
@@ -1,7 +1,7 @@
 ---
 fa:
   about:
-    about_mastodon_html: 'شبکهٔ اجتماعی آینده: بدون تبلیغات، بدون شنود از طرف شرکت‌ها، طراحی اخلاق‌مدار، و معماری غیرمتمرکز! با ماستودون صاحب داده‌های خودتان باشید!'
+    about_mastodon_html: 'شبکه‌ی اجتماعی آینده: بدون تبلیغات، بدون شنود از طرف شرکت‌ها، طراحی اخلاق‌مدار، و معماری غیرمتمرکز! با ماستودون صاحب داده‌های خودتان باشید!'
     contact_missing: تنظیم نشده
     contact_unavailable: موجود نیست
     hosted_on: ماستودون، میزبانی‌شده روی %{domain}
@@ -91,6 +91,7 @@ fa:
       moderation:
         active: فعّال
         all: همه
+        disabled: غیرفعال
         pending: منتظر
         silenced: محدود
         suspended: تعلیق شده
@@ -128,6 +129,7 @@ fa:
       search: جستجو
       search_same_email_domain: دیگر کاربران با دامنهٔ رایانامهٔ یکسان
       search_same_ip: دیگر کاربران با IP یکسان
+      security: امنیت
       security_measures:
         only_password: فقط گذرواژه
         password_and_2fa: گذرواژه و ۲عاملی
@@ -201,6 +203,7 @@ fa:
         reject_user: رد کاربر
         remove_avatar_user: برداشتن تصویر نمایه
         reopen_report: بازگشایی گزارش
+        resend_user: ارسال مجدد رایانامه تایید
         reset_password_user: بازنشانی گذرواژه
         resolve_report: رفع گزارش
         sensitive_account: علامت‌گذاری رسانه در حسابتان به عنوان حساس
@@ -215,6 +218,7 @@ fa:
         update_custom_emoji: به‌روز رسانی اموجی سفارشی
         update_domain_block: به‌روزرسانی مسدودسازی دامنه
         update_status: به‌روز رسانی وضعیت
+        update_user_role: به روزرسانی نقش
       actions:
         approve_appeal_html: "%{name} درخواست تجدیدنظر تصمیم مدیر را از %{target} پذیرفت"
         approve_user_html: "%{name} ثبت نام %{target} را تایید کرد"
@@ -334,7 +338,7 @@ fa:
         other: "<strong>%{count}</strong> گزارش در انتظار"
       pending_tags_html:
         one: "<strong>%{count}</strong> هشتگ در انتظار"
-        other: "<strong>%{count}</strong> هشتگ در انتظار"
+        other: "<strong>%{count}</strong> برچسب منتظر"
       pending_users_html:
         one: "<strong>%{count}</strong> کاربر در انتظار"
         other: "<strong>%{count}</strong> کاربر در انتظار"
@@ -425,6 +429,7 @@ fa:
         title: سیاست‌های محتوا
       dashboard:
         instance_accounts_dimension: حساب‌های پیش‌تر پی‌گیری شده
+        instance_accounts_measure: حساب‌های ذخیره شده
         instance_followers_measure: پی‌گیرندگانمان در آن‌جا
         instance_follows_measure: پی‌گیرندگانشان در این‌جا
         instance_languages_dimension: زبان‌های برتر
@@ -511,9 +516,11 @@ fa:
       assign_to_self: به عهدهٔ من بگذار
       assigned: مدیر عهده‌دار
       by_target_domain: دامنهٔ حساب گزارش‌شده
+      cancel: لغو
       category: دسته
       comment:
         none: هیچ
+      confirm: تأیید
       created_at: گزارش‌شده
       delete_and_resolve: حذف فرسته‌ها
       forwarded: هدایت شده
@@ -549,6 +556,7 @@ fa:
       add_new: افزودن نقش
       categories:
         administration: مدیریت
+        devops: DevOps
         invites: دعوت‌ها
         moderation: نظارت
         special: ویژه
@@ -872,9 +880,7 @@ fa:
     storage: تصویرهای ذخیره‌شده
   featured_tags:
     add_new: افزودن تازه
-    errors:
-      limit: شما بیشترین تعداد مجاز برچسب‌ها را دارید
-    hint_html: "<strong>برچسب‌های برگزیده چیستند؟</strong> این برچسب‌ها (هشتگ‌ها) به طور واضحی روی نمایهٔ عمومی شما نمایش می‌یابند و دیگران می‌توانند نوشته‌های شما را تحت هر کدام از این برچسب‌ها مرور کنند. این یک روش بسیار خوب برای دسته‌بندی آثار خلاقانه یا پروژه‌های بلندمدت شماست."
+    hint_html: "<strong>برچسب‌های برگزیده چیستند؟</strong> این برچسب‌ها به طور واضحی روی نمایهٔ عمومیتان نمایش یافته می‌گذارد افراد فرسته‌های عمومیتان زیرشان را مرور کنند. ابزاری عالی برای دنبال کردن آثار خلاقانه یا پروژه‌های بلندمدت است."
   filters:
     contexts:
       account: نمایه‌ها
@@ -892,8 +898,6 @@ fa:
       title: پالایه‌ها
     new:
       title: افزودن پالایهٔ جدید
-  footer:
-    trending_now: پرطرفدار
   generic:
     all: همه
     changes_saved_msg: تغییرات با موفقیت ذخیره شدند!
@@ -906,8 +910,6 @@ fa:
     validation_errors:
       one: یک چیزی هنوز درست نیست! لطفاً خطاهای زیر را ببینید
       other: یک چیزی هنوز درست نیست! لطفاً %{count} خطای زیر را ببینید
-  html_validator:
-    invalid_markup: 'دارای نشانه‌گذاری نامعتبر HTML است: %{error}'
   imports:
     errors:
       over_rows_processing_limit: دارای بیش از %{count} ردیف
@@ -947,9 +949,6 @@ fa:
       expires_at: تاریخ انقضا
       uses: استفاده‌ها
     title: دعوت دیگران
-  lists:
-    errors:
-      limit: به بیشینهٔ مقدار سیاهه‌ها رسیدید
   login_activities:
     authentication_methods:
       otp: کارهٔ تأیید هویت دوعاملی
@@ -1133,7 +1132,6 @@ fa:
       ios: آی‌اواس
       linux: لینوکس
       mac: مک
-      other: سیستم ناشناخته
       windows: ویندوز
       windows_mobile: ویندوز همراه
       windows_phone: تلفن ویندوزی
@@ -1178,8 +1176,8 @@ fa:
     boosted_from_html: تقویت شده از طرف %{acct_link}
     content_warning: 'هشدا محتوا: %{warning}'
     disallowed_hashtags:
-      one: 'دارای هشتگ غیرمجاز: %{tags}'
-      other: 'دارای هشتگ‌های غیرمجاز: %{tags}'
+      one: 'دارای برچسبی غیرمجاز: %{tags}'
+      other: 'دارای برچسب‌های غیرمجاز: %{tags}'
     errors:
       in_reply_not_found: به نظر نمی‌رسد وضعیتی که می‌خواهید به آن پاسخ دهید، وجود داشته باشد.
     open_in_web: گشودن در وب
@@ -1243,7 +1241,6 @@ fa:
       '7889238': ۳ ماه
     min_age_label: کرانهٔ سن
     min_favs: نگه داشتن فرسته‌هایی با برگزینش بیش از
-    min_favs_hint: هیچ یک از فرسته‌هایتان را که بیش از این تعداد برگزیده شده باشند، حذف نمی‌کند. برای حذف فرسته‌ها فارغ از تعداد برگزینش‌هایشان، خالی بگذارید
     min_reblogs: نگه داشتن فرسته‌هایی با تقویت بیش از
     min_reblogs_hint: هیچ یک از فرسته‌هایتان را که بیش از این تعداد تقویت شده باشند، حذف نمی‌کند. برای حذف فرسته‌ها فارغ از تعداد تقویت‌هایشان، خالی بگذارید
   stream_entries:
@@ -1322,7 +1319,6 @@ fa:
     seamless_external_login: شما با یک سرویس خارج از مجموعه وارد شده‌اید، به همین دلیل تنظیمات ایمیل و رمز برای شما در دسترس نیست.
     signed_in_as: 'واردشده به نام:'
   verification:
-    explanation_html: 'شما می‌توانید <strong>خود را به عنوان مالک صفحه‌ای که در نمایه‌تان به آن پیوند داده‌اید تأیید کنید.</strong> برای این کار، صفحه‌ای که به آن پیوند داده‌اید، خودش باید پیوندی به نمایهٔ ماستودون شما داشته باشد. پیوند در آن صفحه <strong>باید</strong> عبارت <code>rel="me"‎</code> را به عنوان مشخّصهٔ (attribute) در خود داشته باشد. محتوای متن پیوند اهمتی ندارد. یک نمونه از چنین پیوندی:'
     verification: تأیید
   webauthn_credentials:
     add: افزودن کلید امنیتی
diff --git a/config/locales/fi.yml b/config/locales/fi.yml
index 00540a1d5..7cfac413e 100644
--- a/config/locales/fi.yml
+++ b/config/locales/fi.yml
@@ -12,7 +12,7 @@ fi:
       one: Seuraaja
       other: Seuraajat
     following: Seuraaja
-    instance_actor_flash: Tämä on virtuaalitili, jota käytetään edustamaan itse palvelinta eikä yksittäistä käyttäjää. Sitä käytetään yhdistämistarkoituksiin, eikä sitä tule keskeyttää.
+    instance_actor_flash: Tämä on virtuaalitili, jota käytetään edustamaan itse palvelinta eikä yksittäistä käyttäjää. Sitä käytetään yhdistämistarkoituksiin, eikä sitä tule jäädyttää.
     last_active: viimeksi aktiivinen
     link_verified_on: Tämän linkin omistus on tarkastettu %{date}
     nothing_here: Täällä ei ole mitään!
@@ -20,8 +20,8 @@ fi:
       following: Sinun täytyy seurata henkilöä jota haluat tukea
     posts:
       one: Julkaisu
-      other: Julkaisut
-    posts_tab_heading: Julkaisut
+      other: Viestit
+    posts_tab_heading: Viestit
   admin:
     account_actions:
       action: Suorita toimenpide
@@ -91,6 +91,7 @@ fi:
       moderation:
         active: Aktiivinen
         all: Kaikki
+        disabled: Ei käytössä
         pending: Odottavat
         silenced: Rajoitettu
         suspended: Jäähyllä
@@ -121,30 +122,31 @@ fi:
       remove_avatar: Poista profiilikuva
       remove_header: Poista otsakekuva
       removed_avatar_msg: Käyttäjän %{username} avatar-kuva poistettu onnistuneesti
-      removed_header_msg: Käyttäjän %{username} otsikkokuva poistettiin onnistuneesti
+      removed_header_msg: Käyttäjän %{username} otsakekuva poistettiin onnistuneesti
       resend_confirmation:
         already_confirmed: Tämä käyttäjä on jo vahvistettu
-        send: Lähetä varmistusviesti uudelleen
+        send: Lähetä vahvistusviesti uudelleen
         success: Vahvistusviesti onnistuneesti lähetetty!
       reset: Palauta
       reset_password: Palauta salasana
       resubscribe: Tilaa uudelleen
       role: Rooli
       search: Hae
-      search_same_email_domain: Muut käyttäjät, joilla on sama sähköpostiverkkotunnus
-      search_same_ip: Muut käyttäjät samalla IP-osoitteella
+      search_same_email_domain: Muut käyttäjät, joilla on sama sähköpostin verkkotunnus
+      search_same_ip: Muut käyttäjät, joilla on sama IP-osoite
+      security: Turvallisuus
       security_measures:
         only_password: Vain salasana
         password_and_2fa: Salasana ja kaksivaiheinen tunnistautuminen
-      sensitive: Pakotus arkaluontoiseksi
-      sensitized: Merkitty arkaluontoiseksi
+      sensitive: Pakotus arkaluonteiseksi
+      sensitized: Merkitty arkaluonteiseksi
       shared_inbox_url: Jaetun saapuvan postilaatikon osoite
       show:
         created_reports: Tämän tilin luomat raportit
         targeted_reports: Tästä tilistä tehdyt raportit
       silence: Hiljennä
       silenced: Mykistetty
-      statuses: Tilat
+      statuses: Viestit
       strikes: Aiemmat varoitukset
       subscribe: Tilaa
       suspend: Jäädytä
@@ -155,17 +157,17 @@ fi:
       unblock_email: Poista sähköpostiosoitteen esto
       unblocked_email_msg: Käyttäjän %{username} sähköpostiosoitteen esto kumottiin
       unconfirmed_email: Sähköpostia ei vahvistettu
-      undo_sensitized: Kumoa pakotus arkaluontoiseksi tiliksi
+      undo_sensitized: Kumoa pakotus arkaluonteiseksi tiliksi
       undo_silenced: Peru hiljennys
       undo_suspension: Peru jäähy
-      unsilenced_msg: "%{username} -tilin rajoituksen kumoaminen onnistui"
+      unsilenced_msg: Tilin %{username} rajoituksen kumoaminen onnistui
       unsubscribe: Lopeta tilaus
-      unsuspended_msg: "%{username} -tilin keskeytyksen kumoaminen onnistui"
+      unsuspended_msg: Tilin %{username} jäädytyksen kumoaminen onnistui
       username: Käyttäjätunnus
       view_domain: Näytä verkkotunnuksen yhteenveto
       warn: Varoita
       web: Verkko
-      whitelisted: Sallittu liittämiselle
+      whitelisted: Sallittu federaatioon
     action_logs:
       action_types:
         approve_appeal: Hyväksy valitus
@@ -178,9 +180,9 @@ fi:
         create_announcement: Luo ilmoitus
         create_canonical_email_block: Luo sähköpostin esto
         create_custom_emoji: Luo mukautettu emoji
-        create_domain_allow: Salli palvelin
-        create_domain_block: Estä palvelin
-        create_email_domain_block: Estä sähköpostipalvelin
+        create_domain_allow: Luo verkkotunnuksen salliminen
+        create_domain_block: Luo verkkotunnuksen esto
+        create_email_domain_block: Luo sähköpostin verkkotunnuksen esto
         create_ip_block: Luo IP-sääntö
         create_unavailable_domain: Luo ei-saatavilla oleva verkkotunnus
         create_user_role: Luo rooli
@@ -188,21 +190,21 @@ fi:
         destroy_announcement: Poista ilmoitus
         destroy_canonical_email_block: Poista sähköpostin esto
         destroy_custom_emoji: Poista mukautettu emoji
-        destroy_domain_allow: Salli verkkotunnuksen poisto
+        destroy_domain_allow: Poista verkkotunnuksen salliminen
         destroy_domain_block: Poista verkkotunnuksen esto
-        destroy_email_domain_block: Poista sähköpostipalvelimen esto
+        destroy_email_domain_block: Poista sähköpostin verkkotunnuksen esto
         destroy_instance: Tyhjennä verkkotunnus
         destroy_ip_block: Poista IP-sääntö
-        destroy_status: Poista julkaisu
+        destroy_status: Poista viesti
         destroy_unavailable_domain: Poista ei-saatavilla oleva verkkotunnus
         destroy_user_role: Hävitä rooli
         disable_2fa_user: Poista kaksivaiheinen tunnistautuminen käytöstä
-        disable_custom_emoji: Estä mukautettu emoji
+        disable_custom_emoji: Poista mukautettu emoji käytöstä
         disable_sign_in_token_auth_user: Estä käyttäjältä sähköpostitunnuksen todennus
-        disable_user: Tili poistettu käytöstä
+        disable_user: Poista tili käytöstä
         enable_custom_emoji: Käytä mukautettuja emojeita
         enable_sign_in_token_auth_user: Salli käyttäjälle sähköpostitunnuksen todennus
-        enable_user: Tili otettu käyttöön
+        enable_user: Ota tili käyttöön
         memorialize_account: Muuta muistotiliksi
         promote_user: Käyttäjä ylennetty
         reject_appeal: Hylkää valitus
@@ -231,32 +233,32 @@ fi:
         approve_user_html: "%{name} hyväksyi käyttäjän rekisteröitymisen kohteesta %{target}"
         assigned_to_self_report_html: "%{name} otti raportin %{target} tehtäväkseen"
         change_email_user_html: "%{name} vaihtoi käyttäjän %{target} sähköpostiosoitteen"
-        change_role_user_html: "%{name} muutti roolia %{target}"
+        change_role_user_html: "%{name} muutti käyttäjän %{target} roolia"
         confirm_user_html: "%{name} vahvisti käyttäjän %{target} sähköpostiosoitteen"
-        create_account_warning_html: "%{name} lähetti varoituksen henkilölle %{target}"
+        create_account_warning_html: "%{name} lähetti varoituksen käyttäjälle %{target}"
         create_announcement_html: "%{name} loi uuden ilmoituksen %{target}"
         create_canonical_email_block_html: "%{name} esti sähköpostin hashilla %{target}"
         create_custom_emoji_html: "%{name} lähetti uuden emojin %{target}"
-        create_domain_allow_html: "%{name} salli yhdistäminen verkkotunnuksella %{target}"
+        create_domain_allow_html: "%{name} salli federaation verkkotunnuksella %{target}"
         create_domain_block_html: "%{name} esti verkkotunnuksen %{target}"
         create_email_domain_block_html: "%{name} esti sähköpostin %{target}"
-        create_ip_block_html: "%{name} luonut IP-säännön %{target}"
+        create_ip_block_html: "%{name} loi IP-säännön %{target}"
         create_unavailable_domain_html: "%{name} pysäytti toimituksen verkkotunnukseen %{target}"
-        create_user_role_html: "%{name} luonut %{target} roolin"
+        create_user_role_html: "%{name} loi roolin %{target}"
         demote_user_html: "%{name} alensi käyttäjän %{target}"
         destroy_announcement_html: "%{name} poisti ilmoituksen %{target}"
-        destroy_canonical_email_block_html: "%{name} poisti sähköposti eston hashilla %{target}"
+        destroy_canonical_email_block_html: "%{name} poisti sähköpostieston hashilla %{target}"
         destroy_custom_emoji_html: "%{name} poisti emojin %{target}"
-        destroy_domain_allow_html: "%{name} esti yhdistämisen verkkotunnuksella %{target}"
+        destroy_domain_allow_html: "%{name} esti federaation verkkotunnuksella %{target}"
         destroy_domain_block_html: "%{name} poisti verkkotunnuksen %{target} eston"
-        destroy_email_domain_block_html: "%{name} poisti verkkotunnuksen %{target} eston"
+        destroy_email_domain_block_html: "%{name} poisti sähköpostin verkkotunnuksen %{target} eston"
         destroy_instance_html: "%{name} tyhjensi verkkotunnuksen %{target}"
         destroy_ip_block_html: "%{name} poisti IP-säännön %{target}"
-        destroy_status_html: "%{name} poisti viestin %{target}"
+        destroy_status_html: "%{name} poisti käyttäjän %{target} viestin"
         destroy_unavailable_domain_html: "%{name} jatkoi toimitusta verkkotunnukseen %{target}"
-        destroy_user_role_html: "%{name} poisti %{target} roolin"
+        destroy_user_role_html: "%{name} poisti roolin %{target}"
         disable_2fa_user_html: "%{name} poisti käyttäjältä %{target} vaatimuksen kaksivaiheisen todentamiseen"
-        disable_custom_emoji_html: "%{name} poisti emojin %{target}"
+        disable_custom_emoji_html: "%{name} poisti käytöstä emojin %{target}"
         disable_sign_in_token_auth_user_html: "%{name} poisti sähköpostitunnuksen %{target} todennuksen käytöstä"
         disable_user_html: "%{name} poisti kirjautumisen käyttäjältä %{target}"
         enable_custom_emoji_html: "%{name} salli emojin %{target}"
@@ -271,17 +273,17 @@ fi:
         resend_user_html: "%{name} lähetti vahvistusviestin sähköpostitse käyttäjälle %{target}"
         reset_password_user_html: "%{name} palautti käyttäjän %{target} salasanan"
         resolve_report_html: "%{name} ratkaisi raportin %{target}"
-        sensitive_account_html: "%{name} merkitsi %{target} median arkaluonteiseksi"
-        silence_account_html: "%{name} rajoitti %{target} tilin"
+        sensitive_account_html: "%{name} merkitsi käyttäjän %{target} median arkaluonteiseksi"
+        silence_account_html: "%{name} rajoitti käyttäjän %{target} tilin"
         suspend_account_html: "%{name} siirsi käyttäjän %{target} jäähylle"
         unassigned_report_html: "%{name} peruutti raportin määrityksen %{target}"
-        unblock_email_account_html: "%{name} poisti %{target} sähköpostiosoitteen eston"
-        unsensitive_account_html: "%{name} poisti merkinnän %{target} arkaluonteinen media"
+        unblock_email_account_html: "%{name} poisti käyttäjän %{target} sähköpostiosoitteen eston"
+        unsensitive_account_html: "%{name} poisti käyttäjän %{target} median arkaluonteisen merkinnän"
         unsilence_account_html: "%{name} ei tehnyt rajoitusta %{target} tilille"
         unsuspend_account_html: "%{name} perui käyttäjän %{target} jäähyn"
         update_announcement_html: "%{name} päivitti ilmoituksen %{target}"
         update_custom_emoji_html: "%{name} päivitti emojin %{target}"
-        update_domain_block_html: "%{name} päivitti verkkotunnuksen %{target}"
+        update_domain_block_html: "%{name} päivitti verkkotunnuksen %{target} eston"
         update_ip_block_html: "%{name} muutti sääntöä IP-osoitteelle %{target}"
         update_status_html: "%{name} päivitti viestin %{target}"
         update_user_role_html: "%{name} muutti roolia %{target}"
@@ -302,9 +304,9 @@ fi:
       publish: Julkaise
       published_msg: Ilmoitus julkaistu onnistuneesti!
       scheduled_for: Ajastettu %{time}
-      scheduled_msg: Ilmoitus on tarkoitus julkaista!
+      scheduled_msg: Ilmoitus on ajastettu julkaisua varten!
       title: Ilmoitukset
-      unpublish: Julkaisematon
+      unpublish: Lopeta julkaisu
       unpublished_msg: Ilmoituksen julkaisu lopetettu!
       updated_msg: Ilmoitus päivitetty onnistuneesti!
     custom_emojis:
@@ -314,9 +316,9 @@ fi:
       copy: Kopioi
       copy_failed_msg: Emojista ei voitu tehdä paikallista kopiota
       create_new_category: Luo uusi kategoria
-      created_msg: Emojin luonti onnistui!
+      created_msg: Emojin luotu!
       delete: Poista
-      destroyed_msg: Emojon poisto onnistui!
+      destroyed_msg: Emojo poistettu!
       disable: Poista käytöstä
       disabled: Ei käytössä
       disabled_msg: Emojin poisto käytöstä onnistui
@@ -324,14 +326,14 @@ fi:
       enable: Ota käyttöön
       enabled: Käytössä
       enabled_msg: Emojin käyttöönotto onnistui
-      image_hint: PNG tai GIF enintään %{size}
+      image_hint: PNG tai GIF, enintään %{size}
       list: Listaa
       listed: Listassa
       new:
         title: Lisää uusi mukautettu emoji
-      no_emoji_selected: Hymiöitä ei muutettu, koska yhtään ei valittu
+      no_emoji_selected: Emojeita ei muutettu, koska yhtään ei valittu
       not_permitted: Sinulla ei ole oikeutta suorittaa tätä toimintoa
-      overwrite: Kirjoita yli
+      overwrite: Korvaa
       shortcode: Lyhennekoodi
       shortcode_hint: Vähintään kaksi merkkiä, vain kirjaimia, numeroita ja alaviivoja
       title: Mukautetut emojit
@@ -420,13 +422,14 @@ fi:
       delete: Poista
       dns:
         types:
-          mx: MX tietue
+          mx: MX-tietue
       domain: Verkkotunnus
       new:
         create: Lisää verkkotunnus
         resolve: Ratkaise verkkotunnus
         title: Uusi sähköpostiestolistan merkintä
       no_email_domain_block_selected: Sähköpostin verkkotunnuksia ei muutettu, koska yhtään ei valittu
+      not_permitted: Ei sallittu
       resolved_dns_records_hint_html: Verkkotunnuksen nimi määräytyy seuraaviin MX-verkkotunnuksiin, jotka ovat viime kädessä vastuussa sähköpostin vastaanottamisesta. MX-verkkotunnuksen estäminen estää kirjautumisen mistä tahansa sähköpostiosoitteesta, joka käyttää samaa MX-verkkotunnusta, vaikka näkyvä verkkotunnuksen nimi olisikin erilainen. <strong>Varo estämästä suuria sähköpostin palveluntarjoajia.</strong>
       resolved_through_html: Ratkaistu %{domain} kautta
       title: Sähköpostiestolista
@@ -441,6 +444,7 @@ fi:
         private_comment_description_html: 'Tuodun estolistan alkuperän selvillä pitämiseksi, lisätään tietojen yhteyteen seuraava yksityinen kommentti: <q>%{comment}</q>'
         private_comment_template: Tuotu lähteestä %{source}, pvm %{date}
         title: Tuo luettelo verkkoalue-estoista
+      invalid_domain_block: 'Yksi tai useampi verkkotunnuksen lohko ohitettiin seuraavien virheiden vuoksi: %{error}'
       new:
         title: Tuo luettelo verkkoalue-estoista
       no_file: Yhtäkään tiedostoa ei ole valittu
@@ -472,6 +476,7 @@ fi:
       content_policies:
         comment: Sisäinen huomautus
         description_html: Voit määrittää sisältökäytännöt, joita sovelletaan kaikkiin tämän verkkotunnuksen ja sen aliverkkotunnuksien tileihin.
+        limited_federation_mode_description_html: Voit valita sallitaanko federointi tällä verkkotunnuksella.
         policies:
           reject_media: Hylkää media
           reject_reports: Hylkää raportit
@@ -575,19 +580,23 @@ fi:
         mark_as_sensitive_description_html: Ilmoitettujen viestien media merkitään arkaluonteisiksi ja varoitus tallennetaan, jotta voit kärjistää saman tilin tulevia rikkomuksia.
         other_description_html: Katso lisää vaihtoehtoja tilin käytöksen hallitsemiseksi ja ilmoitetun tilin viestinnän mukauttamiseksi.
         resolve_description_html: Ilmoitettua tiliä vastaan ei ryhdytä toimenpiteisiin, varoitusta ei kirjata ja raportti suljetaan.
-        silence_description_html: Profiili näkyy vain niille, jotka jo seuraavat sitä tai etsivät sen manuaalisesti, mikä rajoittaa merkittävästi kattavuutta. Se voidaan aina palauttaa.
-        suspend_description_html: Profiili ja sen koko sisältö eivät ole käytettävissä, kunnes se lopulta poistetaan. Vuorovaikutus tilin kanssa on mahdotonta. Palautettavissa 30 päivän kuluessa.
+        silence_description_html: Tili näkyy vain niille, jotka jo seuraavat sitä tai estävät sen manuaalisesti, mikä rajoittaa merkittävästi sen kattavuutta. Se voidaan aina palauttaa. Sulkee kaikki raportit tätä tiliä vastaan.
+        suspend_description_html: Tili ja kaikki sen sisältö eivät ole käytettävissä ja vuorovaikutus sen kanssa on mahdotonta, sekä lopulta poistetaan. Palautettava 30 päivän kuluessa. Sulkee kaikki raportit tätä tiliä vastaan.
       actions_description_html: Päätä, mihin toimiin ryhdyt tämän ilmoituksen ratkaisemiseksi. Jos ryhdyt rangaistustoimeen ilmoitettua tiliä vastaan, heille lähetetään sähköposti-ilmoitus, paitsi jos <strong>Roskaposti</strong> luokka on valittuna.
+      actions_description_remote_html: Päätä, mihin toimiin ryhdyt tämän raportin ratkaisemiseksi. Tämä vaikuttaa vain siihen, miten <strong>palvelimesi</strong> kommunikoi tämän etätilin kanssa ja käsittelee sen sisältöä.
       add_to_report: Lisää raporttiin
       are_you_sure: Oletko varma?
       assign_to_self: Ota tehtäväksi
       assigned: Määritetty valvoja
       by_target_domain: Ilmoitetun tilin verkkotunnus
+      cancel: Peruuta
       category: Kategoria
       category_description_html: Syy, miksi tämä tili ja/tai sisältö ilmoitettiin, mainitaan yhteydenotossa ilmoitettuun tiliin
       comment:
         none: Ei mitään
       comment_description_html: 'Antaakseen lisätietoja %{name} kirjoitti:'
+      confirm: Vahvista
+      confirm_action: Vahvista moderointitoiminto käyttäjää @%{acct} kohtaan
       created_at: Raportoitu
       delete_and_resolve: Poista viestejä
       forwarded: Välitetty
@@ -604,6 +613,7 @@ fi:
         placeholder: Kuvaile mitä toimia on tehty tai muita päivityksiä tähän raporttiin…
         title: Merkinnät
       notes_description_html: Tarkastele ja jätä merkintöjä muille valvojille ja itsellesi tulevaisuuteen
+      processed_msg: 'Raportti #%{id} käsitelty'
       quick_actions_description_html: 'Suorita nopea toiminto tai vieritä alas nähdäksesi raportoitu sisältö:'
       remote_user_placeholder: etäkäyttäjä instanssista %{instance}
       reopen: Avaa raportti uudestaan
@@ -616,9 +626,28 @@ fi:
       status: Tila
       statuses: Raportoitu sisältö
       statuses_description_html: Loukkaava sisältö mainitaan ilmoitetun tilin yhteydessä
+      summary:
+        action_preambles:
+          delete_html: 'Olet aikeissa <strong>poistaa</strong> joitain käyttäjän <strong>@%{acct}</strong> viestejä. Tästä seuraa:'
+          mark_as_sensitive_html: 'Olet aikeissa <strong>merkitä</strong> joitain käyttäjän <strong>@%{acct}</strong> viestejä <strong>arkaluonteisiksi</strong>. Tästä seuraa:'
+          silence_html: 'Olet aikeissa <strong>rajoittaa</strong> käyttäjän <strong>@%{acct}</strong> tiliä. Tästä seuraa:'
+          suspend_html: 'Olet aikeissa <strong>rajoittaa</strong> käyttäjän <strong>@%{acct}</strong> tiliä. Tästä seuraa:'
+        actions:
+          delete_html: Loukkaavat viestit poistetaan
+          mark_as_sensitive_html: Loukkaavien viestien media merkitään arkaluonteiseksi
+          silence_html: Vakavasti rajoittaa käyttäjän <strong>@%{acct}</strong> tavoitettavuutta tekemällä profiilista ja sen sisällöstä näkyviä vain jo häntä seuraaville tai niille, jotka etsivät profiilia manuaalisesti
+          suspend_html: Rajoita <strong>@%{acct}</strong>, jolloin heidän profiilinsa ja sisällönsä ei ole käytettävissä ja on mahdotonta olla vuorovaikutuksessa
+        close_report: 'Merkitse raportti #%{id} selvitetyksi'
+        close_reports_html: Merkitse <strong>kaikki</strong> käyttäjään <strong>@%{acct}</strong> kohdistuvat raportit ratkaistuiksi
+        delete_data_html: Poista <strong>@%{acct}</strong>profiili ja sisältö 30 päivän kuluttua, ellei jäädytystä tällä välin peruuteta
+        preview_preamble_html: "<strong>@%{acct}</strong> saa varoituksen, jonka sisältö on seuraava:"
+        record_strike_html: Tallenna varoitus <strong>@%{acct}</strong> vastaan, joka auttaa sinua selvittämään tulevia rikkomuksia tältä tililtä
+        send_email_html: Lähetä käyttäjälle <strong>@%{acct}</strong> varoitus sähköpostitse
+        warning_placeholder: Valinnaiset lisäperustelut moderointitoimenpiteelle.
       target_origin: Raportoidun tilin alkuperä
       title: Raportit
       unassign: Määrittämätön
+      unknown_action_msg: 'Tuntematon toiminto: %{action}'
       unresolved: Ratkaisemattomat
       updated_at: Päivitetty
       view_profile: Näytä profiili
@@ -713,6 +742,8 @@ fi:
         preamble: Mielenkiintoisen sisällön esille tuominen auttaa saamaan uusia käyttäjiä, jotka eivät ehkä tunne ketään Mastodonista. Määrittele, kuinka erilaiset etsintäominaisuudet toimivat palvelimellasi.
         profile_directory: Profiilihakemisto
         public_timelines: Julkiset aikajanat
+        publish_discovered_servers: Julkaise löydetyt palvelimet
+        publish_statistics: Julkaise tilastot
         title: Löytäminen
         trends: Trendit
       domain_blocks:
@@ -760,13 +791,14 @@ fi:
       actions:
         delete_statuses: "%{name} poisti käyttäjän %{target} viestit"
         disable: "%{name} jäädytti %{target} tilin"
-        mark_statuses_as_sensitive: "%{name} merkitsi %{target} viestiä arkaluonteiseksi"
+        mark_statuses_as_sensitive: "%{name} merkitsi käyttäjän %{target} viestit arkaluonteisiksi"
         none: "%{name} lähetti varoituksen henkilölle %{target}"
         sensitive: "%{name} merkitsi käyttäjän %{target} tilin arkaluonteiseksi"
         silence: "%{name} rajoitti käyttäjän %{target} tilin"
-        suspend: "%{name} keskeytti käyttäjän %{target} tilin"
+        suspend: "%{name} jäädytti käyttäjän %{target} tilin"
       appeal_approved: Valitti
       appeal_pending: Valitus vireillä
+      appeal_rejected: Muutoksenhaku hylättiin
     system_checks:
       database_schema_check:
         message_html: Tietokannan siirto on vireillä. Suorita ne varmistaaksesi, että sovellus toimii odotetulla tavalla
@@ -780,6 +812,12 @@ fi:
         message_html: Et ole määrittänyt mitään palvelimen sääntöä.
       sidekiq_process_check:
         message_html: Ei ole Sidekiq-prosessia käynnissä jonossa %{value}. Tarkista Sidekiq-asetukset
+      upload_check_privacy_error:
+        action: Katso täältä lisätietoja
+        message_html: "<strong>Verkkopalvelimesi on määritetty virheellisesti, ja käyttäjiesi yksityisyys on vaarassa.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Katso täältä lisätietoja
+        message_html: "<strong>Objektivarastosi on määritetty virheellisesti, ja käyttäjiesi yksityisyys on vaarassa.</strong>"
     tags:
       review: Tarkista tila
       updated_msg: Aihetunnisteen asetukset päivitetty onnistuneesti
@@ -802,6 +840,7 @@ fi:
           other: Jakanut %{count} henkilöä viimeisen viikon aikana
         title: Suositut linkit
         usage_comparison: Jaettu %{today} kertaa tänään verrattuna eilen %{yesterday}
+      not_allowed_to_trend: Ei saa trendata
       only_allowed: Vain sallittu
       pending_review: Odottaa tarkistusta
       preview_card_providers:
@@ -837,7 +876,7 @@ fi:
         not_trendable: Ei näy trendien alla
         not_usable: Ei voida käyttää
         peaked_on_and_decaying: Saavutti huipun %{date}, nyt hiipuu
-        title: Suositut tunnisteet
+        title: Suositut aihetunnisteet
         trendable: Voi näkyä trendien alla
         trending_rank: 'Nousussa #%{rank}'
         usable: Voidaan käyttää
@@ -851,12 +890,12 @@ fi:
       add_new: Lisää uusi
       delete: Poista
       edit_preset: Muokkaa varoituksen esiasetusta
-      empty: Et ole vielä määrittänyt yhtään varoitusesiasetusta.
-      title: Hallinnoi varoitusesiasetuksia
+      empty: Et ole vielä määrittänyt yhtäkään varoitusten esiasetusta.
+      title: Hallinnoi varoitusten esiasetuksia
     webhooks:
       add_new: Lisää päätepiste
       delete: Poista
-      description_html: A <strong>webhook</strong> mahdollistaa Mastodonin työntää <strong>reaaliaikaisia ilmoituksia</strong> valituista tapahtumista omaan sovellukseesi, joten sovelluksesi voi <strong>laukaista automaattisesti reaktioita</strong>.
+      description_html: "<strong>Webhook</strong> mahdollistaa Mastodonin työntää <strong>reaaliaikaisia ilmoituksia</strong> valituista tapahtumista omaan sovellukseesi, joten sovelluksesi voi <strong>laukaista automaattisesti reaktioita</strong>."
       disable: Poista käytöstä
       disabled: Ei käytössä
       edit: Muokkaa päätepistettä
@@ -882,7 +921,7 @@ fi:
         none: varoitus
         sensitive: merkitä heidän tilinsä arkaluonteiseksi
         silence: rajoittaa heidän tilinsä
-        suspend: keskeyttää heidän tilinsä
+        suspend: jäädyttää heidän tilinsä
       body: "%{target} on valittanut valvojan päätöksestä %{action_taken_by} aika %{date}, joka oli %{type}. He kirjoittivat:"
       next_steps: Voit hyväksyä vetoomuksen ja kumota päätöksen tai jättää sen huomiotta.
       subject: "%{username} valittaa valvojan päätöksestä, joka koskee instanssia %{instance}"
@@ -921,7 +960,7 @@ fi:
       body: Mastodonin ovat kääntäneet vapaaehtoiset.
       guide_link: https://crowdin.com/project/mastodon
       guide_link_text: Kaikki voivat osallistua.
-    sensitive_content: Arkaluontoista sisältöä
+    sensitive_content: Arkaluonteinen sisältö
     toot_layout: Viestin asettelu
   application_mailer:
     notification_preferences: Muuta sähköpostiasetuksia
@@ -933,6 +972,7 @@ fi:
   applications:
     created: Sovelluksen luonti onnistui
     destroyed: Sovelluksen poisto onnistui
+    logout: Kirjaudu ulos
     regenerate_token: Luo pääsytunnus uudelleen
     token_regenerated: Pääsytunnuksen uudelleenluonti onnistui
     warning: Säilytä tietoa hyvin. Älä milloinkaan jaa sitä muille!
@@ -940,6 +980,8 @@ fi:
   auth:
     apply_for_account: Pyydä tiliä
     change_password: Salasana
+    confirmations:
+      wrong_email_hint: Jos sähköpostiosoite ei ole oikein, voit muuttaa sen tilin asetuksista.
     delete_account: Poista tili
     delete_account_html: Jos haluat poistaa tilisi, <a href="%{path}">paina tästä</a>. Poisto on vahvistettava.
     description:
@@ -949,7 +991,7 @@ fi:
     didnt_get_confirmation: Etkö saanut vahvistusohjeita?
     dont_have_your_security_key: Eikö sinulla ole suojausavainta?
     forgot_password: Unohditko salasanasi?
-    invalid_reset_password_token: Salasananpalautustunnus on virheellinen tai vanhentunut. Pyydä uusi.
+    invalid_reset_password_token: Salasanan palautustunnus on virheellinen tai vanhentunut. Pyydä uusi.
     link_to_otp: Syötä puhelimesi kaksivaiheinen koodi tai palautuskoodi
     link_to_webauth: Käytä suojausavaintasi
     log_in_with: Kirjaudu käyttäen
@@ -967,13 +1009,15 @@ fi:
     resend_confirmation: Lähetä vahvistusohjeet uudestaan
     reset_password: Palauta salasana
     rules:
+      accept: Hyväksy
+      back: Takaisin
       preamble: "%{domain} valvojat määrittävät ja valvovat sääntöjä."
       title: Joitakin perussääntöjä.
     security: Tunnukset
     set_new_password: Aseta uusi salasana
     setup:
       email_below_hint_html: Jos alla oleva sähköpostiosoite on virheellinen, voit muuttaa sitä täällä ja tilata uuden vahvistussähköpostiviestin.
-      email_settings_hint_html: Vahvistussähköposti lähetettiin osoitteeseen %{email}. Jos sähköpostiosoite ei ole oikea, voit muuttaa sitä tiliasetuksissa.
+      email_settings_hint_html: Vahvistussähköposti lähetettiin osoitteeseen %{email}. Jos sähköpostiosoite ei ole oikea, voit vaihtaa sen tilin asetuksista.
       title: Asetukset
     sign_in:
       preamble_html: Kirjaudu sisään <strong>%{domain}</strong>-tunnuksillasi. Jos tilisi sijaitsee eri palvelimella, et voi sisäänkirjautua täällä.
@@ -984,7 +1028,7 @@ fi:
     status:
       account_status: Tilin tila
       confirming: Odotetaan sähköpostivahvistuksen valmistumista.
-      functional: Tilisi on täysin toimiva.
+      functional: Tilisi on täysin toiminnassa.
       pending: Hakemuksesi odottaa henkilökuntamme tarkastusta. Tämä voi kestää jonkin aikaa. Saat sähköpostiviestin, jos hakemuksesi on hyväksytty.
       redirecting_to: Tilisi ei ole aktiivinen, koska se ohjaa tällä hetkellä kohteeseen %{acct}.
       view_strikes: Näytä tiliäsi koskevia aiempia varoituksia
@@ -1009,7 +1053,7 @@ fi:
     prompt: Vahvista salasanasi jatkaaksesi
   crypto:
     errors:
-      invalid_key: ei ole kelvollinen Ed25519- tai Curve25519 -avain
+      invalid_key: ei ole kelvollinen Ed25519- tai Curve25519-avain
       invalid_signature: ei ole kelvollinen Ed25519-allekirjoitus
   date:
     formats:
@@ -1017,16 +1061,16 @@ fi:
       with_month_name: "%B %d, %Y"
   datetime:
     distance_in_words:
-      about_x_hours: "%{count} h"
+      about_x_hours: "%{count} t"
       about_x_months: "%{count} kk"
       about_x_years: "%{count} v"
       almost_x_years: "%{count} v"
       half_a_minute: Nyt
-      less_than_x_minutes: "%{count} m"
+      less_than_x_minutes: "%{count} min"
       less_than_x_seconds: Nyt
       over_x_years: "%{count} v"
       x_days: "%{count} pv"
-      x_minutes: "%{count} m"
+      x_minutes: "%{count} min"
       x_months: "%{count} kk"
       x_seconds: "%{count} s"
   deletes:
@@ -1072,7 +1116,7 @@ fi:
         none: Varoitus
         sensitive: Tilin merkitseminen arkaluonteiseksi
         silence: Tilin rajoittaminen
-        suspend: Tilin keskeyttäminen
+        suspend: Tilin jäädyttäminen
       your_appeal_approved: Valituksesi on hyväksytty
       your_appeal_pending: Olet lähettänyt valituksen
       your_appeal_rejected: Valituksesi on hylätty
@@ -1100,21 +1144,21 @@ fi:
     archive_takeout:
       date: Päiväys
       download: Lataa arkisto
-      hint_html: Voit pyytää arkistoa omista <strong>tuuttauksistasi ja mediastasi</strong>. Vientitiedot ovat ActivityPub-muodossa, ja ne voi lukea millä tahansa yhteensopivalla ohjelmalla.
+      hint_html: Voit pyytää arkistoa omista <strong>viesteistä ja mediasta</strong>. Viedyt tiedot ovat ActivityPub-muodossa, ja ne voi lukea millä tahansa yhteensopivalla ohjelmalla. Voit pyytää arkistoa viikon välein.
       in_progress: Arkistoa kootaan...
       request: Pyydä arkisto
       size: Koko
     blocks: Estot
     bookmarks: Kirjanmerkit
     csv: CSV
-    domain_blocks: Verkkotunnus estetty
+    domain_blocks: Verkkotunnusten estot
     lists: Listat
     mutes: Mykistetyt
     storage: Media-arkisto
   featured_tags:
     add_new: Lisää uusi
     errors:
-      limit: Olet jo lisännyt enimmäismäärän aihetunnisteita
+      limit: Olet jo nostanut esille enimmäismäärän aihetunnisteita
     hint_html: "<strong>Mitä ovat näkyvillä olevat hashtagit eli aihetunnisteet?</strong> Ne ovat näkyvissä julkisessa profiilissasi ja niiden avulla ihmiset voivat selata julkisia viestejäsi nimenomaan näiden aihetunnisteiden alla. Ne auttavat esimerkiksi luovan työn tai pitkäaikaisten projektien seurannassa."
   filters:
     contexts:
@@ -1126,11 +1170,11 @@ fi:
     edit:
       add_keyword: Lisää avainsana
       keywords: Avainsanat
-      statuses: Yksittäiset postaukset
-      statuses_hint_html: Tämä suodatin koskee yksittäisten postausten valintaa riippumatta siitä, vastaavatko ne alla olevia avainsanoja. <a href="%{path}">Tarkista tai poista viestit suodattimesta</a>.
+      statuses: Yksittäiset viestit
+      statuses_hint_html: Tämä suodatin koskee yksittäisten viestien valintaa riippumatta siitä, vastaavatko ne alla olevia avainsanoja. <a href="%{path}">Tarkista tai poista viestit suodattimesta</a>.
       title: Muokkaa suodatinta
     errors:
-      deprecated_api_multiple_keywords: Näitä parametreja ei voi muuttaa tästä sovelluksesta, koska ne koskevat useampaa kuin yhtä suodattimen avainsanaa. Käytä uudempaa sovellusta tai web-käyttöliittymää.
+      deprecated_api_multiple_keywords: Näitä parametreja ei voi muuttaa tästä sovelluksesta, koska ne koskevat useampaa kuin yhtä suodattimen avainsanaa. Käytä uudempaa sovellusta tai selainkäyttöliittymää.
       invalid_context: Ei sisältöä tai se on virheellinen
     index:
       contexts: Suodattimet %{contexts}
@@ -1158,15 +1202,13 @@ fi:
       index:
         hint: Tämä suodatin koskee yksittäisten viestien valintaa muista kriteereistä riippumatta. Voit lisätä lisää viestejä tähän suodattimeen web-käyttöliittymästä.
         title: Suodatetut viestit
-  footer:
-    trending_now: Suosittua nyt
   generic:
     all: Kaikki
     all_items_on_page_selected_html:
       one: "<strong>%{count}</strong> kohde tällä sivulla on valittu."
       other: Kaikki <strong>%{count}</strong> kohdetta tällä sivulla on valittu.
     all_matching_items_selected_html:
-      one: "<strong>%{count}</strong> tuotetta, joka vastaa hakuasi."
+      one: "<strong>%{count}</strong> kohde, joka vastaa hakuasi."
       other: Kaikki <strong>%{count}</strong> kohdetta, jotka vastaavat hakuasi.
     changes_saved_msg: Muutosten tallennus onnistui!
     copy: Kopioi
@@ -1176,14 +1218,12 @@ fi:
     order_by: Järjestä
     save_changes: Tallenna muutokset
     select_all_matching_items:
-      one: Valitse %{count} kohdetta, joka vastaa hakuasi.
+      one: Valitse %{count} kohde, joka vastaa hakuasi.
       other: Valitse kaikki %{count} kohdetta, jotka vastaavat hakuasi.
     today: tänään
     validation_errors:
       one: Kaikki ei ole aivan oikein! Tarkasta alla oleva virhe
       other: Kaikki ei ole aivan oikein! Tarkasta alla olevat %{count} virhettä
-  html_validator:
-    invalid_markup: 'sisältää virheellisen HTML-merkinnän: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Epäkelpo CSV-tiedosto. Virhe: %{error}'
@@ -1226,7 +1266,7 @@ fi:
     title: Kutsu ihmisiä
   lists:
     errors:
-      limit: Sinulla on jo suurin sallittu määrä listoja
+      limit: Sinulla on enimmäismäärä listoja
   login_activities:
     authentication_methods:
       otp: kaksivaiheinen todennussovellus
@@ -1287,7 +1327,7 @@ fi:
       report:
         subject: "%{name} lähetti raportin"
       sign_up:
-        subject: "%{name} kirjautunut"
+        subject: "%{name} rekisteröityi"
     favourite:
       body: "%{name} tykkäsi tilastasi:"
       subject: "%{name} tykkäsi tilastasi"
@@ -1309,9 +1349,9 @@ fi:
     poll:
       subject: Äänestys käyttäjältä %{name} on päättynyt
     reblog:
-      body: "%{name} buustasi tilaasi:"
-      subject: "%{name} boostasi tilaasi"
-      title: Uusi buustaus
+      body: "%{name} tehosti viestiäsi:"
+      subject: "%{name} tehosti viestiäsi"
+      title: Uusi tehostus
     status:
       subject: "%{name} julkaisi juuri"
     update:
@@ -1357,7 +1397,7 @@ fi:
       too_many_options: ei voi sisältää enempää kuin %{max} kohdetta
   preferences:
     other: Muut
-    posting_defaults: Julkaisujen oletusasetukset
+    posting_defaults: Viestien oletusasetukset
     public_timelines: Julkiset aikajanat
   privacy_policy:
     title: Tietosuojakäytäntö
@@ -1367,7 +1407,11 @@ fi:
       unrecognized_emoji: ei ole tunnistettu emoji
   relationships:
     activity: Tilin aktiivisuus
+    confirm_follow_selected_followers: Haluatko varmasti seurata valittuja seuraajia?
+    confirm_remove_selected_followers: Haluatko varmasti poistaa valitut seuraajat?
+    confirm_remove_selected_follows: Haluatko varmasti poistaa valitut seuraamiset?
     dormant: Horroksessa
+    follow_failure: Joitain valittuja tilejä ei voitu seurata.
     follow_selected_followers: Seuraa valittuja seuraajia
     followers: Seuraajat
     following: Seuratut
@@ -1388,7 +1432,7 @@ fi:
     errors:
       invalid_rules: ei viittaa voimassa oleviin sääntöihin
   rss:
-    content_warning: 'Sisällön varoitus:'
+    content_warning: 'Sisältövaroitus:'
     descriptions:
       account: Julkiset viestit lähettäjältä @%{acct}
       tag: 'Julkiset viestit merkitty #%{hashtag}'
@@ -1407,6 +1451,7 @@ fi:
       electron: Electron
       firefox: Firefox
       generic: Tuntematon selain
+      huawei_browser: Huawei-selain
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi -selain
@@ -1416,6 +1461,7 @@ fi:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Tuntematon selain
       weibo: Weibo
     current_session: Nykyinen istunto
     description: "%{browser} alustalla %{platform}"
@@ -1428,9 +1474,10 @@ fi:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: tuntematon alusta
+      unknown_platform: Tuntematon alusta
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1451,7 +1498,7 @@ fi:
     export: Vie tietoja
     featured_tags: Esitellyt aihetunnisteet
     import: Tuo
-    import_and_export: Tuo / Vie
+    import_and_export: Tuo ja vie
     migrate: Tilin muutto muualle
     notifications: Ilmoitukset
     preferences: Ominaisuudet
@@ -1473,7 +1520,7 @@ fi:
       video:
         one: "%{count} video"
         other: "%{count} videota"
-    boosted_from_html: Tehostettu %{acct_link}
+    boosted_from_html: Tehostus lähteestä %{acct_link}
     content_warning: 'Sisältövaroitus: %{warning}'
     default_language: Sama kuin käyttöliittymän kieli
     disallowed_hashtags:
@@ -1486,9 +1533,9 @@ fi:
     over_character_limit: merkkimäärän rajoitus %{max} ylitetty
     pin_errors:
       direct: Viestejä, jotka ovat näkyvissä vain mainituille käyttäjille, ei voi kiinnittää
-      limit: Olet jo kiinnittänyt suurimman mahdollisen määrän tuuttauksia
-      ownership: Muiden tuuttauksia ei voi kiinnittää
-      reblog: Buustausta ei voi kiinnittää
+      limit: Olet jo kiinnittänyt suurimman mahdollisen määrän viestejä
+      ownership: Muiden viestejä ei voi kiinnittää
+      reblog: Tehostusta ei voi kiinnittää
     poll:
       total_people:
         one: "%{count} henkilö"
@@ -1543,12 +1590,12 @@ fi:
       '7889238': 3 kuukautta
     min_age_label: Ikäraja
     min_favs: Pidä viestit suosikeissa vähintään
-    min_favs_hint: Ei poista yhtään julkaisuasi, jotka ovat saaneet vähintään tämän määrän tykkäyksiä. Jätä tyhjäksi, jos haluat poistaa julkaisuja tykkäyksien määrästä riippumatta
+    min_favs_hint: Toiminto ei poista julkaisujasi, joista on tykätty vähintään tässä kohtaa määritellyn monesti. Jätä kenttä tyhjäksi, jos haluat poistaa julkaisut tykkäyksistä huolimatta
     min_reblogs: Pidä viestit tehostettuna vähintään
     min_reblogs_hint: Ei poista yhtään viestiäsi, jota on tehostettu vähintään näin monta kertaa. Jätä tyhjäksi poistaaksesi viestejä riippumatta niiden tehosteiden määrästä
   stream_entries:
     pinned: Kiinnitetty tuuttaus
-    reblogged: buustasi
+    reblogged: tehosti
     sensitive_content: Arkaluontoista sisältöä
   strikes:
     errors:
@@ -1563,7 +1610,7 @@ fi:
     formats:
       default: "%d.%m.%Y klo %H.%M"
       month: "%b %Y"
-      time: "%H:%M"
+      time: "%H.%M"
   two_factor_authentication:
     add: Lisää
     disable: Poista käytöstä
@@ -1622,7 +1669,7 @@ fi:
         none: Varoitus %{acct}
         sensitive: Sinun viestisi %{acct} merkitään arkaluonteisiksi tästä lähtien
         silence: Tilisi %{acct} on rajoitettu
-        suspend: Tilisi %{acct} on keskeytetty
+        suspend: Tilisi %{acct} on jäädytetty
       title:
         delete_statuses: Viestit poistettu
         disable: Tili jäädytetty
@@ -1630,25 +1677,26 @@ fi:
         none: Varoitus
         sensitive: Tili on merkitty arkaluonteiseksi
         silence: Rajoitettu tili
-        suspend: Tilin käyttäminen keskeytetty
+        suspend: Tilin käyttäminen jäädytetty
     welcome:
-      edit_profile_action: Aseta profiili
-      edit_profile_step: Voit muokata profiiliasi lataamalla profiilikuvan, vaihtamalla näyttönimeä ja paljon muuta. Voit halutessasi arvioida uudet seuraajat ennen kuin he saavat seurata sinua.
+      edit_profile_action: Määritä profiili
+      edit_profile_step: Voit mukauttaa profiiliasi mm. profiilikuvalla ja uudella näyttönimellä. Voit myös valita haluatko tarkastaa ja hyväksyä uudet seuraajat itse.
       explanation: Näillä vinkeillä pääset alkuun
       final_action: Ala julkaista
-      final_step: 'Ala julkaista! Vaikkei sinulla olisi seuraajia, monet voivat nähdä julkiset viestisi esimerkiksi paikallisella aikajanalla ja aihetunnisteilla. Kannattaa myös esittäytyä! Käytä aihetunnistetta #esittely.'
+      final_step: 'Aloita julkaiseminen! Vaikkei sinulla ole seuraajia, voivat muut nähdä julkiset julkaisusi esimerkiksi paikallisella aikajanalla ja aihetunnisteilla. Kannattaa myös esittäytyä käyttämällä aihetunnistetta #introductions.'
       full_handle: Koko käyttäjätunnuksesi
-      full_handle_hint: Kerro tämä ystävillesi, niin he voivat lähettää sinulle viestejä tai löytää sinut toisen instanssin kautta.
+      full_handle_hint: Kerro tämä kavereillesi, niin he voivat lähettää sinulle viestejä tai löytää sinut muiden palvelimien kautta.
       subject: Tervetuloa Mastodoniin
       title: Tervetuloa mukaan, %{name}!
   users:
     follow_limit_reached: Et voi seurata yli %{limit} henkilöä
+    go_to_sso_account_settings: Avaa identiteettitarjoajasi tiliasetukset
     invalid_otp_token: Virheellinen kaksivaiheisen todentamisen koodi
     otp_lost_help_html: Jos sinulla ei ole pääsyä kumpaankaan, voit ottaa yhteyttä osoitteeseen %{email}
     seamless_external_login: Olet kirjautunut ulkoisen palvelun kautta, joten salasana- ja sähköpostiasetukset eivät ole käytettävissä.
-    signed_in_as: 'Kirjautunut henkilönä:'
+    signed_in_as: 'Kirjautunut tilillä:'
   verification:
-    explanation_html: 'Voit <strong>vahvistaa olevasi profiilisi metatiedoissa olevien linkkien omistaja.</strong>. Tätä varten linkitetyn verkkosivuston täytyy sisältää linkki takaisin Mastodon-profiiliisi. Palauttavalla linkillä <strong>täytyy</strong> olla <code>rel="me"</code>-arvo. Linkin tekstisisällöllä ei ole merkitystä. Tässä on esimerkki:'
+    explanation_html: 'Voit <strong>vahvistaa olevasi profiilisi metatietojen sisältämien linkkien omistaja</strong>. Tätä varten linkitetyn verkkosivuston täytyy sisältää paluulinkki Mastodon-profiiliisi. Paluulinkillä <strong>täytyy</strong> olla määre <code>rel="me"</code>. Linkin tekstisisällöllä ei ole merkitystä. Tässä esimerkki:'
     verification: Vahvistus
   webauthn_credentials:
     add: Lisää uusi suojausavain
diff --git a/config/locales/fo.yml b/config/locales/fo.yml
index 5145be40c..f969ac69e 100644
--- a/config/locales/fo.yml
+++ b/config/locales/fo.yml
@@ -91,6 +91,7 @@ fo:
       moderation:
         active: Virkin
         all: Allar
+        disabled: Óvirkið
         pending: Í bíðistøðu
         silenced: Avmarkað
         suspended: Avbrotin
@@ -133,6 +134,7 @@ fo:
       search: Leita
       search_same_email_domain: Aðrir brúkarir eru við sama teldupost domaini
       search_same_ip: Aðrir brúkarar við somu IP
+      security: Trygd
       security_measures:
         only_password: Bara loyniorð
         password_and_2fa: Loyniorð og 2FA
@@ -427,6 +429,7 @@ fo:
         resolve: Loys navnaøki
         title: Blokera nýtt teldupostanavnaøki
       no_email_domain_block_selected: Ongar teldupostanavnaøkisblokeringar vóru broyttar, tí ongar vóru valdar
+      not_permitted: Ikki loyvt
       resolved_dns_records_hint_html: Navnaøkið verður loyst til hesi MX navnaøki, sum í síðsta enda hava ábyrgdina av at móttaka teldupost. At blokera eitt MX navnaøki fer at blokera tilmeldingar frá einum og hvørjum teldupoststaði, sum brúkar sama MX navnaøki, sjálvt um sjónliga navnaøkið er eitt annað. <strong>Ansa eftir ikki at blokera stórar veitarar av telduposti</strong>
       resolved_through_html: Loyst gjøgnum %{domain}
       title: Blokeraði teldupostanavnaøki
@@ -441,6 +444,7 @@ fo:
         private_comment_description_html: Fyri at hjálpa tær at fylgja við í, hvaðani innfluttir blokkar koma, verða innfluttu blokkarnir stovnaðir við hesi privatu viðmerkingini:<q>%{comment}</q>
         private_comment_template: Innflutt frá %{source} tann %{date}
         title: Innflyt navnaøkjablokeringar
+      invalid_domain_block: 'Ein ella fleiri navnaøkjablokeringar vóru umlopnar vegna hesar feil(ir): %{error}'
       new:
         title: Innflyt navnaøkjablokeringar
       no_file: Eingin fíla vald
@@ -472,6 +476,7 @@ fo:
       content_policies:
         comment: Innanhýsis viðmerking
         description_html: Tú kanst áseta innihaldspolitikkir, sum verða áløgd øllum kontum frá hesum navnaøkinum og øllum tess undirnavnaøkjum.
+        limited_federation_mode_description_html: Tú kanst velja, hvørt tú loyvir sameining við hetta navnaøkið.
         policies:
           reject_media: Vraka miðil
           reject_reports: Vraka meldingar
@@ -575,19 +580,23 @@ fo:
         mark_as_sensitive_description_html: Miðlarnir í meldaðu postunum verða merkir sum viðkvæmir, og ein atsókn verður skrásett, soleiðis at tú veist, um onnur stig skulu takast, um sama konta ger fleiri brot í framtíðini.
         other_description_html: Sí fleiri valmøguleikar at stýra atferðini hjá kontuni og at tillagað samskiftið við meldaðu kontuni.
         resolve_description_html: Eingin atgerð verður tikin móti meldaðu kontuni, eingin atsókn verður skrásett og meldingin verður lukkað.
-        silence_description_html: Vangin verður einans sjónligur hjá teimum, sum longu fylgja honum ella leita eftir honum við hond, og tað minkar nógv um, hvussu langt hann røkkur. Kann altíð angrast.
-        suspend_description_html: Vangin og alt tilfar hjá vanganum verður óatkomandi inntil hann at enda verður strikaður. Tað verður ómøguligt at samvirka við hesa kontuna. Vendast kann aftur innan 30 dagar.
+        silence_description_html: Kontan verður einans sjónlig hjá teimum, sum longu fylgja henni ella leita eftir henni við hond, og tað minkar nógv um, hvussu langt hon røkkur. Kann altíð angrast. Lukkar allar rapporteringar um hesa kontuna.
+        suspend_description_html: Kontan og alt innihald hjá kontuni gerast óatkomulig og við tíðini strikaði, og tað verður ógjørligt at samvirka við henni. Kann angrast innan 30 dagar. Lukkar allar rapporteringar av hesi kontuni.
       actions_description_html: Ger av hvør atgerð skal takast fyri at avgreiða hesa meldingina. Revsitiltøk móti meldaðu kontuni føra við sær, at ein teldupostfráboðan verður send teimum, undantikið tá <strong>Ruskpostur</strong> verður valdur.
+      actions_description_remote_html: Tak avgerð um hvat skal gerast fyri at avgreiða hesa rapporteringina. Hetta fer einans at ávirka, hvussu <strong>tín</strong> ambætari samskiftir við hesa fjarkontuna og hvussu hann handfer tilfar frá henni.
       add_to_report: Legg meira afturat meldingini
       are_you_sure: Er tú vís/ur?
       assign_to_self: Tilluta mær
       assigned: Tilnevnt umsjónarfólk
       by_target_domain: Navnaøki hjá meldaðu kontuni
+      cancel: Angra
       category: Bólkur
       category_description_html: Orsøkin, at hendan kontan og/ella tilfarið var melda verður fráboðað í samskifti við meldaðu kontuni
       comment:
         none: Eingin
       comment_description_html: 'Fyri at veita fleiri upplýsingar skrivaði %{name}:'
+      confirm: Vátta
+      confirm_action: Vátta umsjónaratgerð móti @%{acct}
       created_at: Meldað
       delete_and_resolve: Strika postar
       forwarded: Víðarisent
@@ -604,6 +613,7 @@ fo:
         placeholder: Greið frá, hvørjar atgerðir hava verið gjørdar ella aðrar viðkomandi dagføringar...
         title: Viðmerkingar
       notes_description_html: Vís ella skriva viðmerkingar til onnur umsjónarfólk ella teg sjálva/n í framtíðini
+      processed_msg: 'Rapportering #%{id} liðugt viðgjørd'
       quick_actions_description_html: 'Tak eina skjóta avgerð ella skrulla niðureftir fyri at síggja meldaða innihaldið:'
       remote_user_placeholder: fjarbrúkarin frá %{instance}
       reopen: Lat melding uppaftur
@@ -616,9 +626,28 @@ fo:
       status: Støða
       statuses: Meldað innihald
       statuses_description_html: Tilfarið, sum brotið viðvíkur, fer at vera siterað í samskifti við meldaðu kontuni
+      summary:
+        action_preambles:
+          delete_html: 'Tú er í ferð við at <strong>strika</strong> nakrar av postunum hjá <strong>@%{acct}</strong>. Hetta fer at:'
+          mark_as_sensitive_html: 'Tú er í ferð við at <strong>merkja</strong> nakrar av postunum hjá <strong>@%{acct}</strong> sum <strong>viðkvæmar</strong>. Hetta fer at:'
+          silence_html: 'Tú er í ferð við at <strong>avmarka</strong> kontuna hjá <strong>@%{acct}</strong>. Hetta fer at:'
+          suspend_html: 'Tú er í ferð við at gera kontuna hjá <strong>@%{acct}</strong> <strong>óvirkna</strong>. Hetta fer at:'
+        actions:
+          delete_html: Strika postar, ið eru farnir útum mark
+          mark_as_sensitive_html: Merk postar, ið eru farnir um mark, sum viðkvæmar
+          silence_html: Avmarka hvussu langt <strong>@%{acct}</strong> røkkur við einans at lata vangan og innihaldið hjá teimum vera sjónligt hjá fólki, sum longu fylgja teimum ella manuelt sláa vangan upp
+          suspend_html: Ger kontuna <strong>@%{acct}</strong> óvirkna, soleiðis at vangin og innihaldið verður óframkomiligt og ómøguligt at samvirka við
+        close_report: 'Merk kæruna #%{id} sum loysta'
+        close_reports_html: Merk <strong>allar</strong> kærur móti <strong>@%{acct}</strong> sum loystar
+        delete_data_html: Strika vangan og innihaldið hjá <strong>@%{acct}</strong> um 30 dagar uttan so at kontan verður gjørd virkin aftur áðrenn tað
+        preview_preamble_html: "<strong>@%{acct}</strong> ger at móttaka eina ávaring við hesum innihaldi:"
+        record_strike_html: Skráset eitt brot á <strong>@%{acct}</strong>, soleiðis at tað kann havast í huga í samband við møgulig framtíðar mishald
+        send_email_html: Send <strong>@%{acct}</strong> eitt teldubræv við eini ávaring
+        warning_placeholder: Møguligar eyka grundgevingar fyri umsjónaratgerð.
       target_origin: Uppruni hjá meldaðu kontuni
       title: Meldingar
       unassign: Strika tillutan
+      unknown_action_msg: 'Ókend atgerð: %{action}'
       unresolved: Óloyst
       updated_at: Dagført
       view_profile: Vís vangamynd
@@ -713,6 +742,8 @@ fo:
         preamble: At fáa áhugavert innihald í ljósmála er avgerandi fyri at nýggir brúkarar, sum kanska ongan kenna á Mastodon, kunnu koma væl umborð. Stýr, hvussu hentleikarnir at uppdaga ymiskt rigga á ambætaranum hjá tær.
         profile_directory: Vangaskrá
         public_timelines: Almennar tíðarlinjur
+        publish_discovered_servers: Kunnger uppdagaðu ambætararnar
+        publish_statistics: Legg hagtøl út
         title: Uppdaging
         trends: Rák
       domain_blocks:
@@ -767,6 +798,7 @@ fo:
         suspend: "%{name} setti kontuna hjá %{target} úr gildi"
       appeal_approved: Kært
       appeal_pending: Kæra bíðar eftir avgerð
+      appeal_rejected: Kæra vrakað
     system_checks:
       database_schema_check:
         message_html: Dátugrunnaflytingar bíða. Vinarliga koyr flytingarnar fyri at tryggja at skipanin skikkar sær sum hon skal
@@ -780,6 +812,12 @@ fo:
         message_html: Tú hevur ikki ásett nakrar ambætarareglur.
       sidekiq_process_check:
         message_html: Eingin Sidekiq gongd koyrir fyri %{value} bíðirøðina(r). Vinarliga eftirkanna Sidekiq uppsetingina
+      upload_check_privacy_error:
+        action: Kekka her fyri at fáa fleiri upplýsingar
+        message_html: "<strong>Vevambætarin hjá tær er skeivt uppsettur. Privatlívið hjá brúkarunum hjá tær er í vanda.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Kekka her fyri at fáa fleiri upplýsingar
+        message_html: "<strong>Objekt-goymslan hjá tær er skeivt uppsett. Privatlívið hjá brúkarunum hjá tær er í vanda.</strong>"
     tags:
       review: Eftirkanna støðu
       updated_msg: Frámerkjastillingar dagførdar
@@ -802,6 +840,7 @@ fo:
           other: Deilt av %{count} persónum seinastu vikuna
         title: Vælumtókt leinki
         usage_comparison: Deilt %{today} ferð í dag, samanborið við %{yesterday} ferð í gjár
+      not_allowed_to_trend: Ikki loyvt at gerast rák
       only_allowed: Einans loyvd
       pending_review: Bíðar eftir eftirkannan
       preview_card_providers:
@@ -933,6 +972,7 @@ fo:
   applications:
     created: Umsókn stovnað
     destroyed: Umsókn strikað
+    logout: Rita út
     regenerate_token: Ger atgongdstekn av nýggjum
     token_regenerated: Atgongdsteknið gjørt av nýggjum
     warning: Ver varin við hesum dátum. Deil tað aldrin við nakran!
@@ -940,6 +980,8 @@ fo:
   auth:
     apply_for_account: Bið um eina kontu
     change_password: Loyniorð
+    confirmations:
+      wrong_email_hint: Um hesin teldupoststaðurin ikki er rættur, so kanst tú broyta hann í kontustillingunum.
     delete_account: Strika kontu
     delete_account_html: Ynskir tú at strika kontuna, so kanst tú <a href="%{path}">halda fram her</a>. Tú verður spurd/ur um váttan.
     description:
@@ -967,6 +1009,8 @@ fo:
     resend_confirmation: Send góðkenningarvegleiðing umaftur
     reset_password: Endurstilla loyniorð
     rules:
+      accept: Góðtak
+      back: Aftur
       preamble: Hesi eru ásett og uppihildin av umsjónarfólkunum á %{domain}.
       title: Nakrar grundreglur.
     security: Trygd
@@ -1158,8 +1202,6 @@ fo:
       index:
         hint: Hetta filtrið er galdandi fyri útvaldar stakar postar óansæð aðrar treytir. Tú kanst leggja fleiri postar afturat hesum filtrinum frá vevmarkamótinum.
         title: Filtreraðir postar
-  footer:
-    trending_now: Rák beint nú
   generic:
     all: Alt
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ fo:
     validation_errors:
       one: Okkurt er ikki heilt rætt enn! Vinarliga eftirhygg feilin niðanfyri
       other: Okkurt er ikki heilt rætt enn! Vinarliga eftirhygg teir %{count} feilirnar niðanfyri
-  html_validator:
-    invalid_markup: 'inniheldur ógyldugt HTML markup: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Ógildug CSV-fíla. Error: %{error}'
@@ -1367,7 +1407,11 @@ fo:
       unrecognized_emoji: er ikki eitt kenslutekn, sum kennist aftur
   relationships:
     activity: Kontuvirksemi
+    confirm_follow_selected_followers: Vil tú veruliga fylgja valdu fylgjarunum?
+    confirm_remove_selected_followers: Vil tú veruliga strika valdu fylgjararnar?
+    confirm_remove_selected_follows: Vil tú veruliga strika valdu fylgingarnar?
     dormant: Í dvala
+    follow_failure: Tað bar ikki til at fylgja øllum valdu kontunum.
     follow_selected_followers: Fylg valdu fylgjarum
     followers: Fylgjarar
     following: Fylgi
@@ -1407,6 +1451,7 @@ fo:
       electron: Electron
       firefox: Firefox
       generic: Ókendur kagi
+      huawei_browser: Huawei kagi
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1416,6 +1461,7 @@ fo:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Ókendur kagi
       weibo: Weibo
     current_session: Verandi seta
     description: "%{browser} á %{platform}"
@@ -1428,9 +1474,10 @@ fo:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: ókendur pallur
+      unknown_platform: Ókendur pallur
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1454,7 +1501,7 @@ fo:
     import_and_export: Innflyt og útflyt
     migrate: Flyting av kontu
     notifications: Fráboðanir
-    preferences: Sertokki
+    preferences: Stillingar
     profile: Vangi
     relationships: Fylging og fylgjarar
     statuses_cleanup: Sjálvvirkandi striking av postum
@@ -1643,12 +1690,13 @@ fo:
       title: Vælkomin umborð, %{name}!
   users:
     follow_limit_reached: Tú kanst ikki fylgja fleiri enn %{limit} fólk
+    go_to_sso_account_settings: Far til kontustillingarnar hjá samleikaveitaranum hjá tær
     invalid_otp_token: Ógyldug tvey-stigs koda
     otp_lost_help_html: Hevur tú mist atgongd til bæði, so kanst tú koma í samband við %{email}
     seamless_external_login: Tú er ritað/ur inn umvegis eina uttanhýsis tænastu, so loyniorð og teldupoststillingar eru ikki tøkar.
     signed_in_as: 'Ritað/ur inn sum:'
   verification:
-    explanation_html: 'Tú kanst <strong>vátta teg sjálva/n sum eigara av leinkjunum í metadátunum á vanganum hjá tær</strong>. Til tess má leinkjaða vevstaðið innihalda eitt leinki aftur til Mastodon vangan hjá tær. Leinkið <strong>má</strong> hava eina <code>rel="me"</code> viðseting. Tekstinnihaldið í leikinum er óviðkomandi. Her er eitt dømi:'
+    explanation_html: 'Tú kanst <strong>vátta teg sjálva/n sum eigara av leinkjunum í metadátunum á vanganum hjá tær</strong>. Til tess má leinkjaða vevstaðið innihalda eitt leinki aftur til Mastodon vangan hjá tær. Eftir at tú hevur lagt leinkið afturat, so er møguliga neyðugt at koma aftur her og goyma vangan hjá tær av nýggjum fyri at fáa góðkenningina at rigga. Leinkið <strong>má</strong> hava eina <code>rel="me"</code> viðseting. Tekstinnihaldið í leikinum er óviðkomandi. Her er eitt dømi:'
     verification: Váttan
   webauthn_credentials:
     add: Legg nýggjan trygdarlykil afturat
diff --git a/config/locales/fr-QC.yml b/config/locales/fr-QC.yml
index d48e188e4..27d400b9f 100644
--- a/config/locales/fr-QC.yml
+++ b/config/locales/fr-QC.yml
@@ -91,6 +91,7 @@ fr-QC:
       moderation:
         active: Actifs
         all: Tous
+        disabled: Désactivé
         pending: En cours de traitement
         silenced: Limité
         suspended: Suspendus
@@ -133,6 +134,7 @@ fr-QC:
       search: Rechercher
       search_same_email_domain: Autres utilisateurs·trices avec le même domaine de courriel
       search_same_ip: Autres utilisateur·rice·s avec la même IP
+      security: Sécurité
       security_measures:
         only_password: Mot de passe uniquement
         password_and_2fa: Mot de passe et 2FA
@@ -392,7 +394,7 @@ fr-QC:
         create: Créer le blocage
         hint: Le blocage de domaine n’empêchera pas la création de comptes dans la base de données, mais il appliquera automatiquement et rétrospectivement des méthodes de modération spécifiques sur ces comptes.
         severity:
-          desc_html: "<strong>Limiter</strong> rendra les messages des comptes de ce domaine invisibles à ceux qui ne les suivent pas. <strong>Suspendre</strong> supprimera tout le contenu, les médias, et données de profile pour les comptes de ce domaine de votre serveur. Utilisez <strong>Aucun</strong> si vous voulez simplement rejeter les fichiers multimédia."
+          desc_html: "<strong>Limiter</strong> rendra les messages des comptes de ce domaine invisibles pour tous les comptes qui ne les suivent pas. <strong>Suspendre</strong> supprimera de votre serveur tout le contenu, les médias et données de profil pour les comptes sur ce domaine. Utilisez <strong>Aucun</strong> si vous voulez simplement rejeter les fichiers multimédia."
           noop: Aucune
           silence: Limiter
           suspend: Suspendre
@@ -427,22 +429,24 @@ fr-QC:
         resolve: Résoudre le domaine
         title: Nouveau blocage de domaine de courriel
       no_email_domain_block_selected: Aucun blocage de domaine de courriel n'a été modifié car aucun n'a été sélectionné
+      not_permitted: Non autorisé
       resolved_dns_records_hint_html: Le nom de domaine est relié aux domaines MX suivants, qui ont la responsabilité ultime d'accepter les courriels. Bloquer un domaine MX empêchera les inscriptions à partir de toute adresse courriel utilisant le même domaine MX, même si le nom de domaine affiché est différent. <strong> Veillez à ne pas bloquer les fournisseurs de messagerie d'envergure.</strong>
       resolved_through_html: Résolu par %{domain}
       title: Blocage de domaines de courriel
     export_domain_allows:
       new:
-        title: Autoriser l'importation de domaine
+        title: Importer les autorisations de domaine
       no_file: Aucun fichier sélectionné
     export_domain_blocks:
       import:
-        description_html: Vous êtes sur le point d'importer une liste de blocs de domaine. Veuillez examiner cette liste très attentivement, spécialement si vous n'êtes pas l'auteur de cette liste.
-        existing_relationships_warning: Relations de suivi existantes
-        private_comment_description_html: 'Pour vous aider à suivre d''où viennent les blocs importés, des blocs importés seront créés avec le commentaire privé suivant : <q>%{comment}</q>'
+        description_html: Vous êtes sur le point d'importer une liste de bloqueurs de domaine. Veuillez examiner cette liste très attentivement, surtout si vous ne l'avez pas créée vous-même.
+        existing_relationships_warning: Relations d'abonnement existantes
+        private_comment_description_html: 'Pour vous aider à savoir d''où proviennent les blocages importés, ceux-ci seront créés avec le commentaire privé suivant : <q>%{comment}</q>'
         private_comment_template: Importé depuis %{source} le %{date}
-        title: Importer des blocs de domaine
+        title: Importer des blocages de domaine
+      invalid_domain_block: 'Un ou plusieurs blocages de domaine ont été ignorés en raison des erreurs suivantes : %{error}'
       new:
-        title: Importer des blocs de domaine
+        title: Importer des blocages de domaine
       no_file: Aucun fichier sélectionné
     follow_recommendations:
       description_html: "<strong>Les recommandations d'abonnement aident les nouvelles personnes à trouver rapidement du contenu intéressant</strong>. Si un·e utilisateur·rice n'a pas assez interagi avec les autres pour avoir des recommandations personnalisées, ces comptes sont alors recommandés. La sélection est mise à jour quotidiennement depuis un mélange de comptes ayant le plus d'interactions récentes et le plus grand nombre d'abonné·e·s locaux pour une langue donnée."
@@ -472,6 +476,7 @@ fr-QC:
       content_policies:
         comment: Note interne
         description_html: Vous pouvez définir des politiques de contenu qui seront appliquées à tous les comptes de ce domaine et à tous ses sous-domaines.
+        limited_federation_mode_description_html: Vous pouvez choisir d'autoriser la fédération avec ce domaine.
         policies:
           reject_media: Rejeter les médias
           reject_reports: Rejeter les signalements
@@ -575,19 +580,23 @@ fr-QC:
         mark_as_sensitive_description_html: Les médias des messages signalés seront marqués comme sensibles et une sanction sera enregistrée pour vous aider à prendre les mesures appropriées en cas d'infractions futures par le même compte.
         other_description_html: Voir plus d'options pour contrôler le comportement du compte et personnaliser la communication vers le compte signalé.
         resolve_description_html: Aucune mesure ne sera prise contre le compte signalé, aucune sanction ne sera enregistrée et le sigalement sera clôturé.
-        silence_description_html: Le profil ne sera visible que pour ceux qui le suivent déjà ou le consultent manuellement, ce qui limite considérablement sa portée. Peut toujours être restauré.
-        suspend_description_html: Le profil et tout son contenu deviendront inaccessibles jusqu'à ce qu'il soit éventuellement supprimé. Interagir avec le compte sera impossible. Réversible dans les 30 jours.
+        silence_description_html: Le compte ne sera visible que par ceux qui le suivent déjà ou qui le recherchent manuellement, ce qui limite fortement sa portée. Cette action peut toujours être annulée. Cloture tous les signalements concernant ce compte.
+        suspend_description_html: Le compte et tous ses contenus seront inaccessibles et finalement supprimés, et il sera impossible d'interagir avec lui. Réversible dans les 30 jours. Cloture tous les signalements concernant ce compte.
       actions_description_html: Décidez des mesures à prendre pour résoudre ce signalement. Si vous prenez des mesures punitives contre le compte signalé, une notification sera envoyée par e-mail, sauf si la catégorie <strong>Spam</strong> est sélectionnée.
+      actions_description_remote_html: Décidez des mesures à prendre pour résoudre ce signalement. Cela n'affectera que la manière dont <strong>votre</strong> serveur communique avec ce compte distant et traite son contenu.
       add_to_report: Ajouter davantage au rapport
       are_you_sure: Voulez-vous vraiment faire ça ?
       assign_to_self: Me l’assigner
       assigned: Modérateur assigné
       by_target_domain: Domaine du compte signalé
+      cancel: Annuler
       category: Catégorie
       category_description_html: La raison pour laquelle ce compte et/ou ce contenu a été signalé sera citée dans la communication avec le compte signalé
       comment:
         none: Aucun
       comment_description_html: 'Pour fournir plus d''informations, %{name} a écrit :'
+      confirm: Confirmer
+      confirm_action: Confirmer l'action de modération contre @%{acct}
       created_at: Signalé
       delete_and_resolve: Supprimer les messages
       forwarded: Transféré
@@ -604,6 +613,7 @@ fr-QC:
         placeholder: Décrivez quelles actions ont été prises, ou toute autre mise à jour…
         title: Remarques
       notes_description_html: Voir et laisser des notes aux autres modérateurs et à votre futur moi-même
+      processed_msg: 'Le signalement #%{id} a été traité avec succès'
       quick_actions_description_html: 'Faites une action rapide ou faites défiler vers le bas pour voir le contenu signalé :'
       remote_user_placeholder: l'utilisateur·rice distant·e de %{instance}
       reopen: Ré-ouvrir le signalement
@@ -616,9 +626,28 @@ fr-QC:
       status: Statut
       statuses: Contenu signalé
       statuses_description_html: Le contenu offensant sera cité dans la communication avec le compte signalé
+      summary:
+        action_preambles:
+          delete_html: 'Vous êtes sur le point de <strong>supprimer</strong> certains messages de <strong>@%{acct}</strong>. Cela va :'
+          mark_as_sensitive_html: 'Vous êtes sur le point de <strong>marquer</strong> certains messages de <strong>@%{acct}</strong> comme <strong>sensibles</strong>. Cela va :'
+          silence_html: 'Vous êtes sur le point de <strong>limiter</strong> le compte de <strong>@%{acct}</strong>. Cela va :'
+          suspend_html: 'Vous êtes sur le point de <strong>suspendre</strong> le compte de <strong>@%{acct}</strong>. Cela va :'
+        actions:
+          delete_html: supprimer les messages incriminés
+          mark_as_sensitive_html: marquer le média des messages incriminés comme sensible
+          silence_html: limiter drastiquement la portée du compte de <strong>@%{acct}</strong> en rendant son profil, ainsi que ses contenus, visibles uniquement des personnes déjà abonnées ou qui le recherchent manuellement
+          suspend_html: suspendre le compte de <strong>@%{acct}</strong>, ce qui empêche toute interaction avec son profil et tout accès à ses contenus
+        close_report: 'marquer le signalement #%{id} comme résolu'
+        close_reports_html: marquer <strong>tous</strong> les signalements de <strong>@%{acct}</strong> comme résolus
+        delete_data_html: effacer le profil de <strong>@%{acct}</strong> et ses contenus dans 30 jours, à moins que la suspension du compte ne soit annulée entre temps
+        preview_preamble_html: "<strong>@%{acct}</strong> recevra un avertissement contenant les éléments suivants :"
+        record_strike_html: enregistrer une sanction contre <strong>@%{acct}</strong> pour vous aider à prendre des mesures supplémentaires en cas d'infractions futures de ce compte
+        send_email_html: envoyer un courriel d'avertissement à <strong>@%{acct}</strong>
+        warning_placeholder: Arguments supplémentaires pour l'action de modération (facultatif).
       target_origin: Origine du compte signalé
       title: Signalements
       unassign: Dés-assigner
+      unknown_action_msg: 'Action inconnue : %{action}'
       unresolved: Non résolus
       updated_at: Mis à jour
       view_profile: Voir le profil
@@ -706,13 +735,15 @@ fr-QC:
         preamble: Contrôle comment le contenu créé par les utilisateurs est enregistré et stocké dans Mastodon.
         title: Rétention du contenu
       default_noindex:
-        desc_html: Affecte tous les utilisateurs qui n'ont pas modifié ce paramètre eux-mêmes
-        title: Ne pas indexer par défaut les utilisateurs dans les moteurs de recherche
+        desc_html: Affecte tous les comptes qui n'ont pas modifié ce paramètre
+        title: Par défaut, ne pas indexer les comptes dans les moteurs de recherche
       discovery:
         follow_recommendations: Suivre les recommandations
         preamble: Faire apparaître un contenu intéressant est essentiel pour interagir avec de nouveaux utilisateurs qui ne connaissent peut-être personne sur Mastodonte. Contrôlez le fonctionnement des différentes fonctionnalités de découverte sur votre serveur.
         profile_directory: Annuaire des profils
         public_timelines: Fils publics
+        publish_discovered_servers: Publier les serveurs découverts
+        publish_statistics: Publier les statistiques
         title: Découverte
         trends: Tendances
       domain_blocks:
@@ -767,6 +798,7 @@ fr-QC:
         suspend: "%{name} a suspendu le compte de %{target}"
       appeal_approved: Appel soumis
       appeal_pending: Appel en attente
+      appeal_rejected: Appel rejeté
     system_checks:
       database_schema_check:
         message_html: Vous avez des migrations de base de données en attente. Veuillez les exécuter pour vous assurer que l'application se comporte comme prévu
@@ -780,6 +812,12 @@ fr-QC:
         message_html: Vous n'avez pas défini de règles pour le serveur.
       sidekiq_process_check:
         message_html: Aucun processus Sidekiq en cours d'exécution pour la/les file(s) d'attente %{value}. Veuillez vérifier votre configuration de Sidekiq
+      upload_check_privacy_error:
+        action: Pour plus d'informations, cliquez ici
+        message_html: "<strong>Votre serveur web est mal configuré. La confidentialité de vos utilisateurs est en péril.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Pour plus d'informations, cliquez ici
+        message_html: "<strong>Votre serveur web est mal configuré. La confidentialité de vos utilisateurs est en péril.</strong>"
     tags:
       review: État du traitement
       updated_msg: Paramètres du hashtag mis à jour avec succès
@@ -802,6 +840,7 @@ fr-QC:
           other: Partagé par %{count} personnes au cours de la dernière semaine
         title: Liens tendances
         usage_comparison: Partagé %{today} fois aujourd'hui, comparé à %{yesterday} hier
+      not_allowed_to_trend: Non autorisé à apparaître dans les tendances
       only_allowed: Autorisées seulement
       pending_review: En attente de révision
       preview_card_providers:
@@ -933,6 +972,7 @@ fr-QC:
   applications:
     created: Application créée avec succès
     destroyed: Application supprimée avec succès
+    logout: Se déconnecter
     regenerate_token: Régénérer le jeton d’accès
     token_regenerated: Jeton d’accès régénéré avec succès
     warning: Soyez prudent·e avec ces données. Ne les partagez pas !
@@ -940,6 +980,8 @@ fr-QC:
   auth:
     apply_for_account: Demander un compte
     change_password: Mot de passe
+    confirmations:
+      wrong_email_hint: Si cette adresse de courriel est incorrecte, vous pouvez la modifier dans vos paramètres de compte.
     delete_account: Supprimer le compte
     delete_account_html: Si vous désirez supprimer votre compte, vous pouvez <a href="%{path}">cliquer ici</a>. Il vous sera demandé de confirmer cette action.
     description:
@@ -967,6 +1009,8 @@ fr-QC:
     resend_confirmation: Envoyer à nouveau les consignes de confirmation
     reset_password: Réinitialiser le mot de passe
     rules:
+      accept: Accepter
+      back: Retour
       preamble: Celles-ci sont définies et appliqués par les modérateurs de %{domain}.
       title: Quelques règles de base.
     security: Sécurité
@@ -976,7 +1020,7 @@ fr-QC:
       email_settings_hint_html: Le courriel de confirmation a été envoyé à %{email}. Si cette adresse de courriel n’est pas correcte, vous pouvez la modifier dans les paramètres du compte.
       title: Configuration
     sign_in:
-      preamble_html: Connectez-vous avec vos identifiants <strong>%{domain}</strong> . Si votre compte est hébergé sur un autre serveur, vous ne pourrez pas vous connecter ici.
+      preamble_html: Connectez-vous avec vos identifiants sur <strong>%{domain}</strong>. Si votre compte est hébergé sur un autre serveur, vous ne pourrez pas vous connecter ici.
       title: Se connecter à %{domain}
     sign_up:
       preamble: Avec un compte sur ce serveur Mastodon, vous serez en mesure de suivre toute autre personne sur le réseau, quel que soit l’endroit où son compte est hébergé.
@@ -1158,8 +1202,6 @@ fr-QC:
       index:
         hint: Ce filtre s'applique à la sélection de messages individuels, indépendamment d'autres critères. Vous pouvez ajouter plus de messages à ce filtre à partir de l'interface Web.
         title: Messages filtrés
-  footer:
-    trending_now: Tendance en ce moment
   generic:
     all: Tous
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ fr-QC:
     validation_errors:
       one: Quelque chose ne va pas ! Veuillez vérifiez l’erreur ci-dessous
       other: Certaines choses ne vont pas ! Veuillez vérifier les %{count} erreurs ci-dessous
-  html_validator:
-    invalid_markup: 'contient un balisage HTML invalide: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Fichier CSV non valide. Erreur : %{error}'
@@ -1367,7 +1407,11 @@ fr-QC:
       unrecognized_emoji: n’est pas un émoji reconnu
   relationships:
     activity: Activité du compte
+    confirm_follow_selected_followers: Voulez-vous vraiment suivre les abonné⋅e⋅s sélectionné⋅e⋅s ?
+    confirm_remove_selected_followers: Voulez-vous vraiment supprimer les abonné⋅e⋅s sélectionné⋅e⋅s ?
+    confirm_remove_selected_follows: Voulez-vous vraiment supprimer les abonnements sélectionnés ?
     dormant: Dormant
+    follow_failure: Impossibilité de suivre certains des comptes sélectionnés.
     follow_selected_followers: Suivre les abonné·e·s sélectionné·e·s
     followers: Abonné·e
     following: Abonnement
@@ -1407,6 +1451,7 @@ fr-QC:
       electron: Electron
       firefox: Firefox
       generic: Navigateur inconnu
+      huawei_browser: Navigateur Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1416,6 +1461,7 @@ fr-QC:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Navigateur inconnu
       weibo: Weibo
     current_session: Session courante
     description: "%{browser} sur %{platform}"
@@ -1428,9 +1474,10 @@ fr-QC:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: système inconnu
+      unknown_platform: Plateforme inconnue
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1643,6 +1690,7 @@ fr-QC:
       title: Bienvenue à bord, %{name} !
   users:
     follow_limit_reached: Vous ne pouvez pas suivre plus de %{limit} personnes
+    go_to_sso_account_settings: Accédez aux paramètres du compte de votre fournisseur d'identité
     invalid_otp_token: Le code d’authentification à deux facteurs est invalide
     otp_lost_help_html: Si vous perdez accès aux deux, vous pouvez contacter %{email}
     seamless_external_login: Vous êtes connecté via un service externe, donc les paramètres concernant le mot de passe et le courriel ne sont pas disponibles.
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index c40fd9317..667cd9b97 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -38,12 +38,12 @@ fr:
       avatar: Avatar
       by_domain: Domaine
       change_email:
-        changed_msg: Courriel modifié avec succès !
-        current_email: Courriel actuel
-        label: Modifier le courriel
-        new_email: Nouveau courriel
+        changed_msg: Adresse de courriel modifiée avec succès !
+        current_email: Adresse de courriel actuelle
+        label: Modifier l’adresse de courriel
+        new_email: Nouvelle adresse de courriel
         submit: Modifier le courriel
-        title: Modifier le courriel pour %{username}
+        title: Modifier l’adresse de courriel pour %{username}
       change_role:
         changed_msg: Rôle modifié avec succès !
         label: Modifier le rôle
@@ -64,7 +64,7 @@ fr:
       display_name: Nom affiché
       domain: Domaine
       edit: Éditer
-      email: Courriel
+      email: Adresse de courriel
       email_status: État du courriel
       enable: Dégeler
       enable_sign_in_token_auth: Activer l'authentification basée sur les jetons envoyés par courriel
@@ -91,6 +91,7 @@ fr:
       moderation:
         active: Actifs
         all: Tous
+        disabled: Désactivé
         pending: En cours de traitement
         silenced: Limité
         suspended: Suspendus
@@ -133,6 +134,7 @@ fr:
       search: Rechercher
       search_same_email_domain: Autres utilisateurs·trices avec le même domaine de courriel
       search_same_ip: Autres utilisateur·rice·s avec la même IP
+      security: Sécurité
       security_measures:
         only_password: Mot de passe uniquement
         password_and_2fa: Mot de passe et A2F
@@ -152,9 +154,9 @@ fr:
       suspension_irreversible: Les données de ce compte ont été irréversiblement supprimées. Vous pouvez annuler la suspension du compte pour le rendre utilisable, mais il ne récupérera aucune donnée qu’il avait auparavant.
       suspension_reversible_hint_html: Le compte a été suspendu et les données seront complètement supprimées le %{date}. D’ici là, le compte peut être restauré sans aucun effet néfaste. Si vous souhaitez supprimer toutes les données du compte immédiatement, vous pouvez le faire ci-dessous.
       title: Comptes
-      unblock_email: Débloquer l'adresse courriel
-      unblocked_email_msg: L'adresse courriel de %{username} a été débloquée avec succès
-      unconfirmed_email: Courriel non confirmé
+      unblock_email: Débloquer l’adresse de courriel
+      unblocked_email_msg: L’adresse de courriel de %{username} a été débloquée avec succès
+      unconfirmed_email: Adresse de courriel non confirmée
       undo_sensitized: Annuler sensible
       undo_silenced: Annuler la limitation
       undo_suspension: Annuler la suspension
@@ -169,11 +171,11 @@ fr:
     action_logs:
       action_types:
         approve_appeal: Approuver l'appel
-        approve_user: Approuver l’utilisateur
+        approve_user: Approuver le compte
         assigned_to_self_report: Affecter le signalement
-        change_email_user: Modifier le courriel pour ce compte
-        change_role_user: Changer le rôle de l’utilisateur·rice
-        confirm_user: Confirmer l’utilisateur
+        change_email_user: Modifier l’adresse de courriel pour ce compte
+        change_role_user: Changer le rôle du compte
+        confirm_user: Confirmer le compte
         create_account_warning: Créer une alerte
         create_announcement: Créer une annonce
         create_canonical_email_block: Créer un blocage de domaine de courriel
@@ -202,11 +204,11 @@ fr:
         disable_user: Désactiver le compte
         enable_custom_emoji: Activer les émojis personnalisées
         enable_sign_in_token_auth_user: Activer l'authentification basée sur les jetons envoyés par courriel pour l'utilisateur·rice
-        enable_user: Activer l’utilisateur
+        enable_user: Activer le compte
         memorialize_account: Ériger en mémorial
-        promote_user: Promouvoir l’utilisateur
+        promote_user: Promouvoir le compte
         reject_appeal: Rejeter l'appel
-        reject_user: Rejeter l’utilisateur
+        reject_user: Rejeter le compte
         remove_avatar_user: Supprimer l’avatar
         reopen_report: Rouvrir le signalement
         resend_user: Renvoyer l'e-mail de confirmation
@@ -216,7 +218,7 @@ fr:
         silence_account: Limiter le compte
         suspend_account: Suspendre le compte
         unassigned_report: Ne plus assigner le signalement
-        unblock_email_account: Débloquer l'adresse courriel
+        unblock_email_account: Débloquer l’adresse de courriel
         unsensitive_account: Ne pas marquer les médias de votre compte comme sensibles
         unsilence_account: Annuler la limitation du compte
         unsuspend_account: Annuler la suspension du compte
@@ -235,7 +237,7 @@ fr:
         confirm_user_html: "%{name} a confirmé l'adresse courriel de l'utilisateur %{target}"
         create_account_warning_html: "%{name} a envoyé un avertissement à %{target}"
         create_announcement_html: "%{name} a créé une nouvelle annonce %{target}"
-        create_canonical_email_block_html: "%{name} a bloqué l’e-mail avec le hachage %{target}"
+        create_canonical_email_block_html: "%{name} a bloqué le courriel avec le hachage %{target}"
         create_custom_emoji_html: "%{name} a téléversé un nouvel émoji %{target}"
         create_domain_allow_html: "%{name} a autorisé la fédération avec le domaine %{target}"
         create_domain_block_html: "%{name} a bloqué le domaine %{target}"
@@ -245,7 +247,7 @@ fr:
         create_user_role_html: "%{name} a créé le rôle %{target}"
         demote_user_html: "%{name} a rétrogradé l'utilisateur·rice %{target}"
         destroy_announcement_html: "%{name} a supprimé l'annonce %{target}"
-        destroy_canonical_email_block_html: "%{name} a débloqué l'email avec le hash %{target}"
+        destroy_canonical_email_block_html: "%{name} a débloqué le courriel avec le hachage %{target}"
         destroy_custom_emoji_html: "%{name} a supprimé l'émoji %{target}"
         destroy_domain_allow_html: "%{name} a rejeté la fédération avec le domaine %{target}"
         destroy_domain_block_html: "%{name} a débloqué le domaine %{target}"
@@ -275,7 +277,7 @@ fr:
         silence_account_html: "%{name} a limité le compte de %{target}"
         suspend_account_html: "%{name} a suspendu le compte de %{target}"
         unassigned_report_html: "%{name} a désassigné le signalement %{target}"
-        unblock_email_account_html: "%{name} a débloqué l'adresse courriel de %{target}"
+        unblock_email_account_html: "%{name} a débloqué l’adresse de courriel de %{target}"
         unsensitive_account_html: "%{name} a enlevé le marquage comme sensible du média de %{target}"
         unsilence_account_html: "%{name} a annulé la limitation du compte de %{target}"
         unsuspend_account_html: "%{name} a réactivé le compte de %{target}"
@@ -342,10 +344,10 @@ fr:
       updated_msg: Émoji mis à jour avec succès !
       upload: Téléverser
     dashboard:
-      active_users: utilisateurs actifs
+      active_users: comptes actifs
       interactions: interactions
       media_storage: Stockage des médias
-      new_users: nouveaux utilisateurs
+      new_users: nouveaux comptes
       opened_reports: rapports ouverts
       pending_appeals_html:
         one: "<strong>%{count}</strong> appel en attente"
@@ -392,13 +394,13 @@ fr:
         create: Créer le blocage
         hint: Le blocage de domaine n’empêchera pas la création de comptes dans la base de données, mais il appliquera automatiquement et rétrospectivement des méthodes de modération spécifiques sur ces comptes.
         severity:
-          desc_html: "<strong>Limiter</strong> rendra les messages des comptes de ce domaine invisibles à ceux qui ne les suivent pas. <strong>Suspendre</strong> supprimera tout le contenu, les médias, et données de profile pour les comptes de ce domaine de votre serveur. Utilisez <strong>Aucun</strong> si vous voulez simplement rejeter les fichiers multimédia."
+          desc_html: "<strong>Limiter</strong> rendra les messages des comptes de ce domaine invisibles pour tous les comptes qui ne les suivent pas. <strong>Suspendre</strong> supprimera de votre serveur tout le contenu, les médias et données de profil pour les comptes sur ce domaine. Utilisez <strong>Aucun</strong> si vous voulez simplement rejeter les fichiers multimédia."
           noop: Aucune
           silence: Limiter
           suspend: Suspendre
         title: Nouveau blocage de domaine
       no_domain_block_selected: Aucun blocage de domaine n'a été modifié car aucun n'a été sélectionné
-      not_permitted: Vous n’êtes pas autorisé à effectuer cette action
+      not_permitted: Vous n’êtes pas autorisé⋅e à effectuer cette action
       obfuscate: Obfusquer le nom de domaine
       obfuscate_hint: Obfusquer partiellement le nom de domaine dans la liste si la publication de la liste des limitations de domaine est activée
       private_comment: Commentaire privé
@@ -427,22 +429,24 @@ fr:
         resolve: Résoudre le domaine
         title: Nouveau blocage de domaine de courriel
       no_email_domain_block_selected: Aucun blocage de domaine de courriel n'a été modifié car aucun n'a été sélectionné
+      not_permitted: Non autorisé
       resolved_dns_records_hint_html: Le nom de domaine est relié aux domaines MX suivants, qui ont la responsabilité ultime d'accepter les courriels. Bloquer un domaine MX empêchera les inscriptions à partir de toute adresse courriel utilisant le même domaine MX, même si le nom de domaine affiché est différent. <strong> Veillez à ne pas bloquer les fournisseurs de messagerie d'envergure.</strong>
       resolved_through_html: Résolu par %{domain}
       title: Blocage de domaines de courriel
     export_domain_allows:
       new:
-        title: Autoriser l'importation de domaine
+        title: Importer les autorisations de domaine
       no_file: Aucun fichier sélectionné
     export_domain_blocks:
       import:
-        description_html: Vous êtes sur le point d'importer une liste de blocs de domaine. Veuillez examiner cette liste très attentivement, spécialement si vous n'êtes pas l'auteur de cette liste.
-        existing_relationships_warning: Relations de suivi existantes
-        private_comment_description_html: 'Pour vous aider à suivre d''où viennent les blocs importés, des blocs importés seront créés avec le commentaire privé suivant : <q>%{comment}</q>'
+        description_html: Vous êtes sur le point d'importer une liste de bloqueurs de domaine. Veuillez examiner cette liste très attentivement, surtout si vous ne l'avez pas créée vous-même.
+        existing_relationships_warning: Relations d'abonnement existantes
+        private_comment_description_html: 'Pour vous aider à savoir d''où proviennent les blocages importés, ceux-ci seront créés avec le commentaire privé suivant : <q>%{comment}</q>'
         private_comment_template: Importé depuis %{source} le %{date}
-        title: Importer des blocs de domaine
+        title: Importer des blocages de domaine
+      invalid_domain_block: 'Un ou plusieurs blocages de domaine ont été ignorés en raison des erreurs suivantes : %{error}'
       new:
-        title: Importer des blocs de domaine
+        title: Importer des blocages de domaine
       no_file: Aucun fichier sélectionné
     follow_recommendations:
       description_html: "<strong>Les recommandations d'abonnement aident les nouvelles personnes à trouver rapidement du contenu intéressant</strong>. Si un·e utilisateur·rice n'a pas assez interagi avec les autres pour avoir des recommandations personnalisées, ces comptes sont alors recommandés. La sélection est mise à jour quotidiennement depuis un mélange de comptes ayant le plus d'interactions récentes et le plus grand nombre d'abonné·e·s locaux pour une langue donnée."
@@ -472,6 +476,7 @@ fr:
       content_policies:
         comment: Note interne
         description_html: Vous pouvez définir des politiques de contenu qui seront appliquées à tous les comptes de ce domaine et à tous ses sous-domaines.
+        limited_federation_mode_description_html: Vous pouvez choisir d'autoriser la fédération avec ce domaine.
         policies:
           reject_media: Rejeter les médias
           reject_reports: Rejeter les signalements
@@ -557,7 +562,7 @@ fr:
       pending: En attente de l’approbation du relai
       save_and_enable: Sauvegarder et activer
       setup: Paramétrer une connexion de relais
-      signatures_not_enabled: Les relais ne fonctionneront pas correctement lorsque le mode sécurisé ou le mode liste blanche est activé
+      signatures_not_enabled: Les relais peuvent ne pas fonctionner correctement lorsque les modes sécurisé ou de fédération limitée sont activés
       status: Statut
       title: Relais
     report_notes:
@@ -575,19 +580,23 @@ fr:
         mark_as_sensitive_description_html: Les médias des messages signalés seront marqués comme sensibles et une sanction sera enregistrée pour vous aider à prendre les mesures appropriées en cas d'infractions futures par le même compte.
         other_description_html: Voir plus d'options pour contrôler le comportement du compte et personnaliser la communication vers le compte signalé.
         resolve_description_html: Aucune mesure ne sera prise contre le compte signalé, aucune sanction ne sera enregistrée et le sigalement sera clôturé.
-        silence_description_html: Le profil ne sera visible que pour ceux qui le suivent déjà ou le consultent manuellement, ce qui limite considérablement sa portée. Peut toujours être restauré.
-        suspend_description_html: Le profil et tout son contenu deviendront inaccessibles jusqu'à ce qu'il soit éventuellement supprimé. Interagir avec le compte sera impossible. Réversible dans les 30 jours.
+        silence_description_html: Le compte ne sera visible que par ceux qui le suivent déjà ou qui le recherchent manuellement, ce qui limite fortement sa portée. Cette action peut toujours être annulée. Cloture tous les signalements concernant ce compte.
+        suspend_description_html: Le compte et tous ses contenus seront inaccessibles et finalement supprimés, et il sera impossible d'interagir avec lui. Réversible dans les 30 jours. Cloture tous les signalements concernant ce compte.
       actions_description_html: Décidez des mesures à prendre pour résoudre ce signalement. Si vous prenez des mesures punitives contre le compte signalé, une notification sera envoyée par e-mail, sauf si la catégorie <strong>Spam</strong> est sélectionnée.
+      actions_description_remote_html: Décidez des mesures à prendre pour résoudre ce signalement. Cela n'affectera que la manière dont <strong>votre</strong> serveur communique avec ce compte distant et traite son contenu.
       add_to_report: Ajouter davantage au rapport
       are_you_sure: Voulez-vous vraiment faire ça ?
       assign_to_self: Me l’assigner
       assigned: Modérateur assigné
       by_target_domain: Domaine du compte signalé
+      cancel: Annuler
       category: Catégorie
       category_description_html: La raison pour laquelle ce compte et/ou ce contenu a été signalé sera citée dans la communication avec le compte signalé
       comment:
         none: Aucun
       comment_description_html: 'Pour fournir plus d''informations, %{name} a écrit :'
+      confirm: Confirmer
+      confirm_action: Confirmer l'action de modération contre @%{acct}
       created_at: Signalé
       delete_and_resolve: Supprimer les messages
       forwarded: Transféré
@@ -604,6 +613,7 @@ fr:
         placeholder: Décrivez quelles actions ont été prises, ou toute autre mise à jour…
         title: Remarques
       notes_description_html: Voir et laisser des notes aux autres modérateurs et à votre futur moi-même
+      processed_msg: 'Le signalement #%{id} a été traité avec succès'
       quick_actions_description_html: 'Faites une action rapide ou faites défiler vers le bas pour voir le contenu signalé :'
       remote_user_placeholder: l'utilisateur·rice distant·e de %{instance}
       reopen: Ré-ouvrir le signalement
@@ -616,9 +626,28 @@ fr:
       status: Statut
       statuses: Contenu signalé
       statuses_description_html: Le contenu offensant sera cité dans la communication avec le compte signalé
+      summary:
+        action_preambles:
+          delete_html: 'Vous êtes sur le point de <strong>supprimer</strong> certains messages de <strong>@%{acct}</strong>. Cela va :'
+          mark_as_sensitive_html: 'Vous êtes sur le point de <strong>marquer</strong> certains messages de <strong>@%{acct}</strong> comme <strong>sensibles</strong>. Cela va :'
+          silence_html: 'Vous êtes sur le point de <strong>limiter</strong> le compte de <strong>@%{acct}</strong>. Cela va :'
+          suspend_html: 'Vous êtes sur le point de <strong>suspendre</strong> le compte de <strong>@%{acct}</strong>. Cela va :'
+        actions:
+          delete_html: supprimer les messages incriminés
+          mark_as_sensitive_html: marquer le média des messages incriminés comme sensible
+          silence_html: limiter drastiquement la portée du compte de <strong>@%{acct}</strong> en rendant son profil, ainsi que ses contenus, visibles uniquement des personnes déjà abonnées ou qui le recherchent manuellement
+          suspend_html: suspendre le compte de <strong>@%{acct}</strong>, ce qui empêche toute interaction avec son profil et tout accès à ses contenus
+        close_report: 'marquer le signalement #%{id} comme résolu'
+        close_reports_html: marquer <strong>tous</strong> les signalements de <strong>@%{acct}</strong> comme résolus
+        delete_data_html: effacer le profil de <strong>@%{acct}</strong> et ses contenus dans 30 jours, à moins que la suspension du compte ne soit annulée entre temps
+        preview_preamble_html: "<strong>@%{acct}</strong> recevra un avertissement contenant les éléments suivants :"
+        record_strike_html: enregistrer une sanction contre <strong>@%{acct}</strong> pour vous aider à prendre des mesures supplémentaires en cas d'infractions futures de ce compte
+        send_email_html: envoyer un courriel d'avertissement à <strong>@%{acct}</strong>
+        warning_placeholder: Arguments supplémentaires pour l'action de modération (facultatif).
       target_origin: Origine du compte signalé
       title: Signalements
       unassign: Dés-assigner
+      unknown_action_msg: 'Action inconnue : %{action}'
       unresolved: Non résolus
       updated_at: Mis à jour
       view_profile: Voir le profil
@@ -706,13 +735,15 @@ fr:
         preamble: Contrôle comment le contenu créé par les utilisateurs est enregistré et stocké dans Mastodon.
         title: Rétention du contenu
       default_noindex:
-        desc_html: Affecte tous les utilisateurs qui n'ont pas modifié ce paramètre eux-mêmes
-        title: Ne pas indexer par défaut les utilisateurs dans les moteurs de recherche
+        desc_html: Affecte tous les comptes qui n'ont pas modifié ce paramètre
+        title: Par défaut, ne pas indexer les comptes dans les moteurs de recherche
       discovery:
         follow_recommendations: Suivre les recommandations
-        preamble: Faire apparaître un contenu intéressant est essentiel pour interagir avec de nouveaux utilisateurs qui ne connaissent peut-être personne sur Mastodonte. Contrôlez le fonctionnement des différentes fonctionnalités de découverte sur votre serveur.
+        preamble: Il est essentiel de donner de la visibilité à des contenus intéressants pour attirer des utilisateur⋅rice⋅s néophytes qui ne connaissent peut-être personne sur Mastodon. Contrôlez le fonctionnement des différentes fonctionnalités de découverte sur votre serveur.
         profile_directory: Annuaire des profils
         public_timelines: Fils publics
+        publish_discovered_servers: Publier les serveurs découverts
+        publish_statistics: Publier les statistiques
         title: Découverte
         trends: Tendances
       domain_blocks:
@@ -767,6 +798,7 @@ fr:
         suspend: "%{name} a suspendu le compte de %{target}"
       appeal_approved: Appel soumis
       appeal_pending: Appel en attente
+      appeal_rejected: Appel rejeté
     system_checks:
       database_schema_check:
         message_html: Vous avez des migrations de base de données en attente. Veuillez les exécuter pour vous assurer que l'application se comporte comme prévu
@@ -780,6 +812,12 @@ fr:
         message_html: Vous n'avez pas défini de règles pour le serveur.
       sidekiq_process_check:
         message_html: Aucun processus Sidekiq en cours d'exécution pour la/les file(s) d'attente %{value}. Veuillez vérifier votre configuration de Sidekiq
+      upload_check_privacy_error:
+        action: Pour plus d'informations, cliquez ici
+        message_html: "<strong>Votre serveur web est mal configuré. La confidentialité de vos utilisateurs est en péril.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Pour plus d'informations, cliquez ici
+        message_html: "<strong>Votre serveur web est mal configuré. La confidentialité de vos utilisateurs est en péril.</strong>"
     tags:
       review: État du traitement
       updated_msg: Paramètres du hashtag mis à jour avec succès
@@ -802,6 +840,7 @@ fr:
           other: Partagé par %{count} personnes au cours de la dernière semaine
         title: Liens tendances
         usage_comparison: Partagé %{today} fois aujourd'hui, comparé à %{yesterday} hier
+      not_allowed_to_trend: Non autorisé à apparaître dans les tendances
       only_allowed: Autorisées seulement
       pending_review: En attente de révision
       preview_card_providers:
@@ -816,7 +855,7 @@ fr:
         description_html: Voici les messages dont votre serveur a connaissance qui sont beaucoup partagés et mis en favoris en ce moment. Cela peut aider vos utilisateur⋅rice⋅s, néophytes comme aguerri⋅e⋅s, à trouver plus de comptes à suivre. Aucun message n'est publiquement affiché tant que vous n'en avez pas approuvé l'auteur⋅rice, et seulement si icellui permet que son compte soit suggéré aux autres. Vous pouvez également autoriser ou rejeter les messages individuellement.
         disallow: Proscrire le message
         disallow_account: Proscrire l'auteur·rice
-        no_status_selected: Aucune publication en tendance n'a été changée car aucune n'a été sélectionnée
+        no_status_selected: Aucun message tendance n'a été modifié car aucun n'était sélectionné
         not_discoverable: L'auteur⋅rice n'a pas choisi de pouvoir être découvert⋅e
         shared_by:
           one: Partagé ou ajouté aux favoris une fois
@@ -933,6 +972,7 @@ fr:
   applications:
     created: Application créée avec succès
     destroyed: Application supprimée avec succès
+    logout: Se déconnecter
     regenerate_token: Régénérer le jeton d’accès
     token_regenerated: Jeton d’accès régénéré avec succès
     warning: Soyez prudent·e avec ces données. Ne les partagez pas !
@@ -940,6 +980,8 @@ fr:
   auth:
     apply_for_account: Demander un compte
     change_password: Mot de passe
+    confirmations:
+      wrong_email_hint: Si cette adresse de courriel est incorrecte, vous pouvez la modifier dans vos paramètres de compte.
     delete_account: Supprimer le compte
     delete_account_html: Si vous désirez supprimer votre compte, vous pouvez <a href="%{path}">cliquer ici</a>. Il vous sera demandé de confirmer cette action.
     description:
@@ -967,6 +1009,8 @@ fr:
     resend_confirmation: Envoyer à nouveau les consignes de confirmation
     reset_password: Réinitialiser le mot de passe
     rules:
+      accept: Accepter
+      back: Retour
       preamble: Celles-ci sont définies et appliqués par les modérateurs de %{domain}.
       title: Quelques règles de base.
     security: Sécurité
@@ -976,7 +1020,7 @@ fr:
       email_settings_hint_html: Le courriel de confirmation a été envoyé à %{email}. Si cette adresse de courriel n’est pas correcte, vous pouvez la modifier dans les paramètres du compte.
       title: Configuration
     sign_in:
-      preamble_html: Connectez-vous avec vos identifiants <strong>%{domain}</strong> . Si votre compte est hébergé sur un autre serveur, vous ne pourrez pas vous connecter ici.
+      preamble_html: Connectez-vous avec vos identifiants sur <strong>%{domain}</strong>. Si votre compte est hébergé sur un autre serveur, vous ne pourrez pas vous connecter ici.
       title: Se connecter à %{domain}
     sign_up:
       preamble: Avec un compte sur ce serveur Mastodon, vous serez en mesure de suivre toute autre personne sur le réseau, quel que soit l’endroit où son compte est hébergé.
@@ -1032,7 +1076,7 @@ fr:
   deletes:
     challenge_not_passed: Les renseignements que vous avez entrés n'étaient pas exacts
     confirm_password: Entrez votre mot de passe actuel pour vérifier votre identité
-    confirm_username: Entrez votre nom d'utilisateur pour confirmer la procédure
+    confirm_username: Entrez votre identifiant pour confirmer la procédure
     proceed: Supprimer le compte
     success_msg: Votre compte a été supprimé avec succès
     warning:
@@ -1158,8 +1202,6 @@ fr:
       index:
         hint: Ce filtre s'applique à la sélection de messages individuels, indépendamment d'autres critères. Vous pouvez ajouter plus de messages à ce filtre à partir de l'interface Web.
         title: Messages filtrés
-  footer:
-    trending_now: Tendance en ce moment
   generic:
     all: Tous
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ fr:
     validation_errors:
       one: Quelque chose ne va pas ! Veuillez vérifiez l’erreur ci-dessous
       other: Certaines choses ne vont pas ! Veuillez vérifier les %{count} erreurs ci-dessous
-  html_validator:
-    invalid_markup: 'contient un balisage HTML invalide: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Fichier CSV non valide. Erreur : %{error}'
@@ -1367,7 +1407,11 @@ fr:
       unrecognized_emoji: n’est pas un émoji reconnu
   relationships:
     activity: Activité du compte
+    confirm_follow_selected_followers: Voulez-vous vraiment suivre les abonné⋅e⋅s sélectionné⋅e⋅s ?
+    confirm_remove_selected_followers: Voulez-vous vraiment supprimer les abonné⋅e⋅s sélectionné⋅e⋅s ?
+    confirm_remove_selected_follows: Voulez-vous vraiment supprimer les abonnements sélectionnés ?
     dormant: Dormant
+    follow_failure: Impossibilité de suivre certains des comptes sélectionnés.
     follow_selected_followers: Suivre les abonné·e·s sélectionné·e·s
     followers: Abonné·e
     following: Abonnement
@@ -1407,6 +1451,7 @@ fr:
       electron: Electron
       firefox: Firefox
       generic: Navigateur inconnu
+      huawei_browser: Navigateur Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1416,6 +1461,7 @@ fr:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Navigateur inconnu
       weibo: Weibo
     current_session: Session courante
     description: "%{browser} sur %{platform}"
@@ -1428,9 +1474,10 @@ fr:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: système inconnu
+      unknown_platform: Plateforme inconnue
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1643,6 +1690,7 @@ fr:
       title: Bienvenue à bord, %{name} !
   users:
     follow_limit_reached: Vous ne pouvez pas suivre plus de %{limit} personnes
+    go_to_sso_account_settings: Accédez aux paramètres du compte de votre fournisseur d'identité
     invalid_otp_token: Le code d’authentification à deux facteurs est invalide
     otp_lost_help_html: Si vous perdez accès aux deux, vous pouvez contacter %{email}
     seamless_external_login: Vous êtes connecté via un service externe, donc les paramètres concernant le mot de passe et le courriel ne sont pas disponibles.
diff --git a/config/locales/fy.yml b/config/locales/fy.yml
index 3888c2c14..669da37aa 100644
--- a/config/locales/fy.yml
+++ b/config/locales/fy.yml
@@ -1,7 +1,7 @@
 ---
 fy:
   about:
-    about_mastodon_html: Mastodon is in sosjaal netwurk dat gebrûkt makket fan iepen webprotokollen en frije software. It is krekt lykas e-mail desintralisearre!
+    about_mastodon_html: Mastodon is in sosjaal netwurk dat gebrûk makket fan iepen webprotokollen en frije software. It is krekt lykas desintralisearre e-mail!
     contact_missing: Net ynsteld
     contact_unavailable: Net fan tapassing
     hosted_on: Mastodon op %{domain}
@@ -91,6 +91,7 @@ fy:
       moderation:
         active: Aktyf
         all: Alle
+        disabled: Utskeakele
         pending: Yn ôfwachting
         silenced: Beheind
         suspended: Utsteld
@@ -133,6 +134,7 @@ fy:
       search: Sykje
       search_same_email_domain: Oare brûkers mei itselde e-maildomein
       search_same_ip: Oare brûkers mei itselde IP-adres
+      security: Befeiliging
       security_measures:
         only_password: Allinnich wachtwurd
         password_and_2fa: Wachtwurd en 2FA
@@ -427,6 +429,7 @@ fy:
         resolve: Domein opsykje
         title: Nije e-maildomein blokkearje
       no_email_domain_block_selected: Der binne gjin e-maildomeinblokkaden wizige, omdat der gjin ien selektearre waard
+      not_permitted: Net tastien
       resolved_dns_records_hint_html: De domeinnamme slacht op de folgjende MX-domeinen dy’t úteinlik ferantwurdlik binne foar it akseptearjen fan e-mail. It blokkearjen fan in MX-domein blokkearret oanmeldingen fan elk e-mailadres dat itselde MX-domein brûkt, sels as de sichtbere domeinnamme oars is. <strong>Pas op dat jo gjin grutte e-mailproviders blokkearje.</strong>
       resolved_through_html: Blokkearre fia %{domain}
       title: Blokkearre e-maildomeinen
@@ -441,6 +444,7 @@ fy:
         private_comment_description_html: 'Om jo te helpen byhâlden wêr’t de ymportearre blokkaden wei komme, wurde de ymportearre blokkaden mei de folgjende priveeopmerking oanmakke: <q>%{comment}</q>'
         private_comment_template: Ymportearre fan %{source} op %{date}
         title: Domeinblokkaden ymportearje
+      invalid_domain_block: 'Ien of mear domeinblokkaden binne oerslein, fanwegen de folgjende flater(s): %{error}'
       new:
         title: Domeinblokkaden ymportearje
       no_file: Gjin bestân selektearre
@@ -472,6 +476,7 @@ fy:
       content_policies:
         comment: Ynterne reden
         description_html: Jo kinne it belied bepale dat op de accounts fan dit domein en alle subdomeinen fan tapassing is.
+        limited_federation_mode_description_html: Jo kinne kieze oft jo federaasje mei dit domein tastean wolle.
         policies:
           reject_media: Mediabestannen wegerje
           reject_reports: Rapportaazjes wegerje
@@ -575,19 +580,23 @@ fy:
         mark_as_sensitive_description_html: De media yn de rapportearre berjochten wurde markearre as gefoelich en der wurdt in oertrêding registrearre om takomstige oertrêdingen fan itselde account flugger ôfhannelje te kinnen.
         other_description_html: Besjoch mear opsjes foar it kontrolearjen fan it gedrach fan en de kommunikaasje mei it rapportearre account.
         resolve_description_html: Der wurdt tsjin it rapportearre account gjin maatregel nommen, gjin oertrêding registrearre en de rapportaazje wurdt markearre as oplost.
-        silence_description_html: It profyl sil allinnich sichtber wêze foar dyjinge dy’t it al folgje of it hânmjittich opsykje, wêrtroch it berik earnstich beheind wurdt. Kin altyd weromdraaid wurde.
-        suspend_description_html: It profyl en alle accountgegevens wurde earst net tagonklik makke, oant dizze úteinlik definityf fuortsmiten wurde. It is net mear mooglik om ynteraksje te hawwen mei de account. Dit kin binnen 30 dagen weromdraaid wurde.
+        silence_description_html: De account sil allinnich sichtber wêze foar dyjinge dy’t it al folgje of it hânmjittich opsykje, wêrtroch it berik earnstich beheind wurdt. Kin altyd weromdraaid wurde. Dit slút alle rapportaazjes oer dizze account.
+        suspend_description_html: De account en alle ynhâld sil net tagonklik wêze en úteinlik fuortsmiten wurde, en ynteraksje hjirmei sil net mooglik wêze. Binnen 30 dagen werom te draaien. Dit slút alle rapportaazjes oer dizze account.
       actions_description_html: Beslis hokker maatregel nommen wurde moat om dizze rapportaazje op te lossen. Wannear’t jo in (straf)maatregel tsjin it rapportearre account nimme, kriget de account in e-mailmelding, behalve wannear’t de <strong>spam</strong>-kategory keazen is.
+      actions_description_remote_html: Beslút hokker aksje nommen wurde moat om dizze rapportaazje ôf te hanneljen. Dit hat allinnich ynfloed op hoe’t <strong>jo</strong> server kommunisearret mei dizze eksterne account en omgiet mei de ynhâld.
       add_to_report: Mear oan de rapportaazje tafoegje
       are_you_sure: Binne jo wis?
       assign_to_self: Oan my tawize
       assigned: Tawizen moderator
       by_target_domain: Domein fan rapportearre account
+      cancel: Annulearje
       category: Kategory
       category_description_html: De reden wêrom dizze account en/of ynhâld rapportearre waard, wurdt oan it rapportearre account meidield
       comment:
         none: Gjin
       comment_description_html: 'Om mear ynformaasje te jaan, skreau %{name}:'
+      confirm: Befêstigje
+      confirm_action: Moderaasjemaatregelen tsjin %{acct} befêstigje
       created_at: Rapportearre op
       delete_and_resolve: Berjocht fuortsmite
       forwarded: Trochstjoerd
@@ -604,6 +613,7 @@ fy:
         placeholder: Beskriuw hokker maatregels nommen binne of oare relatearre opmerkingen…
         title: Opmerkingen
       notes_description_html: Besjoch en lit opmerkingen efter foar oare moderatoaren en foar jo takomstige sels
+      processed_msg: 'Rapprtaazje #%{id} mei sukses ferwurke'
       quick_actions_description_html: 'Nim in flugge maatregel of skow nei ûnder om de rapportearre ynhâld te besjen:'
       remote_user_placeholder: de eksterne brûker fan %{instance}
       reopen: Rapport opnij iepenje
@@ -616,9 +626,28 @@ fy:
       status: Steat
       statuses: Rapportearre ynhâld
       statuses_description_html: De problematyske ynhâld wurdt oan it rapportearre account meidield
+      summary:
+        action_preambles:
+          delete_html: 'Jo stean op it punt om inkelde berjochten fan <strong>@%{acct}</strong> <strong>fuort te smiten</strong>. Dit sil:'
+          mark_as_sensitive_html: 'Jo stean op it punt om inkelde berjochten fan <strong>@%{acct}</strong> as <strong>gefoelich</strong> te <strong>markearjen</strong>. Dit sil:'
+          silence_html: 'Jo stean op it punt de account fan <strong>@%{acct}</strong> te <strong>beheinen</strong>. Dit sil:'
+          suspend_html: 'Jo stean op it punt de account fan <strong>@%{acct}</strong> te <strong>beskoatteljen</strong>. Dit sil:'
+        actions:
+          delete_html: De problematyske berjochten fuortsmite
+          mark_as_sensitive_html: De media fan de problematyske berjochten as gefoelich markearje
+          silence_html: "<strong>@%{acct}</strong> earnstich beheine, troch it profyl ende ynhâld allinnich sichtber te meitsjen foar de minsken dy’t harren al folgje of hantmjittich it profyl opsykje"
+          suspend_html: "<strong>@%{acct}</strong> blokkearje, sadat it profyl en de ynhâld net mear tagonklik is en it net mooglik is om ynteraksje dêr mei te hawwen"
+        close_report: 'Rapportaazje #%{id} as oplost markearje'
+        close_reports_html: "<strong>Alle</strong> meldingen tsjin <strong>@%{acct}</strong> as oplost markearje"
+        delete_data_html: It profyl en de ynhâld fan <strong>@%{acct}</strong> wurde nei 30 dagen fan no ôf fuortsmiten, útsein as de account yn de tuskentiid net mear blokkearre wurdt
+        preview_preamble_html: "<strong>@%{acct}</strong> sil in warskôging ûntfange mei de folgjende ynhâld:"
+        record_strike_html: In ban tsjin <strong>@%{acct}</strong> ynstelle, om jo te helpen by takomstige skeiningen fan dizze acount te eskalearjen
+        send_email_html: Stjoer <strong>@%{acct}</strong> in warskôgings-e-mailberjocht
+        warning_placeholder: Ekstra opsjonele reden foar de moderaasje-aksje.
       target_origin: Orizjineel fan rapportearre account
       title: Rapportaazjes
       unassign: Net langer tawize
+      unknown_action_msg: 'Unbekende aksje: %{action}'
       unresolved: Net oplost
       updated_at: Bywurke
       view_profile: Profyl besjen
@@ -713,6 +742,8 @@ fy:
         preamble: It toanen fan ynteressante ynhâld is fan essinsjeel belang foar it oan board heljen fan nije brûkers dy’t mooglik net ien fan Mastodon kinne. Bepaal hoe’t ferskate funksjes foar it ûntdekken fan ynhâld en brûkers op jo server wurkje.
         profile_directory: Profylmap
         public_timelines: Iepenbiere tiidlinen
+        publish_discovered_servers: Untdekte servers publisearje
+        publish_statistics: Statistiken publisearje
         title: Untdekke
         trends: Trends
       domain_blocks:
@@ -767,6 +798,7 @@ fy:
         suspend: Account %{target} is troch %{name} útsteld
       appeal_approved: Beswier yntsjinne
       appeal_pending: Beswier yn behanneling
+      appeal_rejected: Beswier ôfwêzen
     system_checks:
       database_schema_check:
         message_html: Der binne database migraasjes yn ôfwachting. Jo moatte dizze útfiere om der foar te soargjen dat de applikaasje wurkjen bliuwt sa as it heard
@@ -780,6 +812,12 @@ fy:
         message_html: Jo hawwe foar dizze server gjin regels opsteld.
       sidekiq_process_check:
         message_html: Der draait gjin Sidekiq-proses foar de wachtrige(n) %{value}. Kontrolearje jo Sidekiq-konfiguraasje
+      upload_check_privacy_error:
+        action: Klik hjir foar mear ynformaasje
+        message_html: "<strong>Jo webserver is ferkeard konfigurearre. De privacy fan jo brûkers is yn gefaar.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Klik hjir foar mear ynformaasje
+        message_html: "<strong>Jo objektûnthâld is ferkeard konfigurearre. De privacy fan jo brûkers is yn gefaar.</strong>"
     tags:
       review: Steat beoardiele
       updated_msg: Hashtagynstellingen mei sukses bywurke
@@ -802,6 +840,7 @@ fy:
           other: Dizze wike troch %{count} persoanen dield
         title: Trending keppelingen
         usage_comparison: Hjoed %{today} kear dield, fergelike mei %{yesterday} kear juster
+      not_allowed_to_trend: Trending wurdt net tastien
       only_allowed: Allinnich goedkarre
       pending_review: Moat noch beoardiele wurde
       preview_card_providers:
@@ -933,6 +972,7 @@ fy:
   applications:
     created: Oanmeitsjen tapassing slagge
     destroyed: Fuortsmiten tapassing slagge
+    logout: Ofmelde
     regenerate_token: Tagongskoade opnij oanmeitsje
     token_regenerated: Opnij oanmeitsjen tagongskoade slagge
     warning: Wês foarsichtich mei dizze gegevens. Diel it nea mei in oar!
@@ -940,6 +980,8 @@ fy:
   auth:
     apply_for_account: Account oanfreegje
     change_password: Wachtwurd
+    confirmations:
+      wrong_email_hint: As it e-mailadres net korrekt is, kinne jo dat wizigje yn de accountynstellingen.
     delete_account: Account fuortsmite
     delete_account_html: Wannear’t jo jo account graach fuortsmite wolle, kinne jo dat <a href="%{path}">hjir dwaan</a>. Wy freegje jo dêr om in befêstiging.
     description:
@@ -967,6 +1009,8 @@ fy:
     resend_confirmation: Ferstjoer de befêstigingsynstruksjes nochris
     reset_password: Wachtwurd opnij ynstelle
     rules:
+      accept: Akseptearje
+      back: Tebek
       preamble: Dizze binne fêststeld en wurde yn stân hâlden troch de moderatoaren fan %{domain}.
       title: Inkelde basisrigels.
     security: Befeiliging
@@ -1158,8 +1202,6 @@ fy:
       index:
         hint: Dit filter is fan tapassing om yndividuele berjochten te selektearjen, ûnôfhinklik fan oare kritearia. Jo kinne yn de webomjouwing mear berjochten oan dit filter tafoegje.
         title: Filtere berjochten
-  footer:
-    trending_now: Trends
   generic:
     all: Alle
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ fy:
     validation_errors:
       one: Der is wat net hielendal goed! Besjoch ûndersteande flater
       other: Der is wat net hielendal goed! Besjoch ûndersteande %{count} flaters
-  html_validator:
-    invalid_markup: 'befettet ûnjildige HTML-opmaak: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Unjildich CSV-bestân. Flater: %{error}'
@@ -1367,7 +1407,11 @@ fy:
       unrecognized_emoji: is gjin besteande emoji-reaksje
   relationships:
     activity: Accountaktiviteit
+    confirm_follow_selected_followers: Binne jo wis dat jo de selektearre folgers folgje wolle?
+    confirm_remove_selected_followers: Binne jo wis dat jo de selektearre folgers net mear folgje wolle?
+    confirm_remove_selected_follows: Binne jo wis dat jo de selektearre folge accounts net mear folgje wolle?
     dormant: Slommerjend
+    follow_failure: Kin guon fan de selektearre accounts net folgje.
     follow_selected_followers: Selektearre folgers folgje
     followers: Folgers
     following: Folgjend
@@ -1407,6 +1451,7 @@ fy:
       electron: Electron
       firefox: Firefox
       generic: Unbekende browser
+      huawei_browser: Huawei-browser
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1416,6 +1461,7 @@ fy:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Unbekende browser
       weibo: Weibo
     current_session: Aktuele sesje
     description: "%{browser} op %{platform}"
@@ -1428,9 +1474,10 @@ fy:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: ûnbekend platfoarm
+      unknown_platform: Unbekend platfoarm
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1643,12 +1690,13 @@ fy:
       title: Wolkom oan board %{name}!
   users:
     follow_limit_reached: Jo kinne net mear as %{limit} accounts folgje
+    go_to_sso_account_settings: Gean nei de accountynstellingen fan jo identiteitsprovider
     invalid_otp_token: Unjildige twa-stapstagongskoade
     otp_lost_help_html: As jo tagong ta beide kwytrekke binne, nim dan kontakt op fia %{email}
     seamless_external_login: Jo binne oanmeld fia in eksterne tsjinst, dêrom binne wachtwurden en e-mailynstellingen net beskikber.
     signed_in_as: 'Oanmeld as:'
   verification:
-    explanation_html: 'Jo kinne <strong>josels ferifiearje as de eigener fan de keppelingen yn de metadata fan jo profyl</strong>. Hjirfoar moat op de keppele website in keppeling werom nei jo Mastodon-profyl stean. Dizze keppeling <strong>moat</strong> it <code>rel="me"</code>-attribút befetsje. De omskriuwing fan de keppeling makket net út. Hjir is in foarbyld:'
+    explanation_html: 'Jo kinne <strong>josels ferifiearje as de eigener fan de keppelingen yn de metadata fan jo profyl</strong>. Hjirfoar moat op de keppele website in keppeling werom nei jo Mastodon-profyl stean. Nei it tafoegjen fan de keppeling moatte jo miskien hjir werom komme en jo profyl opnij bewarje om de ferifikaasje te befêstigjen. Dizze keppeling <strong>moat</strong> it <code>rel="me"</code>-attribút befetsje. De omskriuwing fan de keppeling makket net út. Hjir is in foarbyld:'
     verification: Ferifikaasje
   webauthn_credentials:
     add: Nije befeiligingskaai tafoegje
diff --git a/config/locales/ga.yml b/config/locales/ga.yml
index 767c9f246..f43037987 100644
--- a/config/locales/ga.yml
+++ b/config/locales/ga.yml
@@ -60,14 +60,24 @@ ga:
       follows: Ag leanúint
       header: Ceanntásc
       ip: IP
+      joined: Cláraithe
       location:
         all: Uile
         local: Áitiúil
         remote: Cian
+      login_status: Stádas logála isteach
       memorialize: Déan cuntas chuimhneacháin de
       memorialized: Cuntas chuimhneacháin
       moderation:
+        active: Gníomhach
         all: Uile
+        pending: Ar feitheamh
+        silenced: Teoranta
+        suspended: Ar fionraí
+        title: Modhnóireacht
+      moderation_notes: Nótaí modhnóireacht
+      most_recent_activity: Gníomhaíocht is déanaí
+      most_recent_ip: IP is déanaí
       perform_full_suspension: Fionraí
       previous_strikes: Cionta roimhe seo
       promote: Ardaigh
@@ -86,19 +96,28 @@ ga:
       search: Cuardaigh
       security_measures:
         only_password: Pasfhocal amháin
+      sensitized: Marcáladh mar íogair
+      silence: Teorannaigh
       silenced: Teoranta
       statuses: Postálacha
       subscribe: Cláraigh
+      suspend: Cuir ar fionraí
+      suspended: Ar fionraí
       title: Cuntais
+      username: Ainm úsáideora
       warn: Rabhadh a thabhairt
       web: Gréasán
     action_logs:
       action_types:
         assigned_to_self_report: Sann Tuairisc
         create_account_warning: Cruthaigh Rabhadh
+        create_ip_block: Cruthaigh riail IP
+        create_user_role: Cruthaigh Ról
+        demote_user: Ísligh úsáideoir
         destroy_announcement: Scrios Fógra
         destroy_ip_block: Scrios riail IP
         destroy_status: Scrios Postáil
+        destroy_user_role: Scrios ról
         remove_avatar_user: Bain Abhatár
         reopen_report: Athoscail tuairisc
         reset_password_user: Athshocraigh Pasfhocal
@@ -109,11 +128,22 @@ ga:
         update_user_role: Nuashonraigh Ról
       actions:
         create_account_warning_html: Sheol %{name} rabhadh chuig %{target}
+        destroy_user_role_html: Scrios %{name} ról %{target}
+        update_user_role_html: D'athraigh %{name} ról %{target}
       deleted_account: cuntas scriosta
     announcements:
+      edit:
+        title: Cuir fógra in eagar
       live: Beo
+      new:
+        create: Cruthaigh fógra
+        title: Fógra nua
       publish: Foilsigh
+      title: Fógraí
     custom_emojis:
+      by_domain: Fearann
+      copy: Cóipeáil
+      create_new_category: Cruthaigh catagóir nua
       created_msg: Cruthaíodh emoji go rathúil!
       delete: Scrios
       destroyed_msg: Scriosadh emoji go rathúil!
@@ -121,30 +151,65 @@ ga:
       disabled: Díchumasaithe
       emoji: Emoji
       enable: Cumasaigh
+      enabled: Ar chumas
       list: Liosta
+      listed: Liostaithe
+      overwrite: Forscríobh
+      uncategorized: Neamhchatagóirithe
+      unlist: Neamhliostaigh
+      unlisted: Neamhliostaithe
       upload: Uaslódáil
     dashboard:
+      active_users: úsáideoirí gníomhacha
+      new_users: úsáideoirí nua
+      opened_reports: tuairiscí oscailte
       software: Bogearraí
       title: Deais
       website: Suíomh Gréasáin
+    disputes:
+      appeals:
+        title: Achomhairc
+    domain_allows:
+      export: Easpórtáil
+      import: Iompórtáil
     domain_blocks:
       domain: Fearann
+      export: Easpórtáil
+      import: Iompórtáil
+      new:
+        severity:
+          silence: Teorannaigh
+          suspend: Cuir ar fionraí
     email_domain_blocks:
       delete: Scrios
+      domain: Fearann
     follow_recommendations:
+      language: Don teanga
       status: Stádas
+      suppress: Coisc moladh leanúna
+      suppressed: Coiscthe
       title: Moltaí leanúna
       unsuppress: Aischuir moladh leanúna
     instances:
       back_to_all: Uile
+      back_to_limited: Teoranta
       back_to_warning: Rabhadh
+      by_domain: Fearann
       content_policies:
+        comment: Nóta inmheánach
+        policies:
+          silence: Teorannaigh
+          suspend: Cuir ar fionraí
         policy: Polasaí
+      dashboard:
+        instance_languages_dimension: Teangacha is airde
       delivery:
         all: Uile
+        failing: Ag teip
         unavailable: Níl ar fáil
       moderation:
         all: Uile
+        limited: Teoranta
       purge: Glan
       title: Cónascadh
       total_followed_by_us: Á leanúint againn
@@ -152,7 +217,10 @@ ga:
       filter:
         all: Uile
         available: Ar fáil
+        title: Scag
+      title: Cuirí
     ip_blocks:
+      add_new: Cruthaigh riail
       delete: Scrios
       expires_in:
         '1209600': Coicís
@@ -161,6 +229,7 @@ ga:
         '31556952': Bliain amháin
         '86400': Lá amháin
         '94670856': 3 bhliain
+      title: Rialacha IP
     relays:
       delete: Scrios
       disable: Díchumasaigh
@@ -170,19 +239,32 @@ ga:
       save_and_enable: Sábháil agus cumasaigh
       status: Stádas
     reports:
+      are_you_sure: An bhfuil tú cinnte?
       category: Catagóir
+      created_at: Tuairiscithe
       delete_and_resolve: Scrios postálacha
+      mark_as_resolved: Marcáil mar réitithe
+      mark_as_sensitive: Marcáil mar íogair
       no_one_assigned: Duine ar bith
       notes:
+        create: Cruthaigh nóta
+        create_and_resolve: Réitigh le nóta
         delete: Scrios
         title: Nótaí
       status: Stádas
       title: Tuairiscí
     roles:
+      add_new: Cruthaigh ról
+      categories:
+        administration: Riar
+        invites: Cuirí
       delete: Scrios
       privileges:
+        administrator: Riarthóir
         delete_user_data: Scrios Sonraí Úsáideora
+      title: Róil
     rules:
+      add_new: Cruthaigh riail
       delete: Scrios
     settings:
       appearance:
@@ -190,6 +272,8 @@ ga:
       default_noindex:
         desc_html: I bhfeidhm do ghach úsáideoir nár athraigh an socrú seo iad féin
         title: Diúltaigh d'innéacsú inneall cuardaigh mar réamhshocrú d'úsáideoirí
+      registrations:
+        title: Clárúcháin
     site_uploads:
       delete: Scrios comhad uaslódáilte
     statuses:
@@ -212,11 +296,13 @@ ga:
         delete_statuses: Scrios %{name} postálacha de chuid %{target}
     tags:
       review: Stádas athbhreithnithe
+    title: Riar
     trends:
       allow: Ceadaigh
       disallow: Dícheadaigh
       preview_card_providers:
         title: Foilsitheoirí
+      rejected: Diúltaithe
       statuses:
         allow: Ceadaigh postáil
         allow_account: Ceadaigh údar
@@ -224,20 +310,48 @@ ga:
       delete: Scrios
     webhooks:
       delete: Scrios
+      disable: Díchumasaigh
+      disabled: Díchumasaithe
+      enable: Cumasaigh
+      enabled: Gníomhach
+      events: Eachtraí
+      status: Stádas
   admin_mailer:
     new_appeal:
       actions:
         delete_statuses: a gcuid postálacha a scrios
         none: rabhadh
   auth:
+    change_password: Pasfhocal
     delete_account: Scrios cuntas
+    login: Logáil isteach
     logout: Logáil Amach
+    or_log_in_with: Nó logáil isteach le
+    register: Clárú
+    security: Slándáil
+    status:
+      account_status: Stádas cuntais
     too_fast: Cuireadh an fhoirm isteach róthapa, triail arís.
   authorize_follow:
     follow: Lean
     post_follow:
       return: Taispeáin próifíl an úsáideora
     title: Lean %{acct}
+  challenge:
+    confirm: Lean ar aghaidh
+  datetime:
+    distance_in_words:
+      about_x_hours: "%{count}u"
+      about_x_months: "%{count}m"
+      about_x_years: "%{count}b"
+      almost_x_years: "%{count}b"
+      half_a_minute: Díreach anois
+      less_than_x_seconds: Díreach anois
+      over_x_years: "%{count}b"
+      x_days: "%{count}l"
+      x_minutes: "%{count}n"
+      x_months: "%{count}m"
+      x_seconds: "%{count}s"
   deletes:
     proceed: Scrios cuntas
   disputes:
@@ -255,18 +369,50 @@ ga:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
+  exports:
+    archive_takeout:
+      date: Dáta
+      size: Méid
+    lists: Liostaí
   filters:
     contexts:
+      home: Baile agus liostaí
+      notifications: Fógraí
       thread: Comhráite
+    edit:
+      add_keyword: Cruthaigh eochairfhocal
+      keywords: Eochairfhocal
     index:
       delete: Scrios
+      empty: Níl aon scagairí agat.
       title: Scagairí
   generic:
+    copy: Cóipeáil
     delete: Scrios
+    today: inniu
+  imports:
+    modes:
+      merge: Cumaisc
+      overwrite: Forscríobh
+    types:
+      bookmarks: Leabharmharcanna
+    upload: Uaslódáil
+  invites:
+    expired: As feidhm
+    expires_in:
+      '1800': 30 nóiméid
+      '21600': 6 uair
+      '3600': Uair amháin
+      '43200': 12 uair
+      '604800': Seachtain amháin
+      '86400': Lá amháin
+    expires_in_prompt: In am ar bith
+  login_activities:
+    authentication_methods:
+      password: pasfhocal
+      webauthn: eochracha slándála
   notification_mailer:
     admin:
       report:
@@ -275,27 +421,67 @@ ga:
       body: Tá %{name} do do leanúint anois!
       subject: Tá %{name} do do leanúint anois
       title: Leantóirí nua
+    mention:
+      action: Freagair
     reblog:
       subject: Mhol %{name} do phostáil
       title: Moladh nua
+  otp_authentication:
+    enable: Cumasaigh
+  pagination:
+    newer: Níos nuaí
+    next: An céad eile
+    older: Níos sine
+    prev: Ceann roimhe seo
   relationships:
     follow_selected_followers: Lean leantóirí roghnaithe
     followers: Leantóirí
     following: Ag leanúint
+    primary: Príomha
     remove_selected_followers: Bain leantóirí roghnaithe
     remove_selected_follows: Dí-lean úsáideoirí roghnaithe
+    status: Stádas cuntais
   rss:
     content_warning: 'Rabhadh ábhair:'
+  sessions:
+    browser: Brabhsálaí
+    browsers:
+      alipay: Alipay
+      blackberry: BlackBerry
+      chrome: Chrome
+      edge: Microsoft Edge
+      electron: Electron
+      firefox: Firefox
+      generic: Brabhsálaí anaithnid
+    platforms:
+      android: Android
+      blackberry: BlackBerry
+      chrome_os: ChromeOS
+      firefox_os: Firefox OS
+      ios: iOS
+      linux: Linux
+      mac: macOS
+      windows: Windows
+      windows_mobile: Windows Mobile
+      windows_phone: Windows Phone
+    title: Seisiúin
   settings:
     account: Cuntas
+    account_settings: Socruithe cuntais
     appearance: Cuma
     back: Ar ais go Mastodon
     development: Forbairt
     edit_profile: Cuir an phróifíl in eagar
+    import: Iompórtáil
+    notifications: Fógraí
+    preferences: Sainroghanna pearsanta
     profile: Próifíl
+    webauthn_authentication: Eochracha slándála
   statuses:
     boosted_from_html: Molta ó %{acct_link}
     content_warning: 'Rabhadh ábhair: %{warning}'
+    poll:
+      vote: Vótáil
     show_more: Taispeáin níos mó
     show_newer: Taispeáin níos nuaí
     show_thread: Taispeáin snáithe
@@ -304,6 +490,21 @@ ga:
       private_long: Taispeáin do leantóirí amháin
   statuses_cleanup:
     ignore_favs: Tabhair neamhaird ar toghanna
+    min_age:
+      '1209600': Coicís
+      '15778476': 6 mhí
+      '2629746': Mí amháin
+      '31556952': Bliain amháin
+      '5259492': 2 mhí
+      '604800': Seachtain amháin
+      '63113904': 2 bhliain
+      '7889238': 3 mhí
+  stream_entries:
+    pinned: Postáil pionnáilte
+    sensitive_content: Ábhar íogair
+  two_factor_authentication:
+    edit: Cuir in eagar
+    webauthn: Eochracha slándála
   user_mailer:
     warning:
       appeal: Cuir achomharc isteach
diff --git a/config/locales/gd.yml b/config/locales/gd.yml
index 22f87aa49..2c07fa067 100644
--- a/config/locales/gd.yml
+++ b/config/locales/gd.yml
@@ -95,6 +95,7 @@ gd:
       moderation:
         active: Gnìomhach
         all: Na h-uile
+        disabled: À comas
         pending: Ri dhèiligeadh
         silenced: Cuingichte
         suspended: À rèim
@@ -122,6 +123,8 @@ gd:
       redownloaded_msg: Chaidh a’ phròifil aig %{username} on tùs
       reject: Diùlt
       rejected_msg: Chaidh an t-iarrtas clàraidh aig %{username} a dhiùltadh
+      remote_suspension_irreversible: Chaidh dàta a’ chunntais seo a sguabadh às gu buan.
+      remote_suspension_reversible_hint_html: Chaidh an cunntas a chur à rèim air an fhrithealaiche aca agus thèid an dàta aige a sguabadh às gu buan %{date}. Gus an dig an t-àm ud, ’s urrainn dhan fhrithealaiche chèin an cunntas aiseag fhathast gun droch bhuaidh sam bith air. Nam bu toigh leat gach dàta a’ chunntais a thoirt air falbh sa bhad, ’s urrainn dhut sin a dhèanamh gu h-ìosal.
       remove_avatar: Thoir air falbh an t-avatar
       remove_header: Thoir air falbh am bann-cinn
       removed_avatar_msg: Chaidh dealbh an avatar aig %{username} a thoirt air falbh
@@ -137,6 +140,7 @@ gd:
       search: Lorg
       search_same_email_domain: Cleachdaichean eile aig a bheil an aon àrainn puist-d
       search_same_ip: Cleachdaichean eile aig a bheil an t-aon IP
+      security: Tèarainteachd
       security_measures:
         only_password: Facal-faire a-mhàin
         password_and_2fa: Facal-faire ’s dà-cheumnach
@@ -166,7 +170,7 @@ gd:
       unsubscribe: Cuir crìoch air an fho-sgrìobhadh
       unsuspended_msg: Chaidh an cunntas aig %{username} a chur ann an rèim a-rithist
       username: Ainm-cleachdaiche
-      view_domain: Sealladh geàrr-chunntas na h-àrainn
+      view_domain: Seall geàrr-chunntas na h-àrainn
       warn: Thoir rabhadh
       web: Lìon
       whitelisted: Ceadaichte a chùm co-nasgaidh
@@ -404,6 +408,7 @@ gd:
         create: Cruthaich bacadh
         hint: Cha chuir bacadh na h-àrainne crìoch air cruthachadh chunntasan san stòr-dàta ach cuiridh e dòighean maorsainneachd sònraichte an sàs gu fèin-obrachail air a h-uile dàta a tha aig na cunntasan ud.
         severity:
+          desc_html: Falaichidh an <strong>cuingeachadh</strong> postaichean o chunntasan na h-àrainne seo do dhuine sam bith nach ail a’ leantainn orra. Bheir an <strong>cur à rèim</strong> air falbh gach susbaint, meadhan is dàta pròifil aig cunntasan na h-àrainne seo on fhrithealaiche agad. Tagh <strong>Chan eil gin</strong> mur eil thu ach airson faidhlichean meadhain a dhiùltadh.
           noop: Chan eil gin
           silence: Cuingich
           suspend: Cuir à rèim
@@ -440,6 +445,7 @@ gd:
         resolve: Fuasgail an àrainn
         title: Bac àrainn puist-d ùr
       no_email_domain_block_selected: Cha deach bacadh àrainn puist-d sam bith atharrachadh o nach deach gin dhiubh a thaghadh
+      not_permitted: Chan eil seo ceadaichte
       resolved_dns_records_hint_html: Thèid ainm na h-àrainne fhuasgladh nan àrainnean MX a leanas agus an urra riutha-san gun gabh iad ri post-d. Ma bhacas tu àrainn MX, bacaidh seo an clàradh o sheòladh puist-d sam bith a chleachdas an aon àrainn MX fiù ’s ma bhios ainm àrainne eadar-dhealaichte ’ga sealltainn. <strong>Thoir an aire nach bac thu solaraichean puist-d mòra.</strong>
       resolved_through_html: Chaidh fuasgladh slighe %{domain}
       title: Àrainnean puist-d ’gam bacadh
@@ -449,9 +455,12 @@ gd:
       no_file: Cha deach faidhle a thaghadh
     export_domain_blocks:
       import:
+        description_html: Tha thu an impis bacaidhean àrainne ion-phortadh. Dèan lèirmheas glè chùramach air an liosta seo, gu h-àraidh mur an do chruinnich thu fhèin an liosta seo.
         existing_relationships_warning: Dàimhean leantainn làithreach
+        private_comment_description_html: 'Airson do chuideachadh ach an tracaich thu cò às a thàinig bacaidhean a chaidh ion-phortadh, thèid am beachd prìobhaideach seo a chur ris na bacaidhean air an ion-phortadh: <q>%{comment}</q>'
         private_comment_template: Chaidh ion-phortadh o %{source} %{date}
         title: Ion-phortaich bacaidhean àrainne
+      invalid_domain_block: 'Chaidh leum a ghearradh thar bacadh àrainne no dhà ri linn mearachd: %{error}'
       new:
         title: Ion-phortaich bacaidhean àrainne
       no_file: Cha deach faidhle a thaghadh
@@ -487,6 +496,7 @@ gd:
       content_policies:
         comment: Nòta taobh a-staigh
         description_html: "’S urrainn dhut poileasaidhean susbainte a mhìneachadh a thèid a chur an sàs air a h-uile cunntas on àrainn seo ’s a fo-àrainnean-se."
+        limited_federation_mode_description_html: "’S urrainn dhut taghadh an ceadaich thu co-nasgadh leis an àrainn seo gus nach ceadaich."
         policies:
           reject_media: Diùlt meadhanan
           reject_reports: Diùlt gearanan
@@ -594,19 +604,23 @@ gd:
         mark_as_sensitive_description_html: Thèid comharra an fhrionasachd a chur ris na meadhanan sna postaichean le gearan orra agus rabhadh a chlàradh gus do chuideachadh ach am bi thu nas teinne le droch-ghiùlan on aon chunntas sam àm ri teachd.
         other_description_html: Seall barrachd roghainnean airson giùlan a’ chunntais a stiùireadh agus an conaltradh leis a’ chunntas a chaidh gearan a dhèanamh mu dhèidhinn a ghnàthachadh.
         resolve_description_html: Cha dèid gnìomh sam bith a ghabhail an aghaidh a’ chunntais le gearan air agus thèid an gearan a dhùnadh gun rabhadh a chlàradh.
-        silence_description_html: Chan fhaic ach an fheadhainn a tha ’ga leantainn mu thràth no a lorgas a làimh i a’ phròifil seo agus cuingichidh seo uiread nan daoine a ruigeas i gu mòr. Gabhaidh seo a neo-dhèanamh uair sam bith.
-        suspend_description_html: Cha ghabh a’ phròifil seo agus an t-susbaint gu leòr aice inntrigeadh gus an dèid a sguabadh às air deireadh na sgeòil. Cha ghabh eadar-ghabhail a dhèanamh leis a’ chunntas. Gabhaidh seo a neo-dhèanamh am broinn 30 latha.
+        silence_description_html: Chan fhaic ach an fheadhainn a tha ’ga leantainn mu thràth no a lorgas a làimh e an cunntas seo agus cuingichidh seo uiread nan daoine a ruigeas e gu mòr. Gabhaidh seo a neo-dhèanamh uair sam bith. Dùinidh seo gach gearan mun chunntas seo.
+        suspend_description_html: Cha ghabh an cunntas seo agus an t-susbaint gu leòr aige inntrigeadh gus an dèid a sguabadh às air deireadh na sgeòil agus cha ghabh eadar-ghabhail a dhèanamh leis. Gabhaidh seo a neo-dhèanamh am broinn 30 latha. Dùinidh seo gach gearan mun chunntas seo.
       actions_description_html: Socraich dè a nì thu airson an gearan seo fhuasgladh. Ma chuireas tu peanas air a’ chunntas le gearan air, gheibh iad brath air a’ phost-d mura tagh thu an roinn-seòrsa <strong>Spama</strong>.
+      actions_description_remote_html: Cuir romhad dè an gnìomh a ghabhas tu airson an gearan seo fhuasgladh. Cha bheir seo buaidh ach air mar a làimhsicheas am frithealaiche <strong>agadsa</strong> an cunntas cèin seo is mar a nì e conaltradh leis.
       add_to_report: Cuir barrachd ris a’ ghearan
       are_you_sure: A bheil thu cinnteach?
       assign_to_self: Iomruin dhomh-sa
       assigned: Maor iomruinte
       by_target_domain: Àrainn cunntas a’ ghearain
+      cancel: Sguir dheth
       category: Roinn-seòrsa
       category_description_html: Thèid iomradh a thoirt air adhbhar a’ ghearain mun chunntas/susbaint seo sa chonaltradh leis a’ chunntas mun a chaidh an gearan a thogail
       comment:
         none: Chan eil gin
       comment_description_html: 'Airson barrachd fiosrachaidh a sholar, sgrìobh %{name}:'
+      confirm: Dearbh
+      confirm_action: Dearbh gnìomh na maorsainneachd an aghaidh @%{acct}
       created_at: Chaidh an gearan a dhèanamh
       delete_and_resolve: Sguab às na postaichean
       forwarded: Chaidh a shìneadh air adhart
@@ -623,6 +637,7 @@ gd:
         placeholder: Mìnich dè na ghnìomhan a chaidh a ghabhail no naidheachd sam bith eile mu dhèidhinn…
         title: Nòtaichean
       notes_description_html: Seall is sgrìobh nòtaichean do mhaoir eile is dhut fhèin san àm ri teachd
+      processed_msg: 'Chaidh gearan #%{id} a phròiseasadh'
       quick_actions_description_html: 'Gabh gnìomh luath no sgrolaich sìos a dh’fhaicinn susbaint a’ ghearain:'
       remote_user_placeholder: cleachdaiche cèin o %{instance}
       reopen: Fosgail an gearan a-rithist
@@ -635,9 +650,28 @@ gd:
       status: Staid
       statuses: Susbaint le gearan
       statuses_description_html: Thèid iomradh a thoirt air an t-susbaint oilbheumach sa chonaltradh leis a’ chunntas mun a chaidh an gearan a thogail
+      summary:
+        action_preambles:
+          delete_html: 'Tha thu an impis cuid de na postaichean aig <strong>@%{acct}</strong> a <strong>thoirt air falbh</strong>. Seo na thachras:'
+          mark_as_sensitive_html: 'Tha thu an impis <strong>comharra</strong> a chur gu bheil cuid de na postaichean aig <strong>@%{acct}</strong> <strong>frionasach</strong>. Seo na thachras:'
+          silence_html: 'Tha thu an impis an cunntas aig <strong>@%{acct}</strong> a <strong> chuingeachadh</strong>. Seo na thachras:'
+          suspend_html: 'Tha thu an impis an cunntas aig <strong>@%{acct}</strong> a <strong> chur à rèim</strong>. Seo na thachras:'
+        actions:
+          delete_html: Thoir air falbh na postaichean oilbheumach
+          mark_as_sensitive_html: Cuir comharra gu bheil meadhanan nam postaichean oilbheumach frionasach
+          silence_html: Thèid cò ruigeas <strong>@%{acct}</strong> a chuingeachadh gu mòr air sgàth ’s nach fhaic ach an fheadhainn a bheil ’ga leantainn mu thràth no a tha a’ lorg na pròifil aca a làimh a’ phròifil is an t-susbaint aca
+          suspend_html: Thèid <strong>@%{acct}</strong> a chur à rèim agus cha ghabh a’ phròifil is an t-susbaint aca a ruigsinn no eadar-ghabhail
+        close_report: 'Cuir comharra gun deach gearan #%{id} fhuasgladh'
+        close_reports_html: Cuir comharra gun deach <strong>gach</strong> gearan an aghaidh <strong>@%{acct}</strong> fhuasgladh
+        delete_data_html: Sguab às a’ phròifil ’s an t-susbaint aig <strong>@%{acct}</strong> an ceann 30 latha mura dèid an cur an gnìomh a-rithist roimhe sin
+        preview_preamble_html: 'Gheibh <strong>@%{acct}</strong> rabhadh leis an t-susbaint seo:'
+        record_strike_html: Clàraich rabhadh an aghaidh <strong>@%{acct}</strong> airson do chuideachadh ach am bi thu nas teinne le droch-ghiùlan on chunntas seo san àm ri teachd
+        send_email_html: Cuir post-d rabhaidh gu <strong>@%{acct}</strong>
+        warning_placeholder: Adhbharan roghainneil eile air gnìomh na maorsainneachd.
       target_origin: Tùs cunntas a’ ghearain
       title: Gearanan
       unassign: Dì-iomruin
+      unknown_action_msg: 'Gnìomh nach aithne dhuinn: %{action}'
       unresolved: Gun fhuasgladh
       updated_at: Air ùrachadh
       view_profile: Seall a’ phròifil
@@ -728,11 +762,16 @@ gd:
       content_retention:
         preamble: Stiùirich mar a tha susbaint an luchd-cleachdaidh ’ga stòradh ann am Mastodon.
         title: Glèidheadh na susbaint
+      default_noindex:
+        desc_html: Bidh buaidh air a h-uile cleachdaiche nach do dh’atharraich an roghainn seo dhaibh fhèin
+        title: Thoir air falbh ro-aonta nan cleachdaichean air inneacsadh le einnseanan-luirg mar a’ bhun-roghainn
       discovery:
         follow_recommendations: Molaidhean leantainn
         preamble: Tha tighinn an uachdar susbainte inntinniche fìor-chudromach airson toiseach-tòiseachaidh an luchd-cleachdaidh ùr nach eil eòlach air duine sam bith air Mastodon, ma dh’fhaoidte. Stiùirich mar a dh’obraicheas gleusan an rannsachaidh air an fhrithealaiche agad.
         profile_directory: Eòlaire nam pròifil
         public_timelines: Loidhnichean-ama poblach
+        publish_discovered_servers: Foillsich na frithealaichean a chaidh a lorg
+        publish_statistics: Foillsich an stadastaireachd
         title: Rùrachadh
         trends: Treandaichean
       domain_blocks:
@@ -787,6 +826,7 @@ gd:
         suspend: Chuir %{name} an cunntas aig %{target} à rèim
       appeal_approved: Air ath-thagradh
       appeal_pending: "’Ga ath-thagradh"
+      appeal_rejected: Chaidh ath-thagradh a dhiùltadh
     system_checks:
       database_schema_check:
         message_html: Tha imrichean stòir-dhàta ri dhèiligeadh ann. Ruith iad a dhèanamh cinnteach gum bi giùlan na h-aplacaid mar a bhiodhte ’n dùil
@@ -800,6 +840,12 @@ gd:
         message_html: Cha do mhìnich thu riaghailtean an fhrithealaiche fhathast.
       sidekiq_process_check:
         message_html: Chan eil pròiseas Sidekiq sam bith a ruith dhan chiutha/dha na ciuthan %{value}. Thoir sùil air an rèiteachadh Sidekiq agad
+      upload_check_privacy_error:
+        action: Thoir sùil an-seo airson barrachd fiosrachaidh
+        message_html: "<strong>Chaidh am frithealaiche agad a dhroch-rèiteachadh. Tha prìobhaideachd an luchd-cleachdaidh agad fo chunnart.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Thoir sùil an-seo airson barrachd fiosrachaidh
+        message_html: "<strong>Chaidh stòras nan oibseactan agad a dhroch-rèiteachadh. Tha prìobhaideachd an luchd-cleachdaidh agad fo chunnart.</strong>"
     tags:
       review: Dèan lèirmheas air an staid
       updated_msg: Chaidh roghainnean nan tagaichean hais ùrachadh
@@ -824,6 +870,7 @@ gd:
           two: Chaidh a cho-roinneadh le %{count} rè na seachdain seo chaidh
         title: Ceanglaichean a’ treandadh
         usage_comparison: Chaidh a cho-roinneadh %{today} tura(i)s an-diugh an coimeas ri %{yesterday} an-dè
+      not_allowed_to_trend: Chan fhaod seo treandadh
       only_allowed: An fheadhainn cheadaichte a-mhàin
       pending_review: A’ feitheamh air lèirmheas
       preview_card_providers:
@@ -961,6 +1008,7 @@ gd:
   applications:
     created: Chaidh an t-iarrtas a chruthachadh
     destroyed: Chaidh an t-iarrtas a sguabadh às
+    logout: Clàraich a-mach
     regenerate_token: Ath-ghin an tòcan inntrigidh
     token_regenerated: Chaidh an tòcan inntrigidh ath-ghintinn
     warning: Bi glè chùramach leis an dàta seo. Na co-roinn le duine sam bith e!
@@ -968,6 +1016,8 @@ gd:
   auth:
     apply_for_account: Iarr cunntas
     change_password: Facal-faire
+    confirmations:
+      wrong_email_hint: Mur eil an seòladh puist-d seo mar bu chòir, ’s urrainn dhut atharrachadh ann an roghainnean a’ chunntais.
     delete_account: Sguab às an cunntas
     delete_account_html: Nam bu mhiann leat an cunntas agad a sguabadh às, <a href="%{path}">nì thu an-seo e</a>. Thèid dearbhadh iarraidh ort.
     description:
@@ -995,6 +1045,8 @@ gd:
     resend_confirmation: Cuir an stiùireadh mun dearbhadh a-rithist
     reset_password: Ath-shuidhich am facal-faire
     rules:
+      accept: Gabh ris
+      back: Air ais
       preamble: Tha iad ’gan stèidheachadh is a chur an gnìomh leis na maoir aig %{domain}.
       title: Riaghailtean bunasach.
     security: Tèarainteachd
@@ -1003,6 +1055,9 @@ gd:
       email_below_hint_html: Mur eil am post-d gu h-ìosal mar bu chòir, ’s urrainn dhut atharrachadh an-seo agus gheibh thu post-d dearbhaidh ùr.
       email_settings_hint_html: Chaidh am post-d dearbhaidh a chur gu %{email}. Mur eil an seòladh puist-d seo mar bu chòir, ’s urrainn dhut atharrachadh ann an roghainnean a’ chunntais.
       title: Suidheachadh
+    sign_in:
+      preamble_html: Clàraich a-steach le do theisteas <strong>%{domain}</strong>. Ma tha an cunntas agad ’ga òstadh air frithealaiche eile, chan urrainn dhut clàradh a-steach an-seo.
+      title: Clàraich a-steach gu %{domain}
     sign_up:
       preamble: Le cunntas air an fhrithealaiche Mastodon seo, ’s urrainn dhut neach sam bith a leantainn air an lìonra, ge b’ e càit a bheil an cunntas aca-san ’ga òstadh.
       title: Suidhicheamaid %{domain} dhut.
@@ -1189,8 +1244,6 @@ gd:
       index:
         hint: Bidh a’ chriathrag seo an sàs air postaichean fa leth ge b’ e dè na roghainnean eile. ’S urrainn dhut barrachd phostaichean a chur ris a’ chriathrag seo leis an eadar-aghaidh-lìn.
         title: Postaichean criathraichte
-  footer:
-    trending_now: A’ treandadh an-dràsta
   generic:
     all: Na h-uile
     all_items_on_page_selected_html:
@@ -1221,8 +1274,6 @@ gd:
       one: Tha rud ann nach eil buileach ceart fhathast! Thoir sùil air an %{count} mhearachd gu h-ìosal
       other: Tha rud ann nach eil buileach ceart fhathast! Thoir sùil air an %{count} mearachd gu h-ìosal
       two: Tha rud ann nach eil buileach ceart fhathast! Thoir sùil air an %{count} mhearachd gu h-ìosal
-  html_validator:
-    invalid_markup: 'tha HTML markup mì-dhligheach ann: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Faidhle CSV mì-dhligheach. Mearachd: %{error}'
@@ -1408,7 +1459,11 @@ gd:
       unrecognized_emoji: "– chan aithne dhuinn an Emoji seo"
   relationships:
     activity: Gnìomhachd a’ chunntais
+    confirm_follow_selected_followers: A bheil thu cinnteach gu bheil thu airson an luchd-leantainn a thagh thu a leantainn?
+    confirm_remove_selected_followers: A bheil thu cinnteach gu bheil thu airson an luchd-leantainn a thagh thu a thoirt air falbh?
+    confirm_remove_selected_follows: A bheil thu cinnteach nach eil thu airson an fheadhainn a thagh thu a leantainn tuilleadh?
     dormant: Na thàmh
+    follow_failure: Cha deach le leantainn cuid dhe na cunntasan a thagh thu.
     follow_selected_followers: Lean an luchd-leantainn a thagh thu
     followers: Luchd-leantainn
     following: A’ leantainn
@@ -1448,6 +1503,7 @@ gd:
       electron: Electron
       firefox: Firefox
       generic: Brabhsair nach aithne dhuinn
+      huawei_browser: Brabhsair Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1457,6 +1513,7 @@ gd:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Brabhsair nach aithne dhuinn
       weibo: Weibo
     current_session: An seisean làithreach
     description: "%{browser} air %{platform}"
@@ -1469,9 +1526,10 @@ gd:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: ùrlar nach aithne dhuinn
+      unknown_platform: Ùrlar nach aithne dhuinn
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1696,12 +1754,13 @@ gd:
       title: Fàilte air bòrd, %{name}!
   users:
     follow_limit_reached: Chan urrainn dhut còrr is %{limit} daoine a leantainn
+    go_to_sso_account_settings: Tadhail air roghainnean cunntas solaraiche na dearbh-aithne agad
     invalid_otp_token: Còd dà-cheumnach mì-dhligheach
     otp_lost_help_html: Ma chaill thu an t-inntrigeadh dhan dà chuid diubh, ’s urrainn dhut fios a chur gu %{email}
     seamless_external_login: Rinn thu clàradh a-steach le seirbheis on taobh a-muigh, mar sin chan eil roghainnean an fhacail-fhaire ’s a’ phuist-d ri làimh dhut.
     signed_in_as: 'Chlàraich thu a-steach mar:'
   verification:
-    explanation_html: '’S urrainn dhut <strong>dearbhadh gur e seilbheadair nan ceanglaichean ann am meata-dàta na pròifil agad a th’ annad</strong>. Airson sin a dhèanamh, feumaidh ceangal air ais dhan phròifil Mastodon a bhith aig an làrach-lìn cheangailte. <strong>Feumaidh</strong> buadh <code>rel="me"</code> a bhith aig a’ cheangal air ais. Chan eil e gu diofar dè an t-susbaint a tha ann an teacsa a’ cheangail. Seo ball-eisimpleir dhut:'
+    explanation_html: '’S urrainn dhut <strong>dearbhadh gur e seilbheadair nan ceanglaichean ann am meata-dàta na pròifil agad a th’ annad</strong>. Airson sin a dhèanamh, feumaidh ceangal air ais dhan phròifil Mastodon a bhith aig an làrach-lìn cheangailte. Nuair a bhios tu air a’ cheangal a chur ris, dh’fhaoidte gum bi agad ri tilleadh an-seo agus a’ phròifil agad a shàbhaladh a-rithist mus obraich an dearbhadh. <strong>Feumaidh</strong> buadh <code>rel="me"</code> a bhith aig a’ cheangal air ais. Chan eil e gu diofar dè an t-susbaint a tha ann an teacsa a’ cheangail. Seo ball-eisimpleir dhut:'
     verification: Dearbhadh
   webauthn_credentials:
     add: Cuir iuchair tèarainteachd ùr ris
diff --git a/config/locales/gl.yml b/config/locales/gl.yml
index c14becfbf..aa451a699 100644
--- a/config/locales/gl.yml
+++ b/config/locales/gl.yml
@@ -91,6 +91,7 @@ gl:
       moderation:
         active: Activa
         all: Todo
+        disabled: Desactivada
         pending: Pendente
         silenced: Limitada
         suspended: Suspendidos
@@ -133,6 +134,7 @@ gl:
       search: Procurar
       search_same_email_domain: Outras usuarias co mesmo dominio de email
       search_same_ip: Outras usuarias co mesmo IP
+      security: Seguridade
       security_measures:
         only_password: Só contrasinal
         password_and_2fa: Contrasinal e 2FA
@@ -427,6 +429,7 @@ gl:
         resolve: Resolver dominio
         title: Nova entrada na listaxe negra de email
       no_email_domain_block_selected: Non se cambiou ningún bloqueo de dominio de email porque non se seleccionou ningún
+      not_permitted: Non permitido
       resolved_dns_records_hint_html: O nome de dominio resolve os seguintes rexistros MX, que son os últimos responsables da aceptación de emails. Bloqueando un dominio MX rexeitarás calquera enderezo de email que use este dominio MX, incluso se o nome de dominio visible é outro. <strong>Ten coidado de non bloquear os principais provedores.</strong>
       resolved_through_html: Resolto a través de %{domain}
       title: Listaxe negra de email
@@ -441,6 +444,7 @@ gl:
         private_comment_description_html: 'Para axudarche a lembrar de onde veñen os bloqueos importados, imos crealos engadindo o seguinte comentario privado: <q>%{comment}</q>'
         private_comment_template: Importada desde %{source} o %{date}
         title: Importar bloqueos de dominio
+      invalid_domain_block: 'Un ou varios dominios non se bloquearon debido ao seguinte erro(s): %{error}'
       new:
         title: Importar bloqueos de dominio
       no_file: Ningún ficheiro seleccionado
@@ -472,6 +476,7 @@ gl:
       content_policies:
         comment: Nota interna
         description_html: Podes definir políticas acerca do contido que serán aplicadas a tódalas contas deste dominio e tódolos seus subdominios.
+        limited_federation_mode_description_html: Podes elexir se permites ou non a federación con este dominio.
         policies:
           reject_media: Rexeitar multimedia
           reject_reports: Rexeitar denuncias
@@ -557,7 +562,7 @@ gl:
       pending: Agardando pola aprobación do repetidor
       save_and_enable: Gardar e activar
       setup: Configurar unha conexión ó repetidor
-      signatures_not_enabled: Os repetidores non funcionarán de xeito correcto se o modo seguro ou listaxe branca están activados
+      signatures_not_enabled: Os repetidores non funcionarán de xeito correcto se o modo seguro ou o modo de federación limitada están activados
       status: Estado
       title: Repetidores
     report_notes:
@@ -575,19 +580,23 @@ gl:
         mark_as_sensitive_description_html: Os multimedia das publicacións denunciadas serán marcados como sensibles e engadirase un aviso para axudarche a xestionar futuras infraccións da mesma conta.
         other_description_html: Mira máis opcións para controlar o comportamento da conta e personalizar as comunicacións coa conta denunciada.
         resolve_description_html: Non se van tomar accións contra a conta denunciada, nin se gardarán avisos, e pecharase a denuncia.
-        silence_description_html: O perfil será visible só para quen xa o está a seguir ou quen o buscou manualmente, limitando moito o seu alcance. Pódese cambiar.
-        suspend_description_html: O perfil e tódolos seus contidos será inaccesbles e finalmente eliminados. A interacción coa conta non será posible. Reversible durante 30 días.
+        silence_description_html: A conta será visible só para quen xa a está a seguir ou quen a buscou manualmente, limitando moito o seu alcance. Pódese cambiar. Isto pecha tódalas denuncias acerca desta conta.
+        suspend_description_html: A conta e todo o seu contido non serán accesible e finalmente eliminaranse, será imposible interactuar con ela. A decisión é reversible durante 30 días. Isto pecha tódalas denuncias sobre esta conta.
       actions_description_html: Decide que acción tomar respecto desta denuncia. Se tomas accións punitivas contra a conta denunciada, enviaráselles un email coa notificación, excepto se está seleccionada a categoría <strong>Spam</strong>.
+      actions_description_remote_html: Decide a acción a tomar para resolver a denuncia. Isto só lle afecta ao xeito en que o <strong>teu</strong> servidor se comunica con esta conta remota e xestiona o seu contido.
       add_to_report: Engadir máis á denuncia
       are_you_sure: Tes certeza?
       assign_to_self: Asignarme
       assigned: Moderador asignado
       by_target_domain: Dominio da conta denunciada
+      cancel: Cancelar
       category: Categoría
       category_description_html: A razón para denunciar esta conta ou contido será citada na comunicación coa conta denunciada
       comment:
         none: Ningún
       comment_description_html: 'Como información engadida, %{name} escribiu:'
+      confirm: Confirmar
+      confirm_action: Confirma a acción de moderación contra @%{acct}
       created_at: Denunciado
       delete_and_resolve: Eliminar publicacións
       forwarded: Reenviado
@@ -604,6 +613,7 @@ gl:
         placeholder: Describir que accións foron tomadas ou calquera outra novidade sobre esta denuncia...
         title: Notas
       notes_description_html: Ver e deixar unha nota para ti no futuro e outras moderadoras
+      processed_msg: 'Procesada correctamente a denuncia #%{id}'
       quick_actions_description_html: 'Toma unha acción rápida ou desprázate abaixo para ver o contido denunciado:'
       remote_user_placeholder: a usuaria remota desde %{instance}
       reopen: Reabrir denuncia
@@ -616,9 +626,28 @@ gl:
       status: Estado
       statuses: Contido denunciado
       statuses_description_html: O contido ofensivo será citado na comunicación coa conta denunciada
+      summary:
+        action_preambles:
+          delete_html: 'Vas <strong>eliminar</strong> algunha das publicacións de <strong>@%{acct}</strong>. Serán:'
+          mark_as_sensitive_html: 'Vas <strong>marcar</strong> algunha das publicacións de <strong>@%{acct}</strong> como <strong>sensibles</strong>. Serán:'
+          silence_html: 'Vas <strong>limitar</strong> a conta <strong>@%{acct}</strong>:'
+          suspend_html: 'Vas <strong>suspender</strong> a conta <strong>@%{acct}</strong>:'
+        actions:
+          delete_html: Eliminar as publicacións ofensivas
+          mark_as_sensitive_html: Marcar as publicacións ofensivas como sensibles
+          silence_html: Limitar moito a visibilidade e alcance da conta <strong>@%{acct}</strong> facendo que o seu perfil e contidos sexan visibles só por persoas que a seguen ou que a busquen de xeito activo
+          suspend_html: Suspender a conta <strong>@%{acct}</strong>, facendo que o seu perfil e contidos non sexan accesibles e imposible interactuar con ela
+        close_report: 'Marcar a denuncia #%{id} como resolta'
+        close_reports_html: Marcar <strong>todas</strong> as denuncias contra <strong>@%{acct}</strong> como resoltas
+        delete_data_html: Eliminar o perfil e contidos de <strong>@%{acct}</strong> para os próximos 30 días a non ser que sexa suspendida nese período
+        preview_preamble_html: "<strong>@%{acct}</strong> vai recibir un aviso co seguinte contido:"
+        record_strike_html: Anotar un aviso contra <strong>@%{acct}</strong> para axudarche a xestionar futuros problemas con esta conta
+        send_email_html: Enviar un email de aviso a <strong>@%{acct}</strong>
+        warning_placeholder: Razóns adicionais optativas para a acción de moderación.
       target_origin: Orixe da conta denunciada
       title: Denuncias
       unassign: Non asignar
+      unknown_action_msg: 'Acción descoñecida: %{action}'
       unresolved: Non resolto
       updated_at: Actualizado
       view_profile: Ver perfil
@@ -713,6 +742,8 @@ gl:
         preamble: Destacar contido interesante é importante para axudar a que as novas usuarias se sintan cómodas se non coñecen a ninguén en Mastodon. Xestiona os diferentes xeitos de promocionar contidos.
         profile_directory: Directorio de perfís
         public_timelines: Cronoloxías públicas
+        publish_discovered_servers: Publicar os servidores descubertos
+        publish_statistics: Publicar estatísticas
         title: Descubrir
         trends: Tendencias
       domain_blocks:
@@ -767,6 +798,7 @@ gl:
         suspend: "%{name} suspendeu a conta de %{target}"
       appeal_approved: Recurrida
       appeal_pending: Apelación pendente
+      appeal_rejected: Apelación rexeitada
     system_checks:
       database_schema_check:
         message_html: Existen migracións pendentes na base de datos. Bota man desta tarefa para facer que a aplicación funcione como se agarda dela
@@ -780,6 +812,12 @@ gl:
         message_html: Non tes definidas regras para o servidor.
       sidekiq_process_check:
         message_html: Non hai procesos Sidekiq a funcionar para a cola(s) %{value}. Revisa a túa configuración para Sidekiq
+      upload_check_privacy_error:
+        action: Mira aquí para máis información
+        message_html: "<strong>O teu servidor non está ben configurado. A privacidade das usuarias está en risco.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Mira aquí para máis información
+        message_html: "<strong>A almacenaxe de obxectos está mal configurada. A privacidade das usuarias está en risco.</strong>"
     tags:
       review: Estado de revisión
       updated_msg: Actualizaronse os axustes dos cancelos
@@ -802,6 +840,7 @@ gl:
           other: Compartido por %{count} persoas na última semana
         title: Ligazóns en voga
         usage_comparison: Compartido %{today} veces hoxe, comparado con %{yesterday} onte
+      not_allowed_to_trend: Non permitido como tendencia
       only_allowed: Só as permitidas
       pending_review: Revisión pendente
       preview_card_providers:
@@ -933,6 +972,7 @@ gl:
   applications:
     created: Creouse con éxito este aplicativo
     destroyed: Eliminouse con éxito o aplicativo
+    logout: Pechar sesión
     regenerate_token: Votar a xenerar o testemuño de acceso
     token_regenerated: Rexenerouse con éxito o testemuño de acceso
     warning: Ten moito tino con estos datos. Non os compartas nunca con ninguén!
@@ -940,6 +980,8 @@ gl:
   auth:
     apply_for_account: Solicita unha conta
     change_password: Contrasinal
+    confirmations:
+      wrong_email_hint: Se o enderezo de email non é correcto, podes cambialo nos axustes da conta.
     delete_account: Eliminar conta
     delete_account_html: Se queres eliminar a túa conta, podes <a href="%{path}">facelo aquí</a>. Deberás confirmar a acción.
     description:
@@ -967,6 +1009,8 @@ gl:
     resend_confirmation: Reenviar as intruccións de confirmación
     reset_password: Restablecer contrasinal
     rules:
+      accept: Aceptar
+      back: Volver
       preamble: Son establecidas e aplicadas pola moderación de %{domain}.
       title: Algunhas regras básicas.
     security: Seguranza
@@ -1085,7 +1129,7 @@ gl:
     '406': Esta páxina non está dispoñible no formato solicitado.
     '410': A páxina que estaba a buscar xa non existe.
     '422':
-      content: Fallou a verificación de seguridade. Está bloqueando as cookies?
+      content: Fallou a verificación de seguridade. Estás bloqueando os rastros?
       title: Fallou a verficación de seguridade
     '429': Acelerado
     '500':
@@ -1114,7 +1158,7 @@ gl:
   featured_tags:
     add_new: Engadir novo
     errors:
-      limit: Xa acadaches o número máximo de cancelos
+      limit: Xa sinalaches o número máximo de cancelos permitido
     hint_html: "<strong>¿Qué son os cancelos destacados?</strong> Móstranse destacados no teu perfil público e permítenlle a outras persoas ver os teus toots públicos nos que os utilizaches. Son unha ferramenta moi útil para facer seguimento de traballos creativos e proxectos a longo prazo."
   filters:
     contexts:
@@ -1158,8 +1202,6 @@ gl:
       index:
         hint: Este filtro aplícase para seleccionar publicacións individuais independentemente doutros criterios. Podes engadir máis publicacións a este filtro desde a interface web.
         title: Publicacións filtradas
-  footer:
-    trending_now: Tendencia agora
   generic:
     all: Todo
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ gl:
     validation_errors:
       one: Algo non está ben de todo! Por favor revise abaixo o erro
       other: Algo aínda non está ben! Por favor revise os %{count} erros abaixo
-  html_validator:
-    invalid_markup: 'contén cancelos HTML non válidas: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Ficheiro CSV non válido. Erro: %{error}'
@@ -1226,7 +1266,7 @@ gl:
     title: Convidar xente
   lists:
     errors:
-      limit: Acadou o número máximo de listas
+      limit: Xa acadaches o número máximo de listas
   login_activities:
     authentication_methods:
       otp: app para autenticación con dous factores
@@ -1367,7 +1407,11 @@ gl:
       unrecognized_emoji: non é unha emoticona recoñecida
   relationships:
     activity: Actividade da conta
+    confirm_follow_selected_followers: Tes a certeza de querer seguir as seguidoras seleccionadas?
+    confirm_remove_selected_followers: Tes a certeza de querer deixar de seguir as seguidoras seleccionadas?
+    confirm_remove_selected_follows: Tes a certeza de querer eliminar os seguimentos seleccionados?
     dormant: En repouso
+    follow_failure: Non puido seguir algunha das contas seleccionadas.
     follow_selected_followers: Seguir seguidoras seleccionadas
     followers: Seguidoras
     following: A Seguir
@@ -1407,6 +1451,7 @@ gl:
       electron: Electron
       firefox: Firefox
       generic: Navegador descoñecido
+      huawei_browser: Navegador Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Navegador Nokia S40 Ovi
@@ -1416,6 +1461,7 @@ gl:
       qq: Navegador QQ
       safari: Safari
       uc_browser: Navegador QQ
+      unknown_browser: Navegador descoñecido
       weibo: Weibo
     current_session: Sesión actual
     description: "%{browser} en %{platform}"
@@ -1428,9 +1474,10 @@ gl:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: plataforma descoñecida
+      unknown_platform: Plataforma descoñecida
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1543,7 +1590,7 @@ gl:
       '7889238': 3 meses
     min_age_label: Límite temporal
     min_favs: Manter as publicacións favoritas máis de
-    min_favs_hint: Non elimina ningunha das túas publicacións que recibiron máis desta cantidade de favoritos. Deixa en branco para eliminar publicacións independentemente do número de favorecementos
+    min_favs_hint: Non elimina ningunha das túas publicacións que recibiron alomenos esta cantidade de favorecementos. Deixa en branco para eliminar publicacións independentemente do número de favorecementos
     min_reblogs: Manter publicacións promovidas máis de
     min_reblogs_hint: Non elimina ningunha das túas publicacións se foron promovidas máis deste número de veces. Deixa en branco para eliminar publicacións independentemente do seu número de promocións
   stream_entries:
@@ -1643,12 +1690,13 @@ gl:
       title: Benvida, %{name}!
   users:
     follow_limit_reached: Non pode seguir a máis de %{limit} persoas
+    go_to_sso_account_settings: Ir aos axustes da conta no teu provedor de identidade
     invalid_otp_token: O código do segundo factor non é válido
     otp_lost_help_html: Se perdes o acceso a ambos, podes contactar con %{email}
     seamless_external_login: Accedeches a través dun servizo externo, polo que os axustes de contrasinal e email non están dispoñibles.
     signed_in_as: 'Rexistrada como:'
   verification:
-    explanation_html: 'Podes <strong>validarte a ti mesma como a dona das ligazóns nos metadatos do teu perfil</strong>. Para esto, o sitio web ligado debe conter unha ligazón de retorno ao perfil de Mastodon. Esta ligazón de retorno <strong>ten que</strong> ter un atributo <code>rel="me"</code>. O texto da ligazón non importa. Aquí tes un exemplo:'
+    explanation_html: 'Podes <strong>validarte a ti mesma como a dona das ligazóns nos metadatos do perfil</strong>. Para isto, o sitio web ligado debe conter unha ligazón de retorno ao perfil de Mastodon. Despois de engadir a ligazón tes que volver aquí e volver a gardar o teu perfil para que a verificación tome efecto. A ligazón de retorno <strong>ten que</strong> ter un atributo <code>rel="me"</code>. O texto da ligazón non ten importancia. Aquí tes un exemplo:'
     verification: Validación
   webauthn_credentials:
     add: Engadir nova chave de seguridade
diff --git a/config/locales/he.yml b/config/locales/he.yml
index 201d8c45f..ab53dc581 100644
--- a/config/locales/he.yml
+++ b/config/locales/he.yml
@@ -95,6 +95,7 @@ he:
       moderation:
         active: פעילים
         all: הכל
+        disabled: מושבת
         pending: בהמתנה
         silenced: מוגבלים
         suspended: מושהים
@@ -139,6 +140,7 @@ he:
       search: חיפוש
       search_same_email_domain: משתמשים אחרים מאותו דומיין דוא"ל
       search_same_ip: משתמשים אחרים מאותה כתובת IP
+      security: אבטחה
       security_measures:
         only_password: סיסמה בלבד
         password_and_2fa: סיסמה ואימות דו-שלבי
@@ -443,6 +445,7 @@ he:
         resolve: פתור דומיין
         title: חסימת דומיין דוא"ל
       no_email_domain_block_selected: לא בוצעו שינויים לחסימת דומייני דוא"ל שכן לא נבחרו דומיינים
+      not_permitted: נאסר
       resolved_dns_records_hint_html: שם הדומיין מוביל לדומייניי ה-MX הבאים, שהם בסופו של דבר אחראיים לקבלת דוא"ל. חסימת דומיין MX תוביל לחסימת הרשמות מכל כתובת דוא"ל שעושה שימוש בדומיין MX זה, אפילו אם הדומיין הגלוי שונה. <strong>יש להמנע מלחסום ספקי דוא"ל מובילים.</strong>
       resolved_through_html: נמצא דרך %{domain}
       title: דומייניי דוא"ל חסומים
@@ -457,6 +460,7 @@ he:
         private_comment_description_html: 'כדי לסייע במעקב מאיכן הגיעו חסימות, חסימות מיובאות ילוו בהערה פרטית זו: <q>%{comment}</q>'
         private_comment_template: יובא מתוך %{source} בתאריך %{date}
         title: יבוא רשימת שרתים חסומים
+      invalid_domain_block: 'חסימה של שרת אחד או יותר דולגו בשל השגיאה הבאה: %{error}'
       new:
         title: יבוא רשימת שרתים חסומים
       no_file: לא נבחר קובץ
@@ -492,6 +496,7 @@ he:
       content_policies:
         comment: הערה פנימית
         description_html: ביכולתך להגדיר מדיניות תוכן שתופעל על כל החשבונות מדומיין זה ומתת-דומייניו.
+        limited_federation_mode_description_html: ניתן לבחור אם להרשות תקשורת הדדית עם שרת זה.
         policies:
           reject_media: דחיית מדיה
           reject_reports: דחיית דו"חות
@@ -534,7 +539,7 @@ he:
       public_comment: תגובה פומבית
       purge: טיהור
       purge_description_html: אם יש יסוד להניח שדומיין זה מנותק לעד, ניתן למחוק את כל רשומות החשבונות והמידע המשוייך לדומיין זה משטח האפסון שלך. זה עשוי לקחת זמן מה.
-      title: שרתים מוכרים
+      title: שרתים בפדרציה
       total_blocked_by_us: חסום על ידינו
       total_followed_by_them: נעקב על ידם
       total_followed_by_us: נעקב על ידינו
@@ -599,19 +604,23 @@ he:
         mark_as_sensitive_description_html: המדיה בהודעות מדווחות תסומן כרגישה ועבירה תרשם כדי לעזור לך להסלים באינטראקציות עתידיות עם אותו החשבון.
         other_description_html: ראו אפשרויות נוספות לשליטה בהתנהגות החשבון וכדי לבצע התאמות בתקשורת עם החשבון המדווח.
         resolve_description_html: אף פעולה לא תבוצע נגד החשבון עליו דווח, לא תירשם עבירה, והדיווח ייסגר.
-        silence_description_html: הפרופיל יהיה גלוי אך ורק לאלה שכבר עוקבים אחריו או לאלה שיחפשו אותו ידנית, מה שיגביל מאד את תפוצתו. ניתן תמיד להחזיר את המצב לקדמותו.
-        suspend_description_html: הפרופיל וכל תכולתו יחסמו לכל גישה עד שבסופו של דבר ימחקו. כל תקשורת עם החשבון תהיה בלתי אפשרית. ניתן לביטול תוך 30 יום.
+        silence_description_html: הפרופיל יהיה גלוי אך ורק לאלה שכבר עוקבים אחריו או לאלה שיחפשו אותו ידנית, מה שיגביל מאד את תפוצתו. ניתן תמיד להחזיר את המצב לקדמותו. פעולה זו תסגור את כל הדיווחים נגד הפרופיל.
+        suspend_description_html: חשבון זה על כל תכניו יחסמו וברבות הימים ימחקו, כל פעילות מולו לא תתאפשר. הפעולה ניתנת לביטול תוך 30 ימים, והיא תסגור כל דיווח התלוי ועומד נגד החשבון.
       actions_description_html: בחר/י איזו פעולה לבצע על מנת לפתור את הדו"ח. אם תופעל פעולת ענישה כנגד החשבון המדווח, הודעת דוא"ל תשלח אליהם, אלא אם נבחרה קטגוריית ה<strong>ספאם</strong>.
+      actions_description_remote_html: בחרו איזו פעולה לבצע כדי לפתור את הדיווח שהוגש. פעולה זו תשפיע רק על התקשורת מול השרת <strong>שלך</strong> עם החשבון המרוחק ותוכנו.
       add_to_report: הוספת פרטים לדיווח
       are_you_sure: 100% על בטוח?
       assign_to_self: הקצה אלי
       assigned: מנחה מוקצה
       by_target_domain: דומיין החשבון המדווח
+      cancel: ביטול
       category: קטגוריה
       category_description_html: הסיבה בגללה חשבון זה ו/או תוכנו דווחו תצוטט בתקשורת עם החשבון המדווח
       comment:
         none: ללא
       comment_description_html: 'על מנת לספק עוד מידע, %{name} כתב\ה:'
+      confirm: אישור
+      confirm_action: נא לאשר פעולת משמעת לגבי חשבון %{acct}
       created_at: מדווח
       delete_and_resolve: מחיקת הודעות
       forwarded: קודם
@@ -628,6 +637,7 @@ he:
         placeholder: תאר/י אילו פעולות ננקטו, או עדכונים קשורים אחרים...
         title: הערות
       notes_description_html: צפייה והשארת הערות למנחים אחרים או לעצמך העתידי
+      processed_msg: דיווח %{id} עוּבָּד בהצלחה
       quick_actions_description_html: 'נקוט/י פעולה מהירה או גלול/י למטה לצפייה בתוכן המדווח:'
       remote_user_placeholder: המשתמש המרוחק מ-%{instance}
       reopen: פתיחת דו"ח מחדש
@@ -640,9 +650,28 @@ he:
       status: מצב
       statuses: התוכן עליו דווח
       statuses_description_html: התוכן הפוגע יצוטט בתקשורת עם החשבון המדווח
+      summary:
+        action_preambles:
+          delete_html: 'הפעולה <strong>תמחק</strong> כמה הודעות של חשבון <strong>@%{acct}</strong>. תוצאות הפעולה יהיו:'
+          mark_as_sensitive_html: 'הפעולה הבאה <strong>תסמן</strong> כמה הודעות של חשבון <strong>@%{acct}</strong> כ<strong>רגישות</strong>. תוצאות הפעולה יהיו:'
+          silence_html: 'הפעולה הבאה <strong>תגביל</strong> את החשבון <strong>@%{acct}</strong>. תוצאות הפעולה יהיו:'
+          suspend_html: 'הפעולה הבאה <strong>תקפיא</strong> את החשבון <strong>@%{acct}</strong>. תוצאות הפעולה יהיו:'
+        actions:
+          delete_html: הסרת ההודעות החורגות
+          mark_as_sensitive_html: סימון המדיה בהודעות החורגות כרגיש לצפיה
+          silence_html: הגבלה חמורה של תפוצת <strong>@%{acct}</strong> על ידי הפיכת החשבון ותכניו לזמינים רק למי שכבר עוקב אחריו או מי שיחפשו את עמוד הפרופיל שלו ישירות
+          suspend_html: הקפאת החשבון <strong>@%{acct}</strong>, הפיכת הפרופיל והתוכן לנסתרים ולא ניתנים לתקשורת
+        close_report: 'סימון דיווח #%{id} בתור פתור'
+        close_reports_html: סימון <strong>כל</strong> הדיווחים נגד <strong>@%{acct}</strong> בתור פתורים
+        delete_data_html: למחוק את הפרופיל והתוכן של <strong>@%{acct}</strong> בעוד 30 יום אלא אם תוסר ההגבלה עליהם לפני כן
+        preview_preamble_html: 'שליחת אזהרה אל <strong>@%{acct}</strong> בזו הלשון:'
+        record_strike_html: ציין נקודה שחורה נגד <strong>@%{acct}</strong> כדי לסייע בשיפוטו בדיווחים עתידיים על חריגות
+        send_email_html: שליחת דואל אזהרה אל <strong>@%{acct}</strong>
+        warning_placeholder: צידוקים אפשריים נוספים לפעולה המשמעתית.
       target_origin: מקור החשבון המדווח
       title: דיווחים
       unassign: ביטול הקצאה
+      unknown_action_msg: 'פעולה לא מוכרת: %{action}'
       unresolved: לא פתור
       updated_at: עודכן
       view_profile: צפה בפרופיל
@@ -741,7 +770,9 @@ he:
         preamble: הצפה של תוכן מעניין בקבלת פני משתמשות חדשות שאולי אינן מכירות עדיין א.נשים במסטודון. ניתן לשלוט איך אפשרויות גילוי שונות עובדות על השרת שלך.
         profile_directory: מדריך פרופילים
         public_timelines: פידים פומביים
-        title: איתור
+        publish_discovered_servers: פרסום שרתים שנתגלו
+        publish_statistics: פרסום הסטטיסטיקות בפומבי
+        title: גילוי
         trends: נושאים חמים
       domain_blocks:
         all: לכולם
@@ -795,6 +826,7 @@ he:
         suspend: "%{name} השעה/תה את חשבונו/ה של %{target}"
       appeal_approved: עורער
       appeal_pending: בהמתנה לערעור
+      appeal_rejected: הערעור נדחה
     system_checks:
       database_schema_check:
         message_html: נדרשות הגירות מבני נתונים. אנא הריצו אותן כדי להבטיח שהיישום מתנהג כצפוי
@@ -808,6 +840,12 @@ he:
         message_html: לא הוגדרו שום כללי שרת.
       sidekiq_process_check:
         message_html: שום הליכי Sidekiq לא רצים עבור %{value} תור(ות). בחנו בבקשה את הגדרות Sidekiq
+      upload_check_privacy_error:
+        action: למידע נוסף
+        message_html: "<strong>שרת הווב שלך אינו מכוון כראוי. פרטיות המשתמשות והמשתמשים שלך בסכנה.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: למידע נוסף
+        message_html: "<strong>שרות אחסון הענן שלך אינו מוגדר כראוי. פרטיות המשתמשות והמשתמשים שלך בסכנה.</strong>"
     tags:
       review: סקירת מצב
       updated_msg: הגדרות תגיות עודכנו בהצלחה
@@ -832,6 +870,7 @@ he:
           two: הופץ על ידי %{count} אנשים בשבוע האחרון
         title: קישוריות חמות
         usage_comparison: הופץ %{today} פעמים היום, לעומת %{yesterday} אתמול
+      not_allowed_to_trend: לא מורשה להופיע כנושא חם
       only_allowed: רק כאלה שהותרו
       pending_review: בהמתנה לבדיקה
       preview_card_providers:
@@ -969,6 +1008,7 @@ he:
   applications:
     created: ישום נוצר בהצלחה
     destroyed: ישום נמחק בהצלחה
+    logout: יציאה
     regenerate_token: יצירת אסימון גישה מחדש
     token_regenerated: אסימון גישה יוצר מחדש בהצלחה
     warning: זהירות רבה נדרשת עם מידע זה. אין לחלוק אותו אף פעם עם אף אחד!
@@ -976,6 +1016,8 @@ he:
   auth:
     apply_for_account: הגשת בקשה לחשבון
     change_password: סיסמה
+    confirmations:
+      wrong_email_hint: אם כתובת הדואל הזו איננה נכונה, ניתן לשנות אותה בעמוד ההגדרות.
     delete_account: מחיקת חשבון
     delete_account_html: אם ברצונך למחוק את החשבון, ניתן <a href="%{path}">להמשיך כאן</a>. תתבקש/י לספק אישור נוסף.
     description:
@@ -1003,6 +1045,8 @@ he:
     resend_confirmation: שלח הוראות אימות בשנית
     reset_password: איפוס סיסמה
     rules:
+      accept: הסכמה
+      back: בחזרה
       preamble: אלו נקבעים ונאכפים ע"י המנחים של %{domain}.
       title: כמה חוקים בסיסיים.
     security: אבטחה
@@ -1150,7 +1194,7 @@ he:
   featured_tags:
     add_new: הוספת חדש
     errors:
-      limit: המספר המירבי של התגיות כבר מוצג
+      limit: הצגת כבר את המספר המירבי של תגיות נבחרות
     hint_html: "<strong>מהן תגיות נבחרות?</strong> הן מוצגות במובלט בפרופיל הפומבי שלך ומאפשר לאנשים לעיין בהודעות הפומביות שלך המסומנות בתגיות אלה. הן כלי אדיר למעקב אחר עבודות יצירה ופרוייקטים לטווח ארוך."
   filters:
     contexts:
@@ -1200,8 +1244,6 @@ he:
       index:
         hint: סנן זה חל באופן של בחירת הודעות בודדות ללא תלות בקריטריונים אחרים. תוכלו להוסיף עוד הודעות לסנן זה ממנשק הווב.
         title: הודעות שסוננו
-  footer:
-    trending_now: נושאים חמים
   generic:
     all: הכל
     all_items_on_page_selected_html:
@@ -1232,8 +1274,6 @@ he:
       one: משהו עדיין לא בסדר! נא לעיין בשגיאה להלן
       other: משהו עדיין לא בסדר! נא לעיין ב-%{count} השגיאות להלן
       two: משהו עדיין לא בסדר! נא לעיין ב-%{count} השגיאות להלן
-  html_validator:
-    invalid_markup: 'מכיל קוד HTML לא תקין: %{error}'
   imports:
     errors:
       invalid_csv_file: 'קובץ CSV שבור. שגיאה: %{error}'
@@ -1278,7 +1318,7 @@ he:
     title: הזמנת אנשים
   lists:
     errors:
-      limit: הגעת למספר הרשימות המירבי.
+      limit: הגעת למספר הרשימות המירבי
   login_activities:
     authentication_methods:
       otp: יישומון אימות דו-שלבי
@@ -1419,7 +1459,11 @@ he:
       unrecognized_emoji: הוא לא אמוג'י מוכר
   relationships:
     activity: רמת פעילות
+    confirm_follow_selected_followers: האם את/ה בטוח/ה שברצונך לעקוב אחרי החשבונות שסומנו?
+    confirm_remove_selected_followers: האם את/ה בטוח/ה שברצונך להסיר את העוקבים שסומנו?
+    confirm_remove_selected_follows: האם את/ה בטוח/ה שברצונך להסיר את הנעקבים שסומנו?
     dormant: רדומים
+    follow_failure: נכשלה העקיבה אחרי חלק מהחשבונות שבחרת.
     follow_selected_followers: עקוב אחר הנעקבים שנבחרו
     followers: עוקבים
     following: נעקבים
@@ -1459,6 +1503,7 @@ he:
       electron: אלקטרון
       firefox: פיירפוקס
       generic: דפדפן לא ידוע
+      huawei_browser: דפדפן וואווי
       ie: אינטרנט אקספלורר
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1468,6 +1513,7 @@ he:
       qq: דפדפן QQ
       safari: ספארי
       uc_browser: דפדפן UC
+      unknown_browser: דפדפן לא מזוהה
       weibo: Weibo
     current_session: חיבור נוכחי
     description: "%{browser} על %{platform}"
@@ -1480,9 +1526,10 @@ he:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: מערכת הפעלה KaiOS
       linux: לינוקס
       mac: macOS
-      other: סביבה לא ידועה
+      unknown_platform: פלטפורמה לא מזוהה
       windows: חלונות
       windows_mobile: חלונות מובייל
       windows_phone: טלפון חלונות
@@ -1607,7 +1654,7 @@ he:
       '7889238': 3 חודשים
     min_age_label: סף גיל
     min_favs: השאר הודעות מחובבות לפחות
-    min_favs_hint: לא מוחק מי מהודעותיך שקיבלו לפחות את המספר הזה של חיבובים. להשאיר ריק כדי למחוק הודעות ללא קשר למספר החיבובים שקיבלו
+    min_favs_hint: לא תימחקנה הודעות שלך שקיבלו לפחות מספר זה של חיבובים. אם יישאר ריק, הודעות תימחקנה ללא תלות במספר החיבובים שלהן.
     min_reblogs: שמור הודעות מהודהדות לפחות
     min_reblogs_hint: לא מוחק מי מהודעותיך שקיבלו לפחות את המספר הזה של הדהודים. להשאיר ריק כדי למחוק הודעות ללא קשר למספר ההדהודים שקיבלו
   stream_entries:
@@ -1707,12 +1754,13 @@ he:
       title: ברוך/ה הבא/ה, %{name} !
   users:
     follow_limit_reached: לא תוכל לעקוב אחר יותר מ %{limit} אנשים
+    go_to_sso_account_settings: מעבר לאפיוני החשבון שלך בשרת הזהות
     invalid_otp_token: קוד דו-שלבי שגוי
     otp_lost_help_html: אם איבדת גישה לשניהם, ניתן ליצור קשר ב-%{email}
     seamless_external_login: את.ה מחובר דרך שירות חיצוני, לכן אפשרויות הסיסמא והדוא"ל לא מאופשרות.
     signed_in_as: 'מחובר בתור:'
   verification:
-    explanation_html: 'ניתן <strong>לאמת את עצמך כבעלים של הקישורית במטא-נתונים של פרופילך</strong>. כדי לעשות זאת, האתר המקושר חייב להכיל קישורית חוזרת לפרופיל המסטודון שלך. הקישורית החוזרת <strong>חייבת</strong> להכיל תכונת <code>rel="me"</code>. התוכן הטקסטואלי של הקישורית לא משנה. הנה דוגמא:'
+    explanation_html: 'תוכל/י <strong>לאמת את עצמך כבעל/ת הקישורים שבפרופיל שלך</strong>. לשם כך, על האתר המקושר להכיל קישור חוזר לפרופיל המסטודון שלך. אחרי הוספת הקישור ניתן לשוב לפה ולשמור מחדש את הפרופיל כדי להפעיל את אימות הקישור. הקישור החוזר <strong>חייב</strong> להכיל בקוד ה-HTML שלו את התכונה <code>rel="me"</code>. התוכן הטקסטואלי של הקישור לא משנה. הנה דוגמא:'
     verification: אימות
   webauthn_credentials:
     add: הוספת מפתח אבטחה חדש
diff --git a/config/locales/hi.yml b/config/locales/hi.yml
index 7678edc38..809e425d8 100644
--- a/config/locales/hi.yml
+++ b/config/locales/hi.yml
@@ -1,12 +1,28 @@
 ---
 hi:
+  admin:
+    system_checks:
+      upload_check_privacy_error:
+        message_html: "<strong> आपके वेब सर्वर का कन्फिगरेशन सही नहीं है। उपयोगकर्ताओं की निजता खतरे में है। </strong>"
+      upload_check_privacy_error_object_storage:
+        action: अधिक जानकारी हेतु यहां क्लिक करें।
+        message_html: "<strong> आपके वेब सर्वर का कन्फिगरेशन सही नहीं है। उपयोगकर्ताओं की निजता खतरे में है। </strong>"
   errors:
     '400': The request you submitted was invalid or malformed.
     '403': You don't have permission to view this page.
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
+  relationships:
+    follow_failure: चुने हुए अकाउंट्स में से कुछ को फ़ॉलो नहीं किया जा सकता
+  sessions:
+    browsers:
+      huawei_browser: हुआवे ब्राउज़र
+      unknown_browser: अनजान ब्राउज़र
+    platforms:
+      kai_os: काइ ओएस
+      unknown_platform: अनजान प्लेटफॉर्म
+  verification:
+    explanation_html: 'आप अपने <strong> प्रोफाइल में इस्तेमाल किए गए लिंक वेरिफाई कर सकते हैं</strong>। इसके लिए आपके वेबसाइट पर आपके मॅस्टोडॉन प्रोफाइल का लिंक होना चाहिए। वेरिफिकेशन पूरा करने के लिए लिंक जोड़ने के बाद यहाँ वापस आकर अपना प्रोफाइल पुनः सेव करें। लिंक बैक में <code>rel="me"</code> अट्रीब्यूट <strong>ज़रूर</strong> होना चाहिए। लिंक पर लिखे टेक्स्ट से कोई मतलब नहीं। ये रहा उदाहरण:'
diff --git a/config/locales/hr.yml b/config/locales/hr.yml
index 7a9ee2dc3..94cfb82ae 100644
--- a/config/locales/hr.yml
+++ b/config/locales/hr.yml
@@ -81,9 +81,7 @@ hr:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
   exports:
     archive_takeout:
@@ -101,8 +99,6 @@ hr:
       title: Filteri
     new:
       title: Dodaj novi filter
-  footer:
-    trending_now: Popularno
   generic:
     all: Sve
     changes_saved_msg: Izmjene su uspješno sačuvane!
@@ -169,8 +165,6 @@ hr:
   remote_follow:
     missing_resource: Nije moguće pronaći traženi URL preusmjeravanja za Vaš račun
   sessions:
-    platforms:
-      other: nepoznata platforma
     revoke: Opozovi
     revoke_success: Sesija je uspješno opozvana
     title: Sesije
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index 3cec6e979..0ab763ce4 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -91,6 +91,7 @@ hu:
       moderation:
         active: Aktív
         all: Összes
+        disabled: Letiltva
         pending: Függőben
         silenced: Korlátozott
         suspended: Felfüggesztve
@@ -104,10 +105,10 @@ hu:
       not_subscribed: Nincs feliratkozás
       pending: Engedélyezés alatt
       perform_full_suspension: Felfüggesztés
-      previous_strikes: Korábbi szankciók
+      previous_strikes: Korábbi felrótt vétségek
       previous_strikes_description_html:
-        one: Ezt a fiókot <strong>egyszer</strong> szankcionálták.
-        other: Ezt a fiókot <strong>%{count}</strong> esetben szankcionálták.
+        one: Ehhez a fiókhoz <strong>egyszer</strong> róttak fel vétséget.
+        other: Ehhez a fiókhoz <strong>%{count}</strong> esetben róttak fel vétséget.
       promote: Előléptetés
       protocol: Protokoll
       public: Nyilvános
@@ -133,6 +134,7 @@ hu:
       search: Keresés
       search_same_email_domain: Felhasználók ugyanezzel az email domainnel
       search_same_ip: Más felhasználók ugyanezzel az IP-vel
+      security: Biztonság
       security_measures:
         only_password: Csak jelszó
         password_and_2fa: Jelszó és kétlépcsős hitelesítés
@@ -427,6 +429,7 @@ hu:
         resolve: Domain feloldása
         title: Új e-mail domain tiltása
       no_email_domain_block_selected: Nem változott meg egy domain tiltás sem, mert semmi sem volt kiválasztva
+      not_permitted: Nem engedélyezett
       resolved_dns_records_hint_html: A domain név a következő MX domain-ekre oldódik fel, melyek valójában fogadják az e-mailt. Az MX domain letiltása minden olyan feliratkozást tiltani fog, melyben az e-mailcím ugyanazt az MX domaint használja, még akkor is, ha a látható domain név más. <strong>Légy óvatos, hogy ne tilts le nagy e-mail szolgáltatókat.</strong>
       resolved_through_html: Feloldva %{domain}-n keresztül
       title: Tiltott e-mail domainek
@@ -441,6 +444,7 @@ hu:
         private_comment_description_html: 'Az importált tiltások forrásának könnyebb követése érdekében, az importált tiltások a következő privát megjegyzéssel lesznek létrehozva: <q>%{comment}</q>'
         private_comment_template: 'Innen importálva: %{source}, ekkor: %{date}'
         title: Domain tiltások importálása
+      invalid_domain_block: 'Egy vagy több domain letiltást kihagytunk a következő hiba(ák) miatt: %{error}'
       new:
         title: Domain tiltások importálása
       no_file: Nincs fájl kiválasztva
@@ -472,6 +476,7 @@ hu:
       content_policies:
         comment: Belső megjegyzés
         description_html: Definiálhatsz tartalmi szabályokat, melyek ezen domain és minden aldomain fiókjára vonatkozni fognak.
+        limited_federation_mode_description_html: Eldöntheted, hogy engeded-e a föderációt ezzel a domainnel.
         policies:
           reject_media: Média elutasítása
           reject_reports: Bejelentések elutasítása
@@ -571,23 +576,27 @@ hu:
       action_log: Audit napló
       action_taken_by: 'Kezelte:'
       actions:
-        delete_description_html: A bejelentett bejegyzéseket törölni fogjuk és feljegyzünk egy szankciót, hogy segítsük az eszkalációt a fiók későbbi kihágásai esetén.
-        mark_as_sensitive_description_html: A bejelentett bejegyzések médaitartalmait érzékenynek jelöljük, és rögzítünk egy szankciót, hogy segítsük az eszkalációt a fiók későbbi kihágásai esetében.
+        delete_description_html: A bejelentett bejegyzéseket törölni fogjuk és feljegyzünk egy vétséget, hogy segítsük az eszkalációt a fiók későbbi kihágásai esetén.
+        mark_as_sensitive_description_html: A bejelentett bejegyzések médaitartalmait érzékenynek jelöljük, és rögzítünk egy vétséget, hogy segítsük az eszkalációt a fiók későbbi kihágásai esetén.
         other_description_html: További lehetőségek megjelenítése a fiók viselkedésének szabályozásához, és a jelentett fiók kommunikációjának testreszabásához.
-        resolve_description_html: Nem csinálunk semmit a bejelentett fiókkal, nem jegyzünk fel szankciót, és bezárjuk a bejelentést.
-        silence_description_html: A profil csak azok számára lesz látható, akik már követik, vagy kézileg megkeresik, rendkívül korlátozva az elérését. Ez mindig visszafordítható.
-        suspend_description_html: A profil és az összes tartalma elérhetetlen lesz, amíg végleg törlésre nem kerül. A fiókkal történő minden interakció lehetetlen lesz. 30 napon belül még visszaállítható.
+        resolve_description_html: Nem csinálunk semmit a bejelentett fiókkal, nem jegyzünk fel vétséget, és bezárjuk a bejelentést.
+        silence_description_html: A profil csak azok számára lesz látható, akik már követik, vagy kézzel rákeresnek, jelentősen korlátozva annak elérését. Ez a művelet bármikor visszafordítható. A fiókkal szemben indított minden bejelentést lezárunk.
+        suspend_description_html: A fiók és minden tartalma elérhetetlenné válik és végül törlésre kerül. A fiókkal kapcsolatbalépni lehetetlen lesz. Ez a művelet 30 napig visszafordítható. A fiók ellen indított minden bejelentést lezárunk.
       actions_description_html: Döntsd el, mit csináljunk, hogy megoldjuk ezt a bejelentést. Ha valamilyen büntető intézkedést hozol a bejelentett fiók ellen, küldünk neki egy figyelmeztetést e-mail-ben, kivéve ha a <strong>Spam</strong> kategóriát választod.
+      actions_description_remote_html: Döntsd el, mit tegyünk a bejelentés lezárásának érdekében. Ez csak azt befolyásolja, hogy a <strong>saját</strong> kiszolgálód hogyan kommunikál ezzel a távoli fiókkal és hogyan kezeli annak tartalmait.
       add_to_report: Továbbiak hozzáadása a bejelentéshez
       are_you_sure: Biztos vagy benne?
       assign_to_self: Magamhoz rendelés
       assigned: Hozzárendelt moderátor
       by_target_domain: A bejelentett fiók domainje
+      cancel: Mégse
       category: Kategória
       category_description_html: A fiók vagy tartalom bejelentésének oka a jelentett fiókkal kapcsolatos kommunikációban idézve lesz
       comment:
         none: Egyik sem
       comment_description_html: 'Hogy további információkat adjon, %{name} ezt írta:'
+      confirm: Megerősítés
+      confirm_action: Moderációs művelet jóváhagyása @%{acct} fiókon
       created_at: Jelentve
       delete_and_resolve: Bejegyzések törlése
       forwarded: Továbbítva
@@ -604,6 +613,7 @@ hu:
         placeholder: Jegyezd le, mi tettünk az ügy érdekében, vagy bármilyen változást...
         title: Megjegyzések
       notes_description_html: Megtekintés, és megjegyzések hagyása más moderátoroknak
+      processed_msg: 'Bejelentés #%{id} sikeresen feldolgozva'
       quick_actions_description_html: 'Hozz egy gyors intézkedést, vagy görgess le a bejelentett tartalomhoz:'
       remote_user_placeholder: 'a távoli felhasználó innen: %{instance}'
       reopen: Bejelentés újranyitása
@@ -616,9 +626,28 @@ hu:
       status: Állapot
       statuses: Jelentett tartalom
       statuses_description_html: A sértő tartalmat idézni fogjuk a bejelentett fiókkal való kommunikáció során
+      summary:
+        action_preambles:
+          delete_html: 'Arra készülsz, hogy <strong>eltávolítsd</strong> <strong>@%{acct}</strong> néhány bejegyzését. Ez a következőket okozza:'
+          mark_as_sensitive_html: 'Arra készülsz, hogy <strong>érzékenynek jelöld</strong> <strong>@%{acct}</strong> néhány tartalmát. Ez a következőket okozza:'
+          silence_html: 'Arra készülsz, hogy <strong>korlátozd</strong> <strong>@%{acct}</strong> fiókját. Ez a következőket okozza:'
+          suspend_html: 'Arra készülsz, hogy <strong>felfüggeszd</strong> <strong>@%{acct}</strong> fiókját. Ez a következőket okozza:'
+        actions:
+          delete_html: Sértő bejegyzések eltávolítása
+          mark_as_sensitive_html: Sértő bejegyzések médiatartalmainak érzékenyként történő megjelölése
+          silence_html: "<strong>@%{acct}</strong> fiók elérésének jelentős korlátozása azzal, hogy ennek profilja és tartalmai csak olyanoknak legyen látható, akik követik vagy manuálisan rákeresnek"
+          suspend_html: "<strong>@%{acct}</strong> felfüggesztése a profil és tartalmainak elérhetetlenné tételével, a fiókkal való interakció ellehetetlenítésével"
+        close_report: 'Bejelentés #%{id} megjelölése megoldottként'
+        close_reports_html: "<strong>Minden</strong> <strong>@%{acct}</strong> ellen tett bejelentés megjelölése megoldottként"
+        delete_data_html: "<strong>@%{acct}</strong> profiljának és tartalmainak törlése 30 nap múlva, hacsak addig nem oldják fel a felfüggesztést"
+        preview_preamble_html: "<strong>@%{acct}</strong> a következő tartalommal fog figyelmeztetést kapni:"
+        record_strike_html: Vétség felrovása a <strong>@%{acct}</strong> fiók ellen, hogy segítsük az eszkalációt a fiók jövőbeni kihágásai esetén
+        send_email_html: Figyelmeztető email küldése <strong>@%{acct}</strong> fiók részére
+        warning_placeholder: Opcionális kiegészítő indoklás a moderációs művelethez.
       target_origin: A jelentett fiók eredete
       title: Bejelentések
       unassign: Hozzárendelés törlése
+      unknown_action_msg: 'Ismeretlen művelet: %{action}'
       unresolved: Megoldatlan
       updated_at: Frissítve
       view_profile: Profil megtekintése
@@ -713,6 +742,8 @@ hu:
         preamble: Az érdekes tartalmak felszínre hozása fontos szerepet játszik az új felhasználók bevonásában, akik esetleg nem ismerik a Mastodont. Szabályozd, hogy a különböző felfedezési funkciók hogyan működjenek a kiszolgálón.
         profile_directory: Profiladatbázis
         public_timelines: Nyilvános idővonalak
+        publish_discovered_servers: Felfedezett kiszolgálók közzététele
+        publish_statistics: Statisztikák közzététele
         title: Felfedezés
         trends: Trendek
       domain_blocks:
@@ -767,6 +798,7 @@ hu:
         suspend: "%{name} felfüggesztette %{target} fiókját"
       appeal_approved: Megfellebbezve
       appeal_pending: Fellebbezés folyamatban
+      appeal_rejected: Fellebbezés visszautasítva
     system_checks:
       database_schema_check:
         message_html: Vannak esedékes adatbázis migrációink. Kérlek, futtasd őket, hogy biztosítsd, hogy az alkalmazás megfelelően működjön
@@ -780,6 +812,12 @@ hu:
         message_html: Még nem definiáltál egy szerver szabályt sem.
       sidekiq_process_check:
         message_html: Nincs Sidekiq folyamat, mely a %{value} sorhoz van rendelve. Kérlek, nézd át a Sidekiq beállításait
+      upload_check_privacy_error:
+        action: Itt találsz több információt
+        message_html: "<strong>A webkiszolgálód félre van konfigurálva. Kockázat merül fel a felhasználóid adatainak biztonságával kapcsolatban.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Itt találsz több információt
+        message_html: "<strong>Az objektumtárolód félre van konfigurálva. Kockázat merül fel a felhasználóid adatainak biztonságával kapcsolatban.</strong>"
     tags:
       review: Engedélyezés állapota
       updated_msg: A hashtag beállításokat sikeresen frissítettük
@@ -802,6 +840,7 @@ hu:
           other: "%{count} ember osztotta meg a múlt héten"
         title: Felkapott hivatkozások
         usage_comparison: "%{today} alkalommal lett ma megosztva, a tegnapi %{yesterday} alkalomhoz képest"
+      not_allowed_to_trend: Nem engedélyezett a trendekben
       only_allowed: Csak engedélyezett
       pending_review: Áttekintésre vár
       preview_card_providers:
@@ -933,6 +972,7 @@ hu:
   applications:
     created: Alkalmazás sikeresen létrehozva
     destroyed: Alkalmazás sikeresen eltávolítva
+    logout: Kijelentkezés
     regenerate_token: Hozzáférési kulcs újragenerálása
     token_regenerated: Hozzáférési kulcs sikeresen újragenerálva
     warning: Ez érzékeny adat. Soha ne oszd meg másokkal!
@@ -940,6 +980,8 @@ hu:
   auth:
     apply_for_account: Fiók kérése
     change_password: Jelszó
+    confirmations:
+      wrong_email_hint: Ha az emailcím nem helyes, a fiókbeállításokban megváltoztathatod.
     delete_account: Felhasználói fiók törlése
     delete_account_html: Felhasználói fiókod törléséhez <a href="%{path}">kattints ide</a>. A rendszer újbóli megerősítést fog kérni.
     description:
@@ -967,6 +1009,8 @@ hu:
     resend_confirmation: Megerősítési lépések újraküldése
     reset_password: Jelszó visszaállítása
     rules:
+      accept: Elfogadás
+      back: Vissza
       preamble: Ezeket a(z) %{domain} moderátorai adjak meg és tartatják be.
       title: Néhány alapszabály.
     security: Biztonság
@@ -987,7 +1031,7 @@ hu:
       functional: A fiókod teljesen működőképes.
       pending: A jelentkezésed engedélyezésre vár. Ez eltarthat egy ideig. Kapsz egy e-mailt, ha a kérelmedet jóváhagyták.
       redirecting_to: A fiókod inaktív, mert jelenleg ide %{acct} van átirányítva.
-      view_strikes: Fiókod elleni korábbi szankciók megtekintése
+      view_strikes: Fiókod ellen felrótt korábbi vétségek megtekintése
     too_fast: Túl gyorsan küldted el az űrlapot, próbáld később.
     use_security_key: Biztonsági kulcs használata
   authorize_follow:
@@ -1050,7 +1094,7 @@ hu:
     strikes:
       action_taken: Intézkedés
       appeal: Fellebbezés
-      appeal_approved: Ezt a szankciót eredményesen fellebbezték, így már nem érvényes
+      appeal_approved: Ezt a felrótt vétséget eredményesen fellebbezték, így már nem érvényes
       appeal_rejected: A fellebbezést visszautasították
       appeal_submitted_at: Fellebbezés beküldve
       appealed_msg: A fellebbezésedet beküldtük. Ha jóváhagyták, értesítünk.
@@ -1114,7 +1158,7 @@ hu:
   featured_tags:
     add_new: Új hozzáadása
     errors:
-      limit: Már kiemelted a maximálisan engedélyezett számú hashtaget
+      limit: Elérted a maximálisan kitűzhető hashtagek számát
     hint_html: "<strong>Mik a kiemelt hashtagek?</strong> Ezek állandóan megjelennek a nyilvános profilodon és lehetővé teszik, hogy mások kifejezetten az ezekhez tartozó bejegyzéseidet böngésszék. Jó eszköz ez kreatív munkák vagy hosszútávú projektek nyomonkövetésére."
   filters:
     contexts:
@@ -1158,8 +1202,6 @@ hu:
       index:
         hint: Ez a szűrő egyedi bejegyzések kiválasztására vonatkozik a megadott kritériumoktól függetlenül. Újabb bejegyzéseket adhatsz hozzá ehhez a szűrőhöz a webes felületen keresztül.
         title: Megszűrt bejegyzések
-  footer:
-    trending_now: Most felkapott
   generic:
     all: Mind
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ hu:
     validation_errors:
       one: Valami nincs rendjén! Tekintsd meg a hibát lent
       other: Valami nincs rendjén! Tekintsd meg a(z) %{count} hibát lent
-  html_validator:
-    invalid_markup: 'hibás HTML leíró: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Érvénytelen CSV-fájl. Hiba: %{error}'
@@ -1226,7 +1266,7 @@ hu:
     title: Meghívások
   lists:
     errors:
-      limit: Elérted a hozzáadható listák maximális számát
+      limit: Elérted a listák maximális számát
   login_activities:
     authentication_methods:
       otp: kétlépcsős azonosító alkalmazás
@@ -1367,7 +1407,11 @@ hu:
       unrecognized_emoji: nem ismert emodzsi
   relationships:
     activity: Fiók aktivitás
+    confirm_follow_selected_followers: Biztos, hogy követni akarod a kiválasztott követőket?
+    confirm_remove_selected_followers: Biztos, hogy el szeretnéd távolítani a kiválasztott követőket?
+    confirm_remove_selected_follows: Biztos, hogy el szeretnéd távolítani a kiválasztott követéseket?
     dormant: Elhagyott
+    follow_failure: Nem sikerült bekövetni néhányat a kiválasztott fiókok közül.
     follow_selected_followers: Kiválasztott követők bekövetése
     followers: Követők
     following: Követve
@@ -1407,6 +1451,7 @@ hu:
       electron: Electron
       firefox: Firefox
       generic: Ismeretlen böngésző
+      huawei_browser: Huawei Böngésző
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Böngésző
@@ -1416,6 +1461,7 @@ hu:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Ismeretlen böngésző
       weibo: Weibo
     current_session: Jelenlegi munkamenet
     description: "%{browser} az alábbi platformon: %{platform}"
@@ -1428,9 +1474,10 @@ hu:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: ismeretlen platform
+      unknown_platform: Ismeretlen platform
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1458,7 +1505,7 @@ hu:
     profile: Profil
     relationships: Követések és követők
     statuses_cleanup: Bejegyzések automatikus törlése
-    strikes: Moderációs szankciók
+    strikes: Moderációs felrótt vétségek
     two_factor_authentication: Kétlépcsős hitelesítés
     webauthn_authentication: Biztonsági kulcsok
   statuses:
@@ -1543,7 +1590,7 @@ hu:
       '7889238': 3 hónap
     min_age_label: Korhatár
     min_favs: Bejegyzések megtartása, melyeket többször jelöltek kedvencnek, mint
-    min_favs_hint: Nem törli egyetlen olyan bejegyzésedet sem, melyeket ennél többször jelöltek kedvencnek. Hagyd üresen, hogy a kedvencnek jelölések számától függetlenül töröljük őket
+    min_favs_hint: Nem törli egyetlen olyan bejegyzésedet sem, melyeket legalább ennyiszer jelöltek kedvencnek. Hagyd üresen, hogy a kedvencnek jelölések számától függetlenül töröljük őket
     min_reblogs: Bejegyzések megtartása, melyeket többször toltak meg, mint
     min_reblogs_hint: Egyetlen olyan bejegyzésedet sem törli, melyet ennél többször toltak meg. Hagyd üresen, hogy a megtolások számától függetlenül töröljük a bejegyzéseket
   stream_entries:
@@ -1552,7 +1599,7 @@ hu:
     sensitive_content: Kényes tartalom
   strikes:
     errors:
-      too_late: Túl késő, hogy fellebbezd ezt a szankciót
+      too_late: Túl késő, hogy fellebbezd ezt a felrótt vétséget
   tags:
     does_not_match_previous_name: nem illeszkedik az előző névvel
   themes:
@@ -1582,11 +1629,11 @@ hu:
   user_mailer:
     appeal_approved:
       action: Ugrás a fiókodhoz
-      explanation: A fiókod %{appeal_date}-i fellebbezése, mely a %{strike_date}-i szankcióval kapcsolatos, jóváhagyásra került. A fiókod megint makulátlan.
+      explanation: A fiókod %{appeal_date}-i fellebbezése, mely a %{strike_date}-i vétségeddel kapcsolatos, jóváhagyásra került. A fiókod megint makulátlan.
       subject: A %{date}-i fellebbezésedet jóváhagyták
       title: Fellebbezés jóváhagyva
     appeal_rejected:
-      explanation: A %{appeal_date}-i fellebbezésed, amely a fiókod %{strike_date}-i szankciójával kapcsolatos, elutasításra került.
+      explanation: A %{appeal_date}-i fellebbezésed, amely a fiókod %{strike_date}-i vétségével kapcsolatos, elutasításra került.
       subject: A %{date}-i fellebbezésedet visszautasították
       title: Fellebbezés visszautasítva
     backup_ready:
@@ -1643,12 +1690,13 @@ hu:
       title: Üdv a fedélzeten, %{name}!
   users:
     follow_limit_reached: Nem követhetsz több, mint %{limit} embert
+    go_to_sso_account_settings: Ugrás az azonosítási szolgáltatód fiókbeállításaihoz
     invalid_otp_token: Érvénytelen ellenőrző kód
     otp_lost_help_html: Ha mindkettőt elvesztetted, kérhetsz segítséget itt %{email}
     seamless_external_login: Külső szolgáltatáson keresztül jelentkeztél be, így a jelszó és e-mail beállítások nem elérhetőek.
     signed_in_as: Bejelentkezve mint
   verification:
-    explanation_html: 'A profilodon <strong>hitelesítheted magad, mint az itt található linkek tulajdonosa</strong>. Ehhez a linkelt weboldalnak tartalmaznia kell egy linket vissza a Mastodon profilodra. Ennek <strong>tartalmaznia kell</strong> a <code>rel="me"</code> attribútumot. A link szövege bármi lehet. Itt egy példa:'
+    explanation_html: 'A profilodon <strong>hitelesítheted magad, mint az itt található hivatkozások tulajdonosa</strong>. Ehhez a hivatkozott weboldalnak tartalmaznia kell egy visszahivatkozást a Mastodon-profilodra. A hivatkozás hozzáadása után lehet, hogy vissza kell ide térned, és újra mentened kell a profilodat, hogy az ellenőrzés életbe lépjen. A visszahivatkozásnak <strong>tartalmaznia kell</strong> a <code>rel="me"</code> attribútumot. A hivatkozás szövege bármi lehet. Itt egy példa:'
     verification: Hitelesítés
   webauthn_credentials:
     add: Biztonsági kulcs hozzáadása
diff --git a/config/locales/hy.yml b/config/locales/hy.yml
index 5cd6d53a3..a3658eae9 100644
--- a/config/locales/hy.yml
+++ b/config/locales/hy.yml
@@ -5,6 +5,7 @@ hy:
     contact_missing: Սահմանված չէ
     contact_unavailable: Ոչինչ չկա
     hosted_on: Մաստոդոնը տեղակայուած է %{domain}ում
+    title: Մասին
   accounts:
     follow: Հետևել
     followers:
@@ -45,6 +46,7 @@ hy:
       confirm: Հաստատել
       confirmed: Հաստատված է
       confirming: Հաստատման սպասող
+      custom: Յատուկ
       delete: Ջնջել տվյալները
       deleted: Ջնջված է
       demote: Աստիճանազրկել
@@ -248,6 +250,8 @@ hy:
     domain_allows:
       add_new: Թոյլատրել ֆեդերացիա տիրոյթի հետ
       created_msg: Տիրոյթը յաջողութեամբ թոյլատրուեց ֆեդերացուելու
+      export: Արտահանել
+      import: Ներմուծել
       undo: Չթոյլատրել ֆեդերացիան տիրոյթի հետ
     domain_blocks:
       add_new: Աւելացնել նոր տիրոյթի արգելափակում
@@ -255,6 +259,8 @@ hy:
       destroyed_msg: Տիրոյթի արգելափակումը ետարկուեց
       domain: Տիրոյթ
       edit: Խմբագրել տիրոյթի արգելափակումը
+      export: Արտահանել
+      import: Ներմուծել
       new:
         create: Ստեղծել արգելափակում
         severity:
@@ -281,10 +287,15 @@ hy:
       status: Կարգավիճակ
       title: Խորհուրդ ենք տալիս հետեւել
     instances:
+      availability:
+        title: Հասանելի
       back_to_all: Բոլորը
       back_to_limited: Սահամանփակ
       back_to_warning: Զգուշացում
       by_domain: Դոմեն
+      content_policies:
+        policies:
+          silence: Սահմանափակ
       delivery:
         all: Բոլորը
         unavailable: Անհասանելի է
@@ -354,6 +365,7 @@ hy:
       notes:
         create: Ավելացնել նշում
         delete: Ջնջել
+        title: Նշում
       reopen: Վերաբացել բողոքը
       report: 'Բողոք #%{id}'
       reported_account: Բողոքարկուած հաշիւ
@@ -365,6 +377,14 @@ hy:
       unresolved: Չլուծուած
       updated_at: Թարմացուած
       view_profile: Նայել անձնական էջը
+    roles:
+      categories:
+        invites: Հրաւէրներ
+        moderation: Մոդերացիա
+        special: Յատուկ
+      delete: Ջնջել
+      privileges:
+        administrator: Ադմինիստրատոր
     rules:
       add_new: Աւելացնել կանոն
       delete: Ջնջել
@@ -372,6 +392,8 @@ hy:
       empty: Սերուերի կանոնները դեռեւս սահմանուած չեն։
       title: Սերուերի կանոնները
     settings:
+      about:
+        title: Մասին
       domain_blocks:
         all: Բոլորին
         disabled: Ոչ մէկին
@@ -517,7 +539,6 @@ hy:
     '404': Էջը, որը փնտրում ես գոյութիւն չունի։
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Չափազանց շատ հարցումներ
     '500':
       title: Էջը ճիշտ չէ
@@ -554,8 +575,6 @@ hy:
       title: Ֆիլտրեր
     new:
       title: Ավելացնել ֆիլտր
-  footer:
-    trending_now: Այժմ արդիական
   generic:
     all: Բոլորը
     changes_saved_msg: Փոփոխութիւնները յաջող պահուած են
@@ -737,7 +756,6 @@ hy:
       ios: iOS
       linux: Լինուքս
       mac: macOS
-      other: անհայտ հարթակ
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -863,7 +881,6 @@ hy:
     invalid_otp_token: Անվաւեր 2F կոդ
     signed_in_as: Մոտք գործել որպէս․
   verification:
-    explanation_html: Պիտակների յղումների հեղինակութիւնը կարելի է վաւերացնել։ Անհրաժեշտ է որ յղուած կայքը պարունակի յետադարձ յղում ձեր մաստադոնի էջին, որը <strong>պէտք է</strong> ունենայ <code>rel="me"</code> նիշքը։ Յղման բովանդակութիւնը կարևոր չէ։ Օրինակ՝
     verification: Ստուգում
   webauthn_credentials:
     delete: Ջնջել
diff --git a/config/locales/id.yml b/config/locales/id.yml
index 70c65081d..790296654 100644
--- a/config/locales/id.yml
+++ b/config/locales/id.yml
@@ -114,6 +114,7 @@ id:
       reject: Tolak
       rejected_msg: Berhasil menolak permintaan pendaftaran %{username}
       remote_suspension_irreversible: Data akun ini telah dihapus permanen.
+      remote_suspension_reversible_hint_html: Akun ini telah ditangguhkan di server mereka, dan data akan dihapus total pada %{date}. Sebelum itu, server jarak jauh dapat memulihkan akun ini tanpa efek samping. Jika Anda ingin menghapus semua data akun langsung, Anda dapat mengikuti bawah ini.
       remove_avatar: Hapus avatar
       remove_header: Hapus header
       removed_avatar_msg: Berhasil menghapus gambar avatar %{username}
@@ -432,6 +433,7 @@ id:
         private_comment_description_html: 'Untuk membantu Anda melacak asal blok yang diimpor, blok yang diimpor akan dibuat dengan komentar pribadi berikut: <q>%{comment}</q>'
         private_comment_template: Diimpor dari %{source} pada %{date}
         title: Impor blok domain
+      invalid_domain_block: 'Satu atau lebih blokir domain dilewati karena kesalahan berikut: %{error}'
       new:
         title: Impor blok domain
       no_file: Tidak ada file dipilih
@@ -562,8 +564,6 @@ id:
         mark_as_sensitive_description_html: Media di dalam kiriman terlapor akan ditandai sebagai sensitif dan hukuman akan direkam untuk membantu menangani pelanggaran dari akun yang sama di masa mendatang.
         other_description_html: Lihat opsi lain untuk mengendalikan perilaku akun dan menyesuaikan komunikasi ke akun yang dilaporkan.
         resolve_description_html: Tidak ada tindakan untuk akun yang dilaporkan, tidak ada peringatan yang direkam, dan laporan akan ditutup.
-        silence_description_html: Profil hanya dapat terlihat oleh pengguna yang telah mengikuti atau melihatnya secara manual, yang sangat membatasi jangkauannya. Selalu dapat dipulihkan kembali.
-        suspend_description_html: Profil dan semua kontennya tidak dapat diakses sampai ia terhapus. Interaksi dengan akun tersebut tidak dimungkinkan. Dapat dipulihkan dalam waktu 30 hari.
       actions_description_html: Memutuskan tindakan yang tepat untuk menyelesaikan laporan ini. Jika Anda memutuskan tindakan hukuman kepada akun terlapor, notifikasi email akan dikirim ke mereka, kecuali saat kategori <strong>Spam</strong> dipilih.
       add_to_report: Tambahkan lebih banyak untuk lapor
       are_you_sure: Apakah Anda yakin?
@@ -1093,8 +1093,6 @@ id:
     storage: Penyimpanan media
   featured_tags:
     add_new: Tambah baru
-    errors:
-      limit: Anda sudah menampilkan tagar unggulan dengan jumlah maksimum
     hint_html: "<strong>Apa itu tagar yang diunggulkan?</strong> Mereka ditampilkan secara mencolok di profil publik Anda dan mengizinkan orang-orang untuk menjelajahi kiriman publik khususnya yang ada di bawah tagar tersebut. Mereka adalah alat yang bagus untuk melacak pekerjaan kreatif atau proyek jangka panjang."
   filters:
     contexts:
@@ -1135,8 +1133,6 @@ id:
       index:
         hint: Saringan ini diterapkan ke beberapa kiriman individu tanpa memengaruhi oleh kriteria lain. Anda dapat menambahkan lebih banyak kiriman ke saringan ini dari antarmuka web.
         title: Kiriman yang disaring
-  footer:
-    trending_now: Sedang tren
   generic:
     all: Semua
     all_items_on_page_selected_html:
@@ -1155,8 +1151,6 @@ id:
     today: hari ini
     validation_errors:
       other: Ada yang belum benar! Silakan tinjau %{count} kesalahan di bawah ini
-  html_validator:
-    invalid_markup: 'berisi markup HTML yang tidak valid: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Berkas CVS tidak sah. Kesalahan: %{error}'
@@ -1196,9 +1190,6 @@ id:
       expires_at: Kedaluwarsa
       uses: Penggunaan
     title: Undang orang
-  lists:
-    errors:
-      limit: Daftar Anda telah mencapai jumlah maksimum
   login_activities:
     authentication_methods:
       otp: aplikasi otentikasi dua-faktor
@@ -1402,7 +1393,6 @@ id:
       ios: iOS
       linux: Linux
       mac: Mac
-      other: platform yang tidak diketahui
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1509,7 +1499,6 @@ id:
       '7889238': 3 bulan
     min_age_label: Batas usia
     min_favs: Simpan kiriman favorit lebih dari
-    min_favs_hint: Tidak menghapus kiriman Anda yang mendapatkan sekian favorit. Kosongkan bila ingin menghapus kiriman tanpa peduli jumlah favoritnya
     min_reblogs: Simpan kiriman yang di-boost lebih dari
     min_reblogs_hint: Tidak menghapus kiriman Anda yang di-boost lebih dari sekian kali. Kosongkan bila ingin menghapus kiriman tanpa peduli jumlah boost-nya
   stream_entries:
@@ -1614,7 +1603,6 @@ id:
     seamless_external_login: Anda masuk via layanan eksternal, sehingga pengaturan kata sandi dan email tidak tersedia.
     signed_in_as: 'Masuk sebagai:'
   verification:
-    explanation_html: 'Anda dapat <strong>memverifikasi diri Anda sebagai pemiliki tautan pada metadata profil</strong>. Situsweb yang ditautkan haruslah berisi tautan ke profil Mastodon Anda. Tautan tersebut <strong>harus</strong> memiliki atribut <code>rel="me"</code>. Isi teks tautan tidaklah penting. Ini contohnya:'
     verification: Verifikasi
   webauthn_credentials:
     add: Tambahkan kunci keamanan baru
diff --git a/config/locales/ig.yml b/config/locales/ig.yml
index c32706518..645939638 100644
--- a/config/locales/ig.yml
+++ b/config/locales/ig.yml
@@ -6,7 +6,5 @@ ig:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/io.yml b/config/locales/io.yml
index 3f18bf7fc..849b80cb9 100644
--- a/config/locales/io.yml
+++ b/config/locales/io.yml
@@ -555,8 +555,6 @@ io:
         mark_as_sensitive_description_html: Medii en raportizita posti markizesos quale sentoza e streko rekordigesos por helpar vu intensigar en nexta malagi da la sama konto.
         other_description_html: Videz plu multa opcioni por dominacar konduto di konto e kustumizar komuniko a raportizita konto.
         resolve_description_html: Nulo agesos kontre raportizita konto, streko ne rekordizesos e raporto klozesos.
-        silence_description_html: Profilo esos videbla nur por personi quo ja sequis o manuale trovis, se severe limitizas ola porteo. Sempre povas inversigesar.
-        suspend_description_html: Ca profilo e omna ola kontenaji divenos neacesebla til ol eventuale efacesas. Interagar kun la konto divenos neposibla. Inversigebla til 30 dii.
       actions_description_html: Decidez ago por rezolvar ca raporto. Se vu decidar puniso kontre raportizesis konto, retpostoavizo sendesos a ol, ecepte kande <strong>Spam</strong> kategorio selektesis.
       add_to_report: Insertez pluse a raporto
       are_you_sure: Ka vu esas certa?
@@ -1071,8 +1069,6 @@ io:
     storage: Konservado di kontenajo
   featured_tags:
     add_new: Insertez novo
-    errors:
-      limit: Vu ja estelis maxima hashtagi
     hint_html: "<strong>Quo esas estelita hashtagi?</strong> Ol montresas eminente che vua publika profilo e povigas personi vidar vua publika posti partikulare kun ta hashtagi. Oli esas bona utensilo por jeretar kreiva agaji e longa projetaji."
   filters:
     contexts:
@@ -1116,8 +1112,6 @@ io:
       index:
         hint: Ca filtrilo aplikesas a selektita posti ne segun altra kriterio. Vu povas pozar plu multa posti a ca filtrilo de retintervizajo.
         title: Filtrita posti
-  footer:
-    trending_now: Nuna tendenco
   generic:
     all: Omna
     all_items_on_page_selected_html:
@@ -1140,8 +1134,6 @@ io:
     validation_errors:
       one: Ulo ne eventis senprobleme! Voluntez konsultar la suba eror-raporto
       other: Ulo ne eventis senprobleme! Voluntez konsultar la suba %{count} eror-raporti
-  html_validator:
-    invalid_markup: 'kontenas nevalida kompozuro di HTML: %{error}'
   imports:
     errors:
       over_rows_processing_limit: kontenas plu kam %{count} horizontala lineo
@@ -1181,9 +1173,6 @@ io:
       expires_at: Expiros
       uses: Uzi
     title: Invitez personi
-  lists:
-    errors:
-      limit: Vu astingas maxima listi
   login_activities:
     authentication_methods:
       otp: 2-faktoryurizessoftwaro
@@ -1387,7 +1376,6 @@ io:
       ios: iOS
       linux: Linux
       mac: macOS
-      other: nesavata platformo
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1500,7 +1488,6 @@ io:
       '7889238': 3 monati
     min_age_label: Oldeslimito
     min_favs: Retenez favorizita posti mine
-    min_favs_hint: Ne efacas irga vua posti quo ganis mine ca favorizquanto. Restez quale vakua por efacar posti sen suciar olia favorizquanto
     min_reblogs: Retenez bustita posti mine
     min_reblogs_hint: Ne efacas irga vua posti quo bustigesos mine ca foyoquanto. Restez quale vakua por efacar posti sen suciar olia foyoquanto
   stream_entries:
@@ -1605,7 +1592,6 @@ io:
     seamless_external_login: Vu enirar tra externa serveso, do pasvorto e retpostoopcioni ne esas disponebla.
     signed_in_as: 'Eniris quale:'
   verification:
-    explanation_html: 'Vu povas <strong>verifikar su kom proprietero di ligili en vua profilmetainformi</strong>. En ta kazo, ligita retsito <strong>mustas</strong> havar <code>rel="me"</code> atributo. Textokontenajo di ligilo ne esas importanta. Co esas exemplo:'
     verification: Verifikeso
   webauthn_credentials:
     add: Insertez nova sekuresklefo
diff --git a/config/locales/is.yml b/config/locales/is.yml
index d08d8c6a7..4020e7ba3 100644
--- a/config/locales/is.yml
+++ b/config/locales/is.yml
@@ -91,6 +91,7 @@ is:
       moderation:
         active: Virkur
         all: Allt
+        disabled: Óvirkt
         pending: Í bið
         silenced: Takmarkað
         suspended: Í frysti
@@ -133,6 +134,7 @@ is:
       search: Leita
       search_same_email_domain: Aðra notendur með sama tölvupóstlén
       search_same_ip: Aðrir notendur með sama IP-vistfang
+      security: Öryggi
       security_measures:
         only_password: Aðeins lykilorð
         password_and_2fa: Lykilorð og 2-þátta auðkenning
@@ -427,6 +429,7 @@ is:
         resolve: Leysa lén
         title: Útiloka nýtt tölvupóstlén
       no_email_domain_block_selected: Engum útilokunum tölvupóstléna var breytt þar sem ekkert var valið
+      not_permitted: Ekki leyft
       resolved_dns_records_hint_html: Heiti lénsins vísar til eftirfarandi MX-léna, sem bera endanlega ábyrgð á að tölvupóstur skili sér. Útilokun á MX-léni mun koma í veg fyrir nýskráningar með hverju því tölvupóstfangi sem notar sama MX-lén, jafnvel þótt sýnilega lénsheitið sé frábrugðið. <strong>Farðu varlega svo þú útilokir ekki algengar tölvupóstþjónustur.</strong>
       resolved_through_html: Leyst í gegnum %{domain}
       title: Útilokuð tölvupóstlén
@@ -441,6 +444,7 @@ is:
         private_comment_description_html: 'Tið að aðstoða þig við að rekja hvaðan lokkanir koma, innfluttar lokanir verða búnar til með eftirfarndi athugasemd: <q>%{comment}</q>'
         private_comment_template: Flutt inn frá %{source} þann %{date}
         title: Flytja inn útilokanir léna
+      invalid_domain_block: 'Einni eða fleiri útilokunum léna var sleppt vegna eftirfarandi villu/villna: %{error}'
       new:
         title: Flytja inn útilokanir léna
       no_file: Engin skrá valin
@@ -472,6 +476,7 @@ is:
       content_policies:
         comment: Innri minnispunktur
         description_html: Þú getur skilgreint stefnu varðandi efni sem verður beitt á alla aðganga frá þessu léni og öllum undirlénum þess.
+        limited_federation_mode_description_html: Þú getur valið hvort leyfa eigi skýjasamband við þetta lén.
         policies:
           reject_media: Hafna myndefni
           reject_reports: Hafna kærum
@@ -575,19 +580,23 @@ is:
         mark_as_sensitive_description_html: Myndefnið í kærðu færslunum verður merkt sem viðkvæmt og refsing verður skráð til minnis fyrir viðbrögð gegn mögulegum framtíðarbrotum frá sama notandaaðgangi.
         other_description_html: Skoðaðu fleir valkosti fyrir stjórnun á hegðun notandaaðgangsins og til að stýra samskiptum við kærðan notandaaðgang.
         resolve_description_html: Til engra aðgerða verður tekið gagnvart kærðum færslum, engin refsing verður skráð og kærunni verður lokað.
-        silence_description_html: Notandaaðgangurinn verður einungis sýnilegur þeim sem þegar fylgjast með honum eða sem fletta honum upp handvirkt, sem takmarkar útbreiðslu efnis verulega. Er alltaf hægt að afturkalla.
-        suspend_description_html: Notandaaðgangurinn og allt efni á honum mun verða óaðgengilegt og á endanum eytt út. Samskipti við aðganginn verða ekki möguleg. Hægt að afturkalla innan 30 daga.
+        silence_description_html: Notandaaðgangurinn verður einungis sýnilegur þeim sem þegar fylgjast með honum eða sem fletta honum upp handvirkt, sem takmarkar útbreiðslu efnis verulega. Er alltaf hægt að afturkalla. Lokar öllum kærum gagnvart þessum aðgangi.
+        suspend_description_html: Notandaaðgangurinn og allt efni á honum mun verða óaðgengilegt og á endanum eytt út og samskipti við aðganginn verða ekki möguleg. Hægt að afturkalla innan 30 daga. Lokar öllum kærum gagnvart þessum aðgangi.
       actions_description_html: Ákveddu til hvaða aðgerða eigi að taka til að leysa þessa kæru. Ef þú ákveður að refsa kærða notandaaðgangnum, verður viðkomandi send tilkynning í tölvupósti, nema ef flokkurinn <strong>Ruslpóstur</strong> sé valinn.
+      actions_description_remote_html: Ákveddu til hvaða aðgerða eigi að taka til að leysa þessa kæru. Þetta mun aðeins hafa áhrif á hvernig <strong>netþjónninn þinn</strong> meðhöndlar þennan fjartengda aðgang og efnið á honum.
       add_to_report: Bæta fleiru í kæru
       are_you_sure: Ertu viss?
       assign_to_self: Úthluta mér
       assigned: Úthlutaður umsjónarmaður
       by_target_domain: Lén kærða notandaaðgangsins
+      cancel: Hætta við
       category: Flokkur
       category_description_html: Ástæðan fyrir því að þessi notandaaðgangur og/eða efni hans var kært mun verða tiltekin í samskiptum við kærðan notandaaðgang
       comment:
         none: Ekkert
       comment_description_html: 'Til að gefa nánari upplýsingar skrifaði %{name}:'
+      confirm: Staðfesta
+      confirm_action: Staðfesta umsjónaraðgerðir gagnvart @%{acct}
       created_at: Tilkynnt
       delete_and_resolve: Eyða færslum
       forwarded: Áframsent
@@ -604,6 +613,7 @@ is:
         placeholder: Lýstu til hvaða aðgerða hefur verið gripið eða uppfærðu inn aðrar tengdar upplýsingar...
         title: Minnispunktar
       notes_description_html: Skoðaðu og skrifaðu minnispunkta til annarra stjórnenda og sjálfs þín
+      processed_msg: 'Tókst að meðhöndla kæruna #%{id}'
       quick_actions_description_html: 'Beittu flýtiaðgerð eða skrunaðu niður til að skoða kært efni:'
       remote_user_placeholder: fjartengda notandann frá %{instance}
       reopen: Enduropna kæru
@@ -616,9 +626,28 @@ is:
       status: Staða
       statuses: Kært efni
       statuses_description_html: Óviðeigandi efni verður tiltekið í samskiptum við kærðan notandaaðgang
+      summary:
+        action_preambles:
+          delete_html: 'Þú er í þann mund að fara að <strong>fjarlægja</strong> sumar af færslunum frá <strong>@%{acct}</strong>. Þetta mun:'
+          mark_as_sensitive_html: 'Þú er í þann mund að fara að <strong>merkja</strong> sumar af færslunum frá <strong>@%{acct}</strong> sem <strong>viðkvæmar</strong>. Þetta mun:'
+          silence_html: 'Þú er í þann mund að fara að <strong>takmarka aðganginn</strong> <strong>@%{acct}</strong>. Þetta mun:'
+          suspend_html: 'Þú er í þann mund að fara að <strong>frysta</strong> aðganginn hjá <strong>@%{acct}</strong>. Þetta mun:'
+        actions:
+          delete_html: Fjarlægja viðkomandi færslur
+          mark_as_sensitive_html: Merkja myndefni í viðkomandi færslum sem viðkvæmt
+          silence_html: Takmarka verulega umfangið hjá <strong>@%{acct}</strong> með því að gera notandasniðið og efni þess einungis sýnilegt fólki sem þegar fylgist með viðkomandi eða þeim sem fletta handvirkt upp upplýsingunum
+          suspend_html: Setja <strong>@%{acct}</strong> í frysti, gera notandasniðið og efni þess óaðgengilegt án mögulegrar gagnvirkni
+        close_report: 'Merkja kæruna #%{id} sem leysta'
+        close_reports_html: Merkja <strong>allar</strong> kærur gagnavart <strong>@%{acct}</strong> sem leystar
+        delete_data_html: Eyða notandasniði <strong>@%{acct}</strong> og efni þess eftir 30 daga nema viðkomandi verði tekinn úr frysti í millitíðinni
+        preview_preamble_html: "<strong>@%{acct}</strong> mun fá aðvörun með eftirfarandi texta:"
+        record_strike_html: Skrá refsingu gagnvart <strong>@%{acct}</strong> til að geta betur átt við brot frá þessum aðgangi í framtíðinni
+        send_email_html: Senda <strong>@%{acct}</strong> aðvörun í tölvupósti
+        warning_placeholder: Valkvæðar aðrar ástæður fyrir umsjónaraðgerðum.
       target_origin: Uppruni kærða notandaaðgangsins
       title: Kærur
       unassign: Aftengja úthlutun
+      unknown_action_msg: 'Óþekkt aðgerð: %{action}'
       unresolved: Óleyst
       updated_at: Uppfært
       view_profile: Skoða notandasnið
@@ -713,6 +742,8 @@ is:
         preamble: Að láta áhugavert efni koma skýrt fram er sérstaklega mikilvægt til að nálgast nýja notendur sem ekki þekkja neinn sem er á Mastodon. Stýrðu því hvernig hinir ýmsu eiginleikar við uppgötvun efnis virka á netþjóninum þínum.
         profile_directory: Notendamappa
         public_timelines: Opinberar tímalínur
+        publish_discovered_servers: Birta uppgötvaða netþjóna
+        publish_statistics: Birta tölfræði
         title: Uppgötvun
         trends: Vinsælt
       domain_blocks:
@@ -767,6 +798,7 @@ is:
         suspend: "%{name} setti notandaaðganginn %{target} í frysti"
       appeal_approved: Áfrýjað
       appeal_pending: Áfrýjun í bið
+      appeal_rejected: Áfrýjun hafnað
     system_checks:
       database_schema_check:
         message_html: Það eru fyrirliggjandi yfirfærslur á gagnagrunnum. Keyrðu þær til að tryggja að forritið hegði sér eins og skyldi
@@ -780,6 +812,12 @@ is:
         message_html: Þú hefur ekki skilgreint neinar reglur fyrir netþjón.
       sidekiq_process_check:
         message_html: Ekkert Sidekiq-ferli er í gangi fyrir %{value} biðröð/biðraðir. Endilega athugaðu Sidekiq-uppsetninguna þína
+      upload_check_privacy_error:
+        action: Skoðaðu hér til að fá frekari upplýsingar
+        message_html: "<strong>Vefþjónninn þinn er ekki rétt stilltur. Friðhelgi notendanna þinna gæti verið í hættu.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Skoðaðu hér til að fá frekari upplýsingar
+        message_html: "<strong>Gagnageymslan þín er ekki rétt stillt. Friðhelgi notendanna þinna gæti verið í hættu.</strong>"
     tags:
       review: Yfirfara stöðufærslu
       updated_msg: Það tókst að uppfæra stillingar myllumerkja
@@ -802,6 +840,7 @@ is:
           other: Deilt af %{count} aðilum síðustu vikuna
         title: Vinsælir tenglar
         usage_comparison: Deilt %{today} sinnum í dag, samanborið við %{yesterday} í gær
+      not_allowed_to_trend: Ekki leyft að verða vinsælt
       only_allowed: Aðeins leyfð
       pending_review: Bíður eftir yfirlestri
       preview_card_providers:
@@ -933,6 +972,7 @@ is:
   applications:
     created: Það tókst að búa til forrit
     destroyed: Það tókst að eyða forriti
+    logout: Skrá út
     regenerate_token: Endurgera aðgangsteikn
     token_regenerated: Það tókst að endurgera aðgangsteiknið
     warning: Farðu mjög varlega með þessi gögn. Þú skalt aldrei deila þeim með neinum!
@@ -940,6 +980,8 @@ is:
   auth:
     apply_for_account: Biðja um notandaaðgang
     change_password: Lykilorð
+    confirmations:
+      wrong_email_hint: Ef það tölvupóstfang er ekki rétt geturðu breytt því í stillingum notandaaðgangsins.
     delete_account: Eyða notandaaðgangi
     delete_account_html: Ef þú vilt eyða notandaaðgangnum þínum, þá geturðu <a href="%{path}">farið í það hér</a>. Þú verður beðin/n um staðfestingu.
     description:
@@ -967,6 +1009,8 @@ is:
     resend_confirmation: Senda leiðbeiningar vegna staðfestingar aftur
     reset_password: Endursetja lykilorð
     rules:
+      accept: Samþykkja
+      back: Til baka
       preamble: Þær eru settar og þeim framfylgt af umsjónarmönnum %{domain}.
       title: Nokkrar grunnreglur.
     security: Öryggi
@@ -1114,7 +1158,7 @@ is:
   featured_tags:
     add_new: Bæta við nýju
     errors:
-      limit: Þú ert þegar búin/n að gefa hámarksfjölda myllumerkja aukið vægi
+      limit: Þú hefur þegar gefið hámarksfjölda myllumerkja aukið vægi
     hint_html: "<strong>Hvað eru myllumerki með aukið vægi?</strong> Þau eru birt áberandi á opinbera notandasniðinu þínu og gera fólki kleift að fletta í gegnum opinberu færslurnar þínar sérstaklega undir þessum myllumerkjum. Þau eru frábær aðferð við að halda utan um skapandi vinnu eða langtíma verkefni."
   filters:
     contexts:
@@ -1158,8 +1202,6 @@ is:
       index:
         hint: Þessi sía virkar til að velja stakar færslur án tillits til annarra skilyrða. Þú getur bætt fleiri færslum í þessa síu í vefviðmótinu.
         title: Síaðar færslur
-  footer:
-    trending_now: Í umræðunni núna
   generic:
     all: Allt
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ is:
     validation_errors:
       one: Ennþá er ekk alvegi allt í lagi! Skoðaðu vel villuna hér fyrir neðan
       other: Ennþá er ekki alveg allt í lagi! Skoðaðu vel villurnar %{count} hér fyrir neðan
-  html_validator:
-    invalid_markup: 'inniheldur ógildar HTML-merkingar: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Ógild CSV-skrá. Villa: %{error}'
@@ -1367,7 +1407,11 @@ is:
       unrecognized_emoji: er ekki þekkt tjáningartákn
   relationships:
     activity: Virkni aðgangs
+    confirm_follow_selected_followers: Ertu viss um að þú viljir fylgjast með völdum fylgjendum?
+    confirm_remove_selected_followers: Ertu viss um að þú viljir fjarlægja valda fylgjendur?
+    confirm_remove_selected_follows: Ertu viss um að þú viljir fjarlægja valið sem fylgst er með?
     dormant: Sofandi
+    follow_failure: Gat ekki fylgst með sumum af völdu aðgöngunum.
     follow_selected_followers: Fylgjast með völdum fylgjendum
     followers: Fylgjendur
     following: Fylgist með
@@ -1407,6 +1451,7 @@ is:
       electron: Electron
       firefox: Firefox
       generic: Óþekktur vafri
+      huawei_browser: Huawei-vafri
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi vafri
@@ -1416,6 +1461,7 @@ is:
       qq: QQ vafri
       safari: Safari
       uc_browser: UC-vafrinn
+      unknown_browser: Óþekktur vafri
       weibo: Weibo
     current_session: Núverandi seta
     description: "%{browser} á %{platform}"
@@ -1428,9 +1474,10 @@ is:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: óþekktu stýrikerfi
+      unknown_platform: Óþekkt stýrikerfi
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1643,12 +1690,13 @@ is:
       title: Velkomin/n um borð, %{name}!
   users:
     follow_limit_reached: Þú getur ekki fylgst með fleiri en %{limit} aðilum
+    go_to_sso_account_settings: Fara í stillingar aðgangsins hjá auðkennisveitunni þinni
     invalid_otp_token: Ógildur tveggja-þátta kóði
     otp_lost_help_html: Ef þú hefur misst aðganginn að hvoru tveggja, geturðu sett þig í samband við %{email}
     seamless_external_login: Innskráning þín er í gegnum utanaðkomandi þjónustu, þannig að stillingar fyrir lykilorð og tölvupóst eru ekki aðgengilegar.
     signed_in_as: 'Skráð inn sem:'
   verification:
-    explanation_html: 'Þú getur <strong>vottað að þú sért eigandi og ábyrgur fyrir tenglunum í lýsigögnum notandasniðsins þíns</strong>. Til að það virki, þurfa vefsvæðin sem vísað er í að innihalda tengil til baka í Mastodon-notandasniðið. Tengillinn sem vísar til baka <strong>verður</strong> að vera með <code>rel="me"</code> eigindi. Textinn í tenglinum skiptir ekki máli. Hérna er dæmi:'
+    explanation_html: 'Þú getur <strong>sannvottað sjálfa/n þig sem eiganda tenglanna í notandasniðinu þinu</strong>. Til að það virki, þarf tilvísaða vefsvæðið að innihalda tengil til baka á notandasnið þitt á Mastodon. Eftir að tenglinum hefur verið bætt inn, gætirðu þurft að koma aftur hingað og vista aftur notandasniðið þitt áður en sannvottunin fer að virka. Tengillinn til baka <strong>verður</strong> að innihalda <code>rel="me"</code> eigindi. Efni textans í tenglinum skiptir ekki máli. Hér er dæmi:'
     verification: Sannprófun
   webauthn_credentials:
     add: Bæta við nýjum öryggislykli
diff --git a/config/locales/it.yml b/config/locales/it.yml
index 59d9f910a..98c1689c9 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -91,6 +91,7 @@ it:
       moderation:
         active: Attivo
         all: Tutto
+        disabled: Disattivato
         pending: In sospeso
         silenced: Limitato
         suspended: Sospeso
@@ -133,6 +134,7 @@ it:
       search: Cerca
       search_same_email_domain: Altri utenti con lo stesso dominio email
       search_same_ip: Altri utenti con lo stesso IP
+      security: Sicurezza
       security_measures:
         only_password: Solo password
         password_and_2fa: Password e A2F
@@ -142,8 +144,8 @@ it:
       show:
         created_reports: Rapporti creati
         targeted_reports: Segnalato da altri
-      silence: Limita
-      silenced: Limitato
+      silence: Silenzia
+      silenced: Silenziato
       statuses: Toot
       strikes: Provvedimenti precedenti
       subscribe: Iscriviti
@@ -156,7 +158,7 @@ it:
       unblocked_email_msg: Indirizzo email di %{username} sbloccato correttamente
       unconfirmed_email: Email non confermata
       undo_sensitized: Annulla sensibile
-      undo_silenced: Annulla limitazione
+      undo_silenced: Rimuovi silenzia
       undo_suspension: Annulla sospensione
       unsilenced_msg: Limitazione del profilo di %{username} annullata correttamente
       unsubscribe: Disiscriviti
@@ -427,6 +429,7 @@ it:
         resolve: Risolvi dominio
         title: Nuova voce della lista nera delle email
       no_email_domain_block_selected: Nessun blocco di dominio di posta elettronica è stato modificato in quanto nessuno è stato selezionato
+      not_permitted: Non consentito
       resolved_dns_records_hint_html: Il nome di dominio si risolve ai seguenti domini MX, che sono in ultima analisi responsabili per l'accettazione di e-mail. Il blocco di un dominio MX bloccherà le iscrizioni da qualsiasi indirizzo e-mail che utilizza lo stesso dominio MX, anche se il nome di dominio visibile è diverso. <strong>Fai attenzione a non bloccare i principali provider di posta elettronica.</strong>
       resolved_through_html: Risolto attraverso %{domain}
       title: Lista nera email
@@ -441,6 +444,7 @@ it:
         private_comment_description_html: 'Per aiutarti a tenere traccia della provenienza dei blocchi importati, i blocchi importati verranno creati con il seguente commento privato: <q>%{comment}</q>'
         private_comment_template: Importato da %{source} il %{date}
         title: Importare i blocchi di dominio
+      invalid_domain_block: 'Uno o più blocchi di dominio sono stati saltati a causa dei seguenti errori: %{error}'
       new:
         title: Importare i blocchi di dominio
       no_file: Nessun file selezionato
@@ -472,6 +476,7 @@ it:
       content_policies:
         comment: Nota interna
         description_html: Puoi definire le politiche del contenuto che saranno applicate a tutti i profili da questo dominio e ognuno dei suoi sottodomini.
+        limited_federation_mode_description_html: Puoi scegliere se consentire la federazione con questo dominio.
         policies:
           reject_media: Rifiuta multimedia
           reject_reports: Rifiuta rapporti
@@ -575,19 +580,23 @@ it:
         mark_as_sensitive_description_html: I contenuti nei post segnalati saranno segnati come sensibili e verrà registrata una sanzione per aiutarti a prendere ulteriori provvedimenti sulle future infrazioni dello stesso account.
         other_description_html: Vedi altre opzioni per controllare il comportamento dell'account e personalizzare la comunicazione all'account segnalato.
         resolve_description_html: Nessuna azione sarà intrapresa contro l'account segnalato, nessuna sanzione registrata, e la segnalazione sarà chiusa.
-        silence_description_html: Il profilo sarà visibile solo a coloro che lo seguono o lo cercano manualmente, limitandone fortemente la raggiungibilità. Può sempre essere annullato.
-        suspend_description_html: Il profilo e tutti i suoi contenuti diventeranno inaccessibili fino a quando non sarà cancellato. L'interazione con l'account sarà impossibile. Annullabile entro 30 giorni.
+        silence_description_html: L'account sarà visibile solo a coloro che lo seguono o lo cercano manualmente, limitandone fortemente la raggiungibilità. Può sempre essere annullato. Chiude tutte le segnalazioni contro questo account.
+        suspend_description_html: L'account e tutti i suoi contenuti saranno inaccessibili ed eventualmente cancellati, e interagire con esso sarà impossibile. Reversibile entro 30 giorni. Chiude tutte le segnalazioni contro questo account.
       actions_description_html: Decidete quali azioni intraprendere per risolvere la segnalazione. Se si intraprende un'azione punitiva contro l'account segnalato, gli sarà inviata una notifica via e-mail, tranne quando è selezionata la categoria <strong>Spam</strong>.
+      actions_description_remote_html: Decide quali azioni intraprendere per risolvere la relazione. Questo influenzerà solo come <strong>il tuo</strong> server comunica con questo account remoto e ne gestisce il contenuto.
       add_to_report: Aggiungi altro al report
       are_you_sure: Sei sicuro?
       assign_to_self: Assegna a me
       assigned: Moderatore assegnato
       by_target_domain: Dominio dell'account segnalato
+      cancel: Annulla
       category: Categoria
       category_description_html: Il motivo per cui questo account e/o contenuto è stato segnalato sarà citato nella comunicazione con l'account segnalato
       comment:
         none: Nessuno
       comment_description_html: 'Per fornire ulteriori informazioni, %{name} ha scritto:'
+      confirm: Conferma
+      confirm_action: Conferma l'azione di moderazione contro @%{acct}
       created_at: Segnalato
       delete_and_resolve: Cancella post
       forwarded: Inoltrato
@@ -604,6 +613,7 @@ it:
         placeholder: Descrivi quali azioni sono state intraprese, o ogni altro aggiornamento rilevante...
         title: Note
       notes_description_html: Visualizza e lascia note ad altri moderatori e al tuo futuro sé
+      processed_msg: 'Segnalazione #%{id} elaborata correttamente'
       quick_actions_description_html: 'Fai un''azione rapida o scorri verso il basso per vedere il contenuto segnalato:'
       remote_user_placeholder: l'utente remoto da %{instance}
       reopen: Riapri rapporto
@@ -616,9 +626,28 @@ it:
       status: Stato
       statuses: Contenuto segnalato
       statuses_description_html: Il contenuto offensivo sarà citato nella comunicazione con l'account segnalato
+      summary:
+        action_preambles:
+          delete_html: 'Stai per <strong>rimuovere</strong> alcuni post di <strong>@%{acct}</strong>. Questo conseguirà:'
+          mark_as_sensitive_html: 'Stai per <strong>contrassegnare</strong> alcuni post di <strong>@%{acct}</strong> come <strong>sensibili</strong>. Questo conseguirà:'
+          silence_html: 'Stai per <strong>limitare</strong> l''account di <strong>@%{acct}</strong>. Questo conseguirà:'
+          suspend_html: 'Stai per <strong>sospendere</strong> l''account di <strong>@%{acct}</strong>. Questo conseguirà:'
+        actions:
+          delete_html: Rimuovi i post offensivi
+          mark_as_sensitive_html: Contrassegna i file multimediali dei post offensivi come sensibili
+          silence_html: Limita severamente la portata di <strong>@%{acct}</strong> rendendo il suo profilo e il suo contenuto visibili solo a persone che già li seguono o che lo guardano manualmente
+          suspend_html: Sospendere <strong>@%{acct}</strong>, rendendo il suo profilo e i suoi contenuti inaccessibili e impossibilitandone l'interazione
+        close_report: 'Contrassegna la segnalazione #%{id} come risolta'
+        close_reports_html: Contrassegna <strong>tutte</strong> le segnalazioni contro <strong>@%{acct}</strong> come risolte
+        delete_data_html: Elimina il profilo e i contenuti di <strong>@%{acct}</strong> tra 30 giorni da ora, a meno che non vengano riattivati nel frattempo
+        preview_preamble_html: "<strong>@%{acct}</strong> riceverà un avvertimento con i seguenti contenuti:"
+        record_strike_html: Registra un provvedimento contro <strong>@%{acct}</strong> per aiutarti a gestire meglio future violazioni da questo account
+        send_email_html: Invia a <strong>@%{acct}</strong> una e-mail di avvertimento
+        warning_placeholder: Motivazione aggiuntiva facoltativa per l'azione di moderazione.
       target_origin: Origine dell'account segnalato
       title: Rapporti
       unassign: Non assegnare
+      unknown_action_msg: 'Azione sconosciuta: %{action}'
       unresolved: Non risolto
       updated_at: Aggiornato
       view_profile: Visualizza profilo
@@ -713,6 +742,8 @@ it:
         preamble: La comparsa di contenuti interessanti è determinante per l'arrivo di nuovi utenti che potrebbero non conoscere nessuno su Mastodon. Controlla in che modo varie funzionalità di scoperta funzionano sul tuo server.
         profile_directory: Directory del profilo
         public_timelines: Timeline pubbliche
+        publish_discovered_servers: Pubblica i server scoperti
+        publish_statistics: Pubblica le statistiche
         title: Scopri
         trends: Tendenze
       domain_blocks:
@@ -767,6 +798,7 @@ it:
         suspend: "%{name} ha sospeso l'account di %{target}"
       appeal_approved: Sottoposto ad appello
       appeal_pending: Appello in attesa
+      appeal_rejected: Appello respinto
     system_checks:
       database_schema_check:
         message_html: Ci sono migrazioni del database in attesa. Sei pregato di eseguirle per assicurarti che l'applicazione si comporti come previsto
@@ -780,6 +812,12 @@ it:
         message_html: Non hai definito alcuna regola del server.
       sidekiq_process_check:
         message_html: Nessun processo di Sidekiq in esecuzione per le code di %{value}. Sei pregato di revisionare la tua configurazione di Sidekiq
+      upload_check_privacy_error:
+        action: Controlla qui per maggiori informazioni
+        message_html: "<strong>Il tuo server web è mal configurato. La privacy dei tuoi utenti è a rischio.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Controlla qui per maggiori informazioni
+        message_html: "<strong>La tua archiviazione oggetti è mal configurata. La privacy dei tuoi utenti è a rischio.</strong>"
     tags:
       review: Esamina status
       updated_msg: Impostazioni hashtag aggiornate con successo
@@ -802,6 +840,7 @@ it:
           other: Condiviso da %{count} persone nell'ultima settimana
         title: Link in tendenza
         usage_comparison: Condiviso %{today} volte oggi, rispetto a %{yesterday} ieri
+      not_allowed_to_trend: Non è consentito il trend
       only_allowed: Solo consentiti
       pending_review: Revisione in sospeso
       preview_card_providers:
@@ -935,6 +974,7 @@ it:
   applications:
     created: Applicazione creata con successo
     destroyed: Applicazione eliminata con successo
+    logout: Disconnettiti
     regenerate_token: Rigenera il token di accesso
     token_regenerated: Token di accesso rigenerato
     warning: Fa' molta attenzione con questi dati. Non fornirli mai a nessun altro!
@@ -942,6 +982,8 @@ it:
   auth:
     apply_for_account: Richiedi un account
     change_password: Password
+    confirmations:
+      wrong_email_hint: Se l'indirizzo e-mail non è corretto, puoi modificarlo nelle impostazioni dell'account.
     delete_account: Elimina account
     delete_account_html: Se desideri cancellare il tuo account, puoi <a href="%{path}">farlo qui</a>. Ti sarà chiesta conferma.
     description:
@@ -969,6 +1011,8 @@ it:
     resend_confirmation: Invia di nuovo le istruzioni di conferma
     reset_password: Resetta la password
     rules:
+      accept: Accetta
+      back: Indietro
       preamble: Questi sono impostati e applicati dai moderatori di %{domain}.
       title: Alcune regole di base.
     security: Credenziali
@@ -1160,8 +1204,6 @@ it:
       index:
         hint: Questo filtro si applica a singoli post indipendentemente da altri criteri. Puoi aggiungere più post a questo filtro dall'interfaccia Web.
         title: Post filtrati
-  footer:
-    trending_now: Di tendenza ora
   generic:
     all: Tutto
     all_items_on_page_selected_html:
@@ -1184,8 +1226,6 @@ it:
     validation_errors:
       one: Qualcosa ancora non va bene! Per favore, controlla l'errore qui sotto
       other: Qualcosa ancora non va bene! Per favore, controlla i %{count} errori qui sotto
-  html_validator:
-    invalid_markup: 'contiene markup HTML non valido: %{error}'
   imports:
     errors:
       invalid_csv_file: 'File CSV non valido. Errore: %{error}'
@@ -1369,7 +1409,11 @@ it:
       unrecognized_emoji: non è un emoji riconosciuto
   relationships:
     activity: Attività dell'account
+    confirm_follow_selected_followers: Sei sicuro di voler seguire i follower selezionati?
+    confirm_remove_selected_followers: Sei sicuro di voler rimuovere i follower selezionati?
+    confirm_remove_selected_follows: Sei sicuro di voler rimuovere i follow selezionati?
     dormant: Dormiente
+    follow_failure: Impossibile seguire alcuni degli account selezionati.
     follow_selected_followers: Segui i seguaci selezionati
     followers: Seguaci
     following: Seguiti
@@ -1409,6 +1453,7 @@ it:
       electron: Electron
       firefox: Firefox
       generic: Browser sconosciuto
+      huawei_browser: Huawei Browser
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1418,6 +1463,7 @@ it:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Browser sconosciuto
       weibo: Weibo
     current_session: Sessione corrente
     description: "%{browser} su %{platform}"
@@ -1430,9 +1476,10 @@ it:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: piattaforma sconosciuta
+      unknown_platform: Piattaforma sconosciuta
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1545,7 +1592,7 @@ it:
       '7889238': 3 mesi
     min_age_label: Soglia di età
     min_favs: Conserva i post preferiti più di
-    min_favs_hint: Non cancella nessuno dei tuoi post che ha ricevuto più di questo numero di preferiti. Lascia vuoto per cancellare i post indipendentemente dal loro numero di preferiti
+    min_favs_hint: Non cancella nessuno dei tuoi post che ha ricevuto almeno questo numero di preferiti. Lascia vuoto per cancellare i post indipendentemente dal loro numero di preferiti
     min_reblogs: Conserva i post condivisi più di
     min_reblogs_hint: Non cancella nessuno dei tuoi post che è stato condiviso più di questo numero di volte. Lascia vuoto per cancellare i post indipendentemente dal loro numero di condivisioni
   stream_entries:
@@ -1645,12 +1692,13 @@ it:
       title: Benvenuto a bordo, %{name}!
   users:
     follow_limit_reached: Non puoi seguire più di %{limit} persone
+    go_to_sso_account_settings: Vai alle impostazioni dell'account del tuo provider di identità
     invalid_otp_token: Codice d'accesso non valido
     otp_lost_help_html: Se perdessi l'accesso ad entrambi, puoi entrare in contatto con %{email}
     seamless_external_login: Hai effettuato l'accesso tramite un servizio esterno, quindi le impostazioni di password e e-mail non sono disponibili.
     signed_in_as: 'Hai effettuato l''accesso come:'
   verification:
-    explanation_html: 'Puoi <strong>certificare te stesso come proprietario dei link nei metadati del tuo profilo</strong>. Per farlo, il sito a cui punta il link deve contenere un link che punta al tuo profilo Mastodon. Il link di ritorno <strong>deve</strong> avere l''attributo <code>rel="me"</code>. Il testo del link non ha importanza. Ecco un esempio:'
+    explanation_html: 'Puoi <strong>verificarti come proprietario dei link nei metadati del tuo profilo</strong>. Per questo, il sito web collegato deve contenere un collegamento al tuo profilo Mastodon. Dopo aver aggiunto il collegamento, potrebbe essere necessario tornare qui e salvare nuovamente il profilo affinché la verifica abbia effetto. Il link di ritorno <strong>deve</strong> avere un attributo <code>rel="me"</code>. Il contenuto del testo del collegamento non ha importanza. Ecco un esempio:'
     verification: Verifica
   webauthn_credentials:
     add: Aggiungi una nuova chiave di sicurezza
diff --git a/config/locales/ja.yml b/config/locales/ja.yml
index 39d6a6d70..c2faed6a2 100644
--- a/config/locales/ja.yml
+++ b/config/locales/ja.yml
@@ -89,6 +89,7 @@ ja:
       moderation:
         active: アクティブ
         all: すべて
+        disabled: 無効
         pending: 承認待ち
         silenced: 制限
         suspended: 停止済み
@@ -130,6 +131,7 @@ ja:
       search: 検索
       search_same_email_domain: 同じドメインのメールアドレスを使用しているユーザー
       search_same_ip: 同じIPのユーザーを検索
+      security: 認証方法
       security_measures:
         only_password: パスワードのみ
         password_and_2fa: パスワードと二要素認証
@@ -265,7 +267,7 @@ ja:
         reject_user_html: "%{name}さんが%{target}さんからの登録を拒否しました"
         remove_avatar_user_html: "%{name}さんが%{target}さんのアイコンを削除しました"
         reopen_report_html: "%{name}さんが通報 %{target}を未解決に戻しました"
-        resend_user_html: "%{name} が %{target} の確認メールを再送信しました"
+        resend_user_html: "%{name}さんが%{target}の確認メールを再送信しました"
         reset_password_user_html: "%{name}さんが%{target}さんのパスワードをリセットしました"
         resolve_report_html: "%{name}さんが通報 %{target}を解決済みにしました"
         sensitive_account_html: "%{name}さんが%{target}さんのメディアを閲覧注意にマークしました"
@@ -385,7 +387,7 @@ ja:
         create: ブロックを作成
         hint: ドメインブロックはデータベース中のアカウント項目の作成を妨げませんが、遡って自動的に指定されたモデレーションをそれらのアカウントに適用します。
         severity:
-          desc_html: "<strong></strong>はこのドメインでのアカウントの投稿をフォローしていない人から隠します。<strong>停止</strong>はこのドメインでのアカウントのコンテンツ、メディア、プロフィールデータをサーバーから削除します。メディアファイルのみを拒否したい場合は<strong>なし</strong>を選択します。"
+          desc_html: "<strong>制限</strong>は、このドメイン上のアカウントからの投稿が、相手をフォローしている場合を除き非表示になります。<strong>停止</strong>は、このドメイン上のすべてのコンテンツ、メディア、およびプロフィールデータを受け付けなくなります。メディアファイルのみを拒否したい場合は<strong>なし</strong>を選択します。"
           noop: なし
           silence: 制限
           suspend: 停止
@@ -419,6 +421,7 @@ ja:
         resolve: ドメイン解決
         title: 新規メールドメインブロック
       no_email_domain_block_selected: 何も選択されていないためメールドメインブロックを変更しませんでした
+      not_permitted: 権限がありません
       resolved_dns_records_hint_html: ドメイン名はDNSでMXドメインに名前解決され、最終的にメールを受け付ける役割を担います。目に見えるドメイン名が異なっていても、同じMXドメインを使用するメールアドレスからのアカウント登録がブロックされます。<strong>主要なメールプロバイダーをブロックしないように注意して下さい。</strong>
       resolved_through_html: "%{domain}を通して解決しました"
       title: メールドメインブロック
@@ -430,9 +433,10 @@ ja:
       import:
         description_html: ドメインブロックのリストをインポートしようとしています。このリストを自分で作成していない場合は、慎重に確認してください。
         existing_relationships_warning: 既存のフォロー関係
-        private_comment_description_html: インポートされたブロックがどこから来たのかを追跡するため、インポートされたブロックは次のプライベートコメントを追加して作成されます:<q>%{comment}</q>
+        private_comment_description_html: 'ブロックのインポート元を判別できるようにするため、ブロックは次のプライベートコメントを追加してインポートされます: <q>%{comment}</q>'
         private_comment_template: "%{source} から %{date} にインポートしました"
         title: ドメインブロックをインポート
+      invalid_domain_block: 'エラーが発生したため、ブロックできなかったドメインがあります: %{error}'
       new:
         title: ドメインブロックをインポート
       no_file: ファイルが選択されていません
@@ -462,6 +466,7 @@ ja:
       content_policies:
         comment: 内部メモ
         description_html: このドメインとそのサブドメインのすべてのアカウントに適用されるコンテンツポリシーを定義できます。
+        limited_federation_mode_description_html: このドメインとの連合を許可するかどうかを選択できます。
         policies:
           reject_media: メディアを拒否する
           reject_reports: 通報を拒否
@@ -474,7 +479,7 @@ ja:
         instance_accounts_dimension: 最もフォローされているアカウント
         instance_accounts_measure: 保存されたアカウント
         instance_followers_measure: そこでの我々のフォロワー
-        instance_follows_measure: そのインスタンスのこのインスタンスでのフォロワー
+        instance_follows_measure: リモートフォローしているアカウント
         instance_languages_dimension: 人気の言語
         instance_media_attachments_measure: 保存されたメディア
         instance_reports_measure: 通報
@@ -563,19 +568,23 @@ ja:
         mark_as_sensitive_description_html: 報告された投稿のメディアは閲覧注意となり、ストライクが記録され、同じアカウントによる今後の違反行為のエスカレーションに役立てられます。
         other_description_html: アカウントの動作を制御するためのオプションや、報告されたアカウントへの通信をカスタマイズするためのオプションを確認してください。
         resolve_description_html: 報告されたアカウントに対していかなる措置も取られず、ストライクも記録されず、報告は終了します。
-        silence_description_html: 既にフォローしている人、または自分で参照した人にのみ表示されるため、プロフィールが届く範囲が大きく制限されます。いつでも元に戻すことができます。
-        suspend_description_html: プロフィールとすべてのコンテンツは、最終的に削除されるまでアクセスできなくなります。アカウントとのやり取りは不可能です。30日以内に取り消し可能です。
+        silence_description_html: このアカウントは、すでにフォローしている人、または手動で検索した人にしか見えないため、リーチが極端に制限されます。いつでも元に戻すことができます。このアカウントに対するすべての通報をクローズします。
+        suspend_description_html: アカウントとそのすべての内容にアクセスできなくなり、最終的に削除され、やり取りは不可能になります。 30日以内であれば元に戻すことができます。このアカウントに対するすべての通報をクローズします。
       actions_description_html: このレポートを解決するために取るアクションを決定します。 報告されたアカウントに対して懲罰的な措置を取った場合、メール通知が送信されますが<strong>スパム</strong>カテゴリが選択されている場合を除きます。
+      actions_description_remote_html: この通報を解決するためのアクションを選択してください。これは<strong>あなたの</strong>サーバーがこのリモートアカウントと通信し、そのコンテンツを処理する時のみ影響します。
       add_to_report: 通報にさらに追加
       are_you_sure: 本当に実行しますか?
       assign_to_self: 担当になる
       assigned: 担当者
       by_target_domain: ドメイン
+      cancel: キャンセル
       category: カテゴリー
       category_description_html: 選択した理由は通報されたアカウントへの連絡時に引用されます
       comment:
         none: なし
       comment_description_html: "%{name}からの詳細情報:"
+      confirm: 確認
+      confirm_action: "@%{acct} さんに対するアクション"
       created_at: 通報日時
       delete_and_resolve: 投稿を削除
       forwarded: 転送済み
@@ -592,6 +601,7 @@ ja:
         placeholder: どのような措置が取られたか、または関連する更新を記述してください…
         title: メモ
       notes_description_html: 他のモデレーターと将来の自分にメモを残してください
+      processed_msg: '通報 #%{id} が正常に処理されました'
       quick_actions_description_html: 'クイックアクションを実行するかスクロールして報告された通報を確認してください:'
       remote_user_placeholder: "%{instance}からのリモートユーザー"
       reopen: 未解決に戻す
@@ -604,9 +614,28 @@ ja:
       status: ステータス
       statuses: 通報内容
       statuses_description_html: 問題の投稿は通報されたアカウントへの連絡時に引用されます
+      summary:
+        action_preambles:
+          delete_html: "<strong>@%{acct}</strong>さんの投稿を<strong>削除</strong>します。この操作は:"
+          mark_as_sensitive_html: "<strong>@%{acct}</strong>さんの投稿を<strong>閲覧注意</strong>として<strong>マーク</strong>します。この操作は:"
+          silence_html: "<strong>@%{acct}</strong>さんのアカウントを<strong>制限</strong>します。この操作は:"
+          suspend_html: "<strong>@%{acct}</strong>さんのアカウントを<strong>停止</strong>します。この操作は:"
+        actions:
+          delete_html: 当該の投稿を削除します
+          mark_as_sensitive_html: 当該の投稿に含まれるメディアを閲覧注意にします
+          silence_html: プロフィールとコンテンツを、すでにフォローしている人や、意図的にプロフィールにアクセスする人にのみ表示することで、<strong>@%{acct}</strong>さんのリーチを厳しく制限します
+          suspend_html: "<strong>@%{acct}</strong>さんのアカウントが凍結され、プロフィールとコンテンツへのアクセス、および投稿ができなくなります"
+        close_report: '通報 #%{id} を解決済みにします'
+        close_reports_html: "<strong>@%{acct}</strong>さんに対する<strong>すべての</strong>通報を解決済みにします"
+        delete_data_html: 停止が解除されないまま30日経過すると、<strong>@%{acct}</strong>さんのプロフィールとコンテンツは削除されます
+        preview_preamble_html: "<strong>@%{acct}</strong>さんに次の内容の警告を通知します:"
+        record_strike_html: 今後、<strong>@%{acct}</strong>さんが違反行為をしたときにエスカレーションできるように、このアカウントに対するストライクを記録します
+        send_email_html: "<strong>@%{acct}</strong>さんに警告メールを送信します"
+        warning_placeholder: アクションを行使する追加の理由(オプション)
       target_origin: 報告されたアカウントの起源
       title: 通報
       unassign: 担当を外す
+      unknown_action_msg: '不明なアクションです: %{action}'
       unresolved: 未解決
       updated_at: 更新日時
       view_profile: プロフィールを表示
@@ -699,6 +728,8 @@ ja:
         preamble: Mastodon を知らないユーザーを取り込むには、興味深いコンテンツを浮上させることが重要です。サーバー上で様々なディスカバリー機能がどのように機能するかを制御します。
         profile_directory: ディレクトリ
         public_timelines: 公開タイムライン
+        publish_discovered_servers: 接続しているサーバーを公開する
+        publish_statistics: 統計情報を公開する
         title: 見つける
         trends: トレンド
       domain_blocks:
@@ -753,6 +784,7 @@ ja:
         suspend: "%{name}さんが%{target}さんのアカウントを停止しました"
       appeal_approved: 抗議済み
       appeal_pending: 保留中の抗議
+      appeal_rejected: 却下済みの抗議
     system_checks:
       database_schema_check:
         message_html: 未実行のデータベースマイグレーションがあります。実行して正常に動作するようにしてください。
@@ -766,6 +798,12 @@ ja:
         message_html: サーバーのルールを定義していません。
       sidekiq_process_check:
         message_html: "%{value}キューに対応するSidekiqプロセスがありません。Sidekiqの設定を確認してください。"
+      upload_check_privacy_error:
+        action: ここを開いて詳細を確認してください
+        message_html: "<strong>Web サーバーが正しく設定されていません。ユーザーのプライバシーが危険な状態になっています。</strong>"
+      upload_check_privacy_error_object_storage:
+        action: ここを開いて詳細を確認してください
+        message_html: "<strong>オブジェクトストレージが正しく設定されていません。ユーザーのプライバシーが危険な状態になっています。</strong>"
     tags:
       review: 審査状況
       updated_msg: ハッシュタグ設定が更新されました
@@ -787,6 +825,7 @@ ja:
           other: 週間%{count}人に共有されました
         title: トレンドリンク
         usage_comparison: 今日は%{today}回、昨日は%{yesterday}回共有されました
+      not_allowed_to_trend: 未許可のトレンド
       only_allowed: 許可済み
       pending_review: 保留中
       preview_card_providers:
@@ -915,6 +954,7 @@ ja:
   applications:
     created: アプリが作成されました
     destroyed: アプリが削除されました
+    logout: ログアウト
     regenerate_token: アクセストークンの再生成
     token_regenerated: アクセストークンが再生成されました
     warning: このデータは気をつけて取り扱ってください。他の人と共有しないでください!
@@ -922,6 +962,8 @@ ja:
   auth:
     apply_for_account: アカウントのリクエスト
     change_password: パスワード
+    confirmations:
+      wrong_email_hint: メールアドレスが正しくない場合は、アカウント設定で変更できます。
     delete_account: アカウントの削除
     delete_account_html: アカウントを削除したい場合、<a href="%{path}">こちら</a>から手続きが行えます。削除する前に、確認画面があります。
     description:
@@ -949,6 +991,8 @@ ja:
     resend_confirmation: 確認メールを再送する
     reset_password: パスワードを再発行
     rules:
+      accept: 同意する
+      back: 戻る
       preamble: これらは %{domain} モデレータによって設定され、実施されます。
       title: いくつかのルールがあります。
     security: セキュリティ
@@ -958,6 +1002,7 @@ ja:
       email_settings_hint_html: 確認用のメールを%{email}に送信しました。メールアドレスが正しくない場合、以下より変更することができます。
       title: セットアップ
     sign_in:
+      preamble_html: "<strong>%{domain}</strong> の資格情報でサインインします。 あなたのアカウントが別のサーバーでホストされている場合は、ここでログインすることはできません。"
       title: "%{domain}にログイン"
     sign_up:
       preamble: この Mastodon サーバーのアカウントがあれば、ネットワーク上の他の人のアカウントがどこでホストされているかに関係なく、その人をフォローすることができます。
@@ -1095,12 +1140,12 @@ ja:
   featured_tags:
     add_new: 追加
     errors:
-      limit: 注目のハッシュタグの上限に達しました
+      limit: すでに注目のハッシュタグの上限数に達しています
     hint_html: "<strong>注目のハッシュタグとは?</strong> プロフィールページに目立つ形で表示され、そのハッシュタグのついたあなたの公開投稿だけを抽出して閲覧できるようにします。クリエイティブな仕事や長期的なプロジェクトを追うのに優れた機能です。"
   filters:
     contexts:
       account: プロフィール
-      home: ホームタイムライン
+      home: ホームおよびリスト
       notifications: 通知
       public: 公開タイムライン
       thread: 会話
@@ -1136,8 +1181,6 @@ ja:
       index:
         hint: このフィルターは、他の条件に関係なく個々の投稿を選択する場合に適用されます。Webインターフェースからこのフィルターにさらに投稿を追加できます。
         title: フィルターされた投稿
-  footer:
-    trending_now: トレンドタグ
   generic:
     all: すべて
     all_items_on_page_selected_html:
@@ -1156,8 +1199,6 @@ ja:
     today: 今日
     validation_errors:
       other: エラーが発生しました! 以下の%{count}件のエラーを確認してください
-  html_validator:
-    invalid_markup: '無効なHTMLマークアップが含まれています: %{error}'
   imports:
     errors:
       invalid_csv_file: '無効なCSVファイルです。エラー: %{error}'
@@ -1199,7 +1240,7 @@ ja:
     title: 新規ユーザーの招待
   lists:
     errors:
-      limit: リストの上限に達しました
+      limit: リストの上限数に達しています
   login_activities:
     authentication_methods:
       otp: 二要素認証アプリ
@@ -1340,7 +1381,11 @@ ja:
       unrecognized_emoji: は絵文字として認識されていません
   relationships:
     activity: 活動
+    confirm_follow_selected_followers: 選択したフォロワーをフォローしてもよろしいですか?
+    confirm_remove_selected_followers: 選択したフォロワーを削除してもよろしいですか?
+    confirm_remove_selected_follows: 選択したフォローを削除してもよろしいですか?
     dormant: 非アクティブ
+    follow_failure: 選択したアカウントの一部をフォローできませんでした。
     follow_selected_followers: 選択したフォロワーをフォロー
     followers: フォロワー
     following: フォロー中
@@ -1380,6 +1425,7 @@ ja:
       electron: Electron
       firefox: Firefox
       generic: 不明なブラウザ
+      huawei_browser: Huaweiブラウザ
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1389,6 +1435,7 @@ ja:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: 不明なブラウザ
       weibo: Weibo
     current_session: 現在のセッション
     description: "%{platform}上の%{browser}"
@@ -1401,9 +1448,10 @@ ja:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: 不明なプラットフォーム
+      unknown_platform: 不明なプラットフォーム
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1510,7 +1558,7 @@ ja:
       '7889238': 3ヶ月
     min_age_label: 投稿を保持する期間
     min_favs: お気に入りの基準値
-    min_favs_hint: この数以上、お気に入りされた投稿を削除せずに残します。空白にしておくと、お気に入りに登録された数に関わらず投稿を削除します。
+    min_favs_hint: この数以上、お気に入り登録された投稿を削除せずに残します。空白にしておくと、お気に入りの数に関わらず投稿を削除します。
     min_reblogs: ブーストの基準値
     min_reblogs_hint: この数以上、ブーストされた投稿を削除せずに残します。空白にしておくと、ブーストされた数に関わらず投稿を削除します。
   stream_entries:
@@ -1612,12 +1660,13 @@ ja:
       title: ようこそ、%{name}さん!
   users:
     follow_limit_reached: あなたは現在 %{limit}人以上フォローできません
+    go_to_sso_account_settings: 外部サービスアカウントの設定はこちらで行ってください
     invalid_otp_token: 二要素認証コードが間違っています
     otp_lost_help_html: どちらも使用できない場合、%{email}に連絡を取ると解決できるかもしれません
     seamless_external_login: あなたは外部サービスを介してログインしているため、パスワードとメールアドレスの設定は利用できません。
     signed_in_as: '下記でログイン中:'
   verification:
-    explanation_html: <strong>プロフィール内のリンクの所有者であることを認証することができます</strong>。そのためにはリンクされたウェブサイトにMastodonプロフィールへのリンクが含まれている必要があります。リンクには<code>rel="me"</code>属性を<strong>必ず</strong>与えなければなりません。リンクのテキストについては重要ではありません。以下は例です:
+    explanation_html: <strong>プロフィール補足情報のリンクの所有者であることを認証できます</strong>。認証するには、リンク先のウェブサイトにMastodonプロフィールへのリンクを追加してください。リンクを追加後、このページで変更の保存を再実行すると認証が反映されます。プロフィールへのリンクには<code>rel="me"</code>属性が<strong>かならず</strong>付与してください。リンク内のテキストは自由に記述できます。以下は一例です:
     verification: 認証
   webauthn_credentials:
     add: セキュリティキーを追加
diff --git a/config/locales/ka.yml b/config/locales/ka.yml
index 41a51a8b0..80dc2b9f5 100644
--- a/config/locales/ka.yml
+++ b/config/locales/ka.yml
@@ -344,9 +344,6 @@ ka:
       expires_at: ვადა გასდის
       uses: მოხმარება
     title: მოიწვიეთ ხალხი
-  lists:
-    errors:
-      limit: მიაღწიეთ სიების მაქსიმალურ ოდენობას
   media_attachments:
     validations:
       images_and_video: ვიდეოს დართვა სტატუსზე, რომელიც უკვე მოიცავს სურათებს, ვერ მოხერხდება
@@ -392,8 +389,6 @@ ka:
     next: შემდეგი
     older: ძველი
     prev: წინა
-  preferences:
-    other: სხვა
   remote_follow:
     missing_resource: საჭირო გადამისამართების ურლ თქვენი ანგარიშისთვის ვერ მოიძებნა
   sessions:
@@ -426,7 +421,6 @@ ka:
       ios: აი-ოსი
       linux: ლინუქსი
       mac: მაკი
-      other: ამოუცნობი პლატფორმა
       windows: ვინდოუსი
       windows_mobile: ვინდოუს მობაილი
       windows_phone: ვინდოუს ფოუნი
diff --git a/config/locales/kab.yml b/config/locales/kab.yml
index d2a96d41f..ffd79e76f 100644
--- a/config/locales/kab.yml
+++ b/config/locales/kab.yml
@@ -256,6 +256,7 @@ kab:
     domain_blocks:
       add_new: Rni iḥder amaynut n taɣult
       domain: Taγult
+      export: Sifeḍ
       import: Kter
       new:
         create: Rnu-d iḥder
@@ -507,7 +508,6 @@ kab:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
     '500':
       title: Asebter-ayi d arameγtu
@@ -517,6 +517,7 @@ kab:
   exports:
     archive_takeout:
       date: Azemz
+      download: Sider-d aḥraz-ik·im
       size: Teγzi
     bookmarks: Ticraḍ
     csv: CSV
@@ -542,8 +543,6 @@ kab:
       back_to_filter: Tuɣalin ɣer umsizdeg
       batch:
         remove: Kkes seg umsizdeg
-  footer:
-    trending_now: Ayen mucaɛen tura
   generic:
     all: Akk
     changes_saved_msg: Ttwaskelsen ibelliden-ik·im akken ilaq!
@@ -626,8 +625,6 @@ kab:
     next: Γer zdat
     older: Aqbuṛ
     prev: Win iɛeddan
-  preferences:
-    other: Wiyaḍ
   privacy_policy:
     title: Tasertit tabaḍnit
   relationships:
@@ -670,7 +667,6 @@ kab:
       ios: iOS
       linux: Linux
       mac: macOS
-      other: anagraw arussin
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Tiliγri Windows Phone
diff --git a/config/locales/kk.yml b/config/locales/kk.yml
index 4c7189588..1812f8983 100644
--- a/config/locales/kk.yml
+++ b/config/locales/kk.yml
@@ -453,8 +453,6 @@ kk:
     storage: Медиа жинақ
   featured_tags:
     add_new: Жаңасын қосу
-    errors:
-      limit: Хэштег лимитинен асып кеттіңіз
     hint_html: "<strong> Ерекшеліктері бар хэштегтер дегеніміз не? </strong> Олар жалпыға қол жетімді профильде көрсетіледі және адамдарға сіздің жалпы хабарламаларыңызды сол хэштегтердің астына қарауға мүмкіндік береді. Олар шығармашылық жұмыстарды немесе ұзақ мерзімді жобаларды бақылаудың тамаша құралы."
   filters:
     contexts:
@@ -472,8 +470,6 @@ kk:
       title: Фильтрлер
     new:
       title: Жаңа фильтр қосу
-  footer:
-    trending_now: Бүгінгі трендтер
   generic:
     all: Барлығы
     changes_saved_msg: Өзгерістер сәтті сақталды!
@@ -483,8 +479,6 @@ kk:
     validation_errors:
       one: Бір нәрсе дұрыс емес! Төмендегі қатені қараңыз
       other: Бір нәрсе дұрыс емес! Төмендегі %{count} қатені қараңыз
-  html_validator:
-    invalid_markup: 'жарамсыз HTML код: %{error}'
   imports:
     modes:
       merge: Біріктіру
@@ -521,9 +515,6 @@ kk:
       expires_at: Аяқталу мерзімі
       uses: Қолданыс
     title: Адам шақыру
-  lists:
-    errors:
-      limit: Сіз тізімдердің максимум мөлшеріне жеттіңіз
   media_attachments:
     validations:
       images_and_video: Жазбаға видео қоса алмайсыз, тек сурет қосуға болады
@@ -653,7 +644,6 @@ kk:
       ios: iОS
       linux: Lіnux
       mac: Mаc
-      other: белгісіз платформа
       windows: Windоws
       windows_mobile: Windows Mоbile
       windows_phone: Windоws Phone
@@ -768,5 +758,4 @@ kk:
     seamless_external_login: Сыртқы сервис арқылы кіріпсіз, сондықтан құпиясөз және электрондық пошта параметрлері қол жетімді емес.
     signed_in_as: 'Былай кірдіңіз:'
   verification:
-    explanation_html: 'Өзіңіздің профиль метадеректеріңіздегі сілтемелердің иесі ретінде өзіңізді <strong>тексере аласыз</strong>. Ол үшін байланыстырылған веб-сайтта Mastodon профиліне <strong>сілтеме болуы керек. </strong> Сілтемеде <code>rel = «me»</code> атрибуты болуы керек. Сілтеме мәтінінің мазмұны маңызды емес. Міне мысал:'
     verification: Растау
diff --git a/config/locales/kn.yml b/config/locales/kn.yml
index d44eb868f..998f613ef 100644
--- a/config/locales/kn.yml
+++ b/config/locales/kn.yml
@@ -6,7 +6,5 @@ kn:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/ko.yml b/config/locales/ko.yml
index a320723bf..bbfc6f311 100644
--- a/config/locales/ko.yml
+++ b/config/locales/ko.yml
@@ -25,9 +25,9 @@ ko:
       action: 조치 취하기
       title: "%{acct} 계정에 중재 취하기"
     account_moderation_notes:
-      create: 중재 기록 작성하기
-      created_msg: 중재 기록이 성공적으로 작성되었습니다!
-      destroyed_msg: 중재 기록이 성공적으로 삭제되었습니다!
+      create: 참고사항 남기기
+      created_msg: 중재 참고사항을 만들었습니다!
+      destroyed_msg: 중재 참고사항을 지웠습니다!
     accounts:
       add_email_domain_block: 이 이메일 도메인을 차단하기
       approve: 허가
@@ -69,10 +69,10 @@ ko:
       enabled: 활성
       enabled_msg: "%{username}의 계정을 성공적으로 얼리기 해제하였습니다"
       followers: 팔로워
-      follows: 팔로잉
+      follows: 팔로우
       header: 헤더
       inbox_url: 수신함 URL
-      invite_request_text: 가입 하려는 이유
+      invite_request_text: 가입하려는 이유
       invited_by: 초대자
       ip: IP
       joined: 가입
@@ -89,11 +89,12 @@ ko:
       moderation:
         active: 활동
         all: 전체
+        disabled: 비활성화됨
         pending: 대기 중
         silenced: 제한됨
         suspended: 정지 중
         title: 중재
-      moderation_notes: 중재 기록
+      moderation_notes: 중재 참고사항
       most_recent_activity: 최근 활동
       most_recent_ip: 최근 IP
       no_account_selected: 아무 것도 선택 되지 않아 어떤 계정도 변경 되지 않았습니다
@@ -130,6 +131,7 @@ ko:
       search: 검색
       search_same_email_domain: 같은 이메일 도메인을 가진 다른 사용자들
       search_same_ip: 같은 IP의 다른 사용자들
+      security: 보안
       security_measures:
         only_password: 암호만
         password_and_2fa: 암호와 2단계 인증
@@ -139,8 +141,8 @@ ko:
       show:
         created_reports: 이 계정에서 제출된 신고
         targeted_reports: 이 계정에 대한 신고
-      silence: 침묵
-      silenced: 침묵 됨
+      silence: 제한
+      silenced: 제한됨
       statuses: 게시물
       strikes: 이전의 처벌들
       subscribe: 구독하기
@@ -153,12 +155,12 @@ ko:
       unblocked_email_msg: "%{username}의 이메일 주소를 성공적으로 차단 해제했습니다"
       unconfirmed_email: 확인되지 않은 이메일 주소
       undo_sensitized: 민감함으로 설정 취소
-      undo_silenced: 침묵 해제
+      undo_silenced: 제한 해제
       undo_suspension: 정지 해제
       unsilenced_msg: 성공적으로 %{username} 계정을 제한 해제했습니다
       unsubscribe: 구독 해제
       unsuspended_msg: 성공적으로 %{username} 계정을 정지 해제했습니다
-      username: 아이디
+      username: 사용자명
       view_domain: 도메인의 요약 보기
       warn: 경고
       web: 웹
@@ -210,12 +212,12 @@ ko:
         reset_password_user: 암호 재설정
         resolve_report: 신고 처리
         sensitive_account: 당신의 계정의 미디어를 민감함으로 표시
-        silence_account: 계정 침묵
+        silence_account: 계정 제한
         suspend_account: 계정 정지
         unassigned_report: 신고 맡기 취소
         unblock_email_account: 이메일 주소 차단 해제
         unsensitive_account: 당신의 계정의 미디어를 민감함으로 표시하지 않음
-        unsilence_account: 계정 침묵 취소
+        unsilence_account: 계정 제한 취소
         unsuspend_account: 계정 정지 취소
         update_announcement: 공지사항 업데이트
         update_custom_emoji: 커스텀 에모지 업데이트
@@ -269,12 +271,12 @@ ko:
         reset_password_user_html: "%{name} 님이 사용자 %{target}의 암호를 초기화했습니다"
         resolve_report_html: "%{name} 님이 신고 %{target}를 처리됨으로 변경하였습니다"
         sensitive_account_html: "%{name} 님이 %{target}의 미디어를 민감함으로 표시했습니다"
-        silence_account_html: "%{name} 님이 %{target}의 계정을 침묵시켰습니다"
+        silence_account_html: "%{name} 님이 %{target}의 계정을 제한시켰습니다"
         suspend_account_html: "%{name} 님이 %{target}의 계정을 정지시켰습니다"
         unassigned_report_html: "%{name} 님이 신고 %{target}을 할당 해제했습니다"
         unblock_email_account_html: "%{name} 님이 %{target}의 이메일 주소를 차단 해제했습니다"
         unsensitive_account_html: "%{name} 님이 %{target}의 미디어를 민감하지 않음으로 표시했습니다"
-        unsilence_account_html: "%{name} 님이 %{target}의 계정에 대한 침묵을 해제했습니다"
+        unsilence_account_html: "%{name} 님이 %{target}의 계정에 대한 제한을 해제했습니다"
         unsuspend_account_html: "%{name} 님이 %{target}의 계정에 대한 정지를 해제했습니다"
         update_announcement_html: "%{name} 님이 공지사항 %{target}을 갱신했습니다"
         update_custom_emoji_html: "%{name} 님이 에모지 %{target}를 업데이트 했습니다"
@@ -297,7 +299,7 @@ ko:
         create: 공지사항 생성
         title: 새 공지사항
       publish: 게시
-      published_msg: 공지가 성공적으로 발행되었습니다!
+      published_msg: 공지사항이 성공적으로 발행되었습니다!
       scheduled_for: "%{time}에 예약됨"
       scheduled_msg: 공지의 발행이 예약되었습니다!
       title: 공지사항
@@ -342,7 +344,7 @@ ko:
       active_users: 활성 사용자
       interactions: 상호 작용
       media_storage: 미디어 저장소
-      new_users: 새로운 사용자
+      new_users: 새 사용자
       opened_reports: 신고 열림
       pending_appeals_html:
         other: "<strong>%{count}</strong>개의 대기 중인 이의 제기"
@@ -421,8 +423,9 @@ ko:
         resolve: 도메인 검사
         title: 새 이메일 도메인 차단
       no_email_domain_block_selected: 아무 것도 선택 되지 않아 어떤 이메일 도메인 차단도 변경되지 않았습니다
+      not_permitted: 권한 없음
       resolved_dns_records_hint_html: 도메인 네임은 다음의 MX 도메인으로 연결되어 있으며, 이메일을 받는데 필수적입니다. MX 도메인을 차단하면 같은 MX 도메인을 사용하는 어떤 이메일이라도 가입할 수 없게 되며, 보여지는 도메인이 다르더라도 적용됩니다. <strong>주요 이메일 제공자를 차단하지 않도록 조심하세요.</strong>
-      resolved_through_html: "%{domain}을 통해 해결됨"
+      resolved_through_html: "%{domain}을 통해 리졸빙됨"
       title: Email 도메인 차단
     export_domain_allows:
       new:
@@ -435,6 +438,7 @@ ko:
         private_comment_description_html: '어디서 불러온 것인지 추적을 원활하게 하기 위해서, 불러온 차단들은 다음과 같은 비공개 주석과 함께 생성될 것입니다: <q>%{comment}</q>'
         private_comment_template: "%{date}에 %{source}에서 불러옴"
         title: 도메인 차단 불러오기
+      invalid_domain_block: '한 개 이상의 도메인 차단이 생략되었습니다. 에러는 다음과 같습니다: %{error}'
       new:
         title: 도메인 차단 불러오기
       no_file: 선택된 파일이 없습니다
@@ -464,6 +468,7 @@ ko:
       content_policies:
         comment: 내부 참고사항
         description_html: 이 도메인과 하위 도메인의 모든 계정에 적용될 콘텐츠 정책을 정의할 수 있습니다.
+        limited_federation_mode_description_html: 이 도메인의 연합을 허용할지 선택할 수 있습니다.
         policies:
           reject_media: 미디어 거부
           reject_reports: 신고 거부
@@ -494,7 +499,7 @@ ko:
       destroyed_msg: "%{domain}의 데이터는 곧바로 지워지도록 대기열에 들어갔습니다."
       empty: 도메인이 하나도 없습니다.
       known_accounts:
-        other: "%{count}개의 알려진 계정"
+        other: "%{count} 개의 알려진 계정"
       moderation:
         all: 모두
         limited: 제한됨
@@ -557,7 +562,7 @@ ko:
     reports:
       account:
         notes:
-          other: "%{count}개의 기록"
+          other: "%{count} 개의 참고사항"
       action_log: 감사 기록
       action_taken_by: 신고 처리자
       actions:
@@ -565,20 +570,24 @@ ko:
         mark_as_sensitive_description_html: 신고된 게시물의 미디어는 민감함으로 표시될 것이며 이 처벌기록은 같은 계정의 향후 규정 위반에 대해 참고사항으로 쓰일 수 있도록 저장됩니다.
         other_description_html: 계정 동작을 제어하고 신고된 계정과의 의사소통을 사용자 지정하기 위한 추가 옵션을 봅니다.
         resolve_description_html: 신고된 계정에 대해 아무런 동작도 취하지 않으며, 처벌기록이 남지 않으며, 신고는 처리됨으로 변경됩니다.
-        silence_description_html: 이미 팔로우 하고 있는 사람이나 수동으로 찾아보는 사람에게만 프로필이 보여지고, 도달 범위를 엄격하게 제한합니다. 언제든지 되돌릴 수 있습니다.
-        suspend_description_html: 프로필과 모든 콘텐츠가 최종적으로 삭제될 때까지 접근 불가상태가 됩니다. 이 계정과의 상호작용은 불가능해집니다. 30일 이내에 되돌릴 수 있습니다.
+        silence_description_html: 이 계정을 이미 팔로우 하고 있는 사람이나 수동으로 찾아보는 사람에게만 프로필이 보여지고, 도달 범위를 엄격하게 제한합니다. 언제든지 되돌릴 수 있습니다. 이 계정에 대한 모든 신고를 닫습니다.
+        suspend_description_html: 이 계정과 이 계정의 콘텐츠들은 접근 불가능해지고 삭제될 것이며, 상호작용은 불가능해집니다. 30일 이내에 되돌릴 수 있습니다. 이 계정에 대한 모든 신고를 닫습니다.
       actions_description_html: 이 신고를 해결하기 위해 취해야 할 조치를 지정해주세요. 신고된 계정에 대해 처벌 조치를 취하면, <strong>스팸</strong> 카테고리가 선택된 경우를 제외하고 해당 계정으로 이메일 알림이 전송됩니다.
+      actions_description_remote_html: 이 신고를 해결하기 위해 실행할 행동을 결정하세요. 이 결정은 이 원격 계정과 그 콘텐츠를 다루는 방식에 대해 <strong>이 서버</strong>에서만 영향을 끼칩니다
       add_to_report: 신고에 더 추가하기
       are_you_sure: 정말로 실행하시겠습니까?
       assign_to_self: 나에게 할당하기
       assigned: 할당된 중재자
       by_target_domain: 신고된 계정의 도메인
+      cancel: 취소
       category: 카테고리
       category_description_html: 이 계정 또는 게시물이 신고된 이유는 신고된 계정과의 의사소통 과정에 인용됩니다
       comment:
         none: 없음
       comment_description_html: '더 많은 정보를 위해, %{name} 님이 작성했습니다:'
-      created_at: 리포트 시각
+      confirm: 확정
+      confirm_action: "@%{acct}에 취할 중재 결정에 대한 확인"
+      created_at: 신고 시각
       delete_and_resolve: 게시물 삭제
       forwarded: 전달됨
       forwarded_to: "%{domain}에게 전달됨"
@@ -587,28 +596,48 @@ ko:
       mark_as_unresolved: 미해결로 표시
       no_one_assigned: 아무도 없음
       notes:
-        create: 기록 추가
-        create_and_resolve: 기록을 작성하고 해결됨으로 표시
-        create_and_unresolve: 기록 작성과 함께 미해결로 표시
+        create: 참고사항 추가
+        create_and_resolve: 참고사항 기재 및 종결
+        create_and_unresolve: 참고사항 기재 및 재개
         delete: 삭제
-        placeholder: 이 리포트에 대한 조치, 기타 관련 된 사항에 대해 설명합니다…
-        title: 기록
+        placeholder: 어떤 대응을 했는지 설명 또는 그 밖의 관련된 갱신 사항들
+        title: 참고사항
       notes_description_html: 확인하고 다른 중재자나 미래의 자신을 위해 기록을 작성합니다
+      processed_msg: '신고 #%{id}가 정상적으로 처리되었습니다'
       quick_actions_description_html: '빠른 조치를 취하거나 아래로 스크롤해서 신고된 콘텐츠를 확인하세요:'
       remote_user_placeholder: "%{instance}의 리모트 사용자"
-      reopen: 리포트 다시 열기
+      reopen: 신고 재검토
       report: '신고 #%{id}'
       reported_account: 신고 대상 계정
       reported_by: 신고자
       resolved: 해결됨
-      resolved_msg: 리포트가 성공적으로 해결되었습니다!
+      resolved_msg: 신고를 잘 해결했습니다!
       skip_to_actions: 작업으로 건너뛰기
       status: 상태
       statuses: 신고된 콘텐츠
       statuses_description_html: 문제가 되는 콘텐츠는 신고된 계정에게 인용되어 전달됩니다
+      summary:
+        action_preambles:
+          delete_html: "<strong>@%{acct}</strong>의 게시물 중 일부를 <strong>지우려고</strong> 합니다. 이것은 아래의 행동이 수반됩니다:"
+          mark_as_sensitive_html: "<strong>@%{acct}</strong>의 게시물 중 일부를 <strong>민감함으로 표시</strong> 합니다. 이것은 아래의 행동이 수반됩니다:"
+          silence_html: "<strong>@%{acct}</strong>의 계정을 <strong>제한</strong>하려고 합니다. 이것은 아래의 행동이 수반됩니다:"
+          suspend_html: "<strong>@%{acct}</strong>의 계정을 <strong>정지</strong>하려고 합니다. 이것은 아래의 행동이 수반됩니다:"
+        actions:
+          delete_html: 문제가 되는 게시물을 지웁니다
+          mark_as_sensitive_html: 문제가 되는 게시물의 미디어를 민감함으로 표시합니다
+          silence_html: 이미 팔로우하고 있는 사람에게만 프로필을 보이게 하고 나머지 사람들에게는 수동으로 확인을 해야만 볼 수 있게 하여 <strong>@%{acct}</strong>의 도달 범위를 엄격하게 제한합니다.
+          suspend_html: "<strong>@%{acct}</strong>의 계정을 정지합니다. 프로필과 콘텐츠가 사용 불가능하게 됩니다."
+        close_report: '신고 #%{id}를 해결됨으로 표시합니다'
+        close_reports_html: "<strong>@%{acct}</strong>에 대한 <strong>모든</strong> 신고를 해결됨으로 처리합니다"
+        delete_data_html: "<strong>@%{acct}</strong>의 프로필과 콘텐츠를 30일의 유예기간 이후에 삭제합니다"
+        preview_preamble_html: "<strong>@%{acct}</strong>는 다음 내용의 경고를 받게 됩니다:"
+        record_strike_html: 향후 규칙위반에 대한 참고사항이 될 수 있도록 <strong>@%{acct}</strong>에 대한 처벌기록을 남깁니다
+        send_email_html: "<strong>@%{acct}</strong>에게 경고 메일을 보냅니다"
+        warning_placeholder: 중재 결정에 대한 추가적인 이유.
       target_origin: 신고된 계정의 소속
       title: 신고
       unassign: 할당 해제
+      unknown_action_msg: '알 수 없는 액션: %{action}'
       unresolved: 미해결
       updated_at: 업데이트 시각
       view_profile: 프로필 보기
@@ -701,6 +730,8 @@ ko:
         preamble: 흥미로운 콘텐츠를 노출하는 것은 마스토돈을 알지 못할 수도 있는 신규 사용자를 유입시키는 데 중요합니다. 이 서버에서 작동하는 다양한 발견하기 기능을 제어합니다.
         profile_directory: 프로필 책자
         public_timelines: 공개 타임라인
+        publish_discovered_servers: 발견 된 서버들 발행
+        publish_statistics: 통계 발행
         title: 발견하기
         trends: 유행
       domain_blocks:
@@ -741,7 +772,7 @@ ko:
       reblogs: 리블로그
       status_changed: 게시물 변경됨
       title: 계정 게시물
-      trending: 유행중
+      trending: 유행 중
       visibility: 공개 설정
       with_media: 미디어 있음
     strikes:
@@ -755,6 +786,7 @@ ko:
         suspend: "%{name} 님이 %{target}의 계정을 정지시켰습니다"
       appeal_approved: 이의제기됨
       appeal_pending: 이의제기 대기중
+      appeal_rejected: 이의 제기 거절됨
     system_checks:
       database_schema_check:
         message_html: 대기 중인 데이터베이스 마이그레이션이 있습니다. 애플리케이션이 예상대로 동작할 수 있도록 마이그레이션을 실행해 주세요
@@ -768,6 +800,12 @@ ko:
         message_html: 아직 서버규칙을 정하지 않았습니다.
       sidekiq_process_check:
         message_html: "%{value} 큐에 대한 사이드킥 프로세스가 발견되지 않았습니다. 사이드킥 설정을 검토해주세요"
+      upload_check_privacy_error:
+        action: 더 많은 정보를 보려면 여기를 확인하세요.
+        message_html: "<strong>웹 서버가 잘못 구성되었습니다. 사용자의 프라이버시에 위협이 됩니다.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: 더 많은 정보를 보려면 여기를 확인하세요
+        message_html: "<strong>오브젝트 스토리지가 잘못 구성되었습니다. 사용자의 프라이버시에 위협이 됩니다.</strong>"
     tags:
       review: 심사 상태
       updated_msg: 해시태그 설정이 성공적으로 갱신되었습니다
@@ -789,6 +827,7 @@ ko:
           other: 지난 주 동안 %{count} 명의 사람들이 공유했습니다
         title: 유행하는 링크
         usage_comparison: 오늘은 %{today}회 공유되었고, 어제는 %{yesterday}회 공유되었습니다
+      not_allowed_to_trend: 트렌드에 오를 수 없음
       only_allowed: 허용된 것만
       pending_review: 심사 대기
       preview_card_providers:
@@ -827,11 +866,11 @@ ko:
         trendable: 유행 목록에 나타날 수 있습니다
         trending_rank: "#%{rank}위로 유행 중"
         usable: 사용 가능
-        usage_comparison: 오늘은 %{today}회 사용되었고, 어제는 %{yesterday}회 사용되었습니다
+        usage_comparison: 오늘은 %{today}회 쓰였고, 어제는 %{yesterday}회 쓰임
         used_by_over_week:
           other: 지난 주 동안 %{count} 명의 사람들이 사용했습니다
       title: 유행
-      trending: 유행중
+      trending: 유행 중
     warning_presets:
       add_new: 새로 추가
       delete: 삭제
@@ -917,22 +956,25 @@ ko:
   applications:
     created: 애플리케이션이 성공적으로 생성되었습니다
     destroyed: 애플리케이션이 성공적으로 삭제되었습니다
+    logout: 로그아웃
     regenerate_token: 토큰 재생성
     token_regenerated: 액세스 토큰이 성공적으로 재생성되었습니다
     warning: 이 데이터를 조심히 다뤄 주세요. 다른 사람들과 절대로 공유하지 마세요!
     your_token: 액세스 토큰
   auth:
     apply_for_account: 가입 요청하기
-    change_password: 패스워드
+    change_password: 암호
+    confirmations:
+      wrong_email_hint: 만약 이메일 주소가 올바르지 않다면, 계정 설정에서 수정할 수 있습니다.
     delete_account: 계정 삭제
     delete_account_html: 계정을 삭제하고 싶은 경우, <a href="%{path}">여기서</a> 삭제할 수 있습니다. 삭제 전 확인 화면이 표시됩니다.
     description:
-      prefix_invited_by_user: "@%{name} 님이 당신을 이 마스토돈 서버로 초대했습니다!"
+      prefix_invited_by_user: "@%{name}님이 마스토돈 서버에 초대했습니다!"
       prefix_sign_up: 마스토돈에 가입하세요!
       suffix: 계정 하나로 사람들을 팔로우 하고, 게시물을 작성하며 마스토돈을 포함한 다른 어떤 서버의 사용자와도 메시지를 주고 받을 수 있습니다!
     didnt_get_confirmation: 확인 메일을 받지 못하셨습니까?
     dont_have_your_security_key: 보안 키가 없습니까?
-    forgot_password: 비밀번호를 잊어버리셨습니까?
+    forgot_password: 암호를 잊었나요?
     invalid_reset_password_token: 암호 리셋 토큰이 올바르지 못하거나 기간이 만료되었습니다. 다시 요청해주세요.
     link_to_otp: 휴대폰의 2차 코드 혹은 복구 키를 입력해 주세요
     link_to_webauth: 보안 키 장치 사용
@@ -951,10 +993,12 @@ ko:
     resend_confirmation: 확인 메일을 다시 보내기
     reset_password: 암호 재설정
     rules:
+      accept: 수락
+      back: 뒤로가기
       preamble: 다음은 %{domain}의 중재자들에 의해 설정되고 적용되는 규칙들입니다.
       title: 몇 개의 규칙이 있습니다.
     security: 보안
-    set_new_password: 새 암호
+    set_new_password: 새 암호 설정
     setup:
       email_below_hint_html: 아래의 이메일 계정이 올바르지 않을 경우, 여기서 변경하고 새 확인 메일을 받을 수 있습니다.
       email_settings_hint_html: 확인 메일이 %{email}로 보내졌습니다. 이메일 주소가 올바르지 않은 경우, 계정 설정에서 변경하세요.
@@ -982,7 +1026,7 @@ ko:
     follow_request: '당신은 다음 계정에 팔로우 신청을 했습니다:'
     following: '성공! 당신은 다음 계정을 팔로우 하고 있습니다:'
     post_follow:
-      close: 혹은, 당신은 이 윈도우를 닫을 수 있습니다.
+      close: 혹은, 그저 이 창을 닫을 수도 있습니다.
       return: 사용자 프로필 보기
       web: 웹으로 가기
     title: "%{acct} 를 팔로우"
@@ -990,7 +1034,7 @@ ko:
     confirm: 계속
     hint_html: "<strong>팁:</strong> 한 시간 동안 다시 암호를 묻지 않을 것입니다."
     invalid_password: 잘못된 암호
-    prompt: 계속하려면 암호 확인
+    prompt: 계속하려면 암호를 확인하세요.
   crypto:
     errors:
       invalid_key: 유효하지 않은 Ed25519 또는 Curve25519 키
@@ -1027,9 +1071,9 @@ ko:
       email_contact_html: 아직 도착하지 않았다면, <a href="mailto:%{email}">%{email}</a>에 메일을 보내 도움을 요청할 수 있습니다
       email_reconfirmation_html: 아직 확인 메일이 도착하지 않은 경우, <a href="%{path}">다시 요청할 수 있습니다</a>
       irreversible: 계정을 복구하거나 다시 사용할 수 없게 됩니다
-      more_details_html: 더 자세한 정보는, <a href="%{terms_path}">개인정보 정책</a>을 참고하세요.
-      username_available: 당신의 계정명은 다시 사용할 수 있게 됩니다
-      username_unavailable: 당신의 계정명은 앞으로 사용할 수 없습니다
+      more_details_html: 더 자세한 정보는, <a href="%{terms_path}">개인정보처리방침</a>을 참고하세요.
+      username_available: 이 사용자명을 다시 쓸 수 있게 됩니다.
+      username_unavailable: 이 사용자명은 앞으로도 쓸 수 없는 채로 남게 됩니다.
   disputes:
     strikes:
       action_taken: 내려진 징계
@@ -1067,7 +1111,7 @@ ko:
     '403': 이 페이지를 표시할 권한이 없습니다.
     '404': 찾으려는 페이지가 존재하지 않습니다.
     '406': 이 페이지는 요청한 자료형으로 제공되지 않습니다.
-    '410': 당신이 보려는 페이지는 더이상 여기에 존재하지 않습니다.
+    '410': 찾는 페이지가 더 이상 존재하지 않습니다.
     '422':
       content: 보안 인증에 실패했습니다. 쿠키를 차단하고 있진 않습니까?
       title: 보안 인증 실패
@@ -1085,7 +1129,7 @@ ko:
       date: 날짜
       download: 아카이브 다운로드
       hint_html: 당신의 <strong>게시물과 업로드 된 미디어</strong>의 아카이브를 요청할 수 있습니다. 내보내지는 데이터는 ActivityPub 포맷입니다. 호환 되는 모든 소프트웨어에서 읽을 수 있습니다. 7일마다 새로운 아카이브를 요청할 수 있습니다.
-      in_progress: 당신의 아카이브를 컴파일 중입니다…
+      in_progress: 아카이브를 컴파일 중...
       request: 아카이브 요청하기
       size: 크기
     blocks: 차단
@@ -1098,12 +1142,12 @@ ko:
   featured_tags:
     add_new: 추가
     errors:
-      limit: 이미 추천 해시태그의 개수가 최대입니다
-    hint_html: "<strong>추천 해시태그가 무엇이죠?</strong> 당신의 공개 프로필 페이지에 눈에 띄게 표현 되며 사람들이 그 해시태그를 포함한 당신의 글을 찾아 볼 수 있도록 합니다. 창작활동이나 긴 기간을 가지는 프로젝트를 쭉 따라가기에 좋은 도구입니다."
+      limit: 추천 해시태그 최대 개수를 초과합니다
+    hint_html: "<strong>추천 해시태그가 무엇일까요?</strong> 해시태그는 공개 프로필에 눈에 잘 띄게 표시되며, 사람들은 해당 해시태그로 내 공개 글을 검색할 수 있습니다. 창작물이나 장기 프로젝트를 추적하는 데 유용한 도구입니다."
   filters:
     contexts:
       account: 프로필
-      home: 홈 타임라인
+      home: 홈 & 리스트
       notifications: 알림
       public: 퍼블릭 타임라인
       thread: 대화
@@ -1139,8 +1183,6 @@ ko:
       index:
         hint: 이 필터는 다른 기준에 관계 없이 선택된 개별적인 게시물들에 적용됩니다. 웹 인터페이스에서 더 많은 게시물들을 이 필터에 추가할 수 있습니다.
         title: 필터링된 게시물
-  footer:
-    trending_now: 지금 유행중
   generic:
     all: 모두
     all_items_on_page_selected_html:
@@ -1159,8 +1201,6 @@ ko:
     today: 오늘
     validation_errors:
       other: 오류가 발생했습니다. 아래 %{count}개 오류를 확인해 주십시오
-  html_validator:
-    invalid_markup: '올바르지 않은 HTML 마크업을 포함하고 있습니다: %{error}'
   imports:
     errors:
       invalid_csv_file: '올바르지 않은 CSV 파일입니다. 오류: %{error}'
@@ -1202,7 +1242,7 @@ ko:
     title: 초대
   lists:
     errors:
-      limit: 리스트 최대치에 도달했습니다
+      limit: 리스트 최대 개수를 초과합니다
   login_activities:
     authentication_methods:
       otp: 2단계 인증 앱
@@ -1216,7 +1256,7 @@ ko:
     title: 인증 이력
   media_attachments:
     validations:
-      images_and_video: 이미 사진이 첨부 된 게시물엔 동영상을 첨부 할 수 없습니다
+      images_and_video: 이미 사진이 첨부된 게시물엔 동영상을 첨부할 수 없습니다.
       not_ready: 처리가 끝나지 않은 파일은 첨부할 수 없습니다. 잠시 후에 다시 시도해 주세요!
       too_many: 최대 4개까지 첨부할 수 있습니다
   migrations:
@@ -1255,7 +1295,7 @@ ko:
   move_handler:
     carry_blocks_over_text: 이 사용자는 당신이 차단한 %{acct}로부터 이주 했습니다.
     carry_mutes_over_text: 이 사용자는 당신이 뮤트한 %{acct}로부터 이주 했습니다.
-    copy_account_note_text: '이 사용자는 %{acct}에서 옮겨왔으며 이전의 기록은 다음과 같습니다:'
+    copy_account_note_text: '이 사용자는 %{acct}로부터 이동하였습니다. 당신의 이전 노트는 이렇습니다:'
   navigation:
     toggle_menu: 토글 메뉴
   notification_mailer:
@@ -1336,21 +1376,22 @@ ko:
     posting_defaults: 게시물 기본설정
     public_timelines: 공개 타임라인
   privacy_policy:
-    title: 개인정보 정책
+    title: 개인정보처리방침
   reactions:
     errors:
       limit_reached: 리액션 갯수 제한에 도달했습니다
       unrecognized_emoji: 인식 되지 않은 에모지입니다
   relationships:
     activity: 계정 활동
-    confirm_follow_selected_followers: 정말로 선택된 팔로워들을 팔로우 하시겠습니까?
+    confirm_follow_selected_followers: 정말로 선택된 팔로워들을 팔로우하시겠습니까?
     confirm_remove_selected_followers: 정말로 선택된 팔로워들을 삭제하시겠습니까?
     confirm_remove_selected_follows: 정말로 선택된 팔로우를 끊으시겠습니까?
     dormant: 휴면
+    follow_failure: 선택한 계정 중 몇몇은 팔로우 할 수 없었습니다.
     follow_selected_followers: 선택한 팔로워들을 팔로우
     followers: 팔로워
     following: 팔로잉
-    invited: 초대됨
+    invited: 초대함
     last_active: 마지막 활동
     most_recent: 가장 최근
     moved: 이동함
@@ -1386,6 +1427,7 @@ ko:
       electron: 일렉트론
       firefox: 파이어폭스
       generic: 알 수 없는 브라우저
+      huawei_browser: 화웨이 브라우저
       ie: 인터넷 익스플로러
       micro_messenger: 마이크로메신저
       nokia: Nokia S40 Ovi 브라우저
@@ -1395,6 +1437,7 @@ ko:
       qq: QQ 브라우저
       safari: 사파리
       uc_browser: UC 브라우저
+      unknown_browser: 알 수 없는 브라우저
       weibo: 웨이보
     current_session: 현재 세션
     description: "%{platform}의 %{browser}"
@@ -1407,14 +1450,15 @@ ko:
       chrome_os: 크롬OS
       firefox_os: 파이어폭스OS
       ios: iOS
+      kai_os: KaiOS
       linux: 리눅스
       mac: macOS
-      other: 알 수 없는 플랫폼
+      unknown_platform: 알 수 없는 플랫폼
       windows: 윈도우
       windows_mobile: 윈도우 모바일
       windows_phone: 윈도우 폰
-    revoke: 삭제
-    revoke_success: 세션이 성공적으로 삭제되었습니다
+    revoke: 취소
+    revoke_success: 세션을 성공적으로 취소하였습니다.
     title: 세션
     view_authentication_history: 내 계정에 대한 인증 이력 보기
   settings:
@@ -1422,7 +1466,7 @@ ko:
     account_settings: 계정 설정
     aliases: 계정 별명
     appearance: 외관
-    authorized_apps: 인증된 애플리케이션
+    authorized_apps: 승인된 애플리케이션
     back: 마스토돈으로 돌아가기
     delete: 계정 삭제
     development: 개발
@@ -1430,7 +1474,7 @@ ko:
     export: 데이터 내보내기
     featured_tags: 추천 해시태그
     import: 데이터 가져오기
-    import_and_export: 가져오기 / 내보내기
+    import_and_export: 가져오기 & 내보내기
     migrate: 계정 이동
     notifications: 알림
     preferences: 사용자 설정
@@ -1482,7 +1526,7 @@ ko:
       private_long: 팔로워에게만 공개됩니다
       public: 공개
       public_long: 누구나 볼 수 있으며, 공개 타임라인에 표시됩니다
-      unlisted: 공개 타임라인 비표시
+      unlisted: 미등재
       unlisted_long: 누구나 볼 수 있지만, 공개 타임라인에는 표시되지 않습니다
   statuses_cleanup:
     enabled: 오래된 게시물 자동 삭제
@@ -1522,7 +1566,7 @@ ko:
   stream_entries:
     pinned: 고정된 게시물
     reblogged: 님이 부스트 했습니다
-    sensitive_content: 민감한 콘텐츠
+    sensitive_content: 민감한 내용
   strikes:
     errors:
       too_late: 이의를 제기하기에 너무 늦었습니다
@@ -1616,12 +1660,13 @@ ko:
       title: 환영합니다 %{name} 님!
   users:
     follow_limit_reached: 당신은 %{limit}명의 사람을 넘어서 팔로우 할 수 없습니다
+    go_to_sso_account_settings: ID 공급자의 계정 설정으로 이동
     invalid_otp_token: 2단계 인증 코드가 올바르지 않습니다
     otp_lost_help_html: 만약 양쪽 모두를 잃어버렸다면 %{email}을 통해 복구할 수 있습니다
     seamless_external_login: 외부 서비스를 이용해 로그인했으므로 이메일과 암호는 설정할 수 없습니다.
     signed_in_as: '다음과 같이 로그인 중:'
   verification:
-    explanation_html: '당신은 <strong>프로필 메타데이터의 링크 소유자임을 검증할 수 있습니다</strong>. 이것을 하기 위해서는, 링크 된 웹사이트에서 당신의 마스토돈 프로필을 역으로 링크해야 합니다. 역링크는 <strong>반드시</strong> <code>rel="me"</code> 속성을 가지고 있어야 합니다. 링크의 텍스트는 상관이 없습니다. 여기 예시가 있습니다:'
+    explanation_html: '<strong>내 프로필 메타데이터에 담긴 링크의 소유 여부를 검증</strong>할 수 있습니다. 이를 위해서 반드시 링크한 웹사이트에 Mastodon 프로필에 대한 역링크가 포함되어야 합니다. 링크를 추가한 후 이곳으로 돌아와서 프로필을 다시 저장해야 인증을 적용할 수 있습니다. 돌아오는 링크에는 <strong>반드시</strong> <code>rel="me"</code> 속성이 있어야 합니다. 링크의 텍스트 콘텐트는 중요하지 않습니다. 다음 예제를 참고하세요:'
     verification: 검증
   webauthn_credentials:
     add: 보안 키 추가
diff --git a/config/locales/ku.yml b/config/locales/ku.yml
index ccee57b42..a1fce2c36 100644
--- a/config/locales/ku.yml
+++ b/config/locales/ku.yml
@@ -577,8 +577,6 @@ ku:
         mark_as_sensitive_description_html: Şandiyên hatine ragihandin wê werin nîşandan wekî hestyar û were tomarkirin da ku ji te re bibe alîkar ku tu ji hêla heman ajimêrê ve binpêkirinên pêşerojê bêtir bikî.
         other_description_html: Bêtir vebijêrkên ji bo kontrolkirina tevgera ajimêrê bibîne û pêwendiyê li ser ajimêra ragihandî kesane bike.
         resolve_description_html: Li hemberî ajimêra hatiye ragihandin wê tiştek pêk neyê, binpêkirin nayên tomarkirin û ragihandin wê were girtin.
-        silence_description_html: Wê profîl tenê ji kesên ku berê te dişopînin an ji bi destan lê serî lê didin re xuya bibe, gihandina wê bi tundî sînordar dike. Her gav dikare were vegerandin.
-        suspend_description_html: Heya ku di dawiyê de neyê jêbirin, wê profîl û hemû naverokên wê ne gihiştbar bin. Têkiliya bi ajimêrê re wê ne guncav be. Di nav 30 rojan de veger ji jêbirinê pêkan e.
       actions_description_html: Ji bo çareserkirina vê ragihandinê biryar bide ka tê kîjan gav bavêjî. Ku tu li dijî ajimêrê ragihandî çalakiyeke cezakirinê bikî, ji bilî dema ku kategoriya <strong>Spam</strong> were hilbijartin, wê agahdariyek e-nameyê ji wan re were şandin.
       add_to_report: Bo ragihandinê bêtir tevlî bike
       are_you_sure: Gelo tu bawerî?
@@ -1115,8 +1113,6 @@ ku:
     storage: Bîrdanaka medyayê
   featured_tags:
     add_new: Yeka nû tevlî bike
-    errors:
-      limit: Te jixwe berê pirtirîn hashtag destnîşan kiriye
     hint_html: "<strong> Hashtagên destnîşankirî çi ne? </strong> Ew bi eşkere li ser profîla te ya gelemperî têne xuyakirin û dihêlin ku mirov bi taybetî di binê wan hashtagan de li şandiyên te yên gelemperî bigere. Ew ji bo şopandina karên afirîner an projeyên demdirêj amûrek girîng in."
   filters:
     contexts:
@@ -1160,8 +1156,6 @@ ku:
       index:
         hint: Ev parzûn bêyî pîvanên din ji bo hilbijartina şandiyên kesane tê sepandin. Tu dikarî ji navrûya tevnê bêtir şandiyan tevlî vê parzûnê bikî.
         title: Şandiyên parzûnkirî
-  footer:
-    trending_now: Niha rojevê de
   generic:
     all: Hemû
     all_items_on_page_selected_html:
@@ -1184,8 +1178,6 @@ ku:
     validation_errors:
       one: Tiştek hîn ne rast e! Ji kerema xwe çewtiya li jêr di ber çavan re derbas bike
       other: Tiştek hîn ne rast e! Ji kerema xwe %{count} çewtî li jêr di ber çavan re derbas bike
-  html_validator:
-    invalid_markup: 'di nav de nîşana HTML a nederbasdar heye: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Pelê CSV nederbasdar e. Çewtî: %{error}'
@@ -1226,9 +1218,6 @@ ku:
       expires_at: Diqede
       uses: Bikaranîn
     title: Mirovan vexwîne
-  lists:
-    errors:
-      limit: Tu gihîştî hejmara rêzika a herî zêde
   login_activities:
     authentication_methods:
       otp: sepandina rastandina du-gavî
@@ -1432,7 +1421,6 @@ ku:
       ios: iOS
       linux: Linux
       mac: macOS
-      other: platforma nenas
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1545,7 +1533,6 @@ ku:
       '7889238': 3 meh
     min_age_label: Şêmûga demê
     min_favs: Peyamên ku ji wê zêdetir hatine ecibandin veşêre
-    min_favs_hint: Şandî ku ji evqas hezkirin wergirtibe nayê jêbirin. Vala bihêle da ku şandiyan jê bibî tevlî ku çiqas hezkirin wergirtibe
     min_reblogs: Şandiyên ku bêtir hatine bilindkirin veşêre
     min_reblogs_hint: Şandî ku ji ji vê hejmarê bêtir bilindkirin wergirtibe nayê jêbirin. Vala bihêle da ku şandiyan jê bibî tevlî ku çiqas hezkirin wergirtibe
   stream_entries:
@@ -1650,7 +1637,6 @@ ku:
     seamless_external_login: Te bi rajekarke biyanî re têketina xwe kir, ji ber vê yekê borînpeyv û e-name nayê bikaranîn.
     signed_in_as: 'Têketin wekî:'
   verification:
-    explanation_html: 'Tu dikarî <strong>xwe wekî xwediyê girêdanên li daneyê meta profîla xwe piştrast bikî</strong>. Ji bo vê, hewceye girêda malperê di nav profîla te ya mastodonê de girêdanekî paş hebe. Girêdana paş<strong>hewceye</strong> taybetîyek <code>rel="me"</code>hebe. Naveroka nivîsa girêdanê ne girîng e. Ev jî mînakek e:'
     verification: Piştrastkirin
   webauthn_credentials:
     add: Kilîteke ewlehiyê nû tevlî bike
diff --git a/config/locales/kw.yml b/config/locales/kw.yml
index 7683e3042..4085046cf 100644
--- a/config/locales/kw.yml
+++ b/config/locales/kw.yml
@@ -11,9 +11,7 @@ kw:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
   settings:
     account: Akont
diff --git a/config/locales/la.yml b/config/locales/la.yml
index 0a4bec9ee..244938ef6 100644
--- a/config/locales/la.yml
+++ b/config/locales/la.yml
@@ -6,7 +6,5 @@ la:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/lt.yml b/config/locales/lt.yml
index 58d0ae4f4..06eeff6ab 100644
--- a/config/locales/lt.yml
+++ b/config/locales/lt.yml
@@ -331,8 +331,6 @@ lt:
     storage: Medijos sandėlis
   featured_tags:
     add_new: Pridėti naują
-    errors:
-      limit: Jūs jau naudojate maksimalų galimą saitažodžių(#) kiekį
   filters:
     contexts:
       home: Namų laiko juosta
@@ -385,9 +383,6 @@ lt:
       expires_at: Pasibaigia
       uses: Naudojimai
     title: Pakviesti žmones
-  lists:
-    errors:
-      limit: Jūs pasieketė maksimalų sąrašų skaičių
   media_attachments:
     validations:
       images_and_video: Negalima pridėti video prie statuso, kuris jau turi nuotrauką
@@ -424,8 +419,6 @@ lt:
     next: Kitas
     older: Senesnis
     prev: Ankstesnis
-  preferences:
-    other: Kita
   remote_follow:
     missing_resource: Jūsų paskyros nukreipimo URL nerasta
   scheduled_statuses:
@@ -440,8 +433,6 @@ lt:
     current_session: Dabartinė sesija
     description: "%{browser} ant %{platform}"
     explanation: Čia rodomos web naršyklės prijungtos prie Jūsų Mastodon paskyros.
-    platforms:
-      other: nežinoma platforma
     revoke: Atšaukti
     revoke_success: Sesija sėkmingai atšaukta
     title: Sesijos
@@ -526,5 +517,4 @@ lt:
     seamless_external_login: Jūs esate prisijungę per išorini įrenginį, todėl slaptąžodis ir el pašto nustatymai neprieinami.
     signed_in_as: 'Prisijungta kaip:'
   verification:
-    explanation_html: 'Jūs galite <strong>patvirtinti savę kaip savininką nuorodų savo profilio meta duomenyse</strong>. Kad tai padarytumėte, susieta svetainė privalo turėti nuorodą atgal į Jūsų Mastodon profilį. Nuoroda atgal <strong> privalo </strong> turėti <code>rel="me"</code> savybę. Teksto turinys nuorodoje nesvarbus. Štai pavyzdys:'
     verification: Patvirtinimas
diff --git a/config/locales/lv.yml b/config/locales/lv.yml
index e75590664..e7b8372ca 100644
--- a/config/locales/lv.yml
+++ b/config/locales/lv.yml
@@ -93,6 +93,7 @@ lv:
       moderation:
         active: Aktīvie
         all: Visi
+        disabled: Atspējots
         pending: Gaida
         silenced: Ierobežotie
         suspended: Apturētie
@@ -136,6 +137,7 @@ lv:
       search: Meklēt
       search_same_email_domain: Citi lietotāji ar tādu pašu e-pasta domēnu
       search_same_ip: Citi lietotāji ar tādu pašu IP
+      security: Drošība
       security_measures:
         only_password: Tikai parole
         password_and_2fa: Parole un 2FA
@@ -435,6 +437,7 @@ lv:
         resolve: Atrisināt domēnu
         title: Bloķēt jaunu e-pasta domēnu
       no_email_domain_block_selected: Neviens e-pasta domēna bloks netika mainīts, jo neviens netika atlasīts
+      not_permitted: Nav atļauta
       resolved_dns_records_hint_html: Domēna nosaukums tiek izmantots tālāk norādītajos MX domēnos, kas galu galā ir atbildīgi par e-pasta pieņemšanu. Bloķējot MX domēnu, tiks bloķēta reģistrēšanās no jebkuras e-pasta adreses, kas izmanto vienu un to pašu MX domēnu, pat ja redzamais domēna nosaukums atšķiras. <strong>Esi uzmanīgs, lai nebloķētu lielākos e-pasta pakalpojumu sniedzējus.</strong>
       resolved_through_html: Atrisināts, izmantojot %{domain}
       title: Bloķētie e-pasta domēni
@@ -449,6 +452,7 @@ lv:
         private_comment_description_html: 'Lai palīdzētu tev izsekot, no kurienes nāk importētie bloki, tiks izveidoti importētie bloki ar šādu privātu komentāru: <q>%{comment}</q>'
         private_comment_template: Importēts no %{source} %{date}
         title: Importēt bloķētos domēnus
+      invalid_domain_block: 'Viens vai vairāki domēna bloķi tika izlaisti šādas kļūdas(-u) dēļ: %{error}'
       new:
         title: Importēt bloķētos domēnus
       no_file: Nav atlasīts neviens fails
@@ -482,6 +486,7 @@ lv:
       content_policies:
         comment: Iekšējā piezīme
         description_html: Tu vari definēt satura politikas, kas tiks piemērotas visiem kontiem no šī domēna un jebkura tā apakšdomēna.
+        limited_federation_mode_description_html: Tu vari izvēlēties, vai atļaut federāciju ar šo domēnu.
         policies:
           reject_media: Noraidīt multividi
           reject_reports: Noraidīt ziņojumus
@@ -587,19 +592,23 @@ lv:
         mark_as_sensitive_description_html: Multividesu faili ziņojumos, par kuriem ziņots, tiks atzīmēti kā sensitīvi, un tiks reģistrēts brīdinājums, lai palīdzētu tev izvērst turpmākus pārkāpumus saistībā ar to pašu kontu.
         other_description_html: Skatīt vairāk iespēju kontrolēt konta uzvedību un pielāgot saziņu ar paziņoto kontu.
         resolve_description_html: Pret norādīto kontu netiks veiktas nekādas darbības, netiks reģistrēts brīdinājums, un ziņojums tiks slēgts.
-        silence_description_html: Profils būs redzams tikai tiem, kas jau tam seko vai manuāli apskata, tādējādi ievērojami ierobežojot tā sasniedzamību. Šo izvēli vienmēr var mainīt.
-        suspend_description_html: Profils un viss tā saturs kļūs nepieejami, līdz tas beidzot tiek izdzēsts. Mijiedarbība ar kontu būs neiespējama. Atgriežams 30 dienu laikā.
+        silence_description_html: Konts būs redzams tikai tiem, kas tam jau seko vai meklē to manuāli, ievērojami ierobežojot tā sasniedzamību. To vienmēr var atgriezt. Tiek aizvērti visi šī konta pārskati.
+        suspend_description_html: Konts un viss tā saturs nebūs pieejams un galu galā tiks izdzēsts, un mijiedarbība ar to nebūs iespējama. Atgriežams 30 dienu laikā. Tiek aizvērti visi šī konta pārskati.
       actions_description_html: Izlem, kādas darbības jāveic, lai atrisinātu šo ziņojumu. Ja veiksi sodīšanas darbību pret kontu, par kuru ziņots, tam tiks nosūtīts e-pasta paziņojums, izņemot gadījumus, kad ir atlasīta kategorija <strong>Spam</strong>.
+      actions_description_remote_html: Izlem, kādas darbības jāveic, lai atrisinātu šo ziņojumu. Tas ietekmēs tikai to, kā <strong>tavs</strong> serveris sazinās ar šo attālo kontu un apstrādā tā saturu.
       add_to_report: Pievienot varāk paziņošanai
       are_you_sure: Vai esi pārliecināts?
       assign_to_self: Piešķirt man
       assigned: Piešķirtais moderators
       by_target_domain: Ziņotā konta domēns
+      cancel: Atcelt
       category: Kategorija
       category_description_html: Iemesls kāpēc šis konts un / vai saturs tika ziņots, tiks minēts saziņā ar paziņoto kontu
       comment:
         none: Neviens
       comment_description_html: 'Lai sniegtu vairāk informācijas, %{name} rakstīja:'
+      confirm: Apstiprināt
+      confirm_action: Apstipriniet regulēšanas darbību pret @%{acct}
       created_at: Ziņoti
       delete_and_resolve: Izdzēst rakstus
       forwarded: Pārsūtīti
@@ -616,6 +625,7 @@ lv:
         placeholder: Apraksti veiktās darbības vai citus saistītus atjauninājumus...
         title: Piezīmes
       notes_description_html: Skati un atstāj piezīmes citiem moderatoriem un sev nākotnei
+      processed_msg: 'Pārskats #%{id} veiksmīgi apstrādāts'
       quick_actions_description_html: 'Veic ātro darbību vai ritini uz leju, lai skatītu saturu, par kuru ziņots:'
       remote_user_placeholder: attālais lietotājs no %{instance}
       reopen: Atkārtoti atvērt ziņojumu
@@ -628,9 +638,28 @@ lv:
       status: Statuss
       statuses: Ziņotais saturs
       statuses_description_html: Pārkāpuma saturs tiks minēts saziņā ar paziņoto kontu
+      summary:
+        action_preambles:
+          delete_html: 'Jūs gatavojaties <strong>noņemt</strong> dažas no lietotāja <strong>@%{acct}</strong> ziņām. Tas:'
+          mark_as_sensitive_html: 'Jūs gatavojaties <strong>atzīmēt</strong> dažas no lietotāja <strong>@%{acct}</strong> ziņām kā <strong>sensitīvas</strong>. Tas:'
+          silence_html: 'Jūs gatavojaties <strong>ierobežot</strong> <strong>@%{acct}</strong> kontu. Tas:'
+          suspend_html: 'Jūs gatavojaties <strong>apturēt</strong> <strong>@%{acct}</strong> kontu. Tas:'
+        actions:
+          delete_html: Noņemt aizskarošās ziņas
+          mark_as_sensitive_html: Atzīmēt aizskarošo ziņu multivides saturu kā sensitīvu
+          silence_html: Ievērojami ierobežojiet <strong>@%{acct}</strong> sasniedzamību, padarot viņa profilu un saturu redzamu tikai personām, kas jau seko viņiem vai manuāli meklē profilu
+          suspend_html: Apturēt <strong>@%{acct}</strong>, padarot viņu profilu un saturu nepieejamu un neiespējamu mijiedarbību ar
+        close_report: 'Atzīmēt ziņojumu #%{id} kā atrisinātu'
+        close_reports_html: Atzīmējiet <strong>visus</strong> pārskatus par <strong>@%{acct}</strong> kā atrisinātus
+        delete_data_html: Dzēsiet lietotāja <strong>@%{acct}</strong> profilu un saturu pēc 30 dienām, ja vien to darbība pa šo laiku netiks atcelta
+        preview_preamble_html: "<strong>@%{acct}</strong> saņems brīdinājumu ar šādu saturu:"
+        record_strike_html: Ierakstiet brīdinājumu pret <strong>@%{acct}</strong>, lai palīdzētu jums izvērst turpmākus pārkāpumus no šī konta
+        send_email_html: Nosūtiet <strong>@%{acct}</strong> brīdinājuma e-pastu
+        warning_placeholder: Izvēles papildu pamatojums regulēšanas darbībai.
       target_origin: Ziņotā konta izcelsme
       title: Ziņojumi
       unassign: Atsaukt
+      unknown_action_msg: 'Nezināms konts: %{action}'
       unresolved: Neatrisinātie
       updated_at: Atjaunināts
       view_profile: Skatīt profilu
@@ -681,9 +710,9 @@ lv:
         manage_rules: Pārvaldīt Noteikumus
         manage_rules_description: Ļauj lietotājiem mainīt servera noteikumus
         manage_settings: Pārvaldīt Iestatījumus
-        manage_settings_description: Ļauj lietotājiem mainīt vietnes uzstādījumus
+        manage_settings_description: Ļauj lietotājiem mainīt vietnes iestatījumus
         manage_taxonomies: Pārvaldīt Taksonomijas
-        manage_taxonomies_description: Ļauj lietotājiem pārskatīt aktuālo saturu un atjaunināt atsauces iestatījumus
+        manage_taxonomies_description: Ļauj lietotājiem pārskatīt aktuālāko saturu un atjaunināt atsauces iestatījumus
         manage_user_access: Pārvaldīt Lietotāju Piekļuves
         manage_user_access_description: Ļauj lietotājiem atspējot citu lietotāju divu faktoru autentifikāciju, mainīt savu e-pasta adresi un atiestatīt paroli
         manage_users: Pārvaldīt Lietotājus
@@ -727,6 +756,8 @@ lv:
         preamble: Interesanta satura parādīšana palīdz piesaistīt jaunus lietotājus, kuri, iespējams, nepazīst nevienu Mastodon. Kontrolē, kā tavā serverī darbojas dažādi atklāšanas līdzekļi.
         profile_directory: Profila direktorija
         public_timelines: Publiskās ziņu lentas
+        publish_discovered_servers: Publicēt atklātos serverus
+        publish_statistics: Publicēt statistiku
         title: Atklāt
         trends: Tendences
       domain_blocks:
@@ -767,7 +798,7 @@ lv:
       reblogs: Reblogi
       status_changed: Ziņa mainīta
       title: Konta ziņas
-      trending: Tendences
+      trending: Populārākie
       visibility: Redzamība
       with_media: Ar multividi
     strikes:
@@ -781,6 +812,7 @@ lv:
         suspend: "%{name} apturēja %{target} kontu"
       appeal_approved: Pārsūdzēts
       appeal_pending: Apelācija tiek izskatīta
+      appeal_rejected: Apelācija noraidīta
     system_checks:
       database_schema_check:
         message_html: Notiek datubāzu migrācijas. Lūdzu, palaid tās, lai nodrošinātu, ka lietojumprogramma darbojas, kā paredzēts
@@ -794,9 +826,15 @@ lv:
         message_html: Tu neesi definējis nevienu servera nosacījumu.
       sidekiq_process_check:
         message_html: Rindā(s) %{value} nedarbojas neviens Sidekiq process. Lūdzu, pārskati savu Sidekiq konfigurāciju
+      upload_check_privacy_error:
+        action: Pārbaudi šeit, lai iegūtu plašāku informāciju
+        message_html: "<strong>Tavs tīmekļa serveris ir nepareizi konfigurēts. Tavu lietotāju privātums ir apdraudēts.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Pārbaudi šeit, lai iegūtu plašāku informāciju
+        message_html: "<strong>Tava objektu krātuve ir nepareizi konfigurēta. Tavu lietotāju privātums ir apdraudēts.</strong>"
     tags:
       review: Pārskatīt statusu
-      updated_msg: Tēmtura uzstādījumi ir veiksmīgi atjaunināti
+      updated_msg: Tēmtura iestatījumi ir veiksmīgi atjaunināti
     title: Administrēšana
     trends:
       allow: Atļaut
@@ -817,6 +855,7 @@ lv:
           zero: Pēdējās nedēļas laikā kopīgoja %{count} personas
         title: Populārākās saites
         usage_comparison: Šodien kopīgots %{today} reizes, salīdzinot ar %{yesterday} vakar
+      not_allowed_to_trend: Popularizešana nav atļauta
       only_allowed: Tikai atļautās
       pending_review: Gaida pārskatīšanu
       preview_card_providers:
@@ -863,7 +902,7 @@ lv:
           other: Pēdējās nedēļas laikā izmantoja %{count} personas
           zero: Pēdējās nedēļas laikā izmantoja %{count} personas
       title: Tendences
-      trending: Tendences
+      trending: Populārākie
     warning_presets:
       add_new: Pievienot jaunu
       delete: Dzēst
@@ -951,6 +990,7 @@ lv:
   applications:
     created: Lietojumprogramma ir veiksmīgi izveidota
     destroyed: Lietojumprogramma ir veiksmīgi dzēsta
+    logout: Iziet
     regenerate_token: Atjaunot piekļuves marķieri
     token_regenerated: Piekļuves marķieris veiksmīgi atjaunots
     warning: Esi ļoti uzmanīgs ar šiem datiem. Nekad nedalies ne ar vienu ar tiem!
@@ -958,6 +998,8 @@ lv:
   auth:
     apply_for_account: Pieprasīt kontu
     change_password: Parole
+    confirmations:
+      wrong_email_hint: Ja šī e-pasta adrese nav pareiza, varat to mainīt konta iestatījumos.
     delete_account: Dzēst kontu
     delete_account_html: Ja vēlies dzēst savu kontu, tu vari <a href="%{path}">turpināt šeit</a>. Tev tiks lūgts apstiprinājums.
     description:
@@ -985,6 +1027,8 @@ lv:
     resend_confirmation: Atkārtoti nosūtīt apstiprinājuma norādījumus
     reset_password: Atiestatīt paroli
     rules:
+      accept: Pieņemt
+      back: Atpakaļ
       preamble: Tos iestata un ievieš %{domain} moderatori.
       title: Daži pamatnoteikumi.
     security: Drošība
@@ -1019,7 +1063,7 @@ lv:
       close: Vai vienkārši aizver šo logu.
       return: Parādīt lietotāja profilu
       web: Doties uz tīmekli
-    title: Seko %{acct}
+    title: Sekot %{acct}
   challenge:
     confirm: Turpināt
     hint_html: "<strong>Padoms:</strong> Nākamās stundas laikā mēs tev vairs neprasīsim paroli."
@@ -1179,8 +1223,6 @@ lv:
       index:
         hint: Šis filtrs attiecas uz atsevišķu ziņu atlasi neatkarīgi no citiem kritērijiem. Šim filtram tu vari pievienot vairāk ziņu, izmantojot tīmekļa saskarni.
         title: Filtrētās ziņas
-  footer:
-    trending_now: Šobrīd tendences
   generic:
     all: Visi
     all_items_on_page_selected_html:
@@ -1207,8 +1249,6 @@ lv:
       one: Kaut kas vēl nav īsti kārtībā! Lūdzu, pārskati zemāk norādīto kļūdu
       other: Kaut kas vēl nav īsti kārtībā! Lūdzu, pārskati %{count} kļūdas zemāk
       zero: "%{count} kļūdu"
-  html_validator:
-    invalid_markup: 'satur nederīgu HTML marķējumu: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Nederīgs CSV fails. Kļūda: %{error}'
@@ -1224,7 +1264,7 @@ lv:
       blocking: Bloķēšanas saraksts
       bookmarks: Grāmatzīmes
       domain_blocking: Bloķēto domēnu saraksts
-      following: Sekojamo lietotāju saraksts
+      following: Turpmākais saraksts
       muting: Apklusināto lietotāju saraksts
     upload: Augšupielādēt
   invites:
@@ -1252,7 +1292,7 @@ lv:
     title: Uzaicināt cilvēkus
   lists:
     errors:
-      limit: Esi sasniedzis maksimālo sarakstu skaitu
+      limit: Jūs esat sasniedzis maksimālo sarakstu skaitu
   login_activities:
     authentication_methods:
       otp: divfaktoru autentifikācijas lietotne
@@ -1317,7 +1357,7 @@ lv:
     favourite:
       body: 'Tavu ziņu izlasei pievienoja %{name}:'
       subject: "%{name} pievienoja tavu ziņu izlasei"
-      title: Jauns izcēlums
+      title: Jauna izlase
     follow:
       body: "%{name} tagad tev seko!"
       subject: "%{name} tagad tev seko"
@@ -1393,7 +1433,11 @@ lv:
       unrecognized_emoji: nav atpazīta emocijzīme
   relationships:
     activity: Konta aktivitāte
+    confirm_follow_selected_followers: Vai tiešām vēlies sekot atlasītajiem sekotājiem?
+    confirm_remove_selected_followers: Vai tiešām vēlies noņemt atlasītos sekotājus?
+    confirm_remove_selected_follows: Vai tiešām vēlies noņemt atlasītos sekojumus?
     dormant: Snaudošie
+    follow_failure: Nevarēja sekot dažiem atlasītajiem kontiem.
     follow_selected_followers: Sekot atlasītajiem sekotājiem
     followers: Sekotāji
     following: Seko
@@ -1433,6 +1477,7 @@ lv:
       electron: Electron
       firefox: Firefox
       generic: Nezināms pārlūks
+      huawei_browser: Huawei Browser
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1442,6 +1487,7 @@ lv:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Nezināms Pārlūks
       weibo: Weibo
     current_session: Pašreizējā sesija
     description: "%{browser} uz %{platform}"
@@ -1454,9 +1500,10 @@ lv:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: nezināma platforma
+      unknown_platform: Nezināma Platforma
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1575,7 +1622,7 @@ lv:
       '7889238': 3 mēneši
     min_age_label: Vecuma slieksnis
     min_favs: Saglabāt ziņas izlsasē vismaz
-    min_favs_hint: Nedzēš nevienu tavu ziņu, kas ir saņēmusi vismaz tik daudz izlases. Atstāj tukšu, lai dzēstu ziņas neatkarīgi no to izlases skaita
+    min_favs_hint: Nedzēš nevienu jūsu ziņu, kas ir saņēmusi vismaz tik daudz izcēlumu. Atstājiet tukšu, lai dzēstu ziņas neatkarīgi no to izcēlumu skaita
     min_reblogs: Saglabāt ziņas izceltas vismaz
     min_reblogs_hint: Neizdzēš nevienu no tavām ziņām, kas ir izceltas vismaz tik reižu. Atstāj tukšu, lai dzēstu ziņas neatkarīgi no to izcēlumu skaita
   stream_entries:
@@ -1675,12 +1722,13 @@ lv:
       title: Laipni lūgts uz borta, %{name}!
   users:
     follow_limit_reached: Tu nevari sekot vairāk par %{limit} cilvēkiem
+    go_to_sso_account_settings: Dodies uz sava identitātes nodrošinātāja konta iestatījumiem
     invalid_otp_token: Nederīgs divfaktora kods
     otp_lost_help_html: Ja esi zaudējis piekļuvi abiem, tu vari sazināties ar %{email}
     seamless_external_login: Tu esi pieteicies, izmantojot ārēju pakalpojumu, tāpēc paroles un e-pasta iestatījumi nav pieejami.
     signed_in_as: 'Pierakstījies kā:'
   verification:
-    explanation_html: 'Tu vari <strong>apstiprināt sevi kā sava profila metadatos esošo saišu īpašnieku</strong>. Lai to izdarītu, saistītajā vietnē ir jābūt saitei uz tavu Mastodon profilu. Atpakaļsaitē <strong>jābūt</strong> atribūtam <code>rel="me"</code>. Saites teksta saturam nav nozīmes. Šeit ir piemērs:'
+    explanation_html: 'Tu vari <strong>apstiprināt sevi kā sava profila metadatos esošo saišu īpašnieku</strong>. Lai to izdarītu, saistītajā vietnē ir jābūt saitei uz tavu Mastodon profilu. Pēc saites pievienošanas tev, iespējams, vajadzēs atgriezties šeit un atkārtoti saglabāt savu profilu, lai pārbaude stātos spēkā. Atpakaļsaitē <strong>jābūt</strong> atribūtam <code>rel="me"</code>. Saites teksta saturam nav nozīmes. Šeit ir piemērs:'
     verification: Pārbaude
   webauthn_credentials:
     add: Pievienot jaunu drošības atslēgu
diff --git a/config/locales/mk.yml b/config/locales/mk.yml
index b538272de..9504dc972 100644
--- a/config/locales/mk.yml
+++ b/config/locales/mk.yml
@@ -6,7 +6,5 @@ mk:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/ml.yml b/config/locales/ml.yml
index d5442c96c..ae3991145 100644
--- a/config/locales/ml.yml
+++ b/config/locales/ml.yml
@@ -87,9 +87,7 @@ ml:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
   filters:
     contexts:
diff --git a/config/locales/mr.yml b/config/locales/mr.yml
index 9d7609ea4..161e5e571 100644
--- a/config/locales/mr.yml
+++ b/config/locales/mr.yml
@@ -6,7 +6,5 @@ mr:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/ms.yml b/config/locales/ms.yml
index 848580f0c..60db149e0 100644
--- a/config/locales/ms.yml
+++ b/config/locales/ms.yml
@@ -827,7 +827,6 @@ ms:
     '422':
       title: Pengesahan keselamatan gagal
     '429': Terlalu banyak permintaan
-    '500': 
     '503': Halaman tidak dapat disampaikan kerana kegagalan pelayan sementara.
   exports:
     archive_takeout:
@@ -922,7 +921,6 @@ ms:
       android: Android
       ios: iOS
       linux: Linux
-      other: platform tidak dikenali
     title: Sesi
   settings:
     account: Akaun
@@ -972,7 +970,6 @@ ms:
       '63113904': 2 tahun
       '7889238': 3 bulan
     min_favs: Simpan hantaran digemarkan sekurang-kurangnya
-    min_favs_hint: Tidak memadamkan mana-mana hantaran anda yang telah menerima sekurang-kurangnya jumlah gemaran ini. Biarkan kosong untuk memadamkan hantaran tanpa mengira nombor gemaran
   stream_entries:
     pinned: Hantaran disemat
     sensitive_content: Kandungan sensitif
diff --git a/config/locales/my.yml b/config/locales/my.yml
index 399105ce0..816c5c401 100644
--- a/config/locales/my.yml
+++ b/config/locales/my.yml
@@ -1,12 +1,1654 @@
 ---
 my:
+  about:
+    about_mastodon_html: အနာဂတ်အတွက်လူမှုကွန်ရက် - ကြော်ငြာများမရှိခြင်း၊ အဖွဲ့သားများအား စောင့်ကြည့်မှုမရှိခြင်း၊ ကျင့်ဝတ်ပိုင်းဆိုင်ရာစိတ်ချရခြင်းနှင့် ဗဟိုချုပ်ကိုင်မှုမရှိခြင်း၊ သင့်အချက်အလက်များကို Mastodon နှင့်သာ မျှဝေအသုံးပြုလိုက်ပါ။
+    contact_missing: မသတ်မှတ်ထား
+    contact_unavailable: မရှိ
+    hosted_on: "%{domain} မှ လက်ခံဆောင်ရွက်ထားသော Mastodon"
+    title: အကြောင်း
+  accounts:
+    follow: စောင့်ကြည့်မယ်
+    followers:
+      other: စောင့်ကြည့်သူ
+    following: စောင့်ကြည့်နေသည်
+    instance_actor_flash: ဤအကောင့်သည် ဆာဗာကိုယ်တိုင်ကို ကိုယ်စားပြုပြီး မည်သည့်အသုံးပြုသူမျှမဟုတ်ဘဲ အတုအယောင်သရုပ်ဆောင်တစ်ခုဖြစ်သည်။ ၎င်းကို Federation ရည်ရွယ်ချက်များအတွက် အသုံးပြုပြီး ဆိုင်းငံ့မထားသင့်ပါ။
+    last_active: နောက်ဆုံးအသုံးပြုခဲ့သည့်အချိန်
+    link_verified_on: ဤလင့်ခ်၏ ပိုင်ဆိုင်မှုကို %{date} တွင် စစ်ဆေးခဲ့သည်
+    nothing_here: ဤနေရာတွင် မည်သည့်အရာမျှမရှိပါ။
+    pin_errors:
+      following: သင်ထောက်ခံလိုသောလူနောက်သို့ စောင့်ကြည့်ပြီးသားဖြစ်နေပါမည်
+    posts:
+      other: ပို့စ်တင်မယ်
+    posts_tab_heading: ပို့စ်များ
+  admin:
+    account_actions:
+      action: ဆောင်ရွက်ရန်
+      title: "%{acct} စိစစ်မှုလုပ်ဆောင်ရန်"
+    account_moderation_notes:
+      create: မှတ်စုမှထွက်ရန်
+      created_msg: စိစစ်ခြင်းမှတ်စုကို ဖန်တီးပြီးပါပြီ။
+      destroyed_msg: စိစစ်ခြင်းမှတ်စုကို ဖျက်ပစ်လိုက်ပါပြီ။
+    accounts:
+      add_email_domain_block: ဒိုမိန်းကိုပိတ်မည်
+      approve: အတည်ပြုပါ
+      approved_msg: "%{username} ၏ စာရင်းသွင်းခြင်းကို အတည်ပြုပြီးပါပြီ"
+      are_you_sure: သေချာပါသလား။
+      avatar: ကိုယ်စားပြုရုပ်ပုံ
+      by_domain: ဒိုမိန်း
+      change_email:
+        changed_msg: အီးမေးလ် ပြောင်းလဲပြီးပါပြီ။
+        current_email: လက်ရှိအီးမေးလ်
+        label: အီးမေးလ်ပြောင်းရန်
+        new_email: အီးမေးလ်အသစ်
+        submit: အီးမေးလ်ပြောင်းပါ။
+        title: "%{username} အတွက် အီးမေးလ်ပြောင်းပါ"
+      change_role:
+        changed_msg: အခန်းကဏ္ဍကို ပြောင်းလဲပြီးပါပြီ။
+        label: အခန်းကဏ္ဍ ပြောင်းလဲရန်
+        no_role: အခန်းကဏ္ဍမရှိ
+        title: "%{username} အတွက် အခန်းကဏ္ဍပြောင်းပါ"
+      confirm: အတည်ပြု
+      confirmed: အတည်ပြုပြီးပါပြီ
+      confirming: အတည်ပြုနေသည်
+      custom: စိတ်ကြိုက်
+      delete: အချက်အလက်များဖျက်ပါ
+      deleted: ဖျက်ပြီးပါပြီ
+      demote: အဆင့်လျော့မည်
+      destroyed_msg: "%{username} ၏ အချက်အလက်ကို မကြာမီ ဖျက်ပါမည်"
+      disable: ရပ်တန့်
+      disable_sign_in_token_auth: အီးမေးတိုကင် အထောက်အထားပြခြင်းကို ပိတ်ပါ
+      disable_two_factor_authentication: 2FA ကို ပိတ်ပါ
+      disabled: အကောင့်ပိတ်သိမ်းထားသည်
+      display_name: ဖော်ပြမည့်အမည်
+      domain: ဒိုမိန်း
+      edit: ပြင်ဆင်ရန်
+      email: အီးမေးလ်
+      email_status: အီးမေးလ်အခြေအနေ
+      enable: မပိတ်သိမ်းထားသော
+      enable_sign_in_token_auth: အီးမေးတိုကင် စစ်မှန်ကြောင်းအတည်ပြုချက်ကို ဖွင့်ပါ
+      enabled: ဖွင့်ထားသည်
+      enabled_msg: "%{username} ၏ အကောင့်ကို ပိတ်သိမ်းထားသည်"
+      followers: စောင့်ကြည့်သူများ
+      follows: စောင့်ကြည့်မယ်
+      header: မျက်နှာဖုံးပုံ
+      inbox_url: Inbox URL
+      invite_request_text: ပါဝင်ရခြင်း အကြောင်းအရင်း
+      invited_by: ဖိတ်ခေါ်ထားသည်
+      ip: IP
+      joined: စတင်ဝင်ရောက်သည့်နေ့
+      location:
+        all: အားလုံး
+        local: ပြည်တွင်း
+        remote: အဝေးမှ
+        title: တည်နေရာ
+      login_status: အကောင့်ဝင်ရောက်မှုအခြေအနေ
+      media_attachments: မီဒီယာ ပူးတွဲချက်များ
+      memorialize: အမှတ်တရအဖြစ် ပြောင်းပါ
+      memorialized: အမှတ်တရ
+      memorialized_msg: "%{username} ကို အမှတ်တရအကောင့်အဖြစ် ပြောင်းလဲခဲ့သည်"
+      moderation:
+        active: လက်ရှိအသုံးပြုလျက်ရှိခြင်း
+        all: အားလုံး
+        disabled: ပိတ်ထားသည်
+        pending: ဆိုင်းငံ့ထားခြင်း
+        silenced: ကန့်သတ်ထားသော
+        suspended: ရပ်ဆိုင်းထားခြင်း
+        title: စိစစ်ခြင်း
+      moderation_notes: စိစစ်ခြင်းဆိုင်ရာမှတ်စုများ
+      most_recent_activity: နောက်ဆုံးအသုံးပြုခဲ့သည့်အချိန်
+      most_recent_ip: အသုံးပြုလေ့ရှိသည့် IP လိပ်စာ
+      no_account_selected: မည်သည့်အကောင့်ကိုမျှ ရွေးချယ်ထားခြင်းမရှိသောကြောင့် ပြောင်းလဲခြင်းမရှိပါ
+      no_limits_imposed: ကန့်သတ်ချက် မရှိပါ။
+      no_role_assigned: တာဝန်ပေးအပ်ထားခြင်း မရှိပါ
+      not_subscribed: စာရင်းသွင်းထားခြင်းမရှိပါ
+      pending: ဆိုင်းငံ့ထားသော သုံးသပ်ချက်
+      perform_full_suspension: ရပ်ဆိုင်းရန်
+      previous_strikes: ယခင်လုပ်ဆောင်ချက်များ
+      previous_strikes_description_html:
+        other: ဤအကောင့်တွင် လုပ်ဆောင်ချက်<strong>%{count}</strong> ရှိသည်။
+      promote: အထောက်အကူ
+      protocol: လုပ်ထုံးလုပ်နည်း
+      public: အများမြင်
+      push_subscription_expires: PuSH စာရင်းသွင်းမှုမှာ သက်တမ်းကုန်ဆုံးသွားပါပြီ
+      redownload: ပရိုဖိုင်ကို ပြန်လည်စတင်ရန်
+      redownloaded_msg: မူလမှစ၍ %{username} ၏ ပရိုဖိုင်ကို ပြန်လည်စတင်ပြီးပါပြီ
+      reject: ဖယ်ရှားပါ
+      rejected_msg: "%{username} ၏ အကောင့်ဖွင့်အက်ပလီကေးရှင်းကို ဖယ်ရှားလိုက်ပါပြီ"
+      remote_suspension_irreversible: ဤအကောင့်၏အချက်အလက်ကို လုံးဝ ဖျက်လိုက်ပါပြီ။
+      remote_suspension_reversible_hint_html: အကောင့်ကို ၎င်းတို့၏ဆာဗာတွင် ဆိုင်းငံ့ထားပြီး အချက်အလက်ကို %{date} နေ့တွင် လုံးဝဖယ်ရှားလိုက်ပါမည်။ ထိုအချိန်အထိ အဝေးမှထိန်းချုပ်နိုင်သောဆာဗာက ဤအကောင့်ကို ပြန်လည်ရယူနိုင်သည်။ အကောင့်အချက်အလက်အားလုံးကို ချက်ချင်းဖယ်ရှားလိုပါက အောက်ပါအတိုင်း ပြုလုပ်နိုင်ပါသည်။
+      remove_avatar: ကိုယ်စားပြုရုပ်ပုံကို ဖယ်ရှားပါ
+      remove_header: မျက်နှာဖုံးပုံ ဖယ်ရှားရန်
+      removed_avatar_msg: "%{username} ၏ ကိုယ်စားပြုရုပ်ပုံအား ဖယ်ရှားပြီးပါပြီ"
+      removed_header_msg: "%{username} ၏ မျက်နှာဖုံးပုံအား ဖယ်ရှားပြီးပါပြီ"
+      resend_confirmation:
+        already_confirmed: ဤအသုံးပြုသူကို အတည်ပြုပြီးပါပြီ
+        send: အတည်ပြုထားသောအီးမေးလ် ပြန်ပို့ပေးရန်
+        success: အတည်ပြုထားသောအီးမေးလ် ပို့ပြီးပါပြီ။
+      reset: ပြန်သတ်မှတ်မည်
+      reset_password: 'လျှို့ဝှတ်နံပါတ်အားပြန်သတ်မှတ်မည်
+
+        '
+      resubscribe: ပြန်လည်စာရင်းသွင်းပါ
+      role: အခန်းကဏ္ဍ
+      search: ရှာရန်
+      search_same_email_domain: အီးမေးလ်ဒိုမိန်းတူညီသည့် အခြားအသုံးပြုသူများ
+      search_same_ip: IP တူတူ အသုံးပြုသော အခြားသူများ
+      security: လုံခြုံရေး
+      security_measures:
+        only_password: စကားဝှက်ဖြင့်သာ
+        password_and_2fa: စကားဝှက်နှင့် 2FA
+      sensitive: သတိပြုရန်
+      sensitized: သတိထားရသည်ဟု အမှတ်အသားပြုထားပါ
+      shared_inbox_url: inbox URL ကို မျှဝေခဲ့သည်
+      show:
+        created_reports: ဆောင်ရွက်ခဲ့ပြီးသောအစီရင်ခံစာများ
+        targeted_reports: အခြားသူများမှဆောင်ရွက်ခဲ့သော အစီရင်ခံစာများ
+      silence: ကန့်သတ်
+      silenced: ကန့်သတ်ထားသည်
+      statuses: ပို့စ်များ
+      strikes: ယခင်လုပ်ဆောင်ချက်များ
+      subscribe: စာရင်းသွင်းပါ
+      suspend: ရပ်ဆိုင်းပါ
+      suspended: ရပ်ဆိုင်းထားသည်
+      suspension_irreversible: ဤအကောင့်၏ဒေတာကို နောက်ပြန်မဆုတ်ဘဲ ဖျက်လိုက်ပါပြီ။ ၎င်းကိုအသုံးပြုနိုင်စေရန် အကောင့်အား ဆိုင်းငံ့ထားနိုင်သော်လည်း ၎င်းတွင် ယခင်ကရှိထားသည့် မည်သည့်ဒေတာကိုမှ ပြန်လည်ရယူမည်မဟုတ်ပါ။
+      suspension_reversible_hint_html: အကောင့်ကို ဆိုင်းငံ့ထားပြီး၊ ဒေတာကို %{date} တွင် အပြည့်အဝ ဖယ်ရှားပါမည်။ ထိုအချိန်အထိ မည်သည့်ဆိုးကျိုးများမရှိဘဲ အကောင့်ကို ပြန်လည်ရယူနိုင်သည်။ အကောင့်၏ဒေတာအားလုံးကို ချက်ချင်းဖယ်ရှားလိုပါက အောက်ပါအတိုင်း ပြုလုပ်နိုင်ပါသည်။
+      title: အကောင့်များ
+      unblock_email: အီးမေးလ်ကိုပြန်ဖွင့်မည်
+      unblocked_email_msg: " %{username} အီးမေးလ်ကိုပြန်ဖွင့်လိုက်ပါပြီ"
+      unconfirmed_email: အတည်မပြုရသေးသော အီးမေးလ်
+      undo_sensitized: သတိပြုရန်အား ပြန်ဖြုတ်ရန်
+      undo_silenced: ကန့်သတ်ချက်မလုပ်တော့ပါ
+      undo_suspension: ပိတ်ခြင်းကိုပြန်ဖွင့်မည်
+      unsilenced_msg: "%{username} ၏ အကောင့်၏ ကန့်သတ်ချက်ကို အောင်မြင်စွာ ပယ်ဖျက်ခဲ့သည်။"
+      unsubscribe: စာရင်းမှထွက်ရန်
+      unsuspended_msg: "%{username} ၏ အကောင့်ကို ရပ်ဆိုင်းလိုက်ပါပြီ"
+      username: အသုံးပြုသူအမည်
+      view_domain: ဒိုမိန်းအတွက် အကျဉ်းချုပ်ကို ကြည့်ပါ
+      warn: သတိပေးရန်
+      web: ဝဘ်
+      whitelisted: ဖက်ဒီကို ခွင့်ပြုခဲ့သည်
+    action_logs:
+      action_types:
+        approve_appeal: အယူခံကို အတည်ပြုပါ
+        approve_user: အသုံးပြုသူကို အတည်ပြုရန်
+        assigned_to_self_report: မှတ်တမ်းကိုတစ်ယောက်ယောက်အားလုပ်ခိုင်းမည်
+        change_email_user: အသုံးပြုသူအတွက် အီးမေးလ်ပြောင်းရန်
+        change_role_user: အသုံးပြုသူ၏ အခန်းကဏ္ဍကို ပြောင်းလဲရန်
+        confirm_user: အသုံးပြုသူကို လက်ခံရန်
+        create_account_warning: သတိပေးချက်ဖန်တီးပါ
+        create_announcement: ကြေညာချက်ဖန်တီးပါ
+        create_canonical_email_block: အီးမေးလ်ပိတ်ပင်ခြင်းအား ဖန်တီးရန်
+        create_custom_emoji: စိတ်ကြိုက်အီမိုဂျီ ဖန်တီးပါ
+        create_domain_allow: ဒိုမိန်းခွင့်ပြုခြင်းကို ဖန်တီးရန်
+        create_domain_block: ဒိုမိန်းပိတ်ပင်ခြင်းအား ဖန်တီးရန်
+        create_email_domain_block: အီးမေးလ်ဒိုမိန်းပိတ်ပင်ခြင်းအား ဖန်တီးရန်
+        create_ip_block: IP စည်းမျဉ်း ဖန်တီးရန်
+        create_unavailable_domain: အသုံးမပြုနိုင်သောဒိုမိန်းကို ဖန်တီးပါ
+        create_user_role: အခန်းကဏ္ဍဖန်တီးပါ
+        demote_user: အသုံးပြုသူကိုအဆင့်လျော့ချမည်
+        destroy_announcement: ကြေညာချက်ကို ဖျက်ပါ
+        destroy_canonical_email_block: အီးမေးလ်ပိတ်ပင်ခြင်းအား ဖျက်ရန်
+        destroy_custom_emoji: စိတ်ကြိုက်အီမိုဂျီကို ဖျက်ရန်
+        destroy_domain_allow: ဒိုမိန်းခွင့်ပြုခြင်းကို ဖျက်ရန်
+        destroy_domain_block: ဒိုမိန်းပိတ်ပင်ခြင်းအား ဖျက်ရန်
+        destroy_email_domain_block: အီးမေးလ်ဒိုမိန်းပိတ်ပင်ခြင်းအား ဖျက်ရန်
+        destroy_instance: ဒိုမိန်းကို ဖယ်ရှားပါ
+        destroy_ip_block: IP စည်းမျဉ်းကို ဖျက်ပါ
+        destroy_status: Post ကို ဖျက်ပါ
+        destroy_unavailable_domain: အသုံးမပြုနိုင်သောဒိုမိန်းကို ဖျက်ပါ
+        destroy_user_role: အခန်းကဏ္ဍကို ဖျက်ပါ
+        disable_2fa_user: 2FA ကို ပိတ်ပါ
+        disable_custom_emoji: စိတ်ကြိုက်အီမိုဂျီကို ပိတ်ပါ
+        disable_sign_in_token_auth_user: အသုံးပြုသူအတွက် အီးမေးလ်တိုကင် အထောက်အထားပြခြင်းကို ပိတ်ထားသည်
+        disable_user: အသုံးပြုသူကို ပိတ်ပါ
+        enable_custom_emoji: စိတ်ကြိုက်အီမိုဂျီကို ဖွင့်ပါ
+        enable_sign_in_token_auth_user: အသုံးပြုသူအတွက် အီးမေးလ်တိုကင် အထောက်အထားပြခြင်းကို ဖွင့်ထားသည်
+        enable_user: အသုံးပြုသူကို ဖွင့်ပါ
+        memorialize_account: အမှတ်တရအကောင့်
+        promote_user: အသုံးပြုသူ မြှင့်တင်ရန်
+        reject_appeal: အယူခံဝင်မှုကို ငြင်းပယ်ပါ
+        reject_user: အသုံးပြုသူ ဖယ်ရှားရန်
+        remove_avatar_user: ကိုယ်စားပြုရုပ်ပုံကို ဖယ်ရှားပါ
+        reopen_report: အစီရင်ခံစာပြန်ဖွင့်ရန်
+        resend_user: အတည်ပြုရန် မေးလ်ကို ပြန်ပို့ပေးရန်
+        reset_password_user: စကားဝှက်ကို ပြန်လည်ရယူမည်
+        resolve_report: အစီရင်ခံစာကို ဖြေရှင်းရန်
+        sensitive_account: Force-Sensitive အကောင့်
+        silence_account: အကောင့် ကန့်သတ်ပါ
+        suspend_account: အကောင့် ရပ်ဆိုင်းပါ
+        unassigned_report: အစီရင်ခံစာ ဖြုတ်ရန်
+        unblock_email_account: အီးမေးလ်လိပ်စာ ပြန်ဖွင့်ရန်
+        unsilence_account: ကန့်သတ်အကောင့်ကို မလုပ်တော့ပါ
+        unsuspend_account: အကောင့်ကို ရပ်ဆိုင်းပါ။
+        update_announcement: ကြေညာချက်ပြင်ဆင်ရန်
+        update_custom_emoji: စိတ်ကြိုက်အီမိုဂျီကို ပြင်ဆင်ရန်
+        update_domain_block: ဒိုမိန်းပိတ်ပင်ခြင်းအား ပြင်ဆင်ရန်
+        update_ip_block: IP စည်းမျဉ်း ပြင်ဆင်ရန်
+        update_status: ပို့စ်ပြင်ဆင်ရန်
+        update_user_role: အခန်းကဏ္ဍပြင်ဆင်ရန်
+      actions:
+        approve_appeal_html: "%{name} က %{target} မှ စိစစ်ဆုံးဖြတ်ချက်အယူခံဝင်ခြင်းကို အတည်ပြုခဲ့သည်"
+        approve_user_html: " %{name} က %{target} မှ အကောင့်ဖွင့်ခြင်းကို အတည်ပြုထားသည်"
+        assigned_to_self_report_html: "%{name} က အစီရင်ခံစာ %{target} များကို ၎င်းတို့ထံ ပေးအပ်ခဲ့သည်"
+        change_email_user_html: "%{name} က အသုံးပြုသူ %{target} ၏ အီးမေးလ်လိပ်စာကို ပြောင်းခဲ့သည်"
+        change_role_user_html: "%{name} က %{target} ၏ အခန်းကဏ္ဍကို ပြောင်းခဲ့သည်"
+        confirm_user_html: "%{name} က အသုံးပြုသူ %{target} ၏ အီးမေးလ်လိပ်စာကို အတည်ပြုခဲ့သည်"
+        create_account_warning_html: "%{name} က %{target} သို့ သတိပေးချက်တစ်ခု ပေးပို့ခဲ့သည်"
+        create_announcement_html: "%{name} က ကြေညာချက်အသစ် %{target} ကို ဖန်တီးခဲ့သည်"
+        create_canonical_email_block_html: "%{name} က ဟက်ရှ် %{target} ဖြင့် အီးမေးလ်ကို ပိတ်ပင်ထားသည်"
+        create_custom_emoji_html: "%{name} က အီမိုဂျီ %{target} အသစ်ကို ပြင်ဆင်ခဲ့သည်"
+        create_domain_allow_html: "%{name} က ဒိုမိန်း %{target} ဖြင့် ဖက်ဒီကို ခွင့်ပြုခဲ့သည်"
+        create_domain_block_html: "%{name} က ဒိုမိန်း %{target} ကို ပိတ်ပင်ထားသည်"
+        create_email_domain_block_html: "%{name} က အီးမေးလ်ဒိုမိန်း %{target} ကို ပိတ်ပင်ထားသည်"
+        create_ip_block_html: "%{name} က IP %{target} အတွက် စည်းမျဉ်းကို ဖန်တီးထားသည်"
+        create_unavailable_domain_html: "%{name} က ဒိုမိန်း %{target} သို့ ပေးပို့မှုကို ရပ်လိုက်သည်"
+        create_user_role_html: "%{name} က %{target} အခန်းကဏ္ဍကို ဖန်တီးပြီးပါပြီ"
+        demote_user_html: "%{name} က အသုံးပြုသူ %{target} ကို ဖြုတ်ပြီးပါပြီ"
+        destroy_announcement_html: "%{name} က ကြေညာချက် %{target} ကို ဖျက်လိုက်သည်"
+        destroy_canonical_email_block_html: ဟက်ရှ် %{target} ဖြင့် %{name} အီးမေးလ်ပိတ်သိမ်းထားခြင်းကို ဖယ်ရှားပြီးပါပြီ
+        destroy_custom_emoji_html: "%{name} ဖျက်လိုက်သော အီမိုဂျီ %{target}"
+        destroy_domain_allow_html: "%{name} က ဒိုမိန်း %{target} ဖြင့် ဖက်ဒီကို ခွင့်မပြုခဲ့ပါ"
+        destroy_domain_block_html: "%{name} မှ ပြန်ဖွင့်လိုက်သော ဒိုမိန်း %{target}"
+        destroy_email_domain_block_html: "%{name} က အီးမေးလ်ဒိုမိန်း %{target} ကို ပြန်ဖွင့်ထားသည်"
+        destroy_instance_html: "%{name} က ဒိုမိန်း %{target} ကို ဖယ်ရှားခဲ့သည်"
+        destroy_ip_block_html: "%{name} က IP %{target} အတွက် စည်းမျဉ်းကို ဖျက်ထားသည်"
+        destroy_status_html: "%{name} က %{target} မှ တင်ထားသောပို့စ်ကို  ဖယ်ရှားခဲ့သည်"
+        destroy_unavailable_domain_html: "%{name} က ဒိုမိန်း %{target} သို့ ပေးပို့မှုကို ပြန်လည်စတင်ခဲ့သည်"
+        destroy_user_role_html: "%{name} ဖျက်ထားသော အခန်းကဏ္ဍ %{target} "
+        disable_2fa_user_html: "%{name} က အသုံးပြုသူ %{target} အတွက် နှစ်ဆင့်ခံလုံခြုံရေးလိုအပ်ချက်ကို ပိတ်ထားသည်"
+        disable_custom_emoji_html: "%{name} ပိတ်ထားသောအီမိုဂျီ %{target}"
+        disable_sign_in_token_auth_user_html: "%{name}  က %{target} အတွက် အီးမေးလ်တိုကင် အထောက်အထားပြခြင်းကို ပိတ်ထားသည်"
+        disable_user_html: "%{name} က အသုံးပြုသူ %{target} အတွက် အကောင့်ဝင်ခြင်းကို ပိတ်ထားသည်"
+        enable_custom_emoji_html: "%{name} ဖွင့်ထားသည့် အီမိုဂျီ %{target}"
+        enable_sign_in_token_auth_user_html: "%{name} က %{target} အတွက် အီးမေးလ်တိုကင် အထောက်အထားပြခြင်းကို ဖွင့်ထားသည်"
+        enable_user_html: "%{name} က အသုံးပြုသူ %{target} အတွက် အကောင့်ဝင်ခြင်းကို ဖွင့်ထားသည်"
+        memorialize_account_html: "%{name} က %{target} ၏ အကောင့်ကို အမှတ်တရ စာမျက်နှာအဖြစ် ပြောင်းလဲခဲ့သည်"
+        promote_user_html: "%{name} က အသုံးပြုသူ %{target} ကို ထောက်ခံခဲ့သည်"
+        reject_appeal_html: "%{name} က %{target} မှ အယူခံဝင်မှု ဆုံးဖြတ်ချက် စိစစ်ခြင်းကို ပယ်ချခဲ့သည်"
+        reject_user_html: "%{name} က %{target} မှတစ်ဆင့် အကောင့်ဖွင့်ခြင်းကို ပယ်ချခဲ့သည်"
+        remove_avatar_user_html: "%{name} က %{target} ၏ ကိုယ်စားပြုရုပ်ပုံကို ဖယ်ရှားခဲ့သည်"
+        reopen_report_html: "%{name} က အစီရင်ခံစာ %{target} ကို ပြန်ဖွင့်ခဲ့သည်"
+        resend_user_html: "%{name} က %{target} အတွက် အတည်ပြုချက်အီးမေးလ်ကို  ပြန်ပို့ခဲ့သည်"
+        reset_password_user_html: "%{name} က အသုံးပြုသူ %{target} ၏ စကားဝှက်ကို ပြန်လည်သတ်မှတ်ခဲ့သည်"
+        resolve_report_html: "%{name} က အစီရင်ခံစာ %{target} ကို ဖြေရှင်းထားသည်"
+        sensitive_account_html: "%{name} က %{target} ၏ မီဒီယာကို သတိထားရသောမီဒီယာအဖြစ် အမှတ်အသားပြုထားသည်"
+        silence_account_html: "%{name} က %{target} ၏ အကောင့်ကို ကန့်သတ်ထားသည်"
+        suspend_account_html: "%{name} က %{target} ၏ အကောင့်ကို ဆိုင်းငံ့ထားသည်"
+        unassigned_report_html: "%{name} က အစီရင်ခံစာ %{target} ကို ဖြုတ်ထားသည်"
+        unblock_email_account_html: "%{name} က %{target} ၏ အီးမေးလ်လိပ်စာကို ပြန်ဖွင့်ခဲ့သည်"
+        unsensitive_account_html: "%{name} က %{target} ၏ မီဒီယာကို သတိထားရသောမီဒီယာအဖြစ် အမှတ်အသားပြုထားခြင်းမရှိပါ"
+        unsilence_account_html: "%{target} ၏ အကောင့်၏ %{name} ကန့်သတ်ချက် မရှိပါ။"
+        unsuspend_account_html: "%{name} က %{target} ၏ အကောင့်ကို ဆိုင်းငံ့ထားသည်"
+        update_announcement_html: "%{name} က ကြေညာချက် %{target} ကို ပြင်ဆင်ခဲ့သည်"
+        update_custom_emoji_html: "%{name} က အီမိုဂျီ %{target} ကို ပြင်ဆင်ခဲ့သည်"
+        update_domain_block_html: "%{name} က %{target} အတွက် ဒိုမိန်းပိတ်ပင်ခြင်းကို ပြင်ဆင်ခဲ့သည်"
+        update_ip_block_html: "%{name} မှ IP %{target} အတွက် စည်းမျဉ်း ပြောင်းထားသည်"
+        update_status_html: "%{name} က %{target} တင်ထားသောပို့စ်ကို ပြင်ဆင်ခဲ့သည်"
+        update_user_role_html: "%{name} က %{target} အခန်းကဏ္ဍကို ပြောင်းလဲခဲ့ပါသည်"
+      deleted_account: အကောင့်ဖျက်ပြီးပါပြီ
+      empty: မှတ်တမ်းများ မတွေ့ပါ။
+      filter_by_action: လုပ်ဆောင်ချက်အလိုက် စစ်ထုတ်ရန်
+      filter_by_user: အသုံးပြုသူအလိုက် စစ်ထုတ်ရန်
+      title: မှတ်တမ်းများစစ်ဆေးခြင်း
+    announcements:
+      destroyed_msg: ကြေညာချက် ဖျက်ပြီးပါပြီ
+      edit:
+        title: ကြေညာချက် ပြင်ဆင်ရန်
+      empty: ကြေညာချက်များမတွေ့ပါ
+      live: လက်ရှိ
+      new:
+        create: ကြေညာချက်ဖန်တီးပါ
+        title: ကြေညာချက်အသစ်
+      publish: ပို့စ်တင်မည်
+      published_msg: ကြေညာချက်တင်ပြီးပါပြီ။
+      scheduled_for: "%{time} အတွက် စီစဉ်ထားသည်"
+      scheduled_msg: ကြေညာချက်ထုတ်ပြန်ရန် စီစဉ်ထားသည်။
+      title: ကြေညာချက်များ
+      unpublish: ပြန်ဖြုတ်ပါ
+      unpublished_msg: ကြေညာချက်ကို ဖြုတ်ပြီးပါပြီ
+      updated_msg: ကြေညာချက်ကို ပြင်ဆင်ပြီးပါပြီ။
+    custom_emojis:
+      assign_category: အမျိုးအစားသတ်မှတ်ရန်
+      by_domain: ဒိုမိန်း
+      copied_msg: အီမိုဂျီ၏စက်အတွင်းကူးထားသည်များကို အောင်မြင်စွာ ဖန်တီးခဲ့သည်။
+      copy: ကူးယူပါ
+      copy_failed_msg: အီမိုဂျီ၏စက်အတွင်းမကူးနိုင်ပါ
+      create_new_category: အမျိုးအစားအသစ်ဖန်တီးရန်
+      created_msg: အီမိုဂျီ ဖန်တီးပြီးပါပြီ။
+      delete: ဖျက်ပါ
+      destroyed_msg: အီမိုဂျီ ဖျက်ပစ်လိုက်ပါပြီ။
+      disable: ပိတ်ပါ
+      disabled: ပိတ်ပြီးပါပြီ
+      disabled_msg: ထိုအီမိုဂျီကို ပိတ်ပြီးပါပြီ
+      emoji: အီမိုဂျီ
+      enable: ဖွင့်ပါ
+      enabled: ဖွင့်ထားသည်
+      enabled_msg: ထိုအီမိုဂျီကို ဖွင့်ပြီးပါပြီ
+      image_hint: PNG သို့မဟုတ် GIF %{size} အထိ
+      list: စာရင်း
+      listed: စာရင်းသွင်းထားသည်
+      new:
+        title: စိတ်ကြိုက်အီမိုဂျီအသစ် ထည့်ပါ
+      no_emoji_selected: မည်သည့်အီမိုဂျီကိုမျှ ရွေးချယ်ထားခြင်းမရှိသောကြောင့် ပြောင်းလဲခြင်းမရှိပါ
+      not_permitted: ဤလုပ်ဆောင်ချက်ကို ဆောင်ရွက်ရန် သင့်ကို ခွင့်မပြုပါ။
+      overwrite: ထပ်ရေးရန်
+      shortcode: 'တိုတိုကုတ်
+
+        '
+      shortcode_hint: အက္ခရာဂဏန်းများနှင့် underscore များဖြင့် အနည်းဆုံး စာလုံး ၂ လုံးရှိရပါမည်
+      title: စိတ်ကြိုက်အီမိုဂျီများ
+      uncategorized: အမျိုးအစားခွဲခြားထားခြင်းမရှိပါ
+      unlist: စာရင်းမသွင်းထားပါ
+      unlisted: စာရင်းမသွင်းထားပါ
+      update_failed_msg: ထိုအီမိုဂျီကို ပြင်ဆင်၍မရပါ
+      updated_msg: အီမိုဂျီကို ပြင်ဆင်ပြီးပါပြီ။
+      upload: တင္ရန်
+    dashboard:
+      active_users: လက်ရှိအသုံးပြုသူများ
+      interactions: အပြန်အလှန်ဆက်သွယ်မှုများ
+      media_storage: မီဒီယာသိုလှောင်မှု
+      new_users: အသုံးပြုသူအသစ်များ
+      opened_reports: မှတ်တမ်းများကိုဖွင့်လှစ်ခဲ့သည်
+      pending_appeals_html:
+        other: "<strong>%{count}</strong> အယူခံဝင်မှုကို ဆိုင်းငံ့ထားခြင်း"
+      pending_reports_html:
+        other: "<strong>%{count}</strong> မှတ်တမ်းဆောင်ရွက်ဆဲ"
+      pending_tags_html:
+        other: "<strong>%{count}</strong> hashtag ဆောင်ရွက်ဆဲ"
+      pending_users_html:
+        other: "<strong>%{count}</strong> ဆိုင်းငံ့အသုံးပြုသူ"
+      resolved_reports: မှတ်တမ်းများဖြေရှင်းခဲ့သည်
+      software: ဆော့ဖ်ဝဲလ်
+      sources: အကောင့်ဖွင့်ခြင်းဆိုင်ရာ သတင်းရင်းမြစ်များ
+      space: နေရာလွတ်အသုံးပြုမှု
+      title: ဒက်ရှ်ဘုတ်မြင်ကွင်း
+      top_languages: လက်ရှိအသုံးများလျက်ရှိသည့် ဘာသာစကား
+      top_servers: လက်ရှိအသုံးများလျက်ရှိသည့် ဆာဗာများ
+      website: ဝဘ်ဆိုဒ်
+    disputes:
+      appeals:
+        empty: အယူခံဝင်ထားခြင်းမရှိပါ။
+        title: အယူခံဝင်ထားခြင်းများ
+    domain_allows:
+      add_new: ဒိုမိန်းဖြင့် ဖက်ဒီကို ခွင့်ပြုရန်
+      created_msg: ဒိုမိန်းကို ဖက်ဒီအတွက် ခွင့်ပြုပြီးပါပြီ
+      destroyed_msg: ဒိုမိန်းအား ဖက်ဒီမှ ခွင့်မပြုပါ
+      export: ထုတ်ယူခြင်း
+      import: ထည့်သွင်းခြင်း
+      undo: ဒိုမိန်းဖြင့် ဖက်ဒီကို ခွင့်မပြုပါ
+    domain_blocks:
+      add_new: ဒိုမိန်းပိတ်ပင်ခြင်းအသစ် ထည့်ပါ
+      created_msg: ဒိုမိန်းပိတ်ပင်ခြင်းကို ယခုလုပ်ဆောင်နေပါသည်
+      destroyed_msg: ဒိုမိန်းပိတ်ဆို့ခြင်းကို ပြန်ပြင်လိုက်ပါပြီ။
+      domain: ဒိုမိန်း
+      edit: ဒိုမိန်းပိတ်ပင်ခြင်းကို ပြင်ဆင်ရန်
+      existing_domain_block: "%{name}  အပေါ် ပိုမိုတင်းကျပ်သောကန့်သတ်ချက်များကို ချမှတ်ထားပြီးဖြစ်သည်။"
+      existing_domain_block_html: '%{name} အပေါ် ပိုမိုတင်းကျပ်သော ကန့်သတ်ချက်များကို ချမှတ်ထားပြီး <a href="%{unblock_url}">၎င်းကို ပြန်ဖွင့်ရန်</a> လိုအပ်ပါသည်။'
+      export: ထုတ်ယူခြင်း
+      import: ထည့်သွင်းခြင်း
+      new:
+        create: ပိတ်ပင်ခြင်း ဖန်တီးရန်
+        hint: ဒိုမိန်းပိတ်ဆို့ခြင်းသည် ဒေတာဘေ့စ်အတွင်း အကောင့်ထည့်သွင်းမှုများကို မတားဆီးသော်လည်း ထိုအကောင့်များအတွက် တိကျစွာစိစစ်ခြင်းနည်းလမ်းများကို အလိုအလျောက်ပြန်လည်အသုံးချမည်ဖြစ်သည်။
+        severity:
+          desc_html: "<strong>ကန့်သတ်ချက်</strong> သည် ဤဒိုမိန်းရှိ အကောင့်များမှ ပို့စ်များကို ၎င်းတို့ကို မလိုက်ကြည့်သူတိုင်းကို မမြင်နိုင်စေမည်ဖြစ်သည်။ <strong>ဆိုင်းငံ့ထားရန်</strong> သည် ဤဒိုမိန်းအကောင့်များအတွက် အကြောင်းအရာ၊ မီဒီယာနှင့် ပရိုဖိုင်ဒေတာအားလုံးကို သင့်ဆာဗာမှ ဖယ်ရှားပါမည်။ မီဒီယာဖိုင်များကို ငြင်းပယ်လိုပါက <strong>None</strong> ကို အသုံးပြုပါ။"
+          noop: တစ်ခုမျှမရှိ
+          silence: ကန့်သတ်
+          suspend: ရပ်ဆိုင်းပါ
+        title: ဒိုမိန်းပိတ်ပင်ထားခြင်းအသစ်
+      no_domain_block_selected: မည်သည့်ဒိုမိန်းပိတ်ပင်ထားခြင်းကိုမျှ ရွေးချယ်ထားခြင်းမရှိသောကြောင့် ပြောင်းလဲခြင်းမရှိပါ
+      not_permitted: ဤလုပ်ဆောင်ချက်ကို ဆောင်ရွက်ရန် သင့်ကို ခွင့်မပြုပါ။
+      obfuscate: မှတ်ရခက်သောဒိုမိန်းအမည်
+      obfuscate_hint: ဒိုမိန်းကန့်သတ်ချက်များစာရင်းကို ဖွင့်ထားပါက စာရင်းရှိ ဒိုမိန်းအမည်များမှာ ရှုပ်ထွေးစေနိုင်ပါသည်။
+      private_comment: သီးသန့်မှတ်ချက်
+      private_comment_hint: စိစစ်သူများ၏အတွင်းပိုင်းအသုံးပြုမှုအတွက် ဤဒိုမိန်းကန့်သတ်ချက်ဆိုင်ရာမှတ်ချက်။
+      public_comment: အများမြင်မှတ်ချက်
+      public_comment_hint: ဒိုမိန်းကန့်သတ်ချက်များစာရင်းကို ကြော်ငြာထားပါက အများသူငှာအတွက် ဤဒိုမိန်းကန့်သတ်ချက်နှင့်ပတ်သက်၍ ရေးသားပေးထားရန်။
+      reject_media: မီဒီယာဖိုင်များကို ဖယ်ရှားပါ
+      reject_media_hint: စက်တွင်းသိမ်းဆည်းထားသည့် မီဒီယာဖိုင်များကို ဖယ်ရှားပြီး ဒေါင်းလုဒ်လုပ်ခွင့် မရှိပါ။ ဆိုင်းငံ့ခြင်းများအတွက် မသက်ဆိုင်ပါ။
+      reject_reports: မှတ်တမ်းများ ဖယ်ရှားရန်
+      reject_reports_hint: ဤဒိုမိန်းမှလာသော မှတ်တမ်းအားလုံးကို လျစ်လျူရှုပါ။ ဆိုင်းငံ့ခြင်းများအတွက် မသက်ဆိုင်ပါ။
+      undo: ဒိုမိန်းပိတ်ပင်ခြင်းကို ပြန်ဖျက်ရန်
+      view: ဒိုမိန်းပိတ်ပင်ခြင်းကို ကြည့်ရန်
+    email_domain_blocks:
+      add_new: အသစ် ထည့်ပါ
+      attempts_over_week:
+        other: ပြီးခဲ့သည့် ရက်သတ္တပတ်အတွင်း %{count} ကြိုးပမ်းမှု
+      created_msg: အီးမေးလ်ဒိုမိန်းကို ပိတ်ပင်နိုင်ခဲ့ပါသည်
+      delete: ဖျက်ပါ
+      dns:
+        types:
+          mx: MX မှတ်တမ်း
+      domain: ဒိုမိန်း
+      new:
+        create: ဒိုမိန်းထည့်ပါ
+        resolve: ဒိုမိန်းကို ဖြေရှင်းရန်
+        title: အီးမေးလ်ဒိုမိန်းအသစ်ကို ပိတ်ရန်
+      no_email_domain_block_selected: မည်သည့်အီးမေးလ်ဒိုမိန်းပိတ်ပင်ခြင်းကိုမျှ ရွေးချယ်ထားခြင်းမရှိသောကြောင့် ပြောင်းလဲခြင်းမရှိပါ
+      not_permitted: ခွင့်ပြုထားခြင်းမရှိပါ
+      resolved_dns_records_hint_html: ဒိုမိန်းအမည်သည် အီးမေးလ်လက်ခံခြင်းအတွက် နောက်ဆုံးတွင် တာဝန်ရှိသည့် အောက်ပါ MX ဒိုမိန်းများသို့ ဖြေရှင်းပေးသည်။ MX ဒိုမိန်းကို ပိတ်ဆို့ခြင်းသည် တူညီသော MX ဒိုမိန်းကို အသုံးပြုသည့် မည်သည့်အီးမေးလ်လိပ်စာမှ အကောင့်ဖွင့်ခြင်းများကို ပိတ်ဆို့စေမည်၊ မြင်နိုင်သောဒိုမိန်းအမည်သည် ကွဲပြားသော်လည်း၊ <strong>အဓိက အီးမေးလ်ဝန်ဆောင်မှုပေးသူများကို မပိတ်ဆို့ရန် သတိထားပါ။</strong>
+      resolved_through_html: "%{domain} မှတစ်ဆင့် ဖြေရှင်းခဲ့သည်"
+      title: ပိတ်ပင်ထားသော အီးမေးလ်ဒိုမိန်းများ
+    export_domain_allows:
+      new:
+        title: ဒိုမိန်းတင်သွင်းခွင့်ပြုသည်
+      no_file: ဖိုင်ရွေးထားခြင်းမရှိပါ။
+    export_domain_blocks:
+      import:
+        description_html: ဒိုမိန်းပိတ်ဆို့စာရင်း ထည့်သွင်းပါမည်။ ဤစာရင်းကို သင်ကိုယ်တိုင် မရေးသားထားပါက ပြန်လည်သုံးသပ်ရန်လိုပါသည်။
+        existing_relationships_warning: လက်ရှိစောင့်ကြည့်သူများနှင့် ဆက်ဆံရေး
+        private_comment_description_html: ထည့်သွင်းထားသော ဒိုမိန်းပိတ်ဆို့စာရင်းများအား ကြည့်ရှုရန်အတွက် ပိတ်ဆို့စာရင်းများကို အောက်ပါသီးသန့်မှတ်ချက်ဖြင့် ဖန်တီးပါမည် - <q>%{comment}</q>
+        private_comment_template: "%{date} တွင် %{source} မှ ထည့်သွင်းခဲ့သည်"
+        title: ဒိုမိန်းပိတ်ပင်ထားမှုများကို ထည့်သွင်းရန်
+      invalid_domain_block: အောက်ပါအမှား(များ)ကြောင့် တစ်ခု သို့မဟုတ် တစ်ခုထက်ပိုသော ဒိုမိန်းပိတ်ပင်ထားမှုများကို ကျော်သွားခဲ့သည် - %{error}
+      new:
+        title: ဒိုမိန်းပိတ်ပင်ထားမှုများကို ထည့်သွင်းရန်
+      no_file: ဖိုင်ရွေးထားခြင်းမရှိပါ
+    follow_recommendations:
+      description_html: "<strong>အကြံပြုချက်များကို အသုံးပြုသူအသစ်များက လိုက်နာခြင်းဖြင့် စိတ်ဝင်စားစရာအကြောင်းအရာများကို လျင်မြန်စွာရှာဖွေနိုင်စေပါမည်</strong>။ အသုံးပြုသူတစ်ဦးက စိတ်ကြိုက်ပြင်ဆင်ထားသည့် အကြံပြုချက်များလိုက်နာရန်အတွက် အခြားသူများနှင့် လုံလောက်သောအပြန်အလှန်တုံ့ပြန်ခြင်းမရှိပါက ယင်းအစား ဤအကောင့်များကို အကြံပြုလိုပါသည်။ ၎င်းအကောင့်များကို မကြာသေးမီက ထိတွေ့ဆက်ဆံမှုအများဆုံးနှင့် အသုံးပြုထားသည့်ဘာသာစကားမှတစ်ဆင့် ဒေသတွင်းစောင့်ကြည့်သူဦးရေ အများဆုံးရှိသော အကောင့်များကိုအခြေခံ၍ နေ့စဉ် တွက်ချက်ထားခြင်းဖြစ်ပါသည်။"
+      language: ဘာသာစကားအတွက်
+      status: အခြေအနေ
+      title: အကြံပြုချက်များကို စောင့်ကြည့်ပါ
+      unsuppress: အကြံပြုချက်စောင့်ကြည့်ခြင်းအား ပြန်လည်ရယူပါ
+    instances:
+      availability:
+        description_html:
+          other: ဒိုမိန်းသို့ ပေးပို့ခြင်း မအောင်မြင်ဘဲ <strong>%{count} ရက်</strong> မအောင်မြင်ပါက၊ ဒိုမိန်းထံပေးပို့ခြင်း <em>မှ</em> မရရှိပါက နောက်ထပ်ပေးပို့ရန် ကြိုးပမ်းမှုများ ပြုလုပ်မည်မဟုတ်ပါ။
+        failure_threshold_reached: "%{date} နေ့ တွင် မအောင်မြင်ခဲ့ပါ။"
+        failures_recorded:
+          other: "%{count} ရက်နေ့တွင် ကြိုးစားမှု မအောင်မြင်ပါ။"
+        no_failures_recorded: မှတ်တမ်းမရှိပါ။
+        title: ရရှိနိုင်မှု
+        warning: ဤဆာဗာအသုံးပြုနိုင်ရန် နောက်ဆုံးကြိုးပမ်းမှုမှာ မအောင်မြင်ခဲ့ပါ
+      back_to_all: အားလုံး
+      back_to_limited: ကန့်သတ်ထားသည်
+      back_to_warning: သတိပေးချက်
+      by_domain: ဒိုမိန်း
+      confirm_purge: ဤဒိုမိန်းမှ အချက်အလက်များကို အပြီးတိုင်ဖျက်လိုသည်မှာ သေချာပါသလား။
+      content_policies:
+        comment: အတွင်းပိုင်းမှတ်ချက်
+        description_html: ဤဒိုမိန်းနှင့် ဒိုမိန်းခွဲများမှ အကောင့်အားလုံးအတွက် အသုံးချမည့် အကြောင်းအရာမူဝါဒများကို သတ်မှတ်နိုင်သည်။
+        limited_federation_mode_description_html: ဤဒိုမိန်းဖြင့် ဖက်ဒီကို ခွင့်ပြုမလား။
+        policies:
+          reject_media: မီဒီယာဖယ်ရှားရန်
+          reject_reports: အစီရင်ခံစာများ ဖယ်ရှားရန်
+          silence: ကန့်သတ်
+          suspend: ရပ်ဆိုင်းပါ
+        policy: မူဝါဒ
+        reason: မူဝါဒဆိုင်ရာအကြောင်းအရာများ
+        title: အကြောင်းအရာမူဝါဒများ
+      dashboard:
+        instance_accounts_dimension: အများဆုံးစောင့်ကြည့်ထားသည့်အကောင့်များ
+        instance_accounts_measure: သိမ်းဆည်းထားသော အကောင့်များ
+        instance_followers_measure: ကျွန်ုပ်တို့၏စောင့်ကြည့်သူများ အဲ့ဒီနေရာမှာပါ
+        instance_follows_measure: သူတို့၏စောင့်ကြည့်သူများ ဒီနေရာမှာပါ
+        instance_languages_dimension: အသုံးများသည့်ဘာသာစကားများ
+        instance_media_attachments_measure: သိမ်းဆည်းထားသော မီဒီယာပူးတွဲဖိုင်များ
+        instance_reports_measure: "၎င်းတို့နှင့်ဆိုင်သော အစီရင်ခံစာများ"
+        instance_statuses_measure: သိမ်းဆည်းထားသောပို့စ်များ
+      delivery:
+        all: အားလုံး
+        clear: ပေးပို့မှု အမှားအယွင်းများကို ရှင်းလင်းပါ
+        failing: မအောင်မြင်ခြင်း
+        restart: ပေးပို့မှုကို ပြန်လည်စတင်ရန်
+        stop: ပေးပို့မှုကို ရပ်ရန်
+        unavailable: မရရှိနိုင်ပါ
+      delivery_available: ပေးပို့နိုင်ပါပြီ
+      delivery_error_days: ပေးပို့မှု မှားယွင်းသည့်ရက်များ
+      delivery_error_hint: "%{count} ရက်အတွင်း မပေးပို့နိုင်ပါက ၎င်းကို ပေးပို့မရနိုင်ဟု အလိုအလျောက် အမှတ်အသားပြုပါမည်။"
+      destroyed_msg: "%{domain} မှ အချက်အလက်များကို မကြာမီ ဖျက်ပါမည်။"
+      empty: ဒိုမိန်းများ မတွေ့ပါ။
+      known_accounts:
+        other: လူသိများသော အကောင့် %{count} ခု
+      moderation:
+        all: အားလုံး
+        limited: ကန့်သတ်ထားသော
+        title: စိစစ်ခြင်း
+      private_comment: သီးသန့်မှတ်ချက်
+      public_comment: အများမြင်မှတ်ချက်
+      purge: ဖျက်
+      purge_description_html: ဤဒိုမိန်းသည် အော့ဖ်လိုင်းဖြစ်နေပါက သင့်သိုလှောင်မှုမှ အကောင့်မှတ်တမ်းများနှင့် ဆက်စပ်အချက်အလက်အားလုံးကို ဖျက်နိုင်သည်။ အချိန်အနည်းငယ် ကြာနိုင်ပါသည်။
+      title: ဖက်ဒီ
+      total_blocked_by_us: ကျွန်ုပ်တို့မှ ပိတ်ပင်ထားခြင်း
+      total_followed_by_them: "၎င်းတို့မှ စောင့်ကြည့်ခဲ့သည်"
+      total_followed_by_us: ကျွန်ုပ်တို့မှ စောင့်ကြည့်ခဲ့သည်
+      total_reported: "၎င်းတို့နှင့်ဆိုင်သော အစီရင်ခံစာများ"
+      total_storage: မီဒီယာ ပူးတွဲချက်များ
+      totals_time_period_hint_html: အောက်တွင်ဖော်ပြထားသော စုစုပေါင်းမှာ အချိန်တိုင်းအတွက် အချက်အလက်များဖြစ်သည်။
+    invites:
+      deactivate_all: အားလုံးပယ်ဖျက်ရန်
+      filter:
+        all: အားလုံး
+        available: ရရှိနိုင်သော
+        expired: သက်တမ်းကုန်သွားပါပြီ
+        title: စစ်ထုတ်ခြင်း
+      title: ဖိတ်ခေါ်ခြင်း
+    ip_blocks:
+      add_new: စည်းမျဉ်းဖန်တီးပါ
+      created_msg: IP စည်းမျဉ်းအသစ် ထည့်သွင်းပြီးပါပြီ
+      delete: ဖျက်ပါ
+      expires_in:
+        '1209600': ၂ ပတ်
+        '15778476': ၆ လ
+        '2629746': ၁ လ
+        '31556952': ၁ နှစ်
+        '86400': ၁ ရက်
+        '94670856': ၃ နှစ်
+      new:
+        title: IP စည်းမျဉ်းအသစ်ဖန်တီးပါ
+      no_ip_block_selected: မည်သည့် IP စည်းမျဉ်းကိုမျှ ရွေးချယ်ထားခြင်းမရှိသောကြောင့် ပြောင်းလဲခြင်းမရှိပါ
+      title: IP စည်းမျဉ်းများ
+    relationships:
+      title: "%{acct} နှင့် ပတ်သက်မှု"
+    relays:
+      add_new: relay အသစ်ထည့်ပါ
+      delete: ဖျက်ပါ
+      description_html: စာရင်းသွင်းပြီး ထုတ်ဝေသည့် ဆာဗာများအကြား အများသူငှာ ပို့စ်များ အများအပြားကို ဖလှယ်ပေးသည့် ကြားခံဆာဗာတစ်ခုဖြစ်သည်။ <strong>၎င်းသည် အသေးစားနှင့် အလတ်စား ဆာဗာများမှ အကြောင်းအရာများကို ရှာဖွေတွေ့ရှိရန် ကူညီပေးနိုင်သည်</strong>၊ ၎င်းသည် ဒေသဆိုင်ရာအသုံးပြုသူများသည် အဝေးထိန်းဆာဗာများပေါ်တွင် အခြားလူများကို ကိုယ်တိုင်လိုက်လျှောက်ရန် လိုအပ်မည်ဖြစ်သည်။
+      disable: ပိတ်ပါ
+      disabled: ပိတ်ထားသည်
+      enable: ဖွင့်ပါ
+      enable_hint: တစ်ကြိမ်ဖွင့်ပြီးသည်နှင့် သင့်ဆာဗာသည် ဤထပ်ဆင့်လွှင့်မှုမှ အများမြင်ပို့စ်များအားလုံးကို စာရင်းသွင်းလိုက်မည်ဖြစ်ပြီး ဤဆာဗာ၏ အများမြင်ပို့စ်များကို ၎င်းထံ စတင်ပေးပို့မည်ဖြစ်သည်။
+      enabled: ဖွင့်ထားသည်
+      inbox_url: ထပ်ဆင့်ပေးပို့မည့် URL
+      pending: ထပ်ဆင့်အတည်ပြုချက်ကို စောင့်ဆိုင်းနေခြင်း
+      save_and_enable: သိမ်းပြီး ဖွင့်ရန်
+      setup: ထပ်ဆင့်ချိတ်ဆက်မှုစနစ် ထည့်သွင်းပါ
+      signatures_not_enabled: လုံခြုံရေးမုဒ် သို့မဟုတ် ကန့်သတ်ဖက်ဒီမုဒ်ကို ဖွင့်ထားစဉ် ထပ်ဆင့်ပို့ထားမှုများမှာ မှန်ကန်စွာ အလုပ်မလုပ်နိုင်တော့ပါ
+      status: အခြေအနေ
+      title: ထပ်ဆင့်ပေးပို့ခြင်းများ
+    report_notes:
+      created_msg: မှတ်တမ်းမှတ်စုကို ဖန်တီးပြီးပါပြီ။
+      destroyed_msg: မှတ်တမ်းမှတ်စုကို ဖျက်ပြီးပါပြီ။
+    reports:
+      account:
+        notes:
+          other: "%{count} မှတ်စု"
+      action_log: မှတ်တမ်းများစစ်ဆေးခြင်း
+      action_taken_by: ဆောင်ရွက်ခဲ့ပါသည်
+      actions:
+        delete_description_html: အစီရင်ခံထားသော ပို့စ်များကို ဖျက်လိုက်မည်ဖြစ်ပြီး အကောင့်တစ်ခုတည်းမှ နောင်လာမည့် ချိုးဖောက်မှုများအပေါ် အရှိန်မြှင့်ကူညီရန် သတိပေးချက်တစ်ခု မှတ်တမ်းတင်ပါမည်။
+        mark_as_sensitive_description_html: တိုင်ကြားထားသည့်ပို့စ်များရှိ မီဒီယာအား သတိထားရသောမီဒီယာအဖြစ် သတ်မှတ်ပြီး အကောင့်တူဖြင့် နောင် ချိုးဖောက်မှုများရှိပါက သတိပေးချက်တစ်ခုဖြင့် သင့်အတွက် မှတ်တမ်းပြုလုပ်ပေးထားပါမည်။
+        other_description_html: အကောင့်ထိန်းချုပ်ရန်နှင့် တိုင်ကြားထားသည့်သို့ ဆက်သွယ်မှုကို စိတ်ကြိုက်ပြင်ဆင်ရန်တို့အတွက် နောက်ထပ်ရွေးချယ်စရာများကို ကြည့်ပါ။
+        resolve_description_html: တိုင်ကြားထားသည့်အကောင့်ကို အရေးယူမည်မဟုတ်ပါ၊ လုပ်ဆောင်ချက်ကို မှတ်တမ်းတင်ထားခြင်းမရှိသည့်အပြင် တိုင်ကြားထားမှုကိုလည်း ရုပ်သိမ်းပါမည်။
+        silence_description_html: အကောင့်ကို စောင့်ကြည့်ထားသူများ သို့မဟုတ် ရှာဖွေသူများသာ မြင်နိုင်မည်ဖြစ်ပါသည်။ အကောင့်တွေ့ရှိမှုအား ကန့်သတ်ထားသော်လည်း ပြန်ပြောင်းဆောင်ရွက်နိုင်ပါသည်။ ဤအကောင့်နှင့်ပတ်သက်သည့် မှတ်တမ်းအားလုံးကို ပိတ်ပါမည်။
+        suspend_description_html: အကောင့်နှင့် ၎င်း၏အကြောင်းအရာအားလုံးမှာ သုံးခွင့်မရတော့သဖြင့် နောက်ဆုံးတွင် ဖျက်ပစ်မည်ဖြစ်ပါသည်။ ၎င်းနှင့် အပြန်အလှန်မတုံ့ပြန်နိုင်တော့သော်လည်း ရက် ၃၀ အတွင်း ပြန်ယူ၍ရနိုင်ပါသည်။ ဤအကောင့်နှင့်ပတ်သက်သည့် မှတ်တမ်းအားလုံးကို ပိတ်ပါမည်။
+      actions_description_html: ဤမှတ်တမ်းဖြေရှင်းရန်အတွက် မည်သည့်လုပ်ဆောင်ချက် ဆောင်ရွက်မည်ကို ဆုံးဖြတ်ပါ။ တိုင်ကြားထားသောအကောင့်ကို ဆောင်ရွက်လိုပါက <strong>Spam</strong> မရွေးချယ်ဘဲ အီးမေးလ်အကြောင်းကြားစာ ပေးပို့ရပါမည်။
+      actions_description_remote_html: ဤမှတ်တမ်းဖြေရှင်းရန်အတွက် မည်သည့်လုပ်ဆောင်ချက် ဆောင်ရွက်မည်ကို ဆုံးဖြတ်ပါ။ <strong>သင်၏</strong> ဆာဗာသည် အဝေးမှထိန်းချုပ်ထားသောအကောင့်နှင့် ဆက်သွယ်ပြီး အကြောင်းအရာကိုင်တွယ်ပုံပေါ်မှာသာ သက်ရောက်မှုရှိမည်ဖြစ်သည်။
+      add_to_report: အစီရင်ခံစာထပ်ထည့်ရန်
+      are_you_sure: သေချာပါသလား။
+      assign_to_self: ကျွန်ုပ်ကို တာဝန်ပေးရန်
+      assigned: စိစစ်သူကို တာဝန်ပေးရန်
+      by_target_domain: တိုင်ကြားထားသော အကောင့်၏ ဒိုမိန်း
+      cancel: ပယ်ဖျက်မည်
+      category: အမျိုးအစား
+      category_description_html: ဤအကောင့်နှင့်/သို့မဟုတ် အကြောင်းအရာကို အစီရင်ခံထားသည့် အကြောင်းရင်းကို အစီရင်ခံထားသည့်အကောင့်နှင့် ဆက်သွယ်မှုတွင် ကိုးကားပါမည်။
+      comment:
+        none: တစ်ခုမျှမရှိ
+      comment_description_html: "%{name} က နောက်ထပ်အချက်အလက်များ ပံ့ပိုးပေးနိုင်ရန်အတွက် ရေးသားခဲ့သည် -"
+      confirm: အတည်ပြုမည်
+      confirm_action: "@%{acct} ကို စိစစ်အကဲဖြတ်ခြင်းအား အတည်ပြုပါ"
+      created_at: အကြောင်းကြားပြီးပါပြီ
+      delete_and_resolve: ပို့စ်များကို ဖျက်ပါ
+      forwarded: ထပ်ဆင့်ပို့ပြီးပါပြီ
+      forwarded_to: "%{domain} သို့ ထပ်ဆင့်ပို့ထားသည်"
+      mark_as_resolved: ဖြေရှင်းပြီးကြောင်း အမှတ်အသားပြုပါ
+      mark_as_sensitive: သတိထားရသည်ဟု အမှတ်အသားပြုပါ
+      mark_as_unresolved: မဖြေရှင်းရသေးကြောင်း အမှတ်အသားပြုပါ
+      no_one_assigned: တစ်ယောက်မျှမရှိပါ
+      notes:
+        create: မှတ်စုထည့်ရန်
+        create_and_resolve: မှတ်စုဖြင့် ဖြေရှင်းပါ
+        create_and_unresolve: မှတ်စုဖြင့် ပြန်ဖွင့်ရန်
+        delete: ဖျက်ပါ
+        placeholder: မည်သည့်လုပ်ဆောင်ချက်များ ဆောင်ရွက်ခဲ့သည်ကို ဖော်ပြပါ သို့မဟုတ် အခြားဆက်စပ် အပ်ဒိတ်များကို ဖော်ပြပါ ...
+        title: မှတ်စုများ
+      notes_description_html: အခြားစိစစ်သူများနှင့် ကိုယ်တိုင်အတွက် မှတ်စုများ ထားခဲ့ပါ
+      processed_msg: 'အကြောင်းကြားမှု #%{id} ကို ဆောင်ရွက်ပြီးပါပြီ'
+      quick_actions_description_html: တိုင်ကြားထားသောအကြောင်းအရာများကြည့်ရှုရန်အတွက် လုပ်ဆောင်ချက်တစ်ခု ဆောင်ရွက်ပါ သို့မဟုတ် Scroll ဆွဲ၍ ကြည့်ပါ -
+      remote_user_placeholder: "%{instance} မှ အဝေးကနေအသုံးပြုသူ"
+      reopen: အစီရင်ခံစာပြန်ဖွင့်ရန်
+      report: "#%{id} အစီရင်ခံရန်"
+      reported_account: တိုင်ကြားထားသောအကောင့်
+      reported_by: မှ တိုင်ကြားထားသည်
+      resolved: ဖြေရှင်းပြီးပါပြီ
+      resolved_msg: မှတ်တမ်းကို ဖြေရှင်းပြီးပါပြီ။
+      skip_to_actions: လုပ်ဆောင်ချက်များသို့ ကျော်သွားရန်
+      status: အခြေအနေ
+      statuses: တိုင်ကြားထားသောအကြောင်းအရာ
+      statuses_description_html: စိတ်အ​နှောင့်အယှက်ဖြစ်​စေ​သောအကြောင်းအရာများကို အစီရင်ခံထားသောအကောင့်နှင့် ဆက်သွယ်ပြီး အ​ရေးယူ​ဆောင်ရွက်ပါမည်
+      summary:
+        action_preambles:
+          delete_html: သင်သည် <strong>@%{acct}</strong> ၏ ပို့စ်အချို့ကို <strong>ဖယ်ရှား</strong> တော့မည်ဖြစ်သည်။ ၎င်းတို့မှာ -
+          mark_as_sensitive_html: သင်သည် <strong>@%{acct}</strong> ၏ ပို့စ်အချို့ကို <strong>သတိထားရသောပို့စ်များ</strong> အဖြစ် အမှတ်အသားပြုတော့မည်ဖြစ်သည်။ ၎င်းတို့မှာ -
+          silence_html: သင်သည် <strong>@%{acct}</strong> ၏ အကောင့်ကို <strong>ကန့်သတ်</strong> တော့မည်ဖြစ်သည်။ ၎င်းမှာ -
+          suspend_html: သင်သည် <strong>@%{acct}</strong> ၏ အကောင့်ကို <strong>ဆိုင်းငံ့</strong> တော့မည်ဖြစ်သည်။ ၎င်းမှာ -
+        actions:
+          delete_html: စိတ်အနှောင့်အယှက်ဖြစ်စေသောပို့စ်များကို ဖယ်ရှားပါ
+          mark_as_sensitive_html: စိတ်အနှောင့်အယှက်ဖြစ်စေသောပို့စ်များ၏ မီဒီယာကို သတိထားရသောမီဒီယာအဖြစ် အမှတ်အသားပြုပါ
+          suspend_html: "<strong>@%{acct}</strong> ကို ဆိုင်းငံ့ထားသောကြောင့် ပရိုဖိုင်နှင့် အကြောင်းအရာများအား ဝင်ရောက်ခွင့်မရှိတော့သဖြင့် အပြန်အလှန် တုံ့ပြန်၍ မရတော့ခြင်း"
+        close_report: 'တိုင်ကြားစာ #%{id} ကို ဖြေရှင်းပြီးကြောင်း အမှတ်အသားပြုပါ'
+        close_reports_html: "<strong>@%{acct}</strong> နှင့်ပတ်သက်သည့် အစီရင်ခံစာ<strong>အားလုံး</strong> ကို ဖြေရှင်းပြီးကြောင်း အမှတ်အသားပြုပါ"
+        delete_data_html: "<strong>@%{acct}</strong> ၏ ပရိုဖိုင်နှင့် အကြောင်းအရာများကို ဆိုင်းငံ့ထားခြင်းမရှိပါက ယခုမှ ရက်ပေါင်း ၃၀ အတွင်း ဖျက်ရန်"
+        preview_preamble_html: "<strong>@%{acct}</strong> သည် အောက်ပါအကြောင်းအရာများကြောင့် သတိပေးချက်ကို လက်ခံရရှိပါမည် -"
+        send_email_html: သတိပေးချက် အီးမေးလ်တစ်စောင် <strong>@%{acct}</strong> ပေးပို့ပါ
+      target_origin: တိုင်ကြားထားသောအကောင့်၏ မူလအစ
+      title: မှတ်တမ်းများ
+      unassign: တာဝန်မှဖြုတ်ရန်
+      unknown_action_msg: အမည်မသိလုပ်ဆောင်ချက်- %{action}
+      unresolved: မဖြေရှင်းရသေးပါ
+      updated_at: ပြင်ဆင်ပြီးပါပြီ
+      view_profile: ပရိုဖိုင်ကြည့်ရန်
+    roles:
+      add_new: အခန်းကဏ္ဍထည့်ပါ
+      assigned_users:
+        other: "%{count} အသုံးပြုသူ"
+      categories:
+        administration: စီမံခန့်ခွဲခြင်း
+        devops: DevOps
+        invites: ဖိတ်ခေါ်ခြင်း
+        moderation: စိစစ်ခြင်း
+        special: အထူး
+      delete: ဖျက်ပါ
+      description_html: "<strong>အသုံးပြုသူအခန်းကဏ္ဍများ</strong>ဖြင့် Mastodon အသုံးပြုသူများ၏ မည်သည့်လုပ်ဆောင်ချက်များနှင့် နေရာများကိုမဆို သင် စိတ်ကြိုက်ပြင်ဆင်နိုင်ပါသည်။"
+      edit: "'%{name} ၏ အခန်းကဏ္ဍကို ပြင်ဆင်ရန်"
+      everyone: မူလသတ်မှတ်ထားသည့် ခွင့်ပြုချက်များ
+      permissions_count:
+        other: "%{count} ခွင့်ပြုချက်"
+      privileges:
+        administrator: စီမံသူ
+        administrator_description: ဤခွင့်ပြုချက် အသုံးပြုသူများအနေဖြင့် ခွင့်ပြုချက်တိုင်းကို ကျော်သွားနိုင်ပါမည်
+        delete_user_data: အသုံးပြုသူ၏အချက်အလက်ကို ဖျက်ပါ
+        delete_user_data_description: အခြားအသုံးပြုသူများ၏ အချက်အလက်များကို နှောင့်နှေးခြင်းမရှိဘဲ အသုံးပြုသူများအား  ဖျက်ခွင့်ပြုသည်
+        invite_users: အသုံးပြုသူများကို ဖိတ်ခေါ်ရန်
+        invite_users_description: ဆာဗာသို့ လူသစ်များဖိတ်ခေါ်ရန်အတွက် အသုံးပြုသူအား ခွင့်ပြုရန်
+        manage_announcements: ကြေညာချက်များကို စီမံပါ
+        manage_announcements_description: ဆာဗာပေါ်တွင် ကြေညာချက်များစီမံရန်အတွက် အသုံးပြုသူအား ခွင့်ပြုရန်
+        manage_appeals: အယူခံဝင်ထားခြင်းများကို စီမံပါ
+        manage_appeals_description: စိစစ်အရေးယူမှုများအပေါ် အယူခံဝင်မှုများ ပြန်လည်သုံးသပ်ရန်အတွက် အသုံးပြုသူများအား ခွင့်ပြုရန်
+        manage_blocks: ပိတ်ပင်ထားမှုများကို စီမံပါ
+        manage_blocks_description: အသုံးပြုသူများအား အီးမေးလ်ဝန်ဆောင်မှုပေးသူများနှင့် IP လိပ်စာများကို ပိတ်ဆို့ရန် ခွင့်ပြုသည်
+        manage_custom_emojis: စိတ်ကြိုက်အီမိုဂျီများကို ပြင်ဆင်ရန်
+        manage_custom_emojis_description: အသုံးပြုသူများအား ဆာဗာပေါ်တွင် စိတ်ကြိုက်အီမိုဂျီများကို စီမံခန့်ခွဲရန် ခွင့်ပြုသည်။
+        manage_federation: ဖက်ဒီကို စီမံပါ
+        manage_federation_description: အသုံးပြုသူများအား အခြားဒိုမိန်းများနှင့် ပိတ်ပင်ခြင်း၊ ဖက်ဒီခွင့်ပြုခြင်းနှင့် ပေးပို့နိုင်မှုကို ထိန်းချုပ်ခြင်းတို့ကို ခွင့်ပြုသည်။
+        manage_invites: ဖိတ်ခေါ်ခြင်းကို စီမံရန်
+        manage_invites_description: အသုံးပြုသူများအား ဖိတ်ခေါ်ထားသည့်လင့်များကို ရှာဖွေကြည့်ရှုပြီး ပိတ်ရန် ခွင့်ပြုသည်
+        manage_reports: အစီရင်ခံစာများကို စီမံပါ
+        manage_reports_description: အသုံးပြုသူများအား အစီရင်ခံစာများကို ပြန်လည်သုံးသပ်ရန်နှင့် ၎င်းတို့နှင့် ဆန့်ကျင်သည့် စိစစ်အရေးယူမှုများကို လုပ်ဆောင်ရန် ခွင့်ပြုသည်
+        manage_roles: အခန်းကဏ္ဍများကို စီမံပါ
+        manage_roles_description: အသုံးပြုသူများအား ၎င်းတို့၏အောက်တွင်ရှိသော အခန်းကဏ္ဍများကို စီမံခန့်ခွဲရန်နှင့် သတ်မှတ်ရန် ခွင့်ပြုထားသည်
+        manage_rules: စည်းမျဉ်းများကို စီမံပါ
+        manage_rules_description: အသုံးပြုသူများအား ဆာဗာစည်းမျဉ်းများကို ပြောင်းလဲခွင့် ပြုထားသည်
+        manage_settings: သတ်မှတ်ချက်များကို စီမံပါ
+        manage_settings_description: အသုံးပြုသူများကို ဆိုက်သတ်မှတ်ချက်များ ပြောင်းလဲခွင့် ပြုရန်
+        manage_taxonomies: အမျိုးအစားခွဲခြားပါ
+        manage_taxonomies_description: ခေတ်စားနေသော အကြောင်းအရာများကို ပြန်လည်သုံးသပ်ရန်နှင့် hashtag သတ်မှတ်ချက်များကို ပြင်ဆင်ရန်အတွက် အသုံးပြုသူများကို ခွင့်ပြုသည်
+        manage_user_access: အသုံးပြုသူဝင်ရောက်မှုကို စီမံပါ
+        manage_user_access_description: အသုံးပြုသူများကို အခြားအသုံးပြုသူများ၏ နှစ်ဆင့်ခံလုံခြုံရေးစနစ်ကို ပိတ်ရန်၊ ၎င်းတို့၏ အီးမေးလ်လိပ်စာကို ပြောင်းလဲရန်နှင့် စကားဝှက်ကို ပြန်လည်သတ်မှတ်ရန် ခွင့်ပြုသည်
+        manage_users: အသုံးပြုသူများကို စီမံပါ
+        manage_users_description: အသုံးပြုသူများအား အခြားအသုံးပြုသူများ၏ အသေးစိတ်အချက်အလက်များကို ကြည့်ရှုနိုင်ပြီး ၎င်းတို့နှင့် ဆန့်ကျင်သည့် စိစစ်အရေးယူမှုများကို လုပ်ဆောင်ခွင့်ပြုသည်
+        manage_webhooks: Webhooks ကို စီမံပါ
+        manage_webhooks_description: စီမံခန့်ခွဲရေးဆိုင်ရာ ဖြစ်ရပ်များအတွက် အသုံးပြုသူများအား webhook စနစ်ထည့်သွင်းရန် ခွင့်ပြုထားသည်
+        view_audit_log: စာရင်းမှတ်တမ်းကို ကြည့်ရန်
+        view_audit_log_description: အသုံးပြုသူများကို ဆာဗာပေါ်တွင် စီမံခန့်ခွဲရေးဆိုင်ရာ လုပ်ဆောင်ချက်မှတ်တမ်းကို ကြည့်ရှုခွင့်ပြုသည်။
+        view_dashboard: ဒက်ရှ်ဘုတ်မြင်ကွင်းကို ကြည့်ရန်
+        view_dashboard_description: အသုံးပြုသူများကို ဒက်ရှ်ဘုတ်မြင်ကွင်းနှင့် အမျိုးမျိုးသော ဆိုဒ်နှင့်ဆိုင်သောဂဏန်းများအား ဝင်ရောက်ကြည့်ရှုခွင့်ပြုသည်
+        view_devops: DevOps
+        view_devops_description: အသုံးပြုသူများကို Sidekiq နှင့် pgHero ဒက်ရှ်ဘုတ်မြင်ကွင်းများသို့ ဝင်ရောက်ခွင့်ပြုသည်
+      title: အခန်းကဏ္ဍများ
+    rules:
+      add_new: စည်းမျဉ်းထည့်ပါ
+      delete: ဖျက်ပါ
+      edit: စည်းမျဉ်းကို ပြင်ဆင်မည်
+      empty: ဆာဗာစည်းမျဉ်းများကို မသတ်မှတ်ရသေးပါ။
+      title: ဆာဗာစည်းမျဉ်းများ
+    settings:
+      about:
+        manage_rules: ဆာဗာစည်းမျဉ်းများကို စီမံရန်
+        title: အကြောင်း
+      appearance:
+        preamble: Mastodon ၏ ဝဘ်ပုံစံကို စိတ်ကြိုက်ပြင်ဆင်ပါ။
+        title: ပုံပန်းသဏ္ဌာန်
+      branding:
+        title: ခေါင်းစဉ်တပ်ခြင်း
+      content_retention:
+        preamble: Mastodon တွင် အသုံးပြုသူဖန်တီးထားသော အကြောင်းအရာများ မည်သို့သိမ်းဆည်းမည်ကို ထိန်းချုပ်ပါ။
+        title: အကြောင်းအရာ ဆက်လက်ရှိနေခြင်း
+      default_noindex:
+        desc_html: ဤသတ်မှတ်ချက်ကို ကိုယ်တိုင်မပြောင်းရသေးသော အသုံးပြုသူအားလုံးအပေါ် သက်ရောက်မှုရှိသည်
+        title: ပုံမှန်အားဖြင့် ရှာဖွေမှုအညွှန်းကိန်းမှ သုံးစွဲသူများကို ဖယ်ထုတ်ပါ
+      discovery:
+        follow_recommendations: အကြံပြုချက်များကို စောင့်ကြည့်ပါ
+        profile_directory: ပရိုဖိုင်လမ်းညွှန်
+        public_timelines: အများမြင်စာမျက်နှာ
+        publish_discovered_servers: ရှာဖွေတွေ့ရှိထားသော ဆာဗာများကို ထုတ်ပြန်ပါ
+        publish_statistics: စာရင်းဇယားထုတ်ပြန်မည်
+        title: ရှာဖွေတွေ့ရှိမှု
+        trends: လက်ရှိခေတ်စားမှုများ
+      domain_blocks:
+        all: လူတိုင်း
+        disabled: မည်သူ့ကိုမျှ
+        users: အကောင့်ဝင်ထားသော ပြည်တွင်းအသုံးပြုသူများအတွက်
+      registrations:
+        preamble: သင့်ဆာဗာတွင် အကောင့်တစ်ခုမည်သူဖန်တီးနိုင်သည်ကို ထိန်းချုပ်ပါ။
+        title: စာရင်းသွင်းထားခြင်းများ
+      registrations_mode:
+        modes:
+          approved: အကောင့်ဖွင့်ရန်အတွက် အတည်ပြုချက် လိုအပ်ပါသည်
+          none: မည်သူမျှ အကောင့်ဖွင့်၍မရပါ
+          open: မည်သူမဆို အကောင့်ဖွင့်နိုင်ပါသည်
+      title: ဆာဗာသတ်မှတ်ချက်များ
+    site_uploads:
+      delete: တင်ထားသောဖိုင်ဖျက်ရန်
+      destroyed_msg: ဆိုက်အပ်လုဒ်ကို အောင်မြင်စွာ ဖျက်လိုက်ပါပြီ။
+    statuses:
+      account: ရေးသားသူ
+      application: အက်ပလီကေးရှင်း
+      back_to_account: အကောင့်စာမျက်နှာသို့ ပြန်သွားရန်
+      back_to_report: အစီရင်ခံစာ စာမျက်နှာသို့ ပြန်သွားရန်
+      batch:
+        remove_from_report: တိုင်ကြားစာမှ ဖယ်ရှားပါ
+        report: တိုင်ကြားစာ
+      deleted: ဖျက်ပြီးပါပြီ
+      favourites: အကြိုက်ဆုံးများ
+      history: ဗားရှင်းမှတ်တမ်း
+      in_reply_to: ထံ အကြောင်းပြန်ကြားခြင်း
+      language: ဘာသာစကား
+      media:
+        title: မီဒီယာ
+      metadata: Metadata
+      no_status_selected: မည်သည့်ပို့စ်ကိုမျှ ရွေးချယ်ထားခြင်းမရှိသောကြောင့် ပြောင်းလဲခြင်းမရှိပါ
+      open: ပို့စ်ဖွင့်ရန်
+      original_status: မူရင်းပို့စ်
+      reblogs: Reblog များ
+      status_changed: ပို့စ်ပြောင်းပြီးပါပြီ
+      title: အကောင့်ပို့စ်များ
+      trending: လက်ရှိခေတ်စားနေခြင်း
+      visibility: မြင်နိုင်မှု
+      with_media: မီဒီယာနှင့်အတူ
+    strikes:
+      actions:
+        delete_statuses: "%{name} မှ %{target} ၏ ပို့စ်များကို ဖျက်ခဲ့သည်"
+        disable: "%{name} က %{target} ၏ အကောင့်ကို ရပ်ထားသည်"
+        mark_statuses_as_sensitive: "%{name} က %{target} ၏ ပို့စ်များကို သတိထားရသောပို့စ်များအဖြစ် အမှတ်အသားပြုထားသည်"
+        none: "%{name} မှ %{target} သို့ သတိပေးချက်တစ်ခု ပေးပို့ခဲ့သည်"
+        sensitive: "%{name} က %{target} ၏ အကောင့်ကို သတိထားရသောအကောင့်အဖြစ် အမှတ်အသားပြုထားသည်"
+        silence: "%{name} က %{target} ၏ အကောင့်ကို ကန့်သတ်ထားသည်"
+        suspend: "%{name} က %{target} ၏ အကောင့်ကို ဆိုင်းငံ့ထားသည်"
+      appeal_approved: အတည်ပြုခဲ့ပြီး
+      appeal_pending: အတည်ပြုမှုဆိုင်းငံ့ထားခြင်း
+      appeal_rejected: တင်ပြခြင်းကို ပယ်ချခဲ့သည်
+    system_checks:
+      database_schema_check:
+        message_html: ဆိုင်းငံ့ထားသော ဒေတာဘေ့စ် ပြောင်းရွှေ့မှုများ ရှိပါသည်။ အပလီကေးရှင်းသည် မျှော်လင့်ထားသည့်အတိုင်း လုပ်ဆောင်ကြောင်း သေချာစေရန် ၎င်းတို့ကို လုပ်ဆောင်ပါ။
+      elasticsearch_running_check:
+        message_html: Elasticsearch သို့ ချိတ်ဆက်၍မရပါ။ ၎င်းသည် အလုပ်လုပ်နေသလား၊ သို့မဟုတ် စာသားအပြည့်အစုံရှာဖွေမှုကို ပိတ်ပါ။
+      elasticsearch_version_check:
+        message_html: သဟဇာတမဖြစ်သော Elasticsearch ဗားရှင်း- %{value}
+        version_comparison: Elasticsearch %{running_version} သည် %{required_version} လိုအပ်နေချိန်တွင် လုပ်ဆောင်နေသည်
+      rules_check:
+        action: ဆာဗာစည်းမျဉ်းများကို စီမံရန်
+        message_html: သင်သည် မည်သည့်ဆာဗာစည်းမျဉ်းများကိုမျှ မသတ်မှတ်ထားပါ။
+      sidekiq_process_check:
+        message_html: "%{value} တန်းစီ(s) အတွက် Sidekiq လုပ်ငန်းစဉ် မရှိပါ။ သင်၏ Sidekiq ဖွဲ့စည်းမှုကို ပြန်လည်သုံးသပ်ပါ။"
+      upload_check_privacy_error:
+        action: နောက်ထပ်အချက်အလက်များအတွက် ဤနေရာတွင် ကြည့်ပါ
+        message_html: "<strong>သင်၏ဝဘ်ဆာဗာကို မှားယွင်းစွာပြုလုပ်ထားသည်။ သင့်အသုံးပြုသူများ၏ လျှို့ဝှက်ချက်များသည် အန္တရာယ်ရှိသည်။</strong>"
+      upload_check_privacy_error_object_storage:
+        action: နောက်ထပ်အချက်အလက်များအတွက် ဤနေရာတွင် ကြည့်ပါ
+        message_html: "<strong>သင်၏ Object သိုလှောင်မှုအား မှားယွင်းစွာ စီစဉ်သတ်မှတ်ထားပါသည်။ သင့်အသုံးပြုသူများ၏ လျှို့ဝှက်ချက်များသည် အန္တရာယ်ရှိသည်။</strong>"
+    tags:
+      review: အခြေအနေသုံးသပ်ရန်
+      updated_msg: ဟက်ရှ်တဂျ်သတ်မှတ်ချက်များကို အပ်ဒိတ်လုပ်ပြီးပါပြီ
+    title: စီမံခန့်ခွဲခြင်း
+    trends:
+      allow: ခွင့်ပြု
+      approved: အတည်ပြုပြီးပါပြီ
+      disallow: ခွင့်မပြု
+      links:
+        allow: လင့်ခ်ကို ခွင့်ပြုရန်
+        allow_provider: ပို့စ်တင်သူကိုခွင့်ပြုရန်
+        description_html: "၎င်းတို့သည် သင့်ဆာဗာမှ ပို့စ်များကို မြင်သည့် အကောင့်များမှ လောလောဆယ်တွင် အများအပြားမျှဝေနေသည့် လင့်ခ်များဖြစ်သည်။ ၎င်းသည် သင့်အသုံးပြုသူများအား ကမ္ဘာပေါ်တွင် ဖြစ်ပျက်နေသည့်အရာများကို ရှာဖွေရန် ကူညီပေးနိုင်ပါသည်။ ထုတ်ဝေသူအား သင်အတည်မပြုမချင်း လင့်ခ်များကို လူသိရှင်ကြားပြသခြင်းမရှိပါ။ တစ်ဦးချင်း လင့်ခ်များကို သင် ခွင့်ပြုနိုင်သည် သို့မဟုတ် ငြင်းပယ်နိုင်သည်။"
+        disallow: လင့်ခ်ကို ခွင့်မပြုရန်
+        disallow_provider: ပို့စ်တင်သူကို ခွင့်မပြုရန်
+        no_link_selected: မည်သည့်လင့်ခ်ကိုမျှ ရွေးချယ်ထားခြင်းမရှိသောကြောင့် ပြောင်းလဲခြင်းမရှိပါ
+        publishers:
+          no_publisher_selected: မည်သည့်ပို့စ်တင်သူကိုမျှ ရွေးချယ်ထားခြင်းမရှိသောကြောင့် ပြောင်းလဲခြင်းမရှိပါ
+        shared_by_over_week:
+          other: ပြီးခဲ့သည့်အပတ်တွင် လူ %{count} ဦး မှ မျှဝေခဲ့သည်
+        title: လက်ရှိခေတ်စားနေသော လင့်များ
+        usage_comparison: မနေ့က %{yesterday} နှင့် နှိုင်းယှဉ်၍ ယနေ့ %{today} ကြိမ် မျှဝေခဲ့သည်
+      not_allowed_to_trend: ခေတ်စားနေသည့်အကြောင်းအရာပြုလုပ်ရန် ခွင့်မပြုပါ
+      only_allowed: သာခွင့်ပြုသည်
+      pending_review: ဆိုင်းငံ့ထားသော သုံးသပ်ချက်
+      preview_card_providers:
+        allowed: ဤပို့စ်တင်သူထံမှ လင့်ခ်များ ခေတ်စားနိုင်သည်
+        description_html: "၎င်းတို့သည် သင့်ဆာဗာပေါ်တွင် မကြာခဏ လင့်ခ်များကို မျှဝေလေ့ရှိသည့် ဒိုမိန်းများဖြစ်သည်။ လင့်ခ်၏ဒိုမိန်းကို အတည်ပြုမထားပါက လင့်ခ်များသည် လူသိရှင်ကြား လမ်းကြောင်းပေါ်ရှိလိမ့်မည်မဟုတ်ပါ။ သင့်ခွင့်ပြုချက် (သို့မဟုတ် ငြင်းပယ်ခြင်း) သည် ဒိုမိန်းခွဲများသို့ အကျုံးဝင်ပါသည်။"
+        rejected: ဤထုတ်ဝေသူထံမှ လင့်ခ်များ ခေတ်စားလာမည်မဟုတ်ပါ။
+        title: ပို့စ်တင်သူများ
+      rejected: ဖယ်ရှားပြီးပါပြီ
+      statuses:
+        allow: ပို့စ်တင်ခွင့်ပြုရန်
+        allow_account: ပို့စ်တင်သူကို ခွင့်ပြုပါ
+        description_html: ဤအရာများသည် သင့်ဆာဗာမှ သိရှိထားသည့် ပို့စ်များဖြစ်ပြီး လက်ရှိတွင် မျှဝေလျက်ရှိပြီး ယခုအချိန်တွင် လူကြိုက်များသော ပို့စ်များဖြစ်သည်။ ၎င်းသည် သင်၏အသစ်နှင့် ပြန်လာသောအသုံးပြုသူများကို လိုက်ကြည့်ရန် နောက်ထပ်လူများကို ရှာဖွေရန် ကူညီပေးနိုင်သည်။ စာရေးဆရာကို သင်အတည်ပြုသည့်တိုင်အောင် မည်သည့်ပို့စ်ကိုမျှ လူသိရှင်ကြားမပြသဘဲ၊ စာရေးသူက ၎င်းတို့၏အကောင့်ကို အခြားသူများထံ အကြံပြုခွင့်ပြုသည်။ တစ်ဦးချင်း ပို့စ်များကို ခွင့်ပြုနိုင်သည် သို့မဟုတ် ငြင်းပယ်နိုင်သည်။
+        disallow: ပို့စ်ကို တင်ခွင့်မပြုရန်
+        disallow_account: ပို့စ်တင်သူကို ခွင့်မပြုပါနဲ့
+        no_status_selected: မည်သည့်ခေတ်စားနေသောပို့စ်ကိုမျှ ရွေးချယ်ထားခြင်းမရှိသောကြောင့် ပြောင်းလဲခြင်းမရှိပါ
+        not_discoverable: စာရေးသူသည် ရှာဖွေတွေ့ရှိနိုင်မှုကို ရွေးချယ်မထားပါ။
+        shared_by:
+          other: မျှဝေမှုနှင့် နှစ်သက်မှု %{friendly_count} ကြိမ်
+        title: လက်ရှိခေတ်စားနေသော ပို့စ်များ
+      tags:
+        current_score: "%{score} လက်ရှိရမှတ်"
+        dashboard:
+          tag_accounts_measure: အများဆုံးအသုံပြုမှုများ
+          tag_languages_dimension: အသုံးများသည့်ဘာသာစကားများ
+          tag_servers_dimension: အသုံးများသည့်ဆာဗာများ
+          tag_servers_measure: မတူညီသောဆာဗာများ
+          tag_uses_measure: စုစုပေါင်းအသုံးပြုမှု
+        description_html: "၎င်းတို့သည် သင့်ဆာဗာမြင်နေရသည့် ပို့စ်အများအပြားတွင် လက်ရှိမြင်နေရသည့် hashtag များဖြစ်သည်။ ၎င်းသည် သင့်အသုံးပြုသူများအား ယခုအချိန်တွင် လူအများပြောနေသည့်အရာများကို သိရှိနိုင်ရန် ကူညီပေးနိုင်ပါသည်။ ၎င်းတို့ကို သင်အတည်မပြုမချင်း hashtags များကို လူသိရှင်ကြား ပြသမည်မဟုတ်ပါ။"
+        listable: အကြံပြုနိုင်ပါသည်
+        no_tag_selected: မည်သည့်တဂျ်ကိုမျှ ရွေးချယ်ထားခြင်းမရှိသောကြောင့် ပြောင်းလဲခြင်းမရှိပါ
+        not_listable: အကြံပြုမည်မဟုတ်ပါ
+        not_trendable: လက်ရှိခေတ်စားနေသာပို့စ်များအောက်တွင် ပေါ်လာမည်မဟုတ်ပါ
+        not_usable: အသုံးမပြုနိုင်ပါ
+        title: လက်ရှိခေတ်စားနေသော hashtag များ
+        trendable: လက်ရှိခေတ်စားနေသာပို့စ်များအောက်တွင် ပေါ်လာမည်
+        trending_rank: "#%{rank} ခေတ်စားနေခြင်း"
+        usable: အသုံးပြုနိုင်သည်
+        usage_comparison: မနေ့က %{yesterday} နှင့် နှိုင်းယှဉ်လျှင် ယနေ့ %{today} ကြိမ် အသုံးပြုခဲ့သည်
+        used_by_over_week:
+          other: ပြီးခဲ့သည့် ရက်သတ္တပတ်အတွင်း လူ %{count} ဦး အသုံးပြုခဲ့သည်။
+      title: လက်ရှိခေတ်စားမှုများ
+      trending: လက်ရှိခေတ်စားနေခြင်း
+    warning_presets:
+      add_new: အသစ်ထည့်ပါ
+      delete: ဖျက်ပါ
+      edit_preset: ကြိုသတိပေးချက်ကို ပြင်ဆင်ပါ
+      empty: ကြိုသတိပေးချက်များကို မသတ်မှတ်ရသေးပါ။
+      title: ကြိုသတိပေးချက်များကို စီမံပါ
+    webhooks:
+      add_new: ဆုံးမှတ် ထည့်ပါ
+      delete: ဖျက်ပါ
+      description_html: "<strong>webhook</strong> သည် Mastodon အား ရွေးချယ်ထားသော အစီအစဉ်များအကြောင်း သင့်ကိုယ်ပိုင်အက်ပ်လီကေးရှင်းသို့ <strong>အချိန်နှင့်တစ်ပြေးညီ အသိပေးချက်များကို တွန်းပို့နိုင်သည်၊ ထို့ကြောင့် သင့်အပလီကေးရှင်းသည် <strong>တုံ့ပြန်မှုများကို အလိုအလျောက်စတင်နိုင်သည်</strong>။"
+      disable: ပိတ်ပါ
+      disabled: ပိတ်ထားသည်
+      edit: ဆုံးမှတ် ပြင်ဆင်ပါ
+      empty: မည်သည့် webhook endpoints မှ မစီစဉ်ရသေးပါ။
+      enable: ဖွင့်ပါ
+      enabled: လက်ရှိ
+      enabled_events:
+        other: ဖွင့်ထားသောအစီအစဉ်များ %{count} ခု
+      events: ပွဲအစီအစဉ်များ
+      new: webhook အသစ်
+      secret: လျှို့ဝှက်လက်မှတ်ထိုးခြင်း
+      status: အခြေအနေ
+      title: Webhook များ
+      webhook: Webhook
+  admin_mailer:
+    new_appeal:
+      actions:
+        delete_statuses: "၎င်းတို့၏ ပို့စ်များကို ဖျက်ရန်"
+        disable: "၎င်းတို့အကောင့်များအား ရပ်ထားရန်"
+        mark_statuses_as_sensitive: "၎င်းတို့၏ ပို့စ်များကို သတိထားရသော ပို့စ်များအဖြစ် အမှတ်အသားပြုရန်"
+        none: သတိပေးချက်
+        sensitive: "၎င်းတို့၏ အကောင့်များကို သတိထားရသော အကောင့်များအဖြစ် အမှတ်အသားပြုရန်"
+        silence: "၎င်းတို့၏ ပို့စ်များကို ကန့်သတ်ရန်"
+        suspend: "၎င်းတို့၏ အကောင့်ကို ရပ်ဆိုင်းရန်"
+      next_steps: စိစစ်ဆုံးဖြတ်ချက်ပြန်ဖျက်ရန်အတွက် တင်ပြနိုင်သည် သို့မဟုတ် ၎င်းကို လျစ်လျူရှုနိုင်သည်။
+      subject: "%{username} က %{instance} တွင် စိစစ်ဆုံးဖြတ်ချက်ကို တင်ပြနေသည်"
+    new_pending_account:
+      body: အကောင့်သစ်၏အသေးစိတ်ကို အောက်တွင်ဖော်ပြထားသည်။ ၎င်းကို အတည်ပြုနိုင်သည် သို့မဟုတ် ငြင်းပယ်နိုင်သည်။
+      subject: " (%{username}) %{instance} ပေါ်ရှိ ပြန်လည်သုံးသပ်ရမည့် အကောင့်သစ်"
+    new_report:
+      body: "%{reporter} က %{target} ကို တိုင်ကြားခဲ့သည်"
+      body_remote: "%{domain} မှ တစ်စုံတစ်ယောက်က %{target} ကို တိုင်ကြားခဲ့သည်"
+      subject: "%{instance} (#%{id}) အတွက် တိုင်ကြားစာအသစ်"
+    new_trends:
+      body: အောက်ပါအရာများကို အများကိုမပြမီ ပြန်လည်သုံးသပ်ရန် လိုအပ်သည် -
+      new_trending_links:
+        title: လက်ရှိခေတ်စားနေသော လင့်များ
+      new_trending_statuses:
+        title: လက်ရှိခေတ်စားနေသော ပို့စ်များ
+      new_trending_tags:
+        no_approved_tags: လက်ရှိတွင် အတည်ပြုထားသော ခေတ်စားနေသည့် hashtag များမရှိပါ။
+        title: လက်ရှိခေတ်စားနေသော hashtag များ
+      subject: "%{instance} တွင် ပြန်လည်သုံးသပ်ရမည့် ခေတ်စားနေသောပို့စ်အသစ်များ"
+  aliases:
+    add_new: နာမည်တူတစ်ခု ဖန်တီးမည်
+    created_msg: နာမည်တူတစ်ခုကို ဖန်တီးပြီးပါပြီ။ အကောင့်ဟောင်းမှ စတင်ရွှေ့နိုင်ပါပြီ။
+    deleted_msg: နာမည်တူကို ဖယ်ရှားထားပြီးပါပြီ။ ထိုအကောင့်မှ ဤအကောင့်သို့ပြောင်းရန် မဖြစ်နိုင်တော့ပါ။
+    empty: သင့်တွင် နာမည်တူများ မရှိပါ။
+    remove: နာမည်တူများကို လင့်ခ်ဖြုတ်ပါ
+  appearance:
+    advanced_web_interface: အဆင့်မြင့်ဝဘ်ပုံစံ
+    animations_and_accessibility: လှုပ်ရှားမှုဆိုင်ရာများ
+    confirmation_dialogs: အတည်ပြုချက် ဒိုင်ယာလော့ခ်များ
+    discovery: ရှာဖွေတွေ့ရှိမှု
+    localization:
+      body: Mastodon ကို စေတနာ့ဝန်ထမ်းများမှ ဘာသာပြန်ထားပါသည်။
+      guide_link: https://crowdin.com/project/mastodon
+      guide_link_text: လူတိုင်းပါဝင်ကူညီနိုင်ပါတယ်။
+    sensitive_content: သတိထားရသော အကြောင်းအရာ
+    toot_layout: ပို့စ်အပြင်အဆင်
+  application_mailer:
+    notification_preferences: အီးမေးလ် သတ်မှတ်ချက်များကို ပြောင်းပါ
+    salutation: "%{name}"
+    settings: အီးမေးလ် သတ်မှတ်ချက်များကို ပြောင်းပါ - %{link}
+    view: ကြည့်ရှုရန် -
+    view_profile: ပရိုဖိုင်ကိုကြည့်ရန်
+    view_status: ပို့စ်ကိုကြည့်ရန်
+  applications:
+    created: အက်ပလီကေးရှင်းကို ဖန်တီးပြီးပါပြီ
+    destroyed: အက်ပလီကေးရှင်းကို ဖျက်ပြီးပါပြီ
+    logout: ထွက်မယ်
+    regenerate_token: ဝင်ရောက်ခွင့် တိုကင်ကို ပြန်ထုတ်ပါ
+    token_regenerated: အသုံးပြုခွင့်တိုကင်ကို ပြန်လည်ထုတ်ပေးသည်
+    warning: ဤအချက်အလက်ကို သတိထားပါ။ မည်သူ့ကိုမျှ မမျှဝေပါနှင့်။
+    your_token: သင့် အသုံးပြုခွင့်တိုကင်
+  auth:
+    apply_for_account: အကောင့်တစ်ခုတောင်းဆိုပါ
+    change_password: စကားဝှက်
+    confirmations:
+      wrong_email_hint: ထိုအီးမေးလ်လိပ်စာ မမှန်ပါက အကောင့်သတ်မှတ်ချက်များတွင် ပြောင်းလဲနိုင်သည်။
+    delete_account: အကောင့်ဖျက်ပါ
+    delete_account_html: သင့်အကောင့်ဖျက်လိုပါက<a href="%{path}">ဤနေရာတွင် ဆက်လက်လုပ်ဆောင်နိုင်သည်</a>။ အတည်ပြုချက်တောင်းပါမည်။
+    description:
+      prefix_invited_by_user: "@%{name} က Mastodon ၏ ဆာဗာတွင် ပါဝင်ရန် သင့်ကို ဖိတ်ခေါ်ထားသည်။"
+      prefix_sign_up: ယနေ့တွင် Mastodon ၌ စာရင်းသွင်းလိုက်ပါ။
+      suffix: အကောင့်တစ်ခုဖြင့် မည်သည့် Mastodon ဆာဗာများမှ အသုံးပြုသူများနှင့်မဆို စောင့်ကြည့်နိုင်၊ ပို့စ်အသစ်များ တင်နိုင်ပြီး မက်ဆေ့ချ်များ ပေးပို့နိုင်ပါသည်။
+    didnt_get_confirmation: အတည်ပြုချက်ညွှန်ကြားချက်များကို မရရှိခဲ့ဘူးလား။
+    dont_have_your_security_key: သင့်တွင် လုံခြုံရေးကီး မရှိဘူးလား။
+    forgot_password: သင့်စကားဝှက် မေ့နေပါသလား။
+    link_to_otp: သင့်ဖုန်းမှ နှစ်ဆင့်ခံလုံခြုံရေးကုဒ် သို့မဟုတ် ပြန်လည်ရယူရေးကုဒ် ထည့်ပါ
+    link_to_webauth: လုံခြုံရေးကီး အသုံးပြုပါ
+    log_in_with: ဖြင့် ဝင်ရောက်ပါ
+    login: အကောင့်ဝင်ရန်
+    logout: ထွက်မယ်
+    migrate_account: အခြားအကောင့်တစ်ခုသို့ ရွှေ့ရန်
+    migrate_account_html: ဤအကောင့်ကို အခြားအကောင့်သို့ ပြန်ညွှန်းလိုပါက <a href="%{path}">ဤနေရာတွင် စီစဉ်သတ်မှတ်နိုင်သည်</a>။
+    or_log_in_with: သို့မဟုတ် အကောင့်ဖြင့် ဝင်ရောက်ပါ
+    privacy_policy_agreement_html: <a href="%{privacy_policy_path}" target="_blank">ကိုယ်ရေးအချက်အလက်မူဝါဒ</a> ကို ဖတ်ပြီး သဘောတူလိုက်ပါပြီ
+    providers:
+      cas: CAS
+      saml: SAML
+    register: အကောင့်ဖွင့်ရန်
+    registration_closed: "%{instance} သည် အဖွဲ့ဝင်အသစ်များကို လက်ခံထားခြင်းမရှိပါ"
+    resend_confirmation: အတည်ပြုချက်ညွှန်ကြားမှုများကို ပြန်ပို့ရန်
+    rules:
+      accept: လက်ခံပါ
+      back: နောက်သို့
+      title: အခြေခံစည်းမျဉ်းအချို့
+    security: လုံခြုံရေး
+    set_new_password: စကားဝှက်အသစ် သတ်မှတ်ပါ။
+    setup:
+      email_below_hint_html: အောက်ဖော်ပြပါ အီးမေးလ်လိပ်စာ မှားယွင်းနေပါက ဤနေရာတွင် ပြောင်းလဲနိုင်ပြီး အတည်ပြုအီးမေးလ်အသစ် လက်ခံရရှိမည်ဖြစ်ပါသည်။
+      email_settings_hint_html: အတည်ပြုအီးမေးလ်ကို %{email} သို့ ပေးပို့ခဲ့သည်။ ထိုအီးမေးလ်လိပ်စာ မှားယွင်းနေပါက ၎င်းကို အကောင့်သတ်မှတ်ချက်များတွင် ပြောင်းလဲနိုင်သည်။
+      title: သတ်မှတ်
+    sign_in:
+      preamble_html: သင်၏ <strong>%{domain}</strong> အထောက်အထားများဖြင့် ဝင်ရောက်ပါ။ သင့်အကောင့်ကို အခြားဆာဗာတစ်ခုတွင် ဖွင့်ထားပါက ဤနေရာ၌ အကောင့်ဝင်ရောက်နိုင်မည်မဟုတ်ပါ။
+      title: "%{domain} သို့ အကောင့်ဝင်ရန်"
+    sign_up:
+      title: "%{domain} တွင် ထည့်သွင်းရန်။"
+    status:
+      account_status: အကောင့်အခြေအနေ
+      confirming: အီးမေးလ်အတည်ပြုချက် အပြီးသတ်ရန် စောင့်ဆိုင်းခြင်း
+      functional: သင့်အကောင့်မှာ အပြည့်အဝလုပ်ဆောင်နေပါပြီ။
+      redirecting_to: သင့်အကောင့်မှာ လက်ရှိတွင် %{acct} သို့ ပြန်ညွှန်းနေသောကြောင့် သုံးစွဲ၍မရပါ။
+      view_strikes: သင့်အကောင့်ကို ဆန့်ကျင်သည့် ယခင်ကလုပ်ဆောင်ချက်များကို ကြည့်ပါ
+    use_security_key: လုံခြုံရေးကီးကို သုံးပါ
+  authorize_follow:
+    already_following: သင်သည် ဤအကောင့်ကို စောင့်ကြည့်နေပြီဖြစ်ပါသည်
+    already_requested: သင်သည် ထိုအကောင့်စောင့်ကြည့်ရန် တောင်းဆိုမှုတစ်ခု ပေးပို့ခဲ့ပြီးပါပြီ
+    error: ကံမကောင်းစွာဖြင့် အဝေးမှထိန်းချုပ်သောအကောင့်ရှာဖွေရာတွင် အမှားအယွင်းတစ်ခုရှိခဲ့သည်
+    follow: စောင့်ကြည့်မယ်
+    follow_request: သင်သည် စောင့်ကြည့်မည် တောင်းဆိုချက်တစ်ခု ပေးပို့ထားသည်-
+    following: သင် ယခု အောက်ပါအတိုင်း လုပ်ဆောင်နေပါသည် -
+    post_follow:
+      close: သို့မဟုတ် သင်သည် ဤဝင်းဒိုးကို ပိတ်နိုင်သည်
+      return: အသုံးပြုသူ၏ ပရိုဖိုင်ကိုပြရန်
+      web: ဝဘ်သို့ သွားပါ
+    title: "%{acct} ကို စောင့်ကြည့်မယ်"
+  challenge:
+    confirm: ဆက်လုပ်မည်
+    hint_html: "<strong>အကြံပြုချက် -</strong> နောက်နာရီများတွင် သင့်စကားဝှက်ကို ထပ်မံတောင်းဆိုမည်မဟုတ်ပါ။"
+    invalid_password: စကားဝှက် မမှန်ပါ
+    prompt: ဆက်လက်လုပ်ဆောင်ရန်အတွက် စကားဝှက်အတည်ပြုပါ
+  crypto:
+    errors:
+      invalid_key: မှန်ကန်သော Ed25519 သို့မဟုတ် Curve25519 ကီး မဟုတ်ပါ။
+      invalid_signature: မှန်ကန်သော Ed25519 လက်မှတ်မဟုတ်ပါ
+  date:
+    formats:
+      default: "%b %d, %Y"
+      with_month_name: "%B %d, %Y"
+  datetime:
+    distance_in_words:
+      about_x_hours: "%{count}h"
+      about_x_months: "%{count}mo"
+      about_x_years: "%{count}y"
+      almost_x_years: "%{count}y"
+      half_a_minute: အခုလေးတင်
+      less_than_x_minutes: "%{count}m"
+      less_than_x_seconds: အခုလေးတင်
+      over_x_years: "%{count}y"
+      x_days: "%{count}y"
+      x_minutes: "%{count}m"
+      x_months: "%{count}mo"
+      x_seconds: "%{count}s"
+  deletes:
+    challenge_not_passed: သင်ထည့်လိုက်သော အချက်အလက်မှာ မမှန်ပါ
+    confirm_password: အထောက်အထားကိုအတည်ပြုရန်အတွက် သင့်လက်ရှိစကားဝှက်ကို ထည့်ပါ
+    confirm_username: လုပ်ငန်းစဉ်ကို အတည်ပြုရန်အတွက် သင့်အသုံးပြုသူအမည်ကို ထည့်သွင်းပါ
+    proceed: အကောင့်ဖျက်ပါ
+    success_msg: သင့်အကောင့်ကို အောင်မြင်စွာ ဖျက်လိုက်ပါပြီ
+    warning:
+      before: ရှေ့မဆက်မီ ဤမှတ်စုများကို သေချာဖတ်ပါ -
+      caches: အခြားဆာဗာများမှ ကက်ရှ်လုပ်ထားသော အကြောင်းအရာမှာ ဆက်ရှိနေနိုင်သည်
+      data_removal: သင့်ပို့စ်များနှင့် အခြားအချက်အလက်များကို အပြီးတိုင် ဖယ်ရှားပါမည်
+      email_change_html: သင့်အကောင့်မဖျက်ဘဲ <a href="%{path}">သင့်အီးမေးလ်လိပ်စာကို ပြောင်းလဲနိုင်သည်</a>
+      email_contact_html: မရောက်သေးပါက အကူအညီအတွက် <a href="mailto:%{email}">%{email}</a> သို့ အီးမေးလ်ပို့နိုင်ပါသည်
+      email_reconfirmation_html: အတည်ပြုချက်အီးမေးလ် လက်ခံမရရှိပါက <a href="%{path}">ထပ်မံတောင်းဆိုနိုင်သည်</a>
+      irreversible: အကောင့်ကို ပြန်လည်ရယူရန် သို့မဟုတ် ပြန်လည်အသုံးပြုရန် မဖြစ်နိုင်တော့ပါ
+      more_details_html: အသေးစိတ်အချက်အလက်များအတွက်၊ <a href="%{terms_path}">ကိုယ်ရေးကိုယ်တာမူဝါဒ</a> ကို ကြည့်ပါ။
+      username_available: သင့်အသုံးပြုသူအမည်ကို ပြန်လည်ရရှိနိုင်ပါမည်
+      username_unavailable: သင့်အသုံးပြုသူအမည်မှာ မရရှိနိုင်တော့ပါ
+  disputes:
+    strikes:
+      action_taken: ဆောင်ရွက်ခဲ့သည်
+      appeal: အယူခံဝင်ခြင်း
+      appeal_rejected: တင်သွင်းခြင်းကို ပယ်ချလိုက်သည်
+      appeal_submitted_at: အယူခံဝင်မှုကို တင်သွင်းပြီးချိန်
+      appealed_msg: အယူခံဝင်မှုကို တင်သွင်းပြီးဖြစ်သည်။ အတည်ပြုပြီးပါက အကြောင်းကြားပါမည်။
+      appeals:
+        submit: အယူခံဝင်ခွင့်ပြုရန်
+      approve_appeal: တင်သွင်းခြင်းကို အတည်ပြုပါ
+      associated_report: ဆက်စပ်အစီရင်ခံစာ
+      created_at: ရက်စွဲ
+      description_html: ဤအရာများမှာ သင့်အကောင့်နှင့် %{instance} မှ သင့်ထံပေးပို့ထားသော သတိပေးချက်များကို ဆန့်ကျင်သည့် လုပ်ဆောင်ချက်များဖြစ်သည်။
+      recipient: လိပ်မူထားသည်
+      reject_appeal: တင်သွင်းခြင်းကို ပယ်ချပါ
+      status: "#%{id} ပို့စ်"
+      status_removed: ပို့စ်ကို စနစ်မှ ဖယ်ရှားပြီးဖြစ်သည်
+      title: "%{date} မှ %{action}"
+      title_actions:
+        delete_statuses: ပို့စ်ဖယ်ရှားခြင်း
+        disable: အကောင့်ပိတ်သိမ်းခြင်း
+        mark_statuses_as_sensitive: သတိထားရသောပို့စ်များအဖြစ် အမှတ်အသားပြုခြင်း
+        none: သတိပေးချက်
+        sensitive: သတိထားရသောအကောင့်အဖြစ် အမှတ်အသားပြုခြင်း
+        silence: အကောင့်ကန့်သတ်ချက်
+        suspend: အကောင့်ပိတ်ခြင်း
+      your_appeal_approved: သင့်တင်သွင်းခြင်းကို အတည်ပြုပြီးပါပြီ
+      your_appeal_pending: အယူခံဝင်ရန် တင်သွင်းထားသည်
+      your_appeal_rejected: အယူခံဝင်မှုကို ပယ်ချလိုက်သည်
+  domain_validator:
+    invalid_domain: တရားဝင်ဒိုမိန်းအမည်မဟုတ်ပါ
   errors:
-    '400': The request you submitted was invalid or malformed.
-    '403': You don't have permission to view this page.
-    '404': The page you are looking for isn't here.
-    '406': This page is not available in the requested format.
-    '410': The page you were looking for doesn't exist here anymore.
-    '422': 
-    '429': Too many requests
-    '500': 
-    '503': The page could not be served due to a temporary server failure.
+    '400': သင်တင်ပြသော တောင်းဆိုချက်မှာ မမှန်ကန်ပါ သို့မဟုတ် ပုံစံမမှန်ပါ။
+    '403': ဤစာမျက်နှာကြည့်ရှုရန် သင့်တွင် ခွင့်ပြုချက်မရှိပါ။
+    '404': သင်ရှာဖွေနေသော စာမျက်နှာသည် ဤနေရာတွင် မရှိပါ။
+    '406': ဤစာမျက်နှာကို တောင်းဆိုထားသော ပုံစံဖြင့် မရရှိနိုင်ပါ။
+    '410': သင်ရှာဖွေနေသည့် စာမျက်နှာသည် ဤနေရာတွင် မရှိတော့ပါ။
+    '422':
+      content: လုံခြုံရေး အတည်ပြုခြင်း မအောင်မြင်ပါ။ သင်သည် ကွတ်ကီးများကို ပိတ်ပင်ထားပါသလား။
+      title: လုံခြုံရေး အတည်ပြုခြင်း မအောင်မြင်ပါ
+    '429': တောင်းဆိုမှု များနေပါသည်
+    '500':
+      content: စိတ်မကောင်းပါ။ အဆုံးတွင် တစ်ခုမှားယွင်းသွားပါသည်။
+      title: ဤစာမျက်နှာမှာ မမှန်ကန်ပါ
+    '503': ယာယီဆာဗာချို့ယွင်းမှုကြောင့် စာမျက်နှာကို ကြည့်ရှု၍မရပါ။
+    noscript_html: Mastodon ဝဘ်အက်ပလီကေးရှင်းအသုံးပြုရန်အတွက် JavaScript ကို ဖွင့်ပါ။ တစ်နည်းအားဖြင့် သင့် Mastodon ပလက်ဖောင်းအတွက် <a href="%{apps_path}">မူရင်းအက်ပ်များ</a> ထဲမှ တစ်ခုကို စမ်းကြည့်ပါ။
+  existing_username_validator:
+    not_found: ထိုအသုံးပြုသူအမည်ဖြင့် ပြည်တွင်းအသုံးပြုသူကို ရှာမတွေ့ပါ
+    not_found_multiple: "%{usernames} ကို ရှာမတွေ့ပါ"
+  exports:
+    archive_takeout:
+      date: ရက်စွဲ
+      download: သင်၏မှတ်တမ်းကို ဒေါင်းလုဒ်လုပ်ပါ
+      in_progress: သင့်မှတ်တမ်းကို ပြုစုနေသည်...
+      request: မှတ်တမ်း တောင်းဆိုရန်
+      size: အရွယ်အစား
+    blocks: သင်ပိတ်ပင်ထားသည့်လူများစာရင်း
+    bookmarks: မှတ်ထားသည်များ
+    csv: CSV
+    domain_blocks: ဒိုမိန်းပိတ်ပင်ထားမှုများ
+    lists: စာရင်းများ
+    mutes: အသံပိတ်
+    storage: မီဒီယာသိုလှောင်မှု
+  featured_tags:
+    add_new: အသစ် ထည့်ပါ
+    errors:
+      limit: ဟက်ရှ်တဂ်အရေအတွက် အများဆုံးကို ဖော်ပြပြီးဖြစ်သည်
+  filters:
+    contexts:
+      account: ပရိုဖိုင်များ
+      home: ပင်မနှင့် စာရင်းများ
+      notifications: အကြောင်းကြားချက်များ
+      public: အများမြင်စာမျက်နှာ
+      thread: စကားဝိုင်းများ
+    edit:
+      add_keyword: အဓိကစကားလုံး ထည့်ပါ
+      keywords: အဓိကစကားလုံးများ
+      statuses: တစ်ဦးချင်းတင်ထားသောပို့စ်များ
+      title: စစ်ထုတ်ခြင်းကို ပြင်ဆင်ရန်
+    errors:
+      invalid_context: ပံ့ပိုးထား​သော အ​​ကြောင်းအရာ မရှိခြင်း သို့မဟုတ် မမှန်ကန်ခြင်း
+    index:
+      contexts: "%{contexts} ရှိ စစ်ထုတ်ထားမှုများ"
+      delete: ဖျက်ပါ
+      empty: သင့်တွင် စစ်ထုတ်ထားခြင်းများ မရှိပါ။
+      expires_in: "%{distance} တွင် သက်တမ်းကုန်မည်"
+      expires_on: "%{date} ရက်နေ့တွင် သက်တမ်းကုန်မည်"
+      keywords:
+        other: "%{count} အဓိကစကားလုံး"
+      statuses:
+        other: "%{count} ပို့စ်"
+      statuses_long:
+        other: တစ်ဦးချင်းပို့စ် %{count} များ ကို ဖျောက်ထားသည်
+      title: စစ်ထုတ်ခြင်းများ
+    new:
+      save: စစ်ထုတ်မှုအသစ် သိမ်းပါ
+      title: စစ်ထုတ်မှုအသစ် ထည့်ပါ
+    statuses:
+      back_to_filter: စစ်ထုတ်ခြင်းသို့ ပြန်သွားရန်
+      batch:
+        remove: စစ်ထုတ်ခြင်းမှ ဖယ်ရှားရန်
+      index:
+        title: စစ်ထုတ်ထားသော ပို့စ်များ
+  generic:
+    all: အားလုံး
+    all_items_on_page_selected_html:
+      other: ဤစာမျက်နှာရှိ <strong>%{count}</strong> အိုက်တမ်များအားလုံးကို ရွေးချယ်ထားသည်။
+    all_matching_items_selected_html:
+      other: သင့်ရှာဖွေမှုနှင့် ကိုက်ညီသည့် <strong>%{count}</strong> အိုက်တမ်အားလုံးကို ရွေးချယ်ထားသည်။
+    changes_saved_msg: အပြောင်းအလဲများကို သိမ်းဆည်းပြီးပါပြီ။
+    copy: ကူးယူပါ
+    delete: ဖျက်ပါ
+    deselect: အားလုံး ရွေးထားခြင်းကို ဖျက်မည်
+    none: တစ်ခုမျှ
+    order_by: စီထားသည်
+    save_changes: ပြောင်းလဲမှုများကို သိမ်းဆည်းပါ
+    select_all_matching_items:
+      other: သင့်ရှာဖွေမှုနှင့် ကိုက်ညီသည့် %{count} ခု အားလုံးကို ရွေးပါ။
+    today: ယနေ့
+    validation_errors:
+      other: မမှန်သေးပါ။ ကျေးဇူးပြု၍ အောက်ပါ %{count} အမှားများကို ပြန်လည်သုံးသပ်ပါ
+  imports:
+    errors:
+      invalid_csv_file: မမှန်ကန်သော CSV ဖိုင်။ အမှား - %{error}
+      over_rows_processing_limit: "%{count} တန်းထက် ပိုနေပါသည်"
+    modes:
+      merge: ပေါင်း
+      merge_long: ရှိပြီးသားမှတ်တမ်းများ သိမ်းဆည်းပြီး အသစ်များ ထပ်ထည့်ပါ
+      overwrite: ထပ်ရေးရန်
+      overwrite_long: လက်ရှိမှတ်တမ်းများကို အသစ်များဖြင့် အစားထိုးပါ
+    preface: သင်စောင့်ကြည့်နေသည့်လူများစာရင်း သို့မဟုတ် ပိတ်ပင်ထားသည့်စာရင်းကဲ့သို့သော အခြားဆာဗာတစ်ခုမှ သင်ထုတ်ယူထားသည့်အချက်အလက်များကို ပြန်လည်ထည့်သွင်းနိုင်သည်။
+    success: သင့်အချက်အလက်ကို အပ်လုဒ်လုပ်ပြီး သတ်မှတ်ချိန်အတွင်း ဆောင်ရွက်ပါမည်
+    types:
+      blocking: ပိတ်ပင်ထားသည့်စာရင်း
+      bookmarks: Bookmarks
+      domain_blocking: ဒိုမိန်းပိတ်ပင်ထားသည့်စာရင်း
+      following: စောင့်ကြည့်စာရင်း
+      muting: ပိတ်ထားသောစာရင်း
+    upload: တင္ရန်
+  invites:
+    delete: အကောင့်ပိတ်ရန်
+    expired: သက်တမ်းကုန်သွားပါပြီ
+    expires_in:
+      '1800': ၃၀ မိနစ်
+      '21600': ၆ နာရီ
+      '3600': ၁ နာရီ
+      '43200': ၁၂ နာရီ
+      '604800': ၁ ပတ်
+      '86400': ၁ ရက်
+    expires_in_prompt: ဘယ်တော့မှ
+    generate: ဖိတ်ကြားချက်လင့်ခ် ဖန်တီးပါ
+    invited_by: သင့်ကို ဖိတ်ခေါ်ထားသည် -
+    max_uses:
+      other: "%{count} အသုံးပြုမှုများ"
+    max_uses_prompt: အကန့်အသတ်မဲ့
+    prompt: ဤဆာဗာသို့ ဝင်ရောက်ရန်အတွက် လင့်ခ်များဖန်တီးပြီး အခြားသူများကို မျှဝေပါ
+    table:
+      expires_at: သက်တမ်းကုန်သည်
+      uses: အသုံးပြုမှုများ
+    title: ဖိတ်ခေါ်ရန်
+  lists:
+    errors:
+      limit: သင်သည် အများဆုံးစာရင်း အရေအတွက်သို့ ရောက်ရှိသွားပါပြီ
+  login_activities:
+    authentication_methods:
+      otp: နှစ်ဆင့်ခံလုံခြုံရေးစနစ်အက်ပ်
+      password: စကားဝှက်
+      sign_in_token: အီးမေးလ်လုံခြုံရေးကုဒ်
+      webauthn: လုံခြုံရေးကီးများ
+    description_html: သင်မပြုလုပ်သည့် လုပ်ဆောင်ချက်တွေ့ရှိပါက သင့်စကားဝှက်ကို ပြောင်းလဲပြီး နှစ်ဆင့်ခံလုံခြုံရေးစနစ်ကို အသုံးပြုသင့်ပါသည်။
+    empty: စစ်မှန်ကြောင်းအထောက်အထားမရရှိနိုင်ပါ
+    failed_sign_in_html: "%{ip} (%{browser}) မှ %{method} ဖြင့် အကောင့်ဝင်ရောက်ခြင်း မအောင်မြင်ပါ"
+    successful_sign_in_html: "%{ip} (%{browser}) မှ %{method} ဖြင့် အကောင့်ဝင်၍ရပါပြီ"
+    title: အထောက်အထားမှတ်တမ်း
+  media_attachments:
+    validations:
+      images_and_video: ရုပ်ပုံပါရှိပြီးသားပို့စ်တွင် ဗီဒီယို ပူးတွဲ၍မရပါ
+      not_ready: မပြီးသေးသောဖိုင်များကို ပူးတွဲ၍မရပါ။ ခဏအကြာတွင် ထပ်စမ်းကြည့်ပါ။
+      too_many: ဖိုင် ၄ ဖိုင်ထက် ပို၍တွဲမရပါ
+  migrations:
+    acct: သို့ ပြောင်းရွှေ့ရန်
+    cancel: ပြန်ညွှန်းခြင်းကို ပယ်ဖျက်ရန်
+    cancel_explanation: ပြန်ညွှန်းခြင်းပယ်ဖျက်ခြင်းဖြင့် သင့်လက်ရှိအကောင့်မှာ ပြန်လည်အသုံးပြု၍ရမည်ဖြစ်သော်လည်း ထိုအကောင့်သို့ ပြောင်းရွှေ့ထားသော စောင့်ကြည့်သူများကို ပြန်လည်ရရှိတော့မည်မဟုတ်ပါ။
+    cancelled_msg: ပြန်ညွှန်းခြင်းကို ပယ်ဖျက်ပြီးပါပြီ။
+    errors:
+      already_moved: သင်ပြောင်းရွှေ့ပြီးသော အကောင့်နှင့် တူညီပါသည်
+      missing_also_known_as: ဤအကောင့်၏ အမည်တူမဟုတ်ပါ
+      move_to_self: လက်ရှိအကောင့်မဖြစ်နိုင်ပါ
+      not_found: ရှာမတွေ့ပါ
+    followers_count: ပြောင်းရွှေ့ချိန်၌ စောင့်ကြည့်သူများအရေအတွက်
+    incoming_migrations: အခြားအကောင့်တစ်ခုမှ ပြောင်းရွှေ့ခြင်း
+    incoming_migrations_html: အခြားအကောင့်မှ ဤအကောင့်သို့ ရွှေ့ရန် ဦးစွာ သင်သည် <a href="%{path}">အကောင့်အမည်တူတစ်ခု ဖန်တီးရန်</a> လိုအပ်ပါသည်။
+    moved_msg: သင့်အကောင့်မှာ ယခု %{acct} သို့ ပြန်ညွှန်းနေပြီး စောင့်ကြည့်သူများကို ရွှေ့ပြောင်းနေလျက်ရှိပါသည်။
+    not_redirecting: သင့်အကောင့်သည် လက်ရှိတွင် အခြားအကောင့်တစ်ခုသို့ ပြန်ညွှန်းနေခြင်းမဟုတ်ပါ။
+    on_cooldown: မကြာသေးမီက သင့်အကောင့်ကို ပြောင်းရွှေ့ခဲ့သည်။ ဤလုပ်ဆောင်ချက်ကို %{count} ရက်များတွင် ထပ်မံရရှိနိုင်ပါမည်။
+    past_migrations: ယခင်ရွှေ့ပြောင်းမှုများ
+    proceed_with_move: စောင့်ကြည့်သူများကို ရွှေ့ရန်
+    redirected_msg: သင့်အကောင့်သည် %{acct} သို့ ယခုပြန်ညွှန်းနေပါသည်။
+    redirecting_to: သင့်အကောင့်က %{acct} သို့ ပြန်ညွှန်းနေသည်။
+    set_redirect: ပြန်ညွှန်းသတ်မှတ်ပါ
+    warning:
+      backreference_required: အကောင့်အသစ်က ဤအကောင့်ကို ပြန်ကိုးကားရန်အတွက် ဦးစွာ ပြင်ဆင်သတ်မှတ်ရပါမည်
+      before: ဆက်လက်မလုပ်ဆောင်မီ ဤမှတ်စုများကို သေချာဖတ်ပါ -
+      cooldown: ရွှေ့ပြီးပါက နောက်တစ်ကြိမ် ထပ်ရွှေ့ရန် အချိန်စောင့်ရပါမည်
+      disabled_account: နောက်ပိုင်းတွင် သင့်လက်ရှိအကောင့်အား အပြည့်အဝအသုံးပြုနိုင်တော့မည်မဟုတ်သော်လည်း အချက်အလက်ထုတ်ယူခြင်းနှင့် ပြန်လည်အတည်ပြုခြင်းတို့ကို ဆောင်ရွက်နိုင်မည်ဖြစ်သည်။
+      followers: ဤလုပ်ဆောင်ချက်မှာ စောင့်ကြည့်သူအားလုံးကို လက်ရှိအကောင့်မှ အကောင့်အသစ်သို့ ရွှေ့ပြောင်းခြင်းဖြစ်သည်
+      only_redirect_html: တနည်းအားဖြင့် သင်သည် <a href="%{path}">သင့်ပရိုဖိုင်ပေါ်တွင် ပြန်ညွှန်းခြင်းကိုသာ ပြုလုပ်နိုင်သည်</a>။
+      other_data: အခြားအချက်အလက်များကို အလိုအလျောက်ရွှေ့မည်မဟုတ်ပါ
+      redirect: သင့်လက်ရှိအကောင့်၏ပရိုဖိုင်ကို ပြန်လည်ညွှန်းပေးသည့်အသိပေးချက်ဖြင့် ပြင်ဆင်ပေးမည်ဖြစ်ပြီး ရှာဖွေမှုများမှ ဖယ်ထုတ်ပေးမည်ဖြစ်သည်
+  moderation:
+    title: စိစစ်ခြင်း
+  move_handler:
+    carry_blocks_over_text: ဤအသုံးပြုသူသည် သင်ပိတ်ဆို့ထားသော %{acct} မှ ပြောင်းရွှေ့ခဲ့သည်။
+    carry_mutes_over_text: ဤအသုံးပြုသူသည် သင်အသံပိတ်ထားသော %{acct} မှ ပြောင်းရွှေ့ခဲ့သည်။
+    copy_account_note_text: ဤအသုံးပြုသူသည် %{acct} မှ ပြောင်းရွှေ့ခဲ့သည်။ ဤသည်မှာ ၎င်းတို့နှင့်ပတ်သက်သော ယခင်မှတ်စုများဖြစ်သည် -
+  navigation:
+    toggle_menu: 'မီနူးပြောင်းရန်
+
+      '
+  notification_mailer:
+    admin:
+      report:
+        subject: "%{name} က တိုင်ကြားစာတစ်စောင် ပို့ခဲ့သည်"
+      sign_up:
+        subject: "%{name} က အကောင့်ဖွင့်ထားသည်"
+    favourite:
+      body: "%{name} က နှစ်သက်ခဲ့သော သင့်ပို့စ် -"
+      subject: "%{name} က သင့်ပို့စ်ကို နှစ်သက်ခဲ့သည်"
+      title: နှစ်သက်မှုအသစ်တစ်ခု
+    follow:
+      body: "%{name} မှ ယခု သင့်နောက်သို့ စောင့်ကည့်နေပါသည်။"
+      subject: "%{name} မှ ယခု သင့်နောက်သို့ စောင့်ကည့်နေပါသည်။"
+      title: စောင့်ကြည့်သူအသစ်
+    follow_request:
+      action: စောင့်ကြည့်ရန်တောင်းဆိုမှုများကို ပြင်ဆင်ရန်
+      body: "%{name} က သင့်ကို စောင့်ကြည့်ရန် တောင်းဆိုထားသည်"
+      subject: ဆိုင်းငံ့ထားသော စောင့်ကြည့်သူ - %{name}
+      title: စောင့်ကြည့်ခွင့်ပြုရန် တောင်းဆိုမှုအသစ်
+    mention:
+      action: စာပြန်ရန်
+      body: သင့်ကို %{name} မှ ဖော်ပြခဲ့သည်-
+      subject: သင့်ကို %{name} မှ ဖော်ပြခဲ့သည်
+      title: ဖော်ပြမှုအသစ်
+    poll:
+      subject: "%{name} ၏ စစ်တမ်းတစ်ခု ပြီးသွားပါပြီ"
+    reblog:
+      body: "%{name} က သင့်ပို့စ်ကို  Boost လုပ်ခဲ့သည် -"
+      subject: "%{name} က သင့်ပို့စ်ကို Boost လုပ်ခဲ့သည်"
+      title: Boost အသစ်
+    status:
+      subject: "%{name} က အခုလေးတင် ပို့စ်တင်လိုက်ပါပြီ"
+    update:
+      subject: "%{name} က ပို့စ်တစ်ခုကို ပြင်ဆင်ခဲ့သည်"
+  notifications:
+    email_events: အီးမေးလ်သတိပေးချက်များအတွက်အကြောင်းအရာများ
+    email_events_hint: အသိပေးချက်များရယူမည့် အစီအစဉ်များကို ရွေးပါ -
+    other_settings: အခြားအသိပေးချက်များ၏ သတ်မှတ်ချက်များ
+  number:
+    human:
+      decimal_units:
+        format: "%n%u"
+        units:
+          billion: B
+          million: M
+          quadrillion: Q
+          thousand: K
+          trillion: T
+  otp_authentication:
+    code_hint: အတည်ပြုရန်အတွက် အထောက်အထားစိစစ်အက်ပ်မှ ထုတ်ပေးသည့်ကုဒ်ကို ထည့်သွင်းပါ
+    enable: ဖွင့်ပါ
+    manual_instructions: QR ကုဒ်စကင်န်ဖတ်၍မရပါက ကိုယ်တိုင်ရိုက်ထည့်ရန်လိုအပ်ပြီး ဤနေရာသည် စာရွက်အလွတ်နေရာဖြစ်သည်။
+    setup: သတ်မှတ်ရန်
+    wrong_code: ထည့်သွင်းထားသောကုဒ်သည် မမှန်ကန်ပါ။ ဆာဗာအချိန်နှင့် စက်အချိန်မှာ မှန်ကန်ပါသလား။
+  pagination:
+    newer: ပိုသစ်သော
+    next: ရှေ့သို့
+    older: ပိုဟောင်းသော
+    prev: ရှေ့သို့
+    truncate: "&hellip;"
+  polls:
+    errors:
+      already_voted: ဤစစ်တမ်းတွင် သင်ပါဝင်ပြီးဖြစ်သည်
+      duplicate_options: ထပ်တူညီသောအရာများပါဝင်နေသည်
+      duration_too_long: အလှမ်းဝေးလွန်းတယ်
+      duration_too_short: စောလွန်းတယ်
+      expired: စစ်တမ်းပြီးဆုံးသွားပါပြီ
+      invalid_choice: သင်ရွေးချယ်လိုသောအရာမှာ စစ်တမ်းတွင်မပါဝင်ပါ
+      over_character_limit: သတ်မှတ်ထားသောစာလုံးအရေအတွက် %{max} ထက်ပိုနေသည်
+      too_few_options: တစ်ခုထက်ပိုနေသည်
+      too_many_options: သတ်မှတ်ထားသောအရေအတွက် %{max} ကိုကျော်လွန်နေသည်
+  preferences:
+    other: အခြား
+    posting_defaults: ပို့စ်တင်ရာတွင် သတ်မှတ်ချက်များ
+    public_timelines: အများမြင်စာမျက်နှာ
+  privacy_policy:
+    title: ကိုယ်ရေးအချက်အလက်မူဝါဒ
+  reactions:
+    errors:
+      limit_reached: မတူညီသော တုံ့ပြန်မှုများ၏ ကန့်သတ်ချက်သို့ ရောက်ရှိခဲ့သည်။
+      unrecognized_emoji: အသိအမှတ်ပြုထားသော အီမိုဂျီမဟုတ်ပါ
+  relationships:
+    activity: အကောင့်လုပ်ဆောင်ချက်
+    confirm_follow_selected_followers: ရွေးချယ်စောင့်ကြည့်သူများကို စောင့်ကြည့်လိုသည်မှာ သေချာပါသလား။
+    confirm_remove_selected_followers: ရွေးချယ်စောင့်ကြည့်သူများကို ဖယ်ရှားလိုသည်မှာ သေချာပါသလား။
+    confirm_remove_selected_follows: ရွေးချယ်စောင့်ကြည့်သူများကို ဖယ်ရှားလိုသည်မှာ သေချာပါသလား။
+    dormant: Dormant
+    follow_failure: ရွေးချယ်ထားသော အကောင့်အချို့ကို စောင့်ကြည့်၍ရမည်မဟုတ်ပါ။
+    follow_selected_followers: ရွေးချယ်စောင့်ကြည့်သူများကို စောင့်ကြည့်ပါ
+    followers: စောင့်ကြည့်သူများ
+    following: စောင့်ကြည့်နေသည်
+    invited: ဖိတ်ခေါ်ထားပြီး
+    last_active: နောက်ဆုံးအသုံးပြုခဲ့သည့်အချိန်
+    most_recent: မကြာသေးမီက
+    moved: ရွှေ့ပြီးပါပြီ
+    mutual: အပြန်အလှန်စောင့်ကြည့်ထားခြင်း
+    primary: ဦးစားပေး
+    relationship: တော်စပ်ပုံ
+    remove_selected_domains: ရွေးချယ်ထားသော ဒိုမိန်းများမှ စောင့်ကြည့်သူအားလုံးကို ဖယ်ရှားပါ
+    remove_selected_followers: ရွေးချယ်ထားသော စောင့်ကြည့်သူများကို ဖယ်ရှားပါ
+    remove_selected_follows: ရွေးချယ်ထားသော အသုံးပြုသူများကို စောင့်ကြည့်ခြင်းမှ ဖြုတ်ပါ
+    status: အကောင့်အခြေအနေ
+  remote_follow:
+    missing_resource: သင့်အကောင့်အတွက် လိုအပ်သော ပြန်ညွှန်းမည့် URL ကို ရှာမတွေ့ပါ
+  reports:
+    errors:
+      invalid_rules: မှန်ကန်သောစည်းမျဉ်းများကို ကိုးကားထားခြင်း မရှိပါ
+  rss:
+    content_warning: 'အကြောင်းအရာသတိပေးချက် - '
+    descriptions:
+      account: "@%{acct} မှ အများမြင်ပို့စ်များ"
+      tag: "#%{hashtag} ကို တဂ်ထားသော အများမြင်ပို့စ်များ"
+  scheduled_statuses:
+    over_daily_limit: ယနေ့အတွက် စီစဉ်ထားသည့် ပို့စ်များ၏ ကန့်သတ်ချက် %{limit} ကို ကျော်လွန်သွားပါပြီ
+    over_total_limit: စီစဉ်ထားသည့် ပို့စ်များ၏ ကန့်သတ်ချက် %{limit} ကို ကျော်လွန်သွားပါပြီ
+    too_soon: စီစဉ်ထားသောရက်စွဲမှာ အနာဂတ်အတွက်ဖြစ်သည်
+  sessions:
+    activity: နောက်ဆုံးလုပ်ဆောင်ချက်
+    browser: ဘရောက်ဇာ
+    browsers:
+      alipay: Alipay
+      blackberry: BlackBerry
+      chrome: Chrome
+      edge: Microsoft Edge
+      electron: အီလက်ထရွန်
+      firefox: Firefox
+      generic: အမည်မသိဘရောက်ဆာ
+      huawei_browser: Huawei Browser
+      ie: Internet Explorer
+      micro_messenger: MicroMessenger
+      nokia: Nokia S40 Ovi Browser
+      opera: Opera
+      otter: Otter
+      phantom_js: PhantomJS
+      qq: QQ Browser
+      safari: Safari
+      uc_browser: UC Browser
+      unknown_browser: အမည်မသိဘရောက်ဆာ
+      weibo: Weibo
+    current_session: လက်ရှိဆက်ရှင်
+    description: "%{platform} ပေါ်ရှိ %{browser}"
+    explanation: ဤသည်မှာ သင်၏ Mastodon အကောင့်သို့ လက်ရှိဝင်ရောက်ထားသည့် ဝဘ်ဘရောက်ဆာများဖြစ်သည်။
+    ip: IP
+    platforms:
+      adobe_air: Adobe Air
+      android: Android
+      blackberry: BlackBerry
+      chrome_os: ChromeOS
+      firefox_os: Firefox OS
+      ios: iOS
+      kai_os: KaiOS
+      linux: Linux
+      mac: macOS
+      unknown_platform: အမည်မသိ Platform
+      windows: Windows
+      windows_mobile: Windows Mobile
+      windows_phone: Windows Phone
+    revoke: ပြန်ရုပ်သိမ်းရန်
+    revoke_success: ဆက်ရှင်ကို ရုပ်သိမ်းလိုက်ပါပြီ
+    title: ဆက်ရှင်များ
+    view_authentication_history: သင့်အကောင့်၏ စစ်မှန်ကြောင်းအထောက်အထားပြမှုကို ကြည့်ပါ
+  settings:
+    account: အကောင့်
+    account_settings: အကောင့်သတ်မှတ်ချက်များ
+    aliases: အကောင့်အမည်တူများ
+    appearance: ပုံပန်းသဏ္ဌာန်
+    authorized_apps: ခွင့်ပြုထားသောအက်ပ်များ
+    back: Mastodon သို့ ပြန်သွားရန်
+    delete: အကောင့်ဖျက်သိမ်းခြင်း
+    development: Development
+    edit_profile: ပရိုဖိုင်ပြင်ဆင်ရန်
+    export: အချက်အလက်ထုတ်ယူခြင်း
+    featured_tags: အသားပေးဖော်ပြထားသည့် ဟက်ရှ်တဂျ်များ
+    import: ထည့်သွင်းခြင်း
+    import_and_export: ထည့်သွင်းခြင်းနှင့် ထုတ်ယူခြင်း
+    migrate: အကောင့်ပြောင်းရွှေ့ခြင်း
+    notifications: အသိပေးချက်များ
+    preferences: သတ်မှတ်ချက်များ
+    profile: ပရိုဖိုင်
+    relationships: စောင့်ကြည့်သူများနှင့် စောင့်ကြည့်စာရင်း
+    statuses_cleanup: အလိုအလျောက်ပို့စ်ဖျက်ခြင်း
+    strikes: စိစစ်လုပ်ဆောင်ချက်များ
+    two_factor_authentication: နှစ်ဆင့်ခံလုံခြုံရေးစနစ်
+    webauthn_authentication: လုံခြုံရေးကီးများ
+  statuses:
+    attached:
+      audio:
+        other: "%{count} အသံ"
+      description: ပူးတွဲပါ- %{attached}
+      image:
+        other: "%{count} ပုံ"
+      video:
+        other: "%{count} ဗီဒီယို"
+    boosted_from_html: "%{acct_link} မှ Boost လုပ်ခဲ့သည်"
+    content_warning: အကြောင်းအရာသတိပေးချက် - %{warning}
+    default_language: လက်ရှိသုံးနေသောဘာသာစကားအတိုင်း ပြပေးပါမည်
+    disallowed_hashtags:
+      other: ခွင့်မပြုထားသော hashtags များပါရှိသည် - %{tags}
+    edited_at_html: "%{date} ကို ပြင်ဆင်ပြီးပါပြီ"
+    errors:
+      in_reply_not_found: သင် စာပြန်နေသည့်ပို့စ်မှာ မရှိတော့ပါ။
+    open_in_web: ဝဘ်တွင် ဖွင့်ပါ
+    over_character_limit: စာလုံးကန့်သတ်ချက် %{max} ကို ကျော်လွန်သွားပါပြီ
+    pin_errors:
+      direct: အမည်ဖော်ပြထားသည့် ပို့စ်များကို ပင်တွဲ၍မရပါ
+      limit: သင်သည် ပို့စ်အရေအတွက်အများဆုံးကို ပင်တွဲထားပြီးဖြစ်သည်
+      ownership: အခြားသူ၏ပို့စ်ကို ပင်တွဲ၍မရပါ
+      reblog: Boost လုပ်ထားသောပို့စ်ကို ပင်ထား၍မရပါ
+    poll:
+      total_people:
+        other: "%{count} ယောက်"
+      total_votes:
+        other: မဲအရေအတွက် %{count} မဲ
+      vote: မဲပေးမည်
+    show_more: ပိုမိုပြရန်
+    show_newer: ပို့စ်အသစ်များပြရန်
+    show_older: ပို့စ်အဟောင်းများပြရန်
+    show_thread: Thread ကို ပြပါ
+    sign_in_to_participate: စကားဝိုင်းတွင် ပါဝင်ရန် အကောင့်ဝင်ပါ
+    title: '%{name}: "%{quote}"'
+    visibilities:
+      direct: တိုက်ရိုက်
+      private: စောင့်ကြည့်သူများသာ
+      private_long: စောင့်ကြည့်သူများကိုသာ ပြရန်
+      public: အများမြင်
+      public_long: လူတိုင်းမြင်နိုင်ပါသည်
+      unlisted: စာရင်းမသွင်းထားပါ
+      unlisted_long: လူတိုင်းမြင်နိုင်သော်လည်း အများမြင်မည့်စာမျက်နှာများတွင် စာရင်းမသွင်းထားပါ။
+  statuses_cleanup:
+    enabled: ပို့စ်အဟောင်းများကို အလိုအလျောက် ဖျက်ပါ
+    enabled_hint: အောက်ပါခြွင်းချက်များထဲမှ တစ်ခုနှင့် မကိုက်ညီပါက သတ်မှတ်ထားသည့်ကာလအပိုင်းအခြားသို့ ရောက်သည်နှင့် သင့်ပို့စ်များကို အလိုအလျောက် ဖျက်ပါမည်
+    exceptions: ခြွင်းချက်များ
+    explanation: ပို့စ်များကို ဖျက်ခြင်းသည် စျေးကြီးသော လုပ်ဆောင်မှုတစ်ခုဖြစ်သောကြောင့်၊ ဆာဗာမှမဟုတ်ရင် အလုပ်ရှုပ်နေချိန်တွင် အချိန်ကြာလာသည်နှင့်အမျှ ၎င်းကို ဖြည်းဖြည်းချင်း လုပ်ဆောင်ပါသည်။ ထို့ကြောင့်၊ သင့်ပို့စ်များသည် အချိန်သတ်မှတ်ချက်သို့ရောက်ရှိပြီးနောက် ခဏအကြာတွင် ဖျက်လိုက်နိုင်ပါသည်။
+    ignore_favs: နှစ်သက်မှုများကို လျစ်လျူရှုပါ
+    ignore_reblogs: Boost များကို လျစ်လျူရှုပါ
+    interaction_exceptions: အပြန်အလှန်တုံ့ပြန်မှုများအပေါ်အခြေခံသည့် ခြွင်းချက်များ
+    interaction_exceptions_explanation: ပို့စ်များသည် အကြိုက်ဆုံးအဆင့်အောက်သို့ရောက်သွားပါက သို့မဟုတ် ၎င်းတို့ကို တစ်ကြိမ်ကျော်ပြီးပါက ဖျက်ပစ်ရန် အာမခံချက်မရှိကြောင်း သတိပြုပါ။
+    keep_direct: တိုက်ရိုက်မက်ဆေ့ချ်များကို သိမ်းထားပါ
+    keep_direct_hint: တိုက်ရိုက်စကားပြောထားသည်များကို မဖျက်ပါနှင့်
+    keep_media: မီဒီယာဖိုင်များပါသောပို့စ်များကို သိမ်းဆည်းပါ
+    keep_media_hint: မီဒီယာဖိုင်များပါသောပို့စ်များကို မဖျက်ပါနှင့်
+    keep_pinned: ပင်ထိုးထားသော ပို့စ်များကို သိမ်းထားပါ
+    keep_pinned_hint: ပင်ထိုးထားသော ပို့စ်များကို မဖျက်ပါ
+    keep_polls: စစ်တမ်းကိုဆက်လက်ထားမည်
+    keep_polls_hint: သင့်မှတ်တမ်းတစ်ခုမှ မပျက်ပါ
+    keep_self_bookmark: မှတ်ထားသောပို့စ်များကို သိမ်းဆည်းပါ
+    keep_self_bookmark_hint: မှတ်ထားပြီးသော ပို့စ်များကို မဖျက်ပါနှင့်
+    keep_self_fav: သင်နှစ်သက်ခဲ့သောပို့စ်များကို သိမ်းထားပါ
+    keep_self_fav_hint: နှစ်သက်ထားပြီးသော ပို့စ်များကို မဖျက်ပါနှင့်
+    min_age:
+      '1209600': ၂ ပတ်
+      '15778476': ၆ လ
+      '2629746': ၁ လ
+      '31556952': ၁ နှစ်
+      '5259492': ၂ လ
+      '604800': ၁ ပတ်
+      '63113904': ၂ နှစ်
+      '7889238': ၃ လ
+    min_age_label: ကာလအပိုင်းအခြား
+    min_favs: အနည်းဆုံးအားဖြင့် နှစ်သက်သည့်ပို့စ်များကို သိမ်းဆည်းပါ
+    min_favs_hint: အနည်းဆုံး ဤအကြိုက်ဆုံးအရေအတွက်ကို လက်ခံရရှိထားသည့် သင့်ပို့စ်များကို မဖျက်ပါ။ ပို့စ်များကို ၎င်းတို့၏ စိတ်ကြိုက်အရေအတွက်မခွဲခြားဘဲ ဖျက်ရန် ချန်ထားပါ။
+    min_reblogs: အနည်းဆုံးအားဖြင့် Boost လုပ်ထားသည့်ပို့စ်များကို သိမ်းဆည်းပါ
+    min_reblogs_hint: အနည်းဆုံး ဤအကြိမ်အရေအတွက်ကို မြှင့်တင်ထားသည့် သင့်ပို့စ်များကို မဖျက်ပါ။ ၎င်းတို့၏ မြှင့်တင်မှုအရေအတွက်ကို မခွဲခြားဘဲ ပို့စ်များကို ဖျက်ရန် ချန်ထားပါ
+  stream_entries:
+    pinned: ပင်တွဲထားသောပို့စ်
+    reblogged: Boost လုပ်ခဲ့သည်
+    sensitive_content: သတိထားရသော အကြောင်းအရာ
+  strikes:
+    errors:
+      too_late: ဤလုပ်ဆောင်ချက်တင်ပြရန် နောက်ကျသွားပါပြီ
+  tags:
+    does_not_match_previous_name: ယခင်အမည်နှင့် မကိုက်ညီပါ
+  themes:
+    default: Mastodon (အနက်)
+    mastodon-light: Mastodon (အလင်းနောက်ခံ)
+  time:
+    formats:
+      default: "%b %d, %Y, %H:%M"
+      month: "%b %Y"
+      time: "%H:%M"
+  two_factor_authentication:
+    add: ထည့်ရန်
+    disable: 2FA ကို ပိတ်ပါ
+    disabled_success: နှစ်ဆင့်ခံလုံခြုံရေးစနစ်ကို ပိတ်ပြီးပါပြီ
+    edit: ပြင်ဆင်ရန်
+    enabled: နှစ်ဆင့်ခံလုံခြုံရေးစနစ်ကို ဖွင့်ထားသည်
+    enabled_success: နှစ်ဆင့်ခံလုံခြုံရေးစနစ်ကို ဖွင့်ပြီးပါပြီ
+    generate_recovery_codes: ပြန်လည်ရယူရေးကုဒ်များ ဖန်တီးပါ
+    lost_recovery_codes: သင့်ဖုန်းပျောက်ဆုံးသွားပါက ပြန်လည်ရယူခြင်းကုဒ်များသည် သင့်အကောင့်သို့ ပြန်လည်ဝင်ရောက်ခွင့်ရရှိစေမည်ဖြစ်သည်။ သင်၏ ပြန်လည်ရယူရေးကုဒ်များ ပျောက်ဆုံးသွားပါက ၎င်းတို့ကို ဤနေရာတွင် ပြန်လည်ထုတ်ပေးနိုင်ပါသည်။ သင်၏ ပြန်လည်ရယူရေး ကုဒ်ဟောင်းများ ပျက်သွားပါမည်။
+    methods: နှစ်ဆင့်ခံလုံခြုံရေးနည်းလမ်းများ
+    otp: Authenticator အက်ပ်
+    recovery_codes: အရန်ပြန်လည်ရယူရေးကုဒ်များ
+    recovery_codes_regenerated: ပြန်လည်ရယူရေးကုဒ်များကို ပြန်ထုတ်ပေးပြီးပါပြီ
+    recovery_instructions_html: သင့်ဖုန်းသို့ ဝင်ရောက်သုံးစွဲခွင့် ဆုံးရှုံးခဲ့ဖူးပါက၊ သင့်အကောင့်သို့ ပြန်လည်ဝင်ရောက်ခွင့်ရရန် အောက်ပါ ပြန်လည်ရယူရေးကုဒ်များထဲမှ တစ်ခုကို အသုံးပြုနိုင်ပါသည်။ <strong>ပြန်လည်ရယူရေးကုဒ်များကို လုံခြုံအောင်ထားပါ။</strong> ဥပမာအားဖြင့်၊ သင်သည် ၎င်းတို့ကို ပရင့်ထုတ်ပြီး အခြားအရေးကြီးစာရွက်စာတမ်းများဖြင့် သိမ်းဆည်းနိုင်သည်။
+    webauthn: လုံခြုံရေးကီးများ
+  user_mailer:
+    appeal_approved:
+      action: သင့်အကောင့်သို့ သွားပါ
+      explanation: "%{appeal_date} တွင် သင်တင်သွင်းခဲ့သည့် %{strike_date} တွင် သင့်အကောင့်ကို ဆန့်ကျင်သည့် တိုင်ချက်၏ အယူခံဝင်မှုကို အတည်ပြုပြီးဖြစ်သည်။ သင့်အကောင့်သည် ကောင်းမွန်သောအနေအထားတွင် ရှိနေပြန်သည်။"
+      subject: "%{date} တွင် သင့်အယူခံဝင်ချက်ကို အတည်ပြုပြီးပါပြီ"
+      title: အယူခံဝင်သည်
+    appeal_rejected:
+      explanation: "%{appeal_date} တွင် သင်တင်သွင်းခဲ့သည့် %{strike_date} တွင် သင့်အကောင့်ကို ဆန့်ကျင်သည့် တိုင်ချက်၏ အယူခံဝင်မှုကို ပယ်ချခဲ့သည်။"
+      subject: "%{date} တွင် သင့်တင်ပြချက်ကို ပယ်ချခဲ့သည်"
+      title: အယူခံကို ပယ်ချခဲ့သည်
+    backup_ready:
+      explanation: သင့် Mastodon အကောင့် အရန်ကူးယူရန် တောင်းဆိုပြီးဖြစ်သည်။ ယခု ဒေါင်းလုဒ်လုပ်နိုင်ပါပြီ။
+      subject: သင့်မှတ်တမ်း ဒေါင်းလုဒ်ဆွဲရန် အသင့်ဖြစ်ပါပြီ
+      title: မှတ်တမ်းသိမ်းရန်
+    suspicious_sign_in:
+      change_password: သင်၏ စကားဝှက်ပြောင်းလဲပါ
+      details: ဤသည်မှာ အကောင့်ဝင်ရောက်ခြင်းအတွက် အသေးစိတ်အချက်များဖြစ်သည် -
+      explanation: IP လိပ်စာအသစ်တစ်ခုမှနေ၍ သင့်အကောင့်ဝင်ရောက်သည်ကို တွေ့ရှိခဲ့ပါသည်။
+      further_actions_html: သင် မဟုတ်ပါက သင့်အကောင့်လုံခြုံစေရန်အတွက် %{action} ကို ချက်ချင်းဆောင်ရွက်ပြီး နှစ်ဆင့်ခံလုံခြုံရေးစနစ်ဖွင့်ထားရန် အကြံပြုပါသည်။
+      subject: သင့်အကောင့်ကို IP လိပ်စာအသစ်တစ်ခုမှ ဝင်ရောက်အသုံးပြုလျက်ရှိသည်
+      title: အကောင့်ဝင်ရောက်မှုအသစ်
+    warning:
+      appeal: အယူခံတင်သွင်းပါ
+      appeal_description: အမှားအယွင်းတစ်ခုဟု သင်ယုံကြည်ပါက %{instance} ဝန်ထမ်းများထံ တင်ပြနိုင်သည်။
+      categories:
+        spam: Spam
+        violation: အကြောင်းအရာသည် အောက်ပါကွန်မြူနတီလမ်းညွှန်ချက်များကို ချိုးဖောက်သည်။
+      explanation:
+        delete_statuses: သင့်ပို့စ်အချို့သည် တစ်ခု သို့မဟုတ် တစ်ခုထက်ပိုသော ကွန်မြူနတီလမ်းညွှန်ချက်များကို ချိုးဖောက်ကြောင်း တွေ့ရှိပြီး %{instance} ၏ ကြီးကြပ်သူများမှ ဖယ်ရှားလိုက်ပါသည်။
+        disable: သင့်အကောင့်ကို အသုံးမပြုနိုင်တော့သော်လည်း သင့်ပရိုဖိုင်နှင့် အခြားအချက်အလက်များမှာ ကျန်ရှိနေမည်ဖြစ်သည်။ သင့်အချက်အလက်ကို မိတ္တူကူးရန်၊ အကောင့်သတ်မှတ်ချက်များကို ပြောင်းလဲရန် သို့မဟုတ် သင့်အကောင့်ဖျက်သိမ်းရန်တို့အတွက် အရန်သိမ်းဆည်းမှုအား သင် တောင်းဆိုနိုင်သည်။
+        silence: သင့်အကောင့်ကို ဆက်လက်အသုံးပြုနိုင်သော်လည်း သင့်စောင့်ကြည့်သူများသာ ဤဆာဗာတွင် သင့်ပို့စ်များကို မြင်နိုင်မည်ဖြစ်ပြီး အကြောင်းအရာခေါင်းစဉ်အမျိုးမျိုးမှ သင့်ကို ဖယ်ထုတ်ထားနိုင်သည်။ သို့သော် အခြားသူများက သင့်ကို စောင့်ကြည့်နေနိုင်ပါသေးသည်။
+        suspend: သင့်အကောင့်ကို အသုံးမပြုနိုင်တော့သည့်အပြင် ပရိုဖိုင်နှင့် အခြားအချက်အလက်များကိုလည်း အသုံးပြု၍မရတော့ပါ။ သင့်အချက်အလက်ကို ရက်ပေါင်း ၃၀ ခန့်အတွင်း အပြည့်အဝ မဖယ်ရှားမချင်း အချက်အလက်များ အရန်ကူးယူရန်အတွက် အကောင့်သို့ ဝင်ရောက်နိုင်ပါသေးသည်။ သို့သော် အကောင့်ရပ်ဆိုင်းထားမှုရှောင်လွှဲခြင်းမှ ကာကွယ်ရန်အတွက် အခြေခံအချက်အလက်အချို့ကို ကျွန်ုပ်တို့ ထိန်းသိမ်းထားရပါမည်။
+      reason: အကြောင်းပြချက် -
+      statuses: ကိုးကားထားသောပို့စ်များ -
+      subject:
+        delete_statuses: "%{acct} ရှိ သင့်ပို့စ်များကို ဖယ်ရှားလိုက်ပါပြီ"
+        disable: သင့်အကောင့် %{acct} ကို ပိတ်သိမ်းထားသည်
+        mark_statuses_as_sensitive: "%{acct} ရှိ သင့်ပို့စ်များကို သတိထားရသောပို့စ်များအဖြစ် အမှတ်အသားပြုထားသည်"
+        none: "%{acct} အတွက် သတိပေးချက်"
+        sensitive: "%{acct} ရှိ သင့်ပို့စ်များကို ယခုမှစပြီး သတိထားရသောပို့စ်များအဖြစ် အမှတ်အသားပြုပါမည်"
+        silence: သင့်အကောင့် %{acct} ကို ကန့်သတ်ထားသည်
+        suspend: သင့်အကောင့် %{acct} ကို ဆိုင်းငံ့ထားသည်
+      title:
+        delete_statuses: ပို့စ်များကို ဖယ်ရှားခဲ့သည်
+        disable: အကောင့်ပိတ်သွားပါပြီ။
+        mark_statuses_as_sensitive: သတိထားရသောပို့စ်များအဖြစ် အမှတ်အသားပြုပါ
+        none: သတိပေးချက်
+        sensitive: သတိထားရသောအကောင့်အဖြစ် အမှတ်အသားပြုပါ
+        silence: အကောင့်ကန့်သတ်ထားသည်
+        suspend: အကောင့်ရပ်ဆိုင်းထားသည်
+    welcome:
+      edit_profile_action: ပရိုဖိုင်ထည့်သွင်းရန်
+      edit_profile_step: ပရိုဖိုင်ဓာတ်ပုံတစ်ပုံ တင်ခြင်း၊ ဖော်ပြမည့်အမည် ပြောင်းလဲခြင်းနှင့် အခြားအရာများပြုလုပ်ခြင်းတို့ဖြင့် သင့်ပရိုဖိုင်ကို စိတ်ကြိုက်ပြင်ဆင်နိုင်ပါသည်။ စောင့်ကြည့်သူအသစ်များ သင့်ကိုစောင့်ကြည့်ခွင့်မပြုမီ ပြန်လည်သုံးသပ်ရန်အတွက် ဆုံးဖြတ်နိုင်ပါသည်။
+      explanation: ဤသည်မှာ သင် စတင်အသုံးပြုနိုင်ရန်အတွက် အကြံပြုချက်အချို့ဖြစ်ပါသည်
+      final_action: ပို့စ် တင်ရန်
+      final_step: 'ပို့စ်စပြီး တင်နိုင်ပါပြီ။ စောင့်ကြည့်သူများမရှိသေးသော်လည်း သင့်အများမြင်ပို့စ်များကို ဒေသတွင်းစာမျက်နှာ သို့မဟုတ် ဟက်ရှ်တက်စာမျက်နှာတို့တွင် အခြားသူများက မြင်နိုင်ပါသည်။ #introductions ဟက်ရှ်တက်ဖြင့် သင့်ကိုယ်သင် မိတ်ဆက်နိုင်ပါသည်။'
+      full_handle: ကိုယ်တိုင်ထိန်းချုပ်နိုင်သည်
+      full_handle_hint: ဤသည်မှာ သင့်သူငယ်ချင်းများကို အခြားဆာဗာတစ်ခုမှ မက်ဆေ့ချ်ပို့နိုင်ကြောင်း သို့မဟုတ် စောင့်ကြည့်နိုင်ကြောင်း အသိပေးပါမည်။
+      subject: Mastodon မှ လှိုက်လှဲစွာကြိုဆိုပါသည်။
+      title: "%{name} က ကြိုဆိုပါတယ်။"
+  users:
+    follow_limit_reached: လူ %{limit} ထက် ပိုပြီး စောင့်ကြည့်၍မရပါ
+    go_to_sso_account_settings: အထောက်အထားပေးသူ၏အကောင့်သတ်မှတ်ချက်များသို့ သွားပါ
+    invalid_otp_token: မမှန်ကန်သော နှစ်ဆင့်ခံလုံခြုံရေးကုဒ်
+    otp_lost_help_html: နှစ်ခုစလုံးကို ဝင်ရောက်ခွင့် ဆုံးရှုံးသွားပါက %{email} နှင့် ဆက်သွယ်နိုင်ပါသည်
+    seamless_external_login: ပြင်ပဝန်ဆောင်မှုမှတစ်ဆင့် အကောင့်ဝင်ထားသောကြောင့် စကားဝှက်နှင့် အီးမေးလ်သတ်မှတ်ချက်များကို မရနိုင်ပါ။
+    signed_in_as: အဖြစ် အကောင့်ဝင်ခဲ့သည် -
+  verification:
+    verification: စိစစ်ခြင်း
+  webauthn_credentials:
+    add: လုံခြုံရေးကီးအသစ်ထည့်ပါ
+    create:
+      error: သင့်လုံခြုံရေးကီးထည့်ရာတွင် ပြဿနာရှိနေသည်။ ထပ်စမ်းကြည့်ပါ။
+      success: လုံခြုံရေးကီး ထည့်ပြီးပါပြီ။
+    delete: ဖျက်ရန်
+    delete_confirmation: လုံခြုံရေးကီးဖျက်ရန် သေချာပါသလား။
+    description_html: "<strong>လုံခြုံရေးကီးအထောက်အထားစိစစ်ခြင်း</strong>ကို ဖွင့်ထားပါက လုံခြုံရေးကီးများထဲမှ တစ်ခုကို အကောင့်ဝင်ရောက်ရန်အတွက် အသုံးပြုရန်လိုအပ်ပါသည်"
+    destroy:
+      error: သင့်လုံခြုံရေးကီးကို ဖျက်ရာတွင် ပြဿနာရှိနေသည်။ ထပ်စမ်းကြည့်ပါ။
+      success: လုံခြုံရေးကီး ဖျက်ပြီးပါပြီ။
+    invalid_credential: လုံခြုံရေးကီးမမှန်ကန်ပါ
+    nickname_hint: သင့်လုံခြုံရေးကီးအသစ်၏ အမည်ပြောင်ကို ထည့်ပါ။
+    not_enabled: WebAuthn ကို သင် မဖွင့်ရသေးပါ
+    not_supported: ဤဘရောက်ဆာသည် လုံခြုံရေးကီးများကို မပံ့ပိုးပါ
+    otp_required: လုံခြုံရေးကီးများကို အသုံးပြုရန်အတွက် နှစ်ဆင့်ခံလုံခြုံရေးစနစ်စိစစ်ခြင်းကို ဦးစွာဖွင့်ပါ။
+    registered_on: "%{date} တွင် စာရင်းသွင်းထားသည်"
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index 500aadcbf..66a1b7f60 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -91,6 +91,7 @@ nl:
       moderation:
         active: Actief
         all: Alles
+        disabled: Uitgeschakeld
         pending: In afwachting
         silenced: Beperkt
         suspended: Opgeschort
@@ -133,6 +134,7 @@ nl:
       search: Zoeken
       search_same_email_domain: Andere gebruikers met hetzelfde e-maildomein
       search_same_ip: Andere gebruikers met hetzelfde IP-adres
+      security: Beveiliging
       security_measures:
         only_password: Alleen wachtwoord
         password_and_2fa: Wachtwoord en tweestapsverificatie
@@ -228,7 +230,7 @@ nl:
         update_user_role: Rol bijwerken
       actions:
         approve_appeal_html: "%{name} heeft het bezwaar tegen de moderatiemaatregel van %{target} goedgekeurd"
-        approve_user_html: "%{name} heeft het account van %{target} goedgekeurd"
+        approve_user_html: "%{name} heeft de registratie van %{target} goedgekeurd"
         assigned_to_self_report_html: "%{name} heeft rapportage %{target} aan zichzelf toegewezen"
         change_email_user_html: "%{name} veranderde het e-mailadres van gebruiker %{target}"
         change_role_user_html: "%{name} wijzigde de rol van %{target}"
@@ -427,6 +429,7 @@ nl:
         resolve: Domein opzoeken
         title: Nieuw e-maildomein blokkeren
       no_email_domain_block_selected: Er werden geen e-maildomeinblokkades gewijzigd, omdat er geen enkele werd geselecteerd
+      not_permitted: Niet toegestaan
       resolved_dns_records_hint_html: De domeinnaam slaat op de volgende MX-domeinen die uiteindelijk verantwoordelijk zijn voor het accepteren van e-mail. Het blokkeren van een MX-domein blokkeert aanmeldingen van elk e-mailadres dat hetzelfde MX-domein gebruikt, zelfs als de zichtbare domeinnaam anders is. <strong>Pas op dat u geen grote e-mailproviders blokkeert.</strong>
       resolved_through_html: Geblokkeerd via %{domain}
       title: Geblokkeerde e-maildomeinen
@@ -441,6 +444,7 @@ nl:
         private_comment_description_html: 'Om je te helpen bijhouden waar de geïmporteerde blokkades vandaan komen, worden de geïmporteerde blokkades met de volgende privé-opmerking aangemaakt: <q>%{comment}</q>'
         private_comment_template: Geïmporteerd van %{source} op %{date}
         title: Domeinblokkades importeren
+      invalid_domain_block: 'Een of meer domeinblokkades zijn overgeslagen vanwege de volgende fout(en): %{error}'
       new:
         title: Domeinblokkades importeren
       no_file: Geen bestand geselecteerd
@@ -472,6 +476,7 @@ nl:
       content_policies:
         comment: Interne reden
         description_html: Je kunt het beleid bepalen dat op de accounts van dit domein en alle subdomeinen van toepassing is.
+        limited_federation_mode_description_html: Je kunt kiezen of je federatie met dit domein wilt toestaan.
         policies:
           reject_media: Mediabestanden weigeren
           reject_reports: Rapportages weigeren
@@ -575,19 +580,23 @@ nl:
         mark_as_sensitive_description_html: De media in de gerapporteerde berichten worden gemarkeerd als gevoelig en er wordt een overtreding geregistreerd om toekomstige overtredingen van hetzelfde account sneller af te kunnen handelen.
         other_description_html: Bekijk meer opties voor het controleren van het gedrag van en de communicatie met het gerapporteerde account.
         resolve_description_html: Er wordt tegen het gerapporteerde account geen maatregel genomen, geen overtreding geregistreerd en de rapportage wordt gemarkeerd als opgelost.
-        silence_description_html: Het profiel zal alleen zichtbaar zijn voor diegenen die het al volgen of het handmatig opzoeken, waardoor het bereik ernstig wordt beperkt. Kan altijd worden teruggedraaid.
-        suspend_description_html: Het profiel en alle accountgegevens worden eerst ontoegankelijk gemaakt, totdat deze uiteindelijk onomkeerbaar worden verwijderd. Het is niet meer mogelijk om interactie te hebben met het account. Dit kan binnen 30 dagen worden teruggedraaid.
+        silence_description_html: Het account is alleen zichtbaar voor degenen die het al volgen of handmatig opzoeken, waardoor het bereik ernstig wordt beperkt. Dit kan altijd ongedaan worden gemaakt. Dit sluit alle rapporten tegen dit account af.
+        suspend_description_html: Het account en al zijn inhoud zullen niet toegankelijk zijn en uiteindelijk verwijderd worden en er zal geen interactie met het account mogelijk zijn. Dit is omkeerbaar binnen 30 dagen. Dit sluit alle rapporten tegen dit account af.
       actions_description_html: Beslis welke maatregel moet worden genomen om deze rapportage op te lossen. Wanneer je een (straf)maatregel tegen het gerapporteerde account neemt, krijgt het account een e-mailmelding, behalve wanneer de <strong>spam</strong>-categorie is gekozen.
+      actions_description_remote_html: Beslis welke actie moet worden ondernomen om deze rapportage op te lossen. Dit is alleen van invloed op hoe <strong>jouw</strong> server met dit externe account communiceert en de inhoud ervan beheert.
       add_to_report: Meer aan de rapportage toevoegen
       are_you_sure: Weet je het zeker?
       assign_to_self: Aan mij toewijzen
       assigned: Toegewezen moderator
       by_target_domain: Domein van gerapporteerde account
+      cancel: Annuleren
       category: Category
       category_description_html: De reden waarom dit account en/of inhoud werd gerapporteerd wordt aan het gerapporteerde account medegedeeld
       comment:
         none: Geen
       comment_description_html: 'Om meer informatie te verstrekken, schreef %{name}:'
+      confirm: Bevestigen
+      confirm_action: Bevestig moderatiemaatregel tegen @%{acct}
       created_at: Gerapporteerd op
       delete_and_resolve: Bericht verwijderen
       forwarded: Doorgestuurd
@@ -604,6 +613,7 @@ nl:
         placeholder: Beschrijf welke maatregelen zijn genomen of andere gerelateerde opmerkingen...
         title: Opmerkingen
       notes_description_html: Bekijk en laat opmerkingen achter voor andere moderatoren en voor jouw toekomstige zelf
+      processed_msg: 'Rapportage #%{id} succesvol afgehandeld'
       quick_actions_description_html: 'Neem een snelle maatregel of scroll naar beneden om de gerapporteerde inhoud te bekijken:'
       remote_user_placeholder: de externe gebruiker van %{instance}
       reopen: Rapportage heropenen
@@ -616,9 +626,28 @@ nl:
       status: Rapportages
       statuses: Gerapporteerde inhoud
       statuses_description_html: De problematische inhoud wordt aan het gerapporteerde account medegedeeld
+      summary:
+        action_preambles:
+          delete_html: 'Je staat op het punt om enkele berichten van <strong>@%{acct}</strong> te <strong>verwijderen</strong>. Dit zal:'
+          mark_as_sensitive_html: 'Je staat op het punt om enkele berichten van <strong>@%{acct}</strong> als <strong>gevoelig te markeren</strong>. Dit zal:'
+          silence_html: 'Je staat op het punt om het account van <strong>@%{acct}</strong> te <strong>beperken</strong>. Dit zal:'
+          suspend_html: 'Je staat op het punt om het account van <strong>@%{acct}</strong> <strong>op te schorten</strong>. Dit zal:'
+        actions:
+          delete_html: De aanstootgevende berichten verwijderen
+          mark_as_sensitive_html: De media in de aanstootgevende berichten als gevoelig markeren
+          silence_html: Het account van <strong>@%{acct}</strong> ernstig beperken, door diens profiel en inhoud alleen zichtbaar te maken aan mensen die dit account al volgen of aan mensen die het account handmatig opzoeken
+          suspend_html: Het account van <strong>@%{acct}</strong> opschorten, waarmee diens profiel en inhoud niet toegankelijk zijn en het onmogelijk is om interactie te hebben
+        close_report: 'Rapportage #%{id} als opgelost markeren'
+        close_reports_html: "<strong>Alle</strong> rapportages tegen <strong>@%{acct}</strong> als opgelost markeren"
+        delete_data_html: Het account en inhoud van <strong>@%{acct}</strong> over 30 dagen verwijderen, tenzij die in de tussentijd wordt gedeblokkeerd
+        preview_preamble_html: "<strong>@%{acct}</strong> zal een waarschuwing ontvangen met de volgende inhoud:"
+        record_strike_html: Registreer een overtreding van <strong>@%{acct}</strong> om je te helpen met het sneller afhandelen van toekomstige overtredingen van dit account
+        send_email_html: Een waarschuwingsmail naar <strong>@%{acct}</strong> sturen
+        warning_placeholder: Optionele aanvullende redenen voor de moderatie-actie.
       target_origin: Herkomst van de gerapporteerde accounts
       title: Rapportages
       unassign: Niet langer toewijzen
+      unknown_action_msg: 'Onbekende actie: %{action}'
       unresolved: Onopgelost
       updated_at: Bijgewerkt
       view_profile: Profiel bekijken
@@ -713,6 +742,8 @@ nl:
         preamble: Het tonen van interessante inhoud is van essentieel belang voor het aan boord halen van nieuwe gebruikers, die mogelijk niemand van Mastodon kennen. Bepaal hoe verschillende functies voor het ontdekken van inhoud en gebruikers op jouw server werken.
         profile_directory: Gebruikersgids
         public_timelines: Openbare tijdlijnen
+        publish_discovered_servers: Ontdekte servers publiceren
+        publish_statistics: Statistieken publiceren
         title: Ontdekken
         trends: Trends
       domain_blocks:
@@ -767,6 +798,7 @@ nl:
         suspend: "%{name} schortte het account %{target} op"
       appeal_approved: Bezwaar ingediend
       appeal_pending: Bezwaar in behandeling
+      appeal_rejected: Bezwaar afgewezen
     system_checks:
       database_schema_check:
         message_html: Niet alle databasemigraties zijn voltooid. Je moet deze uitvoeren om er voor te zorgen dat de applicatie blijft werken zoals het hoort
@@ -780,6 +812,12 @@ nl:
         message_html: Je hebt voor deze server geen regels opgesteld.
       sidekiq_process_check:
         message_html: Er draait geen Sidekiqproces voor de wachtrij(en) %{value}. Controleer je Sidekiqconfiguratie
+      upload_check_privacy_error:
+        action: Klik hier voor meer informatie
+        message_html: "<strong>Jouw webserver is verkeerd geconfigureerd. De privacy van je gebruikers is in gevaar.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Klik hier voor meer informatie
+        message_html: "<strong>Jouw objectopslag is verkeerd geconfigureerd. De privacy van je gebruikers is in gevaar.</strong>"
     tags:
       review: Status beoordelen
       updated_msg: Instellingen hashtag succesvol bijgewerkt
@@ -802,6 +840,7 @@ nl:
           other: Deze week door %{count} mensen gedeeld
         title: Trending links
         usage_comparison: Vandaag %{today} keer gedeeld, vergeleken met %{yesterday} keer gisteren
+      not_allowed_to_trend: Trending worden niet toegestaan
       only_allowed: Alleen goedgekeurde
       pending_review: In afwachting van beoordeling
       preview_card_providers:
@@ -933,6 +972,7 @@ nl:
   applications:
     created: Aanmaken toepassing geslaagd
     destroyed: Verwijderen toepassing geslaagd
+    logout: Uitloggen
     regenerate_token: Toegangscode opnieuw aanmaken
     token_regenerated: Opnieuw aanmaken toegangscode geslaagd
     warning: Wees voorzichtig met deze gegevens. Deel het nooit met iemand anders!
@@ -940,6 +980,8 @@ nl:
   auth:
     apply_for_account: Account aanvragen
     change_password: Wachtwoord
+    confirmations:
+      wrong_email_hint: Als dat e-mailadres niet correct is, kun je het wijzigen in je accountinstellingen.
     delete_account: Account verwijderen
     delete_account_html: Wanneer je jouw account graag wilt verwijderen, kun je dat <a href="%{path}">hier doen</a>. We vragen jou daar om een bevestiging.
     description:
@@ -967,6 +1009,8 @@ nl:
     resend_confirmation: Verstuur de bevestigingsinstructies nogmaals
     reset_password: Wachtwoord opnieuw instellen
     rules:
+      accept: Accepteren
+      back: Terug
       preamble: Deze zijn vastgesteld en worden gehandhaafd door de moderatoren van %{domain}.
       title: Enkele basisregels.
     security: Beveiliging
@@ -1114,7 +1158,7 @@ nl:
   featured_tags:
     add_new: Nieuwe toevoegen
     errors:
-      limit: Je hebt al het maximaal aantal hashtags uitgelicht
+      limit: Je hebt al het maximale aantal hashtags uitgelicht
     hint_html: "<strong>Wat zijn uitgelichte hashtags?</strong> Deze worden prominent op jouw openbare profiel getoond en stelt mensen in staat om jouw openbare berichten per hashtag te bekijken. Het zijn een goed hulpmiddel om creatieve werkzaamheden of langetermijnprojecten bij te houden."
   filters:
     contexts:
@@ -1158,8 +1202,6 @@ nl:
       index:
         hint: Dit filter is van toepassing om individuele berichten te selecteren, ongeacht andere criteria. Je kunt in de webomgeving meer berichten aan dit filter toevoegen.
         title: Gefilterde berichten
-  footer:
-    trending_now: Trends
   generic:
     all: Alles
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ nl:
     validation_errors:
       one: Er is iets niet helemaal goed! Bekijk onderstaande fout
       other: Er is iets niet helemaal goed! Bekijk onderstaande %{count} fouten
-  html_validator:
-    invalid_markup: 'bevat ongeldige HTML-opmaak: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Ongeldig CSV-bestand. Fout: %{error}'
@@ -1198,7 +1238,7 @@ nl:
     types:
       blocking: Blokkeerlijst
       bookmarks: Bladwijzers
-      domain_blocking: Lijst met genegeerde servers
+      domain_blocking: Lijst met geblokkeerde domeinen
       following: Volglijst
       muting: Negeerlijst
     upload: Uploaden
@@ -1226,7 +1266,7 @@ nl:
     title: Mensen uitnodigen
   lists:
     errors:
-      limit: Je hebt het maximaal aantal lijsten bereikt
+      limit: Je hebt het maximum aantal lijsten bereikt
   login_activities:
     authentication_methods:
       otp: tweestapsverificatie-app
@@ -1367,7 +1407,11 @@ nl:
       unrecognized_emoji: is geen bestaande emoji-reactie
   relationships:
     activity: Accountactiviteit
+    confirm_follow_selected_followers: Weet je zeker dat je de geselecteerde volgers wilt volgen?
+    confirm_remove_selected_followers: Weet je zeker dat je de geselecteerde volgers wilt verwijderen?
+    confirm_remove_selected_follows: Weet je zeker dat je de geselecteerde gevolgde accounts wilt verwijderen?
     dormant: Sluimerend
+    follow_failure: Kan sommige van de geselecteerde accounts niet volgen.
     follow_selected_followers: Geselecteerde volgers volgen
     followers: Volgers
     following: Volgend
@@ -1407,6 +1451,7 @@ nl:
       electron: Electron
       firefox: Firefox
       generic: Onbekende webbrowser
+      huawei_browser: Huawei Browser
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1416,6 +1461,7 @@ nl:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Onbekende browser
       weibo: Weibo
     current_session: Huidige sessie
     description: "%{browser} op %{platform}"
@@ -1428,9 +1474,10 @@ nl:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: Onbekend platform
+      unknown_platform: Onbekend platform
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1456,7 +1503,7 @@ nl:
     notifications: Meldingen
     preferences: Voorkeuren
     profile: Profiel
-    relationships: Volgers en gevolgden
+    relationships: Volgers en gevolgde accounts
     statuses_cleanup: Automatisch berichten verwijderen
     strikes: Vastgestelde overtredingen
     two_factor_authentication: Tweestapsverificatie
@@ -1504,7 +1551,7 @@ nl:
     sign_in_to_participate: Meld je aan om aan dit gesprek mee te doen
     title: '%{name}: "%{quote}"'
     visibilities:
-      direct: Direct
+      direct: Privébericht
       private: Alleen volgers
       private_long: Alleen aan jouw volgers tonen
       public: Openbaar
@@ -1520,8 +1567,8 @@ nl:
     ignore_reblogs: Boosts negeren
     interaction_exceptions: Uitzonderingen op basis van interacties
     interaction_exceptions_explanation: Merk op dat er geen garantie is dat berichten worden verwijderd, wanneer eenmaal het aantal favorieten of boosts boven de ingestelde grenswaarde zijn geweest.
-    keep_direct: Directe berichten behouden
-    keep_direct_hint: Verwijdert geen enkel directe bericht van jou
+    keep_direct: Privéberichten behouden
+    keep_direct_hint: Verwijdert geen enkel privébericht van jou
     keep_media: Berichten met mediabijlagen behouden
     keep_media_hint: Verwijdert geen enkel bericht met mediabijlagen
     keep_pinned: Vastgemaakte berichten behouden
@@ -1643,12 +1690,13 @@ nl:
       title: Welkom aan boord %{name}!
   users:
     follow_limit_reached: Je kunt niet meer dan %{limit} accounts volgen
+    go_to_sso_account_settings: Ga naar de accountinstellingen van je identiteitsprovider
     invalid_otp_token: Ongeldige tweestaps-toegangscode
     otp_lost_help_html: Als je toegang tot beiden kwijt bent geraakt, neem dan contact op via %{email}
     seamless_external_login: Je bent ingelogd via een externe dienst, daarom zijn wachtwoorden en e-mailinstellingen niet beschikbaar.
     signed_in_as: 'Ingelogd als:'
   verification:
-    explanation_html: 'Je kunt <strong>jezelf verifiëren als de eigenaar van de links in de metadata van jouw profiel</strong>. Hiervoor moet op de gelinkte website een link terug naar jouw Mastodonprofiel staan. Deze link <strong>moet</strong> het <code>rel="me"</code>-attribuut bevatten. De omschrijving van de link maakt niet uit. Hier is een voorbeeld:'
+    explanation_html: 'Je kunt <strong>jezelf verifiëren als de eigenaar van de links in de metadata van jouw profiel</strong>. Hiervoor moet op de gelinkte website een link terug naar jouw Mastodonprofiel staan. Na het toevoegen van de link moet je hier mogelijk terugkomen en je profiel opnieuw bewaren om de verificatie te bevestigen. Deze link <strong>moet</strong> het <code>rel="me"</code>-attribuut bevatten. De omschrijving van de link maakt niet uit. Hier is een voorbeeld:'
     verification: Verificatie
   webauthn_credentials:
     add: Nieuwe beveiligingssleutel toevoegen
diff --git a/config/locales/nn.yml b/config/locales/nn.yml
index 18823e55c..ea4d2e09d 100644
--- a/config/locales/nn.yml
+++ b/config/locales/nn.yml
@@ -91,6 +91,7 @@ nn:
       moderation:
         active: Aktiv
         all: Alle
+        disabled: Skrudd av
         pending: Ventar på svar
         silenced: Avgrensa
         suspended: Utvist
@@ -133,6 +134,7 @@ nn:
       search: Søk
       search_same_email_domain: Andre brukarar med same e-postdomene
       search_same_ip: Andre brukarar med same IP
+      security: Tryggleik
       security_measures:
         only_password: Kun passord
         password_and_2fa: Passord og 2FA
@@ -421,6 +423,7 @@ nn:
         resolve: Løs domene
         title: Ny blokkeringsoppføring av e-postdomene
       no_email_domain_block_selected: Blokkering av e-post-domener vart ikkje endra sidan ingen var valde
+      not_permitted: Ikkje tillate
       resolved_dns_records_hint_html: Domenenamnet gjer oppslag til desse MX-domenene som til sist er ansvarlige for å motta e-post. Blokkering av eit MX-domene vil blokkere registreringar frå alle e-postadresser som bruker same MX-domene, sjølv om det synlige domenenavnet skulle vera noko anna. <strong>Pass på så du ikkje blokkerer dei store e-postleverandørane.</strong>
       resolved_through_html: Løyst gjennom %{domain}
       title: Blokkerte e-postadresser
@@ -435,6 +438,7 @@ nn:
         private_comment_description_html: 'For å hjelpa deg med å halda oversikt over kvar importerte blokkeringar kjem frå, vil dei bli oppretta med fylgjande private kommentar: <q>%{comment}</q>'
         private_comment_template: Importert frå %{source} den %{date}
         title: Importer domeneblokkeringar
+      invalid_domain_block: 'Hoppa over ei eller fleire domeneblokker på grunn av fylgjande feil: %{error}'
       new:
         title: Importer domeneblokkeringar
       no_file: Inga fil vald
@@ -466,6 +470,7 @@ nn:
       content_policies:
         comment: Internt notat
         description_html: Du kan definere innholdsregler som vil bli brukt på alle kontoer fra dette domenet og hvilket som helst av underdomenene.
+        limited_federation_mode_description_html: Du kan velja om du vil tillata føderering med dette domenet.
         policies:
           reject_media: Avvis media
           reject_reports: Avvis rapporter
@@ -569,19 +574,23 @@ nn:
         mark_as_sensitive_description_html: Mediene i dei rapporterte innlegga vil verte markerte som ømtolege, og ein merknad vil verte lagra for å hjelpe deg å eskalera ved framtidige regelbrot frå same konto.
         other_description_html: Sjå fleire alternativ når det gjeld kontroll av kontoåtferd og tilpassing av kommunikasjonen til den rapporterte kontoen.
         resolve_description_html: Ingen handling utføres mot den rapporterte kontoen, ingen advarsel gis, og rapporten lukkes.
-        silence_description_html: Profilen vil kun være synlig for dem som allerede følger den eller manuelt slår den opp, noe som sterkt begrenser dens rekkevidde. Kan alltid tilbakestilles.
-        suspend_description_html: Profil med innhald vil bli utilgjengeleg og til sist sletta. Det vil ikkje vera mogleg å samhandla med kontoen. Avgjerda kan opphevast innan 30 dagar.
+        silence_description_html: Profilen vil berre vera synleg for dei som allereie fylgjer han eller søkjer han opp manuelt, noko som gjev profilen mykje mindre rekkjevidd. Du kan oppheva stenginga seinare. Dette avsluttar alle rapportar om brukarkontoen.
+        suspend_description_html: Brukarkontoen og alt innhaldet vil bli utilgjengeleg og til slutt sletta, og det vil vera uråd å samhandla med brukaren. Du kan angra dette innan 30 dagar. Dette avsluttar alle rapportar om kontoen.
       actions_description_html: Avgjer kva som skal gjerast med denne rapporteringa. Dersom du utfører straffetiltak mot den rapporterte kontoen, vil dei motta ein e-post – så sant du ikkje har valt kategorien <strong>Spam</strong>.
+      actions_description_remote_html: Avgjer kva du vil gjera for å løysa denne rapporten. Dette påverkar berre korleis tenaren <strong>din</strong> kommuniserer med kontoen på ein annan tenar, og korleis tenaren din handterer innhald derifrå.
       add_to_report: Legg til i rapporten
       are_you_sure: Er du sikker?
       assign_to_self: Tilegn til meg
       assigned: Tilsett moderator
       by_target_domain: Domenet av rapportert bruker
+      cancel: Avbryt
       category: Kategori
       category_description_html: Årsaka til at kontoen og/eller innhaldet vart rapportert vil bli inkludert i kommunikasjonen med den rapporterte kontoen
       comment:
         none: Ingen
       comment_description_html: 'For å gje meir informasjon, skreiv %{name}:'
+      confirm: Stadfest
+      confirm_action: Stadfest at du vil moderera brukarkontoen @%{acct}
       created_at: Rapportert
       delete_and_resolve: Slett innlegg
       forwarded: Videresendt
@@ -598,6 +607,7 @@ nn:
         placeholder: Beskriv hvilke handlinger som har blitt tatt, eller andre relaterte oppdateringer...
         title: Merknad
       notes_description_html: Sjå og skriv merknadar til andre moderatorar og ditt framtidige sjølv
+      processed_msg: 'Du har handsama rapport #%{id}'
       quick_actions_description_html: 'Utfør ei handling eller bla ned for å sjå det rapporterte innhaldet:'
       remote_user_placeholder: den eksterne brukaren frå %{instance}
       reopen: Opn rapport igjen
@@ -610,9 +620,28 @@ nn:
       status: Status
       statuses: Rapportert innhold
       statuses_description_html: Støytande innhald vil bli inkludert i kommunikasjonen med den rapporterte kontoen
+      summary:
+        action_preambles:
+          delete_html: 'Du er i ferd med å <strong>fjerna</strong> nokre av innlegga til <strong>@%{acct}</strong>. Det vil:'
+          mark_as_sensitive_html: 'Du er i ferd med å <strong>markera</strong> nokre av innlegga til <strong>@%{acct}</strong> som <strong>sensitive</strong>. Dette vil:'
+          silence_html: 'Du er i ferd med å <strong>avgrensa</strong> kontoen til <strong>@%{acct}</strong>. Dette vil:'
+          suspend_html: 'Du er i ferd med å <strong>stenga</strong> kontoen til <strong>@%{acct}</strong>. Dette vil:'
+        actions:
+          delete_html: Fjerna dei påtala innlegga
+          mark_as_sensitive_html: Markera dei påtala mediefilene som sensitive
+          silence_html: Sterkt avgrensa korleis <strong>@%{acct}</strong> kan samhandla ved å gjera brukarprofilen og innhaldet synleg berre for folk som allereie fylgjer brukarkontoen eller søkjer han opp manuelt
+          suspend_html: Stengja brukarkontoen til <strong>@%{acct}</strong>, slik at brukarprofilen og innhaldet blir utilgjengeleg og umogleg å samhandla med
+        close_report: Marker rapport nr. %{id} som løyst
+        close_reports_html: Marker <strong>alle</strong> rapportane om <strong>@%{acct}</strong> som løyste
+        delete_data_html: Slett brukarprofilen og innhaldet til<strong>@%{acct}</strong> om 30 dagar frå no med mindre brukarkontoen blir opna att
+        preview_preamble_html: "<strong>@%{acct}</strong> vil få ei åtvaring med dette innhaldet:"
+        record_strike_html: Noter at ei handling er gjort mot <strong>@%{acct}</strong> for å hjelpa deg å handtera framtidige overtramp frå denne brukarkontoen
+        send_email_html: Send ein åtvaringsepost til <strong>@%{acct}</strong>
+        warning_placeholder: Eventuelle fleire grunnar for å modereringshandlinga.
       target_origin: Opprinnelse for innrapportert konto
       title: Rapportar
       unassign: Avset
+      unknown_action_msg: 'Ukjend handling: %{action}'
       unresolved: Uløyst
       updated_at: Oppdatert
       view_profile: Vis profil
@@ -707,6 +736,8 @@ nn:
         preamble: Å framheva interessant innhald er vitalt i mottakinga av nye brukarar som ikkje nødvendigvis kjenner nokon på Mastodon. Kontroller korleis oppdagingsfunksjonane på tenaren din fungerar.
         profile_directory: Profilkatalog
         public_timelines: Offentlege tidsliner
+        publish_discovered_servers: Publiser oppdaga tenarar
+        publish_statistics: Publiser statistikk
         title: Oppdaging
         trends: Trender
       domain_blocks:
@@ -761,6 +792,7 @@ nn:
         suspend: "%{name} utviste %{target} sin konto"
       appeal_approved: Klage tatt til følge
       appeal_pending: Klage behandles
+      appeal_rejected: Anken er avvist
     system_checks:
       database_schema_check:
         message_html: Det venter på databaseoverføringer. Vennligst kjør disse for å sikre at applikasjonen oppfører seg som forventet
@@ -796,6 +828,7 @@ nn:
           other: Delt av %{count} personer i løpet av den siste uken
         title: Populære lenkjer
         usage_comparison: Delt %{today} ganger i dag, sammenlignet med %{yesterday} i går
+      not_allowed_to_trend: Har ikkje lov å trenda
       only_allowed: Kun tillatne
       pending_review: Avventer gjennomgang
       preview_card_providers:
@@ -822,12 +855,13 @@ nn:
           tag_accounts_measure: unike bruksområder
           tag_languages_dimension: Mest brukte språk
           tag_servers_dimension: Mest brukte servere
-          tag_servers_measure: forskjellige servere
-          tag_uses_measure: samlet bruk
+          tag_servers_measure: ulike tenarar
+          tag_uses_measure: brukarar totalt
         description_html: Dette er emneknagger som for øyeblikket vises i mange innlegg som serveren din ser. Det kan hjelpe dine brukere med å finne ut hva folk snakker mest om i øyeblikket. Ingen emneknagger vises offentlig før du godkjenner dem.
         listable: Kan bli foreslått
+        no_tag_selected: Ingen merkelappar vart endra fordi ingen var valde
         not_listable: Vil ikke bli foreslått
-        not_trendable: Kunne ikke vises under trender
+        not_trendable: Kjem ikkje til å syna under trendar
         not_usable: Kan ikke brukes
         peaked_on_and_decaying: Nådde toppen %{date}, nå på vei ned
         title: Populære emneknagger
@@ -876,6 +910,9 @@ nn:
         sensitive: å merke kontoen sin som følsom
         silence: for å begrense deres konto
         suspend: for å suspendere kontoen deres
+      body: "%{target} ankar på ei modereringsavgjerd av %{action_taken_by} den %{date}, som var %{type}. Dei skreiv:"
+      next_steps: Du kan godkjenna anken for å endra modereringsavgjerda, eller du kan oversjå anken.
+      subject: "%{username} ankar ei modereringsavgjer på %{instance}"
     new_pending_account:
       body: Detaljer om den nye kontoen er nedenfor. Du kan godkjenne eller avvise denne søknaden.
       subject: Ny konto opp til vurdering på %{instance} (%{username})
@@ -884,13 +921,16 @@ nn:
       body_remote: Nokon frå %{domain} har meldt %{target}
       subject: Ny rapport for %{instance} (#%{id})
     new_trends:
+      body: 'Du må sjå gjennom desse elementa før dei kan visast offentleg:'
       new_trending_links:
         title: Populære lenker
       new_trending_statuses:
         title: Populære innlegg
       new_trending_tags:
-        title: Populære emneknagger
-      subject: Ny trender for gjennomsyn av %{instance}
+        no_approved_tags: Det er ingen godkjende populære emneknaggar no.
+        requirements: 'Alle desse kandidatane kan stiga høgare enn den godkjende populære emneknaggen #%{rank}, som er #%{lowest_tag_name} med ei plassering på %{lowest_tag_score}.'
+        title: Populære emneknaggar
+      subject: Nye trendar å sjå gjennom på %{instance}
   aliases:
     add_new: Lag psevdonym
     created_msg: Laga eit nytt kallenamn. No kan du setja i gang med flyttinga frå den gamle kontoen.
@@ -912,7 +952,7 @@ nn:
     toot_layout: Tutoppsett
   application_mailer:
     notification_preferences: Endr e-post-innstillingane
-    salutation: "%{name},"
+    salutation: Hei %{name},
     settings: 'Endr e-post-innstillingar: %{link}'
     view: 'Sjå:'
     view_profile: Sjå profil
@@ -920,6 +960,7 @@ nn:
   applications:
     created: Søknad laga
     destroyed: Søknad sletta
+    logout: Logg ut
     regenerate_token: Lag tilgangsnykel på nytt
     token_regenerated: Tilgangsnykel laga på nytt
     warning: Ver varsam med dette datumet. Aldri del det med nokon!
@@ -927,6 +968,8 @@ nn:
   auth:
     apply_for_account: Søk om ein konto
     change_password: Passord
+    confirmations:
+      wrong_email_hint: Viss epostadressa er feil, kan du endra ho i kontoinnstillingane.
     delete_account: Slett konto
     delete_account_html: Om du vil sletta kontoen din, kan du <a href="%{path}">gå hit</a>. Du vert spurd etter stadfesting.
     description:
@@ -954,6 +997,8 @@ nn:
     resend_confirmation: Send stadfestingsinstruksjonar på nytt
     reset_password: Attstill passord
     rules:
+      accept: Godkjenn
+      back: Attende
       preamble: Disse angis og håndheves av %{domain}-moderatorene.
       title: Noen grunnregler.
     security: Tryggleik
@@ -1101,7 +1146,7 @@ nn:
   featured_tags:
     add_new: Legg til ny
     errors:
-      limit: Du har allereie så mange emneknaggar som det går an å ha
+      limit: Du har allereie framheva så mange emneknaggar som det går an å gjera
     hint_html: "<strong>Hva er utvalgte emneknagger?</strong> De vises frem tydelig på din offentlige profil, og lar folk bla i dine offentlige innlegg som spesifikt har de emneknaggene. De er et bra verktøy for å holde styr på kreative verk eller langtidsprosjekter."
   filters:
     contexts:
@@ -1112,63 +1157,43 @@ nn:
       thread: Samtalar
     edit:
       add_keyword: Legg til stikkord
-      keywords: Nøkkelord
+      keywords: Stikkord
       statuses: Individuelle innlegg
+      statuses_hint_html: Dette filteret gjeld for utvalde, individuelle innlegg, uansett om dei passar til stikkorda under. <a href="%{path}">Sjå gjennom eller fjern innlegg frå filteret</a>.
       title: Endr filter
     errors:
+      deprecated_api_multiple_keywords: Du kan ikkje endra desse parametrane frå dette programmet fordi dei gjeld for meir enn eitt filterord. Bruk eit nyare program, eller nettsida.
       invalid_context: Ingen eller ugild kontekst gjeve
     index:
-      contexts: Filtre i %{contexts}
+      contexts: Filter i %{contexts}
       delete: Slett
       empty: Du har ingen filtre.
-      expires_in: Utløper om %{distance}
-      expires_on: Utløper den %{date}
-      keywords:
-        one: "%{count} nøkkelord"
-        other: "%{count} nøkkelorder"
-      statuses:
-        one: "%{count} innlegg"
-        other: "%{count} innlegger"
-      statuses_long:
-        one: "%{count} enkeltinnlegg skjult"
-        other: "%{count} individuelle innlegger skjult"
+      expires_in: Går ut om %{distance}
+      expires_on: Går ut %{date}
       title: Filter
     new:
       save: Lagre nytt filter
       title: Legg til nytt filter
     statuses:
-      back_to_filter: Tilbake til filter
+      back_to_filter: Tilbake til filteret
       batch:
-        remove: Fjern fra filter
+        remove: Fjern frå filteret
       index:
-        hint: Dette filteret gjelder for å velge individuelle innlegg uavhengig av andre kriterier. Du kan legge til flere innlegg til dette filteret fra webgrensesnittet.
+        hint: Dette filteret gjeld for utvalde, individuelle innlegg, uavhengig av andre kriterium. Du kan leggja til fleire innlegg til dette filteret frå nettsida.
         title: Filtrerte innlegg
-  footer:
-    trending_now: Populært no
   generic:
     all: Alle
-    all_items_on_page_selected_html:
-      one: "<strong>%{count}</strong> element på denne siden er valgt."
-      other: Alle <strong>%{count}</strong> elementer på denne siden er valgt.
-    all_matching_items_selected_html:
-      one: "<strong>%{count}</strong> element som matcher søket ditt er valgt."
-      other: Alle <strong>%{count}</strong> elementer som matcher søket velges.
     changes_saved_msg: Alle endringane vart lagra!
     copy: Kopier
     delete: Slett
-    deselect: Fjern all merking
+    deselect: Vel ingen
     none: Ingen
     order_by: Sorter etter
     save_changes: Lagr endringar
-    select_all_matching_items:
-      one: Velg %{count} element som samsvarer med søket ditt.
-      other: Velg alle %{count} elementer som samsvarer med søket ditt.
     today: i dag
     validation_errors:
       one: Noe er ikke helt riktig ennå. Vennligst se etter en gang til
       other: Noe er ikke helt riktig ennå. Det er ennå %{count} feil å rette på
-  html_validator:
-    invalid_markup: 'rommar ugild HTML-kode: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Ugyldig CSV-fil. Feil: %{error}'
@@ -1177,7 +1202,7 @@ nn:
       merge: Set saman
       merge_long: Hald på eksisterande data og legg til nye
       overwrite: Skriv over
-      overwrite_long: Erstatt gjeldende med de nye
+      overwrite_long: Byt ut dei noverande oppføringane med dei nye
     preface: Du kan henta inn data som du har eksportert frå ein annan tenar, som t.d. ei liste over folka du fylgjer eller blokkerer.
     success: Dataa dine vart lasta opp og vert no handsama så fort som mogeleg
     types:
@@ -1216,7 +1241,7 @@ nn:
     authentication_methods:
       otp: to-faktor autentiseringsapp
       password: passord
-      sign_in_token: e-post sikkerhetskode
+      sign_in_token: tryggingskode på epost
       webauthn: sikkerhetsnøkler
     description_html: Hvis du ser aktivitet som du ikke gjenkjenner, bør du vurdere å endre passordet ditt og aktivere to-trinnsinnlogging.
     empty: Ingen innloggingshistorikk er tilgjengelig
@@ -1266,7 +1291,7 @@ nn:
     carry_mutes_over_text: Denne brukeren flyttet fra %{acct}, som du hadde dempet.
     copy_account_note_text: 'Denne brukeren flyttet fra %{acct}, her var dine tidligere notater om dem:'
   navigation:
-    toggle_menu: Vis/Skjul meny
+    toggle_menu: Vis/gøym menyen
   notification_mailer:
     admin:
       report:
@@ -1292,7 +1317,7 @@ nn:
       subject: Du vart nemnd av %{name}
       title: Ny nemning
     poll:
-      subject: En avstemming av %{name} er avsluttet
+      subject: Meiningsmålinga frå %{name} er avslutta
     reblog:
       body: 'Statusen din vart framheva av %{name}:'
       subject: "%{name} framheva statusen din"
@@ -1352,7 +1377,11 @@ nn:
       unrecognized_emoji: er ikke en gjenkjent emoji
   relationships:
     activity: Kontoaktivitet
+    confirm_follow_selected_followers: Er du sikker på at du ynskjer å fylgja dei valde fylgjarane?
+    confirm_remove_selected_followers: Er du sikker på at du ynskjer å fjerna dei valde fylgjarane?
+    confirm_remove_selected_follows: Er du sikker på at du ynskjer å fjerna det valde følgjet?
     dormant: I dvale
+    follow_failure: Greidde ikkje fylgja alle kontoane du valde.
     follow_selected_followers: Følg valgte tilhengere
     followers: Følgere
     following: Følginger
@@ -1373,7 +1402,7 @@ nn:
     errors:
       invalid_rules: refererer ikke til gyldige regler
   rss:
-    content_warning: 'Innholdsadvarsel:'
+    content_warning: 'Innhaldsvarsel:'
     descriptions:
       account: Offentlige innlegg fra @%{acct}
       tag: 'Offentlige innlegg merket med #%{hashtag}'
@@ -1392,6 +1421,7 @@ nn:
       electron: Electron
       firefox: Firefox
       generic: Ukjend lesar
+      huawei_browser: Huawei-nettlesaren
       ie: Internet Explorer
       micro_messenger: Micromessenger
       nokia: Nokia S40 Ovi-lesar
@@ -1401,6 +1431,7 @@ nn:
       qq: QQ-lesar
       safari: Safari
       uc_browser: QQ-lesar
+      unknown_browser: Ukjend nettlesar
       weibo: Weibo
     current_session: Noverande økt
     description: "%{browser} på %{platform}"
@@ -1413,9 +1444,10 @@ nn:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: IOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: ukjend plattform
+      unknown_platform: Ukjend plattform
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1522,7 +1554,7 @@ nn:
       '7889238': 3 månader
     min_age_label: Aldersterskel
     min_favs: Behold innlegg som er favorittmarkert av minst
-    min_favs_hint: Sletter ingen av dine innlegg som har mottatt minst dette antalet favorittmerkingar. Lat vere blank for å slette innlegg uavhengig av antal favorittmerkingar
+    min_favs_hint: Slettar ingen av innlegga dine som har fått minst så mange favorittar. La det stå tomt for å sletta innlegga uansett kor mange favorittar dei har fått
     min_reblogs: Behold innlegg fremhevet av minst
     min_reblogs_hint: Sletter ikke noen av dine innlegg som har blitt fremhevet minst dette antall ganger. La stå tom for å slette innlegg uavhengig av antall fremhevinger
   stream_entries:
@@ -1627,7 +1659,6 @@ nn:
     seamless_external_login: Du er logga inn gjennom eit eksternt reiskap, so passord og e-postinstillingar er ikkje tilgjengelege.
     signed_in_as: 'Logga inn som:'
   verification:
-    explanation_html: 'Du kan <strong>bekrefte at du selv er eieren av lenkene i din profilmetadata</strong>. For å gjøre det, må det tillenkede nettstedet inneholde en lenke som fører tilbake til Mastodon-profilen din. Lenken tilbake <strong>må</strong> ha en <code>rel="me"</code>-attributt. Tekstinnholdet til lenken er irrelevant. Her er et eksempel:'
     verification: Stadfesting
   webauthn_credentials:
     add: Legg til ny sikkerhetsnøkkel
diff --git a/config/locales/no.yml b/config/locales/no.yml
index 5c5900184..9f09db57e 100644
--- a/config/locales/no.yml
+++ b/config/locales/no.yml
@@ -91,6 +91,7 @@
       moderation:
         active: Aktive
         all: Alle
+        disabled: Deaktivert
         pending: Avventer
         silenced: Begrenset
         suspended: Utvist
@@ -133,6 +134,7 @@
       search: Søk
       search_same_email_domain: Andre brukere med samme E-postdomene
       search_same_ip: Andre brukere med den samme IP-en
+      security: Sikkerhet
       security_measures:
         only_password: Bare passord
         password_and_2fa: Passord og 2FA
@@ -427,6 +429,7 @@
         resolve: Løs domene
         title: Ny blokkeringsoppføring av e-postdomene
       no_email_domain_block_selected: Ingen e-postdomeneblokker ble endret da ingen ble valgt
+      not_permitted: Ikke tillatt
       resolved_dns_records_hint_html: Domenenavnet løser seg til følgende MX-domener som er til slutt ansvarlige for å motta e-post. Blokkering av et MX-domene vil blokkere signaler fra en hvilken som helst e-postadresse som bruker samme MX-domene, selv om det synlige domenenavnet er annerledes. <strong>Vær forsiktig så du ikke blokkerer store e-posttilbydere.</strong>
       resolved_through_html: Løst gjennom %{domain}
       title: Blokkering av e-postdomene
@@ -441,6 +444,7 @@
         private_comment_description_html: 'For å hjelpe deg med å holde oversikt over hvor importerte blokkeringer kommer fra, vil de bli opprettet med følgende private kommentar: <q>%{comment}</q>'
         private_comment_template: Importert fra %{source} den %{date}
         title: Importer domeneblokkeringer
+      invalid_domain_block: 'En eller flere domeneblokker ble hoppet over på grunn av følgende feil(er): %{error}'
       new:
         title: Importer domeneblokkeringer
       no_file: Ingen fil valgt
@@ -575,19 +579,23 @@
         mark_as_sensitive_description_html: Mediene i de rapporterte innleggene vil bli merket som følsomme, og en advarsel vil bli notert for å hjelpe deg eskalere ved fremtidige overtredelser fra samme konto.
         other_description_html: Se flere alternativer for å kontrollere kontoens atferd og tilpasse kommunikasjonen til den oppgitte kontoen.
         resolve_description_html: Ingen handling utføres mot den rapporterte kontoen, ingen advarsel gis, og rapporten lukkes.
-        silence_description_html: Profilen vil kun være synlig for dem som allerede følger den eller manuelt slår den opp, noe som sterkt begrenser dens rekkevidde. Kan alltid tilbakestilles.
-        suspend_description_html: Profilen og alt innholdet blir utilgjengelig inntil det til slutt blir slettet. Samhandle med kontoen vil være umulig. Reversibelt innen 30 dager.
+        silence_description_html: Profilen vil kun være synlig for dem som allerede følger den eller manuelt slår den opp, noe som sterkt begrenser dens rekkevidde. Kan alltid tilbakestilles. Lukker alle rapporter mot denne kontoen.
+        suspend_description_html: Kontoen og alt dens innhold vil være utilgjengelig og til slutt slettet, og det vil ikke være mulig å bruke den. Reversibel innen 30 dager. Lukker alle rapporter mot denne kontoen.
       actions_description_html: Velg hvilke tiltak som skal treffes for å løse denne rapporten. Dersom du tar noen entydige tiltak mot den oppgitte kontoen, blir det sendt en e-post til dem, unntatt når <strong>Søppelpost</strong> -kategorien er valgt.
+      actions_description_remote_html: Velg hvilke tiltak som skal treffes for å løse denne rapporten. Dette påvirker bare hvordan <strong>din</strong> server kommuniserer med denne eksterne kontoen og håndterer innholdet.
       add_to_report: Legg til mer i rapporten
       are_you_sure: Er du sikker?
       assign_to_self: Tilegn til meg
       assigned: Tilegnet moderator
       by_target_domain: Domenet av rapportert bruker
+      cancel: Avbryt
       category: Kategori
       category_description_html: Årsaken til at denne kontoen og/eller innholdet er blitt rapportert vil bli henvist i forbindelse med den rapporterte kontoen
       comment:
         none: Ingen
       comment_description_html: 'For å gi mer informasjon, %{name} skrev:'
+      confirm: Bekreft
+      confirm_action: Bekreft moderasjonshandling mot @%{acct}
       created_at: Rapportert
       delete_and_resolve: Slettede innlegg
       forwarded: Videresendt
@@ -604,6 +612,7 @@
         placeholder: Beskriv hvilke handlinger som har blitt tatt, eller andre relaterte oppdateringer...
         title: Notater
       notes_description_html: Se og skriv notater til andre moderatorer og deg selv i fremtiden
+      processed_msg: 'Rapport #%{id} er behandlet'
       quick_actions_description_html: 'Ta en rask handling eller bla ned for å se rapportert innhold:'
       remote_user_placeholder: ekstern bruker fra %{instance}
       reopen: Gjenåpne rapporten
@@ -616,6 +625,12 @@
       status: Status
       statuses: Rapportert innhold
       statuses_description_html: Innholdet som tilbys, vil bli nevnt i forbindelse med den rapporterte kontoen
+      summary:
+        action_preambles:
+          delete_html: 'Du er i ferd med å <strong>fjerne</strong> noe av <strong>@%{acct}</strong> sine innlegg. Dette vil:'
+          mark_as_sensitive_html: 'Du er i ferd med å <strong>markere</strong> noen av <strong>@%{acct}</strong> sine innlegg som <strong>sensitivt</strong>. Dette vil:'
+          silence_html: 'Du er i ferd med å <strong>avgrense</strong> <strong>@%{acct}</strong> sin konto. Dette vil:'
+          suspend_html: 'Du er i ferd med å <strong>stoppe</strong> <strong>@%{acct}</strong> sin konto. Dette vil:'
       target_origin: Opprinnelse for innrapportert konto
       title: Rapporter
       unassign: Fjern tilegning
@@ -1077,8 +1092,6 @@
     storage: Medialagring
   featured_tags:
     add_new: Legg til ny
-    errors:
-      limit: Du har allerede fremhevet det maksimale antal hashtags
     hint_html: "<strong>Hva er utvalgte emneknagger?</strong> De vises frem tydelig på din offentlige profil, og lar folk bla i dine offentlige innlegg som spesifikt har de emneknaggene. De er et bra verktøy for å holde styr på kreative verk eller langtidsprosjekter."
   filters:
     contexts:
@@ -1118,8 +1131,6 @@
       index:
         hint: Dette filteret gjelder for å velge individuelle innlegg uavhengig av andre kriterier. Du kan legge til flere innlegg til dette filteret fra webgrensesnittet.
         title: Filtrerte innlegg
-  footer:
-    trending_now: Trender nå
   generic:
     all: Alle
     all_items_on_page_selected_html:
@@ -1142,8 +1153,6 @@
     validation_errors:
       one: Noe er ikke helt riktig ennå. Vennligst se etter en gang til
       other: Noe er ikke helt riktig ennå. Det er ennå %{count} feil å rette på
-  html_validator:
-    invalid_markup: 'inneholder ugyldig HTML-markør: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Ugyldig CSV-fil. Feil: %{error}'
@@ -1184,9 +1193,6 @@
       expires_at: Utløper
       uses: Bruk
     title: Inviter personer
-  lists:
-    errors:
-      limit: Du har nådd det maksimale antall lister
   login_activities:
     authentication_methods:
       otp: to-faktor autentiseringsapp
@@ -1390,7 +1396,6 @@
       ios: iOS
       linux: Linux
       mac: macOS
-      other: ukjent plattform
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1503,7 +1508,6 @@
       '7889238': 3 måneder
     min_age_label: Terskel for alder
     min_favs: Behold innlegg som er favorittmarkert av minst
-    min_favs_hint: Sletter ikke noen av dine innlegg som har mottatt minst dette antall favorittmarkeringer. La stå tom for å slette innlegg uavhengig av antall favorittmarkeringer
     min_reblogs: Behold innlegg fremhevet av minst
     min_reblogs_hint: Sletter ikke noen av dine innlegg som har blitt fremhevet minst dette antall ganger. La stå tom for å slette innlegg uavhengig av antall fremhevinger
   stream_entries:
@@ -1608,7 +1612,6 @@
     seamless_external_login: Du er logget inn via en ekstern tjeneste, så passord og e-post innstillinger er ikke tilgjengelige.
     signed_in_as: 'Innlogget som:'
   verification:
-    explanation_html: 'Du kan <strong>bekrefte at du selv er eieren av lenkene i din profilmetadata</strong>. For å gjøre det, må det tillenkede nettstedet inneholde en lenke som fører tilbake til Mastodon-profilen din. Lenken tilbake <strong>må</strong> ha en <code>rel="me"</code>-attributt. Tekstinnholdet til lenken er irrelevant. Her er et eksempel:'
     verification: Bekreftelse
   webauthn_credentials:
     add: Legg til ny sikkerhetsnøkkel
diff --git a/config/locales/oc.yml b/config/locales/oc.yml
index 3457d7801..67005422b 100644
--- a/config/locales/oc.yml
+++ b/config/locales/oc.yml
@@ -36,14 +36,17 @@ oc:
       avatar: Avatar
       by_domain: Domeni
       change_email:
+        changed_msg: Adreça corrèctament cambiada !
         current_email: Adreça actuala
         label: Cambiar d’adreça
         new_email: Novèla adreça
         submit: Cambiar l’adreça
         title: Cambiar l’adreça a %{username}
       change_role:
+        changed_msg: Ròtle corrèctament cambiat !
         label: Cambiar lo ròtle
         no_role: Cap de ròtle
+        title: Cambiar lo ròtle de %{username}
       confirm: Confirmar
       confirmed: Confirmat
       confirming: Confirmacion
@@ -88,6 +91,7 @@ oc:
       most_recent_ip: IP mai recenta
       no_account_selected: Cap de compte pas cambiat estant que cap èra pas seleccionat
       no_limits_imposed: Cap de limit impausat
+      no_role_assigned: Cap de ròtle pas assignat
       not_subscribed: Pas seguidor
       pending: Revision en espèra
       perform_full_suspension: Suspendre
@@ -144,11 +148,13 @@ oc:
         create_announcement: Crear una anóncia
         create_custom_emoji: Crear un emoji personalizat
         create_ip_block: Crear una règla IP
+        create_user_role: Crear un ròtle
         demote_user: Retrogradar l’utilizaire
         destroy_announcement: Suprimir l’anóncia
         destroy_custom_emoji: Suprimir l’emoji personalizat
         destroy_domain_block: Suprimir lo blocatge de domeni
         destroy_status: Suprimir l’estatut
+        destroy_user_role: Destruire ròtle
         disable_2fa_user: Desactivar 2FA
         disable_custom_emoji: Desactivar l’emoji personalizat
         disable_user: Desactivar l’utilizaire
@@ -165,7 +171,9 @@ oc:
         unsilence_account: Levar lo silenci del compte
         update_announcement: Actualizar l’anóncia
         update_custom_emoji: Actualizar l’emoji personalizat
+        update_ip_block: Actualizar règla IP
         update_status: Actualizar l’estatut
+        update_user_role: Actualizar ròtle
       empty: Cap de jornal pas trobat.
       filter_by_action: Filtrar per accion
       filter_by_user: Filtrar per utilizaire
@@ -592,8 +600,6 @@ oc:
     storage: Mèdias gardats
   featured_tags:
     add_new: Ajustar una etiqueta nòva
-    errors:
-      limit: Avètz ja utilizat lo maximum d’etiquetas
   filters:
     contexts:
       account: Perfils
@@ -621,8 +627,6 @@ oc:
       title: Filtres
     new:
       title: Ajustar un nòu filtre
-  footer:
-    trending_now: Tendéncia del moment
   generic:
     all: Tot
     changes_saved_msg: Cambiaments ben realizats !
@@ -634,8 +638,6 @@ oc:
     validation_errors:
       one: I a quicòm que truca ! Mercés de corregir l’error çai-jos
       other: I a quicòm que truca ! Mercés de corregir las %{count} errors çai-jos
-  html_validator:
-    invalid_markup: 'conten un balisatge HTML invalid : %{error}'
   imports:
     modes:
       merge: Fondre
@@ -673,9 +675,6 @@ oc:
       expires_at: Expirats
       uses: Usatges
     title: Convidar de monde
-  lists:
-    errors:
-      limit: Avètz atengut lo maximum de listas
   login_activities:
     authentication_methods:
       password: senhal
@@ -831,7 +830,6 @@ oc:
       ios: iOS
       linux: Linux
       mac: Mac
-      other: plataforma desconeguda
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -939,7 +937,6 @@ oc:
       '7889238': 3 meses
     min_age_label: Sulhet d’ancianetat
     min_favs: Gardar al mens las publicacion en favorit
-    min_favs_hint: Suprimís pas las publicacions qu’an recebut al mens aquesta quantitat de favorits. Daissar blanc per suprimir las publicacion quina quantitat de favorits qu’ajan
     min_reblogs: Gardar las publicacions partejadas al mens
     min_reblogs_hint: Suprimís pas vòstras publicacions qu’an agut aqueste nombre de partiment. Daissar blanc per suprimir las publicacions sens far cas als partiments
   stream_entries:
@@ -1007,7 +1004,6 @@ oc:
     seamless_external_login: Sètz connectat via un servici extèrn, los paramètres de senhal e de corrièl son doncas pas disponibles.
     signed_in_as: 'Session a :'
   verification:
-    explanation_html: 'Podètz <strong>verificar vosautres meteisses coma proprietari dels ligams per las metadonadas de vòstre perfil</strong>. Per aquò far, lo site Web ligat deu conténer un ligam cap a vòstre perfil Mastodon. Lo ligam <strong>deu</strong> aver un atribut <code>rel="me"</code>. Lo contengut tèxte del ligam impòrta pas. Vaquí un exemple :'
     verification: Verificacion
   webauthn_credentials:
     add: Apondre una clau de seguretat novèla
diff --git a/config/locales/pa.yml b/config/locales/pa.yml
index 0fc957a99..1a9193796 100644
--- a/config/locales/pa.yml
+++ b/config/locales/pa.yml
@@ -6,7 +6,5 @@ pa:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index 67976d987..771a1623e 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -4,7 +4,7 @@ pl:
     about_mastodon_html: 'Sieć społecznościowa przyszłości: Bez reklam, bez inwigilacji, zaprojektowana etycznie i zdecentralizowanie! Władaj swoimi danymi z Mastodonem!'
     contact_missing: Nie ustawiono
     contact_unavailable: Nie dotyczy
-    hosted_on: Mastodon hostowany na %{domain}
+    hosted_on: Mastodon prowadzony na %{domain}
     title: O nas
   accounts:
     follow: Obserwuj
@@ -38,7 +38,7 @@ pl:
       add_email_domain_block: Dodaj domenę e-mail na czarną listę
       approve: Przyjmij
       approved_msg: Pomyślnie zaakceptowano wniosek o rejestrację %{username}
-      are_you_sure: Jesteś tego pewien?
+      are_you_sure: Czy na pewno?
       avatar: Awatar
       by_domain: Domena
       change_email:
@@ -95,6 +95,7 @@ pl:
       moderation:
         active: Aktywne
         all: Wszystkie
+        disabled: Wyłączone
         pending: Oczekujące
         silenced: Ograniczone
         suspended: Zawieszone
@@ -139,6 +140,7 @@ pl:
       search: Szukaj
       search_same_email_domain: Inni użytkownicy z tym samym e-mail w tej domenie
       search_same_ip: Inni użytkownicy z tym samym IP
+      security: Bezpieczeństwo
       security_measures:
         only_password: Tylko hasło
         password_and_2fa: Hasło i 2FA
@@ -275,19 +277,19 @@ pl:
         remove_avatar_user_html: "%{name} usunął(-ęła) awatar użytkownikowi %{target}"
         reopen_report_html: "%{name} otworzył(a) ponownie zgłoszenie %{target}"
         resend_user_html: "%{name} ponownie wysłał(a) e-mail z potwierdzeniem dla %{target}"
-        reset_password_user_html: "%{name} przywrócił(a) hasło użytkownikowi %{target}"
+        reset_password_user_html: "%{name} resetuje hasło użytkownika %{target}"
         resolve_report_html: "%{name} rozwiązał(a) zgłoszenie %{target}"
         sensitive_account_html: "%{name} oznaczył(a) zawartość multimedialną %{target} jako wrażliwą"
         silence_account_html: "%{name} wyciszył(a) konto %{target}"
-        suspend_account_html: "%{name} zawiesił(a) konto %{target}"
+        suspend_account_html: Zawieszono konto %{target} przez %{name}
         unassigned_report_html: "%{name} cofnął(-ęła) przypisanie zgłoszenia %{target}"
         unblock_email_account_html: "%{name} odblokował adres e-mail %{target}"
         unsensitive_account_html: "%{name} cofnął(-ęła) oznaczenie zawartości multimedialnej %{target} jako wrażliwą"
         unsilence_account_html: "%{name} cofnął(-ęła) wyciszenie konta %{target}"
         unsuspend_account_html: "%{name} cofnął(-ęła) zawieszenie konta %{target}"
-        update_announcement_html: "%{name} zaktualizował(a) ogłoszenie %{target}"
-        update_custom_emoji_html: "%{name} zaktualizował(a) emoji %{target}"
-        update_domain_block_html: "%{name} zaktualizował(a) blokadę domeny dla %{target}"
+        update_announcement_html: Zaktualizowane ogłoszenie %{target} przez %{name}
+        update_custom_emoji_html: Zaktualizowane emoji %{target} przez %{name}
+        update_domain_block_html: Zaktualizowano blokadę domeny dla %{target} przez %{name}
         update_ip_block_html: "%{name} stworzył(a) regułę dla IP %{target}"
         update_status_html: "%{name} zaktualizował(a) wpis użytkownika %{target}"
         update_user_role_html: "%{name} zmienił rolę %{target}"
@@ -443,6 +445,7 @@ pl:
         resolve: Rozwiąż domenę
         title: Nowa blokada domeny e-mail
       no_email_domain_block_selected: Żadne blokady domeny e-mail nie zostały zmienione, ponieważ żadne z nich nie zostały wybrane
+      not_permitted: Brak uprawnień
       resolved_dns_records_hint_html: Nazwa domeny rozwiązuje się do następujących domen MX, które są ostatecznie odpowiedzialne za przyjmowanie wiadomości e-mail. Blokowanie domeny MX spowoduje zablokowanie rejestracji z dowolnego adresu e-mail, który używa tej samej domeny MX, nawet jeśli widoczna nazwa domeny jest inna. <strong>Uważaj, aby nie blokować głównych dostawców poczty elektronicznej.</strong>
       resolved_through_html: Rozwiązano przez %{domain}
       title: Blokowanie domen e-mail
@@ -457,6 +460,7 @@ pl:
         private_comment_description_html: 'Żebyś wiedział(a) skąd pochodzą zaimportowane bloki, zostaną one utworzone z następującym prywatnym komentarzem: <q>%{comment}</q>'
         private_comment_template: Zaimportowano z %{source} dnia %{date}
         title: Importuj zablokowane domeny
+      invalid_domain_block: 'Jeden lub więcej blokujących domen zostało pominiętych z powodu następującego błędu(ów): %{error}'
       new:
         title: Importuj zablokowane domeny
       no_file: Nie wybrano pliku
@@ -492,6 +496,7 @@ pl:
       content_policies:
         comment: Wewnętrzna notatka
         description_html: Możesz zdefiniować zasady treści, które zostaną zastosowane do wszystkich kont z tej domeny i jej subdomen.
+        limited_federation_mode_description_html: Możesz wybrać, czy zezwolić na federację z tą domeną.
         policies:
           reject_media: Odrzucaj media
           reject_reports: Odrzucaj zgłoszenia
@@ -599,19 +604,23 @@ pl:
         mark_as_sensitive_description_html: Media w zgłaszanych postach zostaną oznaczone jako wrażliwe, a ostrzeżenie zostanie nagrane, aby pomóc w eskalacji przyszłych przewinień na tym samym koncie.
         other_description_html: Zobacz więcej opcji do kontrolowania zachowania konta i dostosuj komunikację do zgłoszonego konta.
         resolve_description_html: Nie zostaną podjęte żadne działania przeciwko zgłoszonemu sprawozdaniu, zdarzenie nie zostanie zarejestrowane, a zgłoszenie zostanie zamknięte.
-        silence_description_html: Profil będzie widoczny tylko dla tych, którzy go już obserwują lub szukaj ręcznie, poważnie ograniczając jego zasięg. Może być zawsze cofnięty.
-        suspend_description_html: Profil i cała jego zawartość staną się niedostępne, dopóki nie zostaną ostatecznie usunięte. Interakcja z kontem będzie niemożliwa. Odwracalne w ciągu 30 dni.
+        silence_description_html: Konto będzie widoczne tylko dla tych, którzy go już obserwują lub przeglądają ręcznie, poważnie ograniczając jego zasięg. Zawsze można cofnąć. Zamyka wszelkie zgłoszenia dotyczące tego konta.
+        suspend_description_html: Konto i cała jego zawartość będą niedostępne i ostatecznie usunięte, a interakcja z nim będzie niemożliwa. Możliwość odwrócenia w ciągu 30 dni. Zamyka wszelkie zgłoszenia dotyczące tego konta.
       actions_description_html: Zdecyduj, jakie działania należy podjąć, aby rozstrzygnąć niniejsze zgłoszenie. Jeśli podejmiesz działania karne przeciwko zgłoszonemu kontowi, zostanie do nich wysłane powiadomienie e-mail, chyba że wybrano kategorię <strong>Spam</strong>.
+      actions_description_remote_html: Zdecyduj, jakie działanie należy podjąć, aby rozwiązać to zgłoszenie. Będzie to miało wpływ jedynie na sposób, w jaki <strong>Twój</strong> serwer komunikuje się z tym kontem zdalnym i obsługuje jego zawartość.
       add_to_report: Dodaj więcej do raportu
       are_you_sure: Czy na pewno?
       assign_to_self: Przypisz do siebie
       assigned: Przypisany moderator
       by_target_domain: Domena zgłaszanego konta
+      cancel: Anuluj
       category: Kategoria
       category_description_html: Powód, dla którego to konto i/lub zawartość zostały zgłoszone, będzie cytowany w komunikacji ze zgłoszonym kontem
       comment:
         none: Brak
       comment_description_html: 'Aby dostarczyć więcej informacji, %{name} napisał:'
+      confirm: Potwierdź
+      confirm_action: Potwierdzenie działań moderacyjnych wobec @%{acct}
       created_at: Zgłoszono
       delete_and_resolve: Usuń posty
       forwarded: Przekazano
@@ -628,6 +637,7 @@ pl:
         placeholder: Opisz wykonane akcje i inne szczegóły dotyczące tego zgłoszenia…
         title: Notatki
       notes_description_html: Przeglądaj i zostaw notatki innym moderatorom i sobie samemu
+      processed_msg: 'Raport #%{id} został pomyślnie przetworzony'
       quick_actions_description_html: 'Wykonaj szybkie działanie lub przewiń w dół, aby zobaczyć zgłoszoną zawartość:'
       remote_user_placeholder: zdalny użytkownik z %{instance}
       reopen: Otwórz ponownie
@@ -640,9 +650,28 @@ pl:
       status: Stan
       statuses: Zgłoszona treść
       statuses_description_html: Obraźliwe treści będą cytowane w komunikacji ze zgłoszonym kontem
+      summary:
+        action_preambles:
+          delete_html: 'Zamierzasz <strong>usunąć</strong> niektóre posty <strong>@%{acct}</strong>. To spowoduje:'
+          mark_as_sensitive_html: 'Zamierzasz <strong>oznaczyć</strong> niektóre posty <strong>@%{acct}</strong> jako <strong>wrażliwe</strong>. To spowoduje:'
+          silence_html: 'Zamierzasz <strong>ograniczyć</strong> konto <strong>@%{acct}</strong>. To spowoduje:'
+          suspend_html: 'Zamierzasz <strong>zawiesić</strong> konto <strong>@%{acct}</strong>. To spowoduje:'
+        actions:
+          delete_html: Usuń obrażające posty
+          mark_as_sensitive_html: Oznacz obraźliwe media postów jako wrażliwe
+          silence_html: Znacząco ogranicz zasięg <strong>@%{acct}</strong>, aby ich profil i zawartość były widoczne tylko dla osób, które je już obserwują lub ręcznie wyszukują jego profil
+          suspend_html: Zawieś <strong>@%{acct}</strong>, co sprawia, że ich profil i zawartość są niedostępne i niemożliwe do interakcji z
+        close_report: 'Oznacz raport #%{id} jako rozwiązany'
+        close_reports_html: Oznacz <strong>wszystkie</strong> zgłoszenia dotyczące <strong>@%{acct}</strong> jako rozwiązane
+        delete_data_html: Usuń profil i zawartość <strong>@%{acct}</strong> za 30 dni, chyba że w tym czasie zostanie zawieszony
+        preview_preamble_html: "<strong>@%{acct}</strong> otrzyma ostrzeżenie o następującej treści:"
+        record_strike_html: Zarejestruj ostrzeżenie przeciwko <strong>@%{acct}</strong>, aby ułatwić sobie eskalację w przypadku przyszłych naruszeń z tego konta
+        send_email_html: Wyślij do <strong>@%{acct}</strong> wiadomość e-mail z ostrzeżeniem
+        warning_placeholder: Opcjonalne dodatkowe uzasadnienie działania moderacyjnego.
       target_origin: Pochodzenie zgłaszanego konta
       title: Zgłoszenia
       unassign: Cofnij przypisanie
+      unknown_action_msg: 'Nieznane działanie: %{action}'
       unresolved: Nierozwiązane
       updated_at: Zaktualizowano
       view_profile: Wyświetl profil
@@ -741,6 +770,8 @@ pl:
         preamble: Prezentowanie interesujących treści ma kluczowe znaczenie dla nowych użytkowników, którzy mogą nie znać nikogo z Mastodona. Kontroluj, jak różne funkcje odkrywania działają na Twoim serwerze.
         profile_directory: Katalog profilów
         public_timelines: Publiczne osie czasu
+        publish_discovered_servers: Opublikuj znane serwery
+        publish_statistics: Publikuj statystyki
         title: Odkrywanie
         trends: Trendy
       domain_blocks:
@@ -795,6 +826,7 @@ pl:
         suspend: "%{name} zawiesił(-a) konto %{target}"
       appeal_approved: Odwołanie
       appeal_pending: Odwołanie w toku
+      appeal_rejected: Odwołanie odrzucone
     system_checks:
       database_schema_check:
         message_html: Istnieją oczekujące migracje bazy danych. Uruchom je, aby upewnić się, że aplikacja działa tak, jak powinna
@@ -808,6 +840,12 @@ pl:
         message_html: Nie zdefiniowano żadnych reguł serwera.
       sidekiq_process_check:
         message_html: Brak uruchomionego procesu Sidekiq dla kolejki(-ek) %{value}. Sprawdź konfigurację Sidekiq
+      upload_check_privacy_error:
+        action: Kliknij tutaj, aby dowiedzieć się więcej
+        message_html: "<strong>Twój serwer internetowy jest nieprawidłowo skonfigurowany. Prywatność twoich użytkowników jest zagrożona.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Kliknij tutaj, aby dowiedzieć się więcej
+        message_html: "<strong>Pamięć obiektu jest nieprawidłowa. Prywatność twoich użytkowników jest zagrożona.</strong>"
     tags:
       review: Stan przeglądu
       updated_msg: Pomyślnie uaktualniono ustawienia hashtagów
@@ -832,6 +870,7 @@ pl:
           other: Udostępnione przez %{count} osoby w ciągu ostatniego tygodnia
         title: Popularne linki
         usage_comparison: Udostępnione %{today} razy dzisiaj, w porównaniu z %{yesterday} wczoraj
+      not_allowed_to_trend: Wyłączona widoczność w popularnych
       only_allowed: Tylko dozwolone
       pending_review: Oczekuje na przegląd
       preview_card_providers:
@@ -969,6 +1008,7 @@ pl:
   applications:
     created: Pomyślnie utworzono aplikację
     destroyed: Pomyślnie usunięto aplikację
+    logout: Wyloguj się
     regenerate_token: Wygeneruj nowy token dostępu
     token_regenerated: Pomyślnie wygenerowano nowy token dostępu
     warning: Przechowuj te dane ostrożnie. Nie udostępniaj ich nikomu!
@@ -976,6 +1016,8 @@ pl:
   auth:
     apply_for_account: Poproś o założenie konta
     change_password: Hasło
+    confirmations:
+      wrong_email_hint: Jeśli ten adres e-mail nie jest poprawny, możesz go zmienić w ustawieniach konta.
     delete_account: Usunięcie konta
     delete_account_html: Jeżeli chcesz usunąć konto, <a href="%{path}">przejdź tutaj</a>. Otrzymasz prośbę o potwierdzenie.
     description:
@@ -1003,6 +1045,8 @@ pl:
     resend_confirmation: Ponownie prześlij instrukcje weryfikacji
     reset_password: Zresetuj hasło
     rules:
+      accept: Zaakceptuj
+      back: Wróć
       preamble: Są one ustawione i wymuszone przez moderatorów %{domain}.
       title: Kilka podstawowych zasad.
     security: Bezpieczeństwo
@@ -1150,7 +1194,7 @@ pl:
   featured_tags:
     add_new: Dodaj nowy
     errors:
-      limit: Już przekroczyłeś(-aś) maksymalną liczbę wyróżnionych hashtagów
+      limit: Przekroczono maksymalną liczbę hasztagów
     hint_html: "<strong>Czym są wyróżnione hashtagi?</strong> Są one na stałe wyświetlane na Twoim profilu i pozwalają innym na przeglądanie Twoich wpisów używających tych hashtagów. Są doskonałym narzędziem do śledzenia kreatywnej twórczości czy długoterminowych projektów."
   filters:
     contexts:
@@ -1200,8 +1244,6 @@ pl:
       index:
         hint: Ten filtr ma zastosowanie do wybierania poszczególnych wpisów niezależnie od pozostałych kryteriów. Możesz dodać więcej wpisów do tego filtra z interfejsu internetowego.
         title: Filtrowane posty
-  footer:
-    trending_now: Obecnie na czasie
   generic:
     all: Wszystkie
     all_items_on_page_selected_html:
@@ -1232,8 +1274,6 @@ pl:
       many: Coś jest wciąż nie tak! Przejrzyj %{count} poniższych błędów
       one: Coś jest wciąż nie tak! Przyjrzyj się poniższemu błędowi
       other: Coś jest wciąż nie tak! Przejrzyj poniższe błędy (%{count})
-  html_validator:
-    invalid_markup: 'zawiera nieprawidłową składnię HTML: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Nieprawidłowy plik CSV. Błąd: %{error}'
@@ -1278,7 +1318,7 @@ pl:
     title: Zaproś użytkowników
   lists:
     errors:
-      limit: Przekroczyłeś maksymalną liczbę utworzonych list
+      limit: Przekroczono maksymalną liczbę utworzonych list
   login_activities:
     authentication_methods:
       otp: aplikacja weryfikacji dwuetapowej
@@ -1355,8 +1395,8 @@ pl:
       title: Nowa prośba o możliwość obsewowania
     mention:
       action: Odpowiedz
-      body: "%{name} wspomniał(a) o Tobie w:"
-      subject: "%{name} wspomniał(a) o Tobie"
+      body: 'Wspomniano o tobie przez %{name} w:'
+      subject: Wspomniano o tobie przez %{name}
       title: Nowe wspomnienie o Tobie
     poll:
       subject: Ankieta %{name} zakończyła się
@@ -1365,7 +1405,7 @@ pl:
       subject: Twój wpis został podbity przez %{name}
       title: Nowe podbicie
     status:
-      subject: "%{name} właśnie opublikował(a) wpis"
+      subject: Właśnie opublikowano wpis %{name}
     update:
       subject: "%{name} edytował/a wpis"
   notifications:
@@ -1419,7 +1459,11 @@ pl:
       unrecognized_emoji: nie jest znanym emoji
   relationships:
     activity: Aktywność konta
+    confirm_follow_selected_followers: Czy na pewno chcesz obserwować wybranych obserwujących?
+    confirm_remove_selected_followers: Czy na pewno chcesz usunąć wybranych obserwujących?
+    confirm_remove_selected_follows: Czy na pewno chcesz usunąć zaznaczone obserwacje?
     dormant: Uśpione
+    follow_failure: Nie można obserwować niektórych wybranych kont.
     follow_selected_followers: Zacznij obserwować wybranych obserwujących
     followers: Obserwujący
     following: Obserwowani
@@ -1459,6 +1503,7 @@ pl:
       electron: Electron
       firefox: Firefox
       generic: nieznana przeglądarka
+      huawei_browser: Przeglądarka Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Przeglądarka Nokia S40 Ovi
@@ -1468,6 +1513,7 @@ pl:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Nieznana Przeglądarka
       weibo: Weibo
     current_session: Obecna sesja
     description: "%{browser} na %{platform}"
@@ -1480,9 +1526,10 @@ pl:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: nieznana platforma
+      unknown_platform: Nieznana platforma
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1707,6 +1754,7 @@ pl:
       title: Witaj na pokładzie, %{name}!
   users:
     follow_limit_reached: Nie możesz obserwować więcej niż %{limit} osób
+    go_to_sso_account_settings: Przejdź do ustawień konta dostawcy tożsamości
     invalid_otp_token: Kod uwierzytelniający jest niepoprawny
     otp_lost_help_html: Jeżeli utracisz dostęp do obu, możesz skontaktować się z %{email}
     seamless_external_login: Zalogowano z użyciem zewnętrznej usługi, więc ustawienia hasła i adresu e-mail nie są dostępne.
diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml
index 82cdbb25c..3669b52fc 100644
--- a/config/locales/pt-BR.yml
+++ b/config/locales/pt-BR.yml
@@ -91,6 +91,7 @@ pt-BR:
       moderation:
         active: Ativo
         all: Todos
+        disabled: Desativado
         pending: Pendente
         silenced: Limitado
         suspended: Suspendido
@@ -133,6 +134,7 @@ pt-BR:
       search: Buscar
       search_same_email_domain: Outros usuários com o mesmo domínio de e-mail
       search_same_ip: Outros usuários com o mesmo IP
+      security: Segurança
       security_measures:
         only_password: Apenas senha
         password_and_2fa: Senha e autenticação de dois fatores
@@ -392,6 +394,7 @@ pt-BR:
         create: Criar bloqueio
         hint: O bloqueio de domínio não vai prevenir a criação de entradas de contas na base de dados, mas vai retroativamente e automaticamente aplicar métodos específicos de moderação nessas contas.
         severity:
+          desc_html: "<strong>Silenciar</strong> vai tornar as publicações da conta invisíveis para qualquer um que não estiver lhe seguindo. <strong>Suspender</strong> vai remover todo o conteúdo, mídia e dados de perfil da conta. Use <strong>Nenhum</strong> se você só quer rejeitar arquivos de mídia."
           noop: Nenhum
           silence: Limitar
           suspend: Banir
@@ -426,6 +429,7 @@ pt-BR:
         resolve: Resolver domínio
         title: Nova entrada de lista negra de e-mail
       no_email_domain_block_selected: Nenhum bloco de domínio de e-mail foi alterado, pois nenhum foi selecionado
+      not_permitted: Não permitido
       resolved_dns_records_hint_html: O nome de domínio resolve os seguintes domínios MX, que são responsáveis finais por aceitar o e-mail. Bloquear um domínio MX bloqueará inscrições de qualquer endereço de e-mail que use o mesmo domínio MX, mesmo que o nome de domínio visível seja diferente. <strong>Cuidado para não bloquear os principais provedores de e-mail.</strong>
       resolved_through_html: Resolvido através de %{domain}
       title: Lista de negra de e-mail
@@ -437,8 +441,10 @@ pt-BR:
       import:
         description_html: Você está prestes a importar uma lista de boqueio de domínio. Por favor, revise esta lista com muito cuidado, especialmente se você mesmo não criou esta lista.
         existing_relationships_warning: Existem relações de seguimento
+        private_comment_description_html: 'Para ajudá-lo a rastrear de onde vêm os blocos importados, serão criados blocos importados com o seguinte comentário privado: <q>%{comment}</q>'
         private_comment_template: Importado de %{source} em %{date}
         title: Importar bloqueio de domínios
+      invalid_domain_block: 'Um ou mais blocos de domínio foram ignorados devido ao(s) seguinte(s) erro(s): %{error}'
       new:
         title: Importar bloqueio de domínios
       no_file: Nenhum arquivo selecionado
@@ -470,6 +476,7 @@ pt-BR:
       content_policies:
         comment: Nota interna
         description_html: Você pode definir políticas de conteúdo que serão aplicadas a todas as contas deste domínio e a qualquer um dos seus subdomínios.
+        limited_federation_mode_description_html: Você pode escolher se deseja permitir a associação com este domínio.
         policies:
           reject_media: Rejeitar mídia
           reject_reports: Rejeitar denúncias
@@ -573,19 +580,23 @@ pt-BR:
         mark_as_sensitive_description_html: Os conteúdos de mídia em publicações denunciadas serão marcados como sensíveis e um aviso de violação será mantido para te informar sobre o agravamento caso essa mesma conta comenta infrações no futuro.
         other_description_html: Veja mais opções para controlar o comportamento da conta e personalizar a comunicação com a conta denunciada.
         resolve_description_html: Nenhuma ação será tomada contra a conta denunciada, nenhuma violação será guardada e a denúncia será encerrada.
-        silence_description_html: O perfil será visível apenas para aqueles que já o seguem ou que o procuram manualmente, limitando severamente seu alcance. Pode ser revertido a qualquer momento.
-        suspend_description_html: O perfil e todo o seu conteúdo ficarão inacessíveis até que seja eventualmente excluído. Interagir com a conta será impossível. Reversível dentro de 30 dias.
+        silence_description_html: A conta ficará visível apenas para aqueles que já a seguem ou que a procuram manualmente, limitando severamente seu alcance. Pode ser revertido a qualquer momento. Fecha todas as denúncias desta conta.
+        suspend_description_html: A conta e todo o seu conteúdo ficará inacessível e, eventualmente, excluído e interagir com ela será impossível. Reversível dentro de 30 dias. Encerra todas as denúncias contra esta conta.
       actions_description_html: Decida que medidas tomar para resolver esta denúncia. Se você decidir punir a conta denunciada, ela receberá uma notificação por e-mail, exceto quando for selecionada a categoria <strong>spam</strong> for selecionada.
+      actions_description_remote_html: Decida quais medidas tomará para resolver esta denúncia. Isso só afetará como <strong>seu servidor</strong> se comunica com esta conta remota e manipula seu conteúdo.
       add_to_report: Adicionar mais à denúncia
       are_you_sure: Você tem certeza?
       assign_to_self: Atribuir para si
       assigned: Moderador responsável
       by_target_domain: Domínio da conta denunciada
+      cancel: Cancelar
       category: Categoria
       category_description_html: O motivo pelo qual esta conta e/ou conteúdo foi denunciado será citado na comunicação com a conta denunciada
       comment:
         none: Nenhum
       comment_description_html: 'Para fornecer mais informações, %{name} escreveu:'
+      confirm: Confirmar
+      confirm_action: Confirmar a moderação de @%{acct}
       created_at: Denunciado
       delete_and_resolve: Excluir publicações
       forwarded: Encaminhados
@@ -602,6 +613,7 @@ pt-BR:
         placeholder: Descreva quais ações foram tomadas ou quaisquer outras atualizações relacionadas...
         title: Notas
       notes_description_html: Visualize e deixe anotações para outros moderadores e para você mesmo no futuro
+      processed_msg: 'Relatório #%{id} processado com sucesso'
       quick_actions_description_html: 'Tome uma ação rápida ou role para baixo para ver o conteúdo denunciado:'
       remote_user_placeholder: o usuário remoto de %{instance}
       reopen: Reabrir denúncia
@@ -614,9 +626,28 @@ pt-BR:
       status: Estado
       statuses: Conteúdo denunciado
       statuses_description_html: Conteúdo ofensivo será citado em comunicação com a conta denunciada
+      summary:
+        action_preambles:
+          delete_html: 'Você está prestes a <strong>remover</strong> algumas das publicações de <strong>@%{acct}</strong>. Isso irá:'
+          mark_as_sensitive_html: 'Você está prestes a <strong>marcar</strong> algumas das publicações de <strong>@%{acct}</strong> como <strong>sensíveis</strong>. Isso irá:'
+          silence_html: 'Você está prestes a <strong>limitar</strong> <strong>a conta de @%{acct}</strong>. Isso irá:'
+          suspend_html: 'Você está prestes a <strong>suspender</strong> <strong>a conta de @%{acct}</strong>. Isso irá:'
+        actions:
+          delete_html: Remover as publicações ofensivas
+          mark_as_sensitive_html: Marcar a mídia de posts ofensivos como sensível
+          silence_html: Limitar firmemente o alcance de <strong>@%{acct}</strong>, tornando seus perfis e conteúdos apenas visíveis para pessoas que já os estão seguindo ou olhando manualmente o perfil
+          suspend_html: Suspender <strong>@%{acct}</strong>, tornando seu perfil e conteúdo inacessíveis, impossibilitando a interação
+        close_report: 'Marcar denúncia #%{id} como resolvida'
+        close_reports_html: Marcar <strong>todas</strong> as denúncias contra <strong>@%{acct}</strong> como resolvidas
+        delete_data_html: Exclua o perfil e o conteúdo de <strong>@%{acct}</strong> daqui a 30 dias, a menos que a suspensão seja desfeita nesse meio tempo
+        preview_preamble_html: "<strong>@%{acct}</strong> receberá um aviso com o seguinte conteúdo:"
+        record_strike_html: Registre uma ação contra <strong>@%{acct}</strong> para te ajudar em futuras violações desta conta
+        send_email_html: Enviar <strong>@%{acct}</strong> um e-mail de aviso
+        warning_placeholder: Argumentos adicionais para a ação de moderação.
       target_origin: Origem da conta denunciada
       title: Denúncias
       unassign: Desatribuir
+      unknown_action_msg: 'Ação desconhecida: %{action}'
       unresolved: Não resolvido
       updated_at: Atualizado
       view_profile: Ver perfil
@@ -705,11 +736,14 @@ pt-BR:
         title: Retenção de conteúdo
       default_noindex:
         desc_html: Afeta qualquer usuário que não tenha alterado esta configuração manualmente
+        title: Optar por excluir usuários da indexação de mecanismos de pesquisa por padrão
       discovery:
         follow_recommendations: Seguir recomendações
         preamble: Navegar por um conteúdo interessante é fundamental para integrar novos usuários que podem não conhecer ninguém no Mastodon. Controle como várias características de descoberta funcionam no seu servidor.
         profile_directory: Diretório de perfis
         public_timelines: Timelines públicas
+        publish_discovered_servers: Publicar servidores descobertos
+        publish_statistics: Publicar estatísticas
         title: Descobrir
         trends: Tendências
       domain_blocks:
@@ -764,6 +798,7 @@ pt-BR:
         suspend: "%{name} suspendeu a conta de %{target}"
       appeal_approved: Revisado
       appeal_pending: Revisão pendente
+      appeal_rejected: Revisão rejeitada
     system_checks:
       database_schema_check:
         message_html: Existem migrações de banco de dados pendentes. Execute-as para garantir que o aplicativo se comporte como esperado
@@ -777,6 +812,12 @@ pt-BR:
         message_html: Você não definiu nenhuma regra de servidor.
       sidekiq_process_check:
         message_html: Nenhum processo Sidekiq rodando para a(s) fila(s) %{value}. Por favor, revise a sua configuração para Sidekiq
+      upload_check_privacy_error:
+        action: Confira aqui para mais informações
+        message_html: "<strong>Seu servidor está mal configurado. A privacidade de seus usuários está em risco.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Confira aqui para mais informações
+        message_html: "<strong>Seu armazenamento de objetos está mal configurado. A privacidade de seus usuários está em risco.</strong>"
     tags:
       review: Status da revisão
       updated_msg: Configurações de hashtag atualizadas
@@ -799,6 +840,7 @@ pt-BR:
           other: Compartilhado por %{count} pessoas na última semana
         title: Em alta no momento
         usage_comparison: Compartilhado %{today} vezes hoje, em comparação com %{yesterday} de ontem
+      not_allowed_to_trend: Não tem permissão para criar tendências
       only_allowed: Somente permitido
       pending_review: Revisão pendente
       preview_card_providers:
@@ -930,6 +972,7 @@ pt-BR:
   applications:
     created: Aplicativo criado com sucesso
     destroyed: Aplicativo excluído com sucesso
+    logout: Sair
     regenerate_token: Gerar código de acesso
     token_regenerated: Código de acesso gerado
     warning: Tenha cuidado com estes dados. Nunca compartilhe com alguém!
@@ -937,6 +980,8 @@ pt-BR:
   auth:
     apply_for_account: Solicitar uma conta
     change_password: Senha
+    confirmations:
+      wrong_email_hint: Se esse endereço de e-mail não estiver correto, você pode alterá-lo nas configurações da conta.
     delete_account: Excluir conta
     delete_account_html: Se você deseja excluir sua conta, você pode <a href="%{path}">fazer isso aqui</a>. Uma confirmação será solicitada.
     description:
@@ -964,6 +1009,9 @@ pt-BR:
     resend_confirmation: Reenviar instruções de confirmação
     reset_password: Redefinir senha
     rules:
+      accept: Aceitar
+      back: Voltar
+      preamble: Estes são definidos e aplicados pelos moderadores de %{domain}.
       title: Algumas regras básicas.
     security: Segurança
     set_new_password: Definir uma nova senha
@@ -972,6 +1020,7 @@ pt-BR:
       email_settings_hint_html: O e-mail de confirmação foi enviado para %{email}. Se esse endereço de e-mail não estiver correto, você pode alterá-lo nas configurações da conta.
       title: Configurações
     sign_in:
+      preamble_html: Entre com suas credenciais de domínios ( <strong>%{domain}</strong> ) . Se sua conta estiver hospedada em um servidor diferente, você não deve conseguir acessar este conteúdo.
       title: Entrar em %{domain}
     sign_up:
       preamble: Com uma conta neste servidor Mastodon, você poderá seguir qualquer outra pessoa na rede, independentemente de onde sua conta esteja hospedada.
@@ -1109,7 +1158,7 @@ pt-BR:
   featured_tags:
     add_new: Adicionar hashtag
     errors:
-      limit: Você atingiu o limite de hashtags em destaque
+      limit: Você já destacou o número máximo de hashtags
     hint_html: "<strong>O que são hashtags em destaque?</strong> Elas são exibidas no seu perfil público e permitem que as pessoas acessem suas publicações públicos que contenham especificamente essas hashtags. São uma excelente ferramenta para acompanhar os trabalhos criativos ou os projetos de longo prazo."
   filters:
     contexts:
@@ -1122,6 +1171,7 @@ pt-BR:
       add_keyword: Adicionar palavra-chave
       keywords: Palavras-chave
       statuses: Publicações individuais
+      statuses_hint_html: Este filtro se aplica para selecionar posts individuais, independentemente de serem compatíveis com as palavras-chave abaixo. <a href="%{path}">Revise ou remova as publicações do filtro</a>.
       title: Editar filtro
     errors:
       deprecated_api_multiple_keywords: Estes parâmetros não podem ser alterados a partir deste aplicativo porque se aplicam a mais de uma palavra-chave de filtro. Use um aplicativo mais recente ou a interface web.
@@ -1152,8 +1202,6 @@ pt-BR:
       index:
         hint: Este filtro se aplica a publicações individuais, independentemente de outros critérios. Você pode adicionar mais postagens a este filtro a partir da interface web.
         title: Publicações filtradas
-  footer:
-    trending_now: Em alta no momento
   generic:
     all: Tudo
     all_items_on_page_selected_html:
@@ -1176,8 +1224,6 @@ pt-BR:
     validation_errors:
       one: Algo não está certo! Analise o erro abaixo
       other: Algo não está certo! Analise os %{count} erros abaixo
-  html_validator:
-    invalid_markup: 'contém HTML inválido: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Arquivo CSV inválido. Erro: %{error}'
@@ -1220,7 +1266,7 @@ pt-BR:
     title: Convidar pessoas
   lists:
     errors:
-      limit: Você atingiu o máximo de listas
+      limit: Você atingiu o número máximo de listas
   login_activities:
     authentication_methods:
       otp: autenticação de dois fatores
@@ -1361,7 +1407,11 @@ pt-BR:
       unrecognized_emoji: não é um emoji reconhecido
   relationships:
     activity: Atividade da conta
+    confirm_follow_selected_followers: Tem certeza que deseja seguir os seguidores selecionados?
+    confirm_remove_selected_followers: Tem certeza que deseja remover os seguidores selecionados?
+    confirm_remove_selected_follows: Tem certeza que deseja remover os grupos seguidos selecionados?
     dormant: Inativo
+    follow_failure: Não foi possível seguir algumas das contas selecionadas.
     follow_selected_followers: Seguir os seguidores selecionados
     followers: Seguidores
     following: Seguindo
@@ -1401,6 +1451,7 @@ pt-BR:
       electron: Electron
       firefox: Firefox
       generic: Navegador desconhecido
+      huawei_browser: Navegador Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Navegador Nokia S40 Ovi
@@ -1410,6 +1461,7 @@ pt-BR:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Navegador desconhecido
       weibo: Weibo
     current_session: Sessão atual
     description: "%{browser} em %{platform}"
@@ -1422,9 +1474,10 @@ pt-BR:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: MacOS
-      other: Plataforma desconhecida
+      unknown_platform: Plataforma desconhecida
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1537,7 +1590,7 @@ pt-BR:
       '7889238': 3 meses
     min_age_label: Validade
     min_favs: Manter publicações favoritadas por ao menos
-    min_favs_hint: Não exclui publicações que receberam pelo menos esta quantidade de favoritos. Deixe em branco para excluir publicações independentemente da quantidade de favoritos
+    min_favs_hint: Não exclui suas publicações que receberam pelo menos esta quantidade de favoritos. Deixe em branco para excluir publicações independentemente da quantidade de favoritos
     min_reblogs: Manter publicações impulsionadas por ao menos
     min_reblogs_hint: Não exclui publicações que receberam pelo menos esta quantidade de impulsos. Deixe em branco para excluir publicações independentemente da quantidade de impulsos
   stream_entries:
@@ -1637,12 +1690,13 @@ pt-BR:
       title: Boas vindas, %{name}!
   users:
     follow_limit_reached: Você não pode seguir mais de %{limit} pessoas
+    go_to_sso_account_settings: Vá para as configurações de conta do seu provedor de identidade
     invalid_otp_token: Código de dois fatores inválido
     otp_lost_help_html: Se você perder o acesso à ambos, você pode entrar em contato com %{email}
     seamless_external_login: Você entrou usando um serviço externo, então configurações de e-mail e senha não estão disponíveis.
     signed_in_as: 'Entrou como:'
   verification:
-    explanation_html: 'Você pode <strong>verificar os links nos metadados do seu perfil</strong>. Para isso, o site citado deve conter um link de volta para o seu perfil do Mastodon. O link de volta <strong>deve</strong> conter um atributo <code>rel="me"</code>. O conteúdo ou texto do link não importa. Aqui está um exemplo:'
+    explanation_html: 'Você pode <strong>se verificar como proprietário dos links nos metadados do seu perfil</strong>. Para isso, o site vinculado deve conter um link de volta para o seu perfil de Mastodon. Depois de adicionar o link, talvez você precise voltar aqui e salvar novamente o seu perfil para que a verificação tenha efeito. O link de volta <strong>deve</strong> ter um atributo <code>rel="me"</code>. O conteúdo do texto do link não importa. Aqui está um exemplo:'
     verification: Verificação
   webauthn_credentials:
     add: Adicionar nova chave de segurança
diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml
index dbc2701f0..67c9102e2 100644
--- a/config/locales/pt-PT.yml
+++ b/config/locales/pt-PT.yml
@@ -91,6 +91,7 @@ pt-PT:
       moderation:
         active: Activo
         all: Todos
+        disabled: Desativado
         pending: Pendente
         silenced: Limitadas
         suspended: Supensos
@@ -133,6 +134,7 @@ pt-PT:
       search: Pesquisar
       search_same_email_domain: Outros utilizadores com o mesmo domínio de correio electrónico
       search_same_ip: Outros utilizadores com o mesmo IP
+      security: Segurança
       security_measures:
         only_password: Apenas palavra-passe
         password_and_2fa: Palavra-passe e 2FA
@@ -427,6 +429,7 @@ pt-PT:
         resolve: Domínio de resolução
         title: Novo bloqueio de domínio de e-mail
       no_email_domain_block_selected: Nenhum bloqueio de domínio de e-mail foi alterado pois nenhum foi selecionado
+      not_permitted: Não permitido
       resolved_dns_records_hint_html: O nome de domínio resolve para os seguintes domínios MX, que são, em última análise, responsáveis por aceitar o e-mail. Bloquear um domínio MX irá bloquear as inscrições de qualquer endereço de e-mail que use o mesmo domínio MX, mesmo quando o nome de domínio visível é diferente. <strong>Cuidado para não bloquear os principais provedores de e-mail.</strong>
       resolved_through_html: Resolvido através de %{domain}
       title: Domínios de e-mail bloqueados
@@ -441,6 +444,7 @@ pt-PT:
         private_comment_description_html: 'Para o ajudar a rastrear a origem dos bloqueios importados, estes serão criados com o seguinte comentário privado: <q>%{comment}</q>'
         private_comment_template: Importado de %{source} em %{date}
         title: Importar bloqueios de domínio
+      invalid_domain_block: 'Um ou mais blocos de domínio foram ignorados devido o(s) seguinte(s) erro(s): %{error}'
       new:
         title: Importar bloqueios de domínio
       no_file: Nenhum ficheiro selecionado
@@ -472,6 +476,7 @@ pt-PT:
       content_policies:
         comment: Nota interna
         description_html: Pode definir políticas de conteúdo que serão aplicadas a todas as contas deste domínio e a qualquer um dos seus subdomínios.
+        limited_federation_mode_description_html: Pode escolher se deseja permitir a federação com este domínio.
         policies:
           reject_media: Rejeitar media
           reject_reports: Rejeitar denúncias
@@ -575,19 +580,23 @@ pt-PT:
         mark_as_sensitive_description_html: A media nas publicações denunciadas será marcada como problemática, e uma reprimenda será registada para ajudá-lo a tomar medidas em futuras infrações pela mesma conta.
         other_description_html: Ver mais opções para controlar o comportamento da conta e personalizar a comunicação para a conta denunciada.
         resolve_description_html: Nenhuma ação será tomada contra a conta denunciada, não será registada nenhuma reprimenda, e a denúncia será fechada.
-        silence_description_html: O perfil será visível apenas para aqueles que já o seguem ou o procurem manualmente, limitando fortemente o seu alcance. Pode sempre ser revertido.
-        suspend_description_html: O perfil e todo o seu conteúdo tornar-se-ão inacessíveis até serem eventualmente apagados. A interacção com a conta será impossível. Reversível no prazo de 30 dias.
+        silence_description_html: O perfil será visível apenas para aqueles que já o seguem ou o procurem manualmente, limitando fortemente o seu alcance. Pode sempre ser revertido. Encerrar todas as denúncias contra esta conta.
+        suspend_description_html: A conta e todo o seu conteúdo ficará inacessível e, eventualmente apagado, pelo que interagir com ela será impossível. Reversível durante 30 dias. Encerra todas as denúncias contra esta conta.
       actions_description_html: Decida a ação a tomar para resolver esta denúncia. Se decidir por uma ação punitiva contra a conta denunciada, um e-mail de notificação será enviado, excetuando quando selecionada a categoria <strong>Spam</strong>.
+      actions_description_remote_html: Decida quais as medidas a tomar para resolver esta denúncia. Isso apenas afetará como <strong>o seu</strong> servido comunica com esta conta remota e gere o seu conteúdo.
       add_to_report: Adicionar mais à denúncia
       are_you_sure: Tem a certeza?
       assign_to_self: Atribuída a mim
       assigned: Atribuída ao moderador
       by_target_domain: Domínio da conta denunciada
+      cancel: Cancelar
       category: Categorização
       category_description_html: A razão pela qual esta conta e/ou conteúdo foi denunciado será citada na comunicação com a conta denunciada
       comment:
         none: Nenhum
       comment_description_html: 'Para fornecer mais informações, %{name} escreveu:'
+      confirm: Confirmar
+      confirm_action: Confirmar a ação de moderação contra @%{acct}
       created_at: Denunciado
       delete_and_resolve: Eliminar publicações
       forwarded: Encaminhado
@@ -604,6 +613,7 @@ pt-PT:
         placeholder: Descreve as ações que foram tomadas ou quaisquer outras atualizações relacionadas...
         title: Notas
       notes_description_html: Visualize e deixe anotações para outros moderadores e para si próprio no futuro
+      processed_msg: 'Relatório #%{id} processado com sucesso'
       quick_actions_description_html: 'Tome uma ação rápida ou deslize para baixo para ver o conteúdo denunciado:'
       remote_user_placeholder: o utilizador remoto de %{instance}
       reopen: Reabrir denúncia
@@ -616,9 +626,28 @@ pt-PT:
       status: Estado
       statuses: Conteúdo denunciado
       statuses_description_html: O conteúdo ofensivo será citado na comunicação com a conta denunciada
+      summary:
+        action_preambles:
+          delete_html: 'Você está prestes a <strong>remover</strong> algumas das publicações de <strong>@%{acct}</strong>. Isto irá:'
+          mark_as_sensitive_html: 'Você está prestes a <strong>marcar</strong> alguns dos posts de <strong>@%{acct}</strong>como <strong>sensível</strong>. Isto irá:'
+          silence_html: 'Você está prestes a <strong>limitar a conta do</strong> <strong>@%{acct}</strong>. Isto irá:'
+          suspend_html: 'Você está prestes a <strong>suspender a conta de</strong> <strong>@%{acct}</strong>. Isto irá:'
+        actions:
+          delete_html: Excluir as publicações ofensivas
+          mark_as_sensitive_html: Marcar a mídia dos posts ofensivos como sensível
+          silence_html: Limitar firmemente o alcance de <strong>@%{acct}</strong>, tornando seus perfis e conteúdos apenas visíveis para pessoas que já os estão seguindo ou olhando manualmente no perfil
+          suspend_html: Suspender <strong>@%{acct}</strong>, tornando seu perfil e conteúdo inacessíveis e impossível de interagir com
+        close_report: 'Marcar relatório #%{id} como resolvido'
+        close_reports_html: Marcar <strong>todos os</strong> relatórios contra <strong>@%{acct}</strong> como resolvidos
+        delete_data_html: Excluir <strong>@%{acct}</strong>perfil e conteúdo 30 dias a menos que sejam dessuspensos
+        preview_preamble_html: "<strong>@%{acct}</strong> receberá um aviso com o seguinte conteúdo:"
+        record_strike_html: Registre um ataque contra <strong>@%{acct}</strong> para ajudá-lo a escalar futuras violações desta conta
+        send_email_html: Enviar <strong>@%{acct}</strong> um e-mail de aviso
+        warning_placeholder: Argumentos adicionais opcionais para a acção de moderação.
       target_origin: Origem da conta denunciada
       title: Denúncias
       unassign: Não atribuir
+      unknown_action_msg: 'Ação desconhecida: %{action}'
       unresolved: Por resolver
       updated_at: Atualizado
       view_profile: Ver perfil
@@ -713,6 +742,8 @@ pt-PT:
         preamble: Revelar conteúdos interessantes é fundamental para a entrada de novos utilizadores que podem não conhecer ninguém no Mastodon. Controle como os vários recursos de descoberta funcionam no seu servidor.
         profile_directory: Diretório de perfis
         public_timelines: Cronologias públicas
+        publish_discovered_servers: Publicar servidores descobertos
+        publish_statistics: Publicar estatísticas
         title: Descobrir
         trends: Em alta
       domain_blocks:
@@ -767,6 +798,7 @@ pt-PT:
         suspend: "%{name} suspendeu a conta de %{target}"
       appeal_approved: Recorrido
       appeal_pending: Recurso pendente
+      appeal_rejected: Recurso rejeitado
     system_checks:
       database_schema_check:
         message_html: Há migrações de base de dados pendentes. Queira executá-las, para garantir que a aplicação se comporta como o esperado
@@ -780,6 +812,12 @@ pt-PT:
         message_html: Não definiu nenhuma regra para a instância.
       sidekiq_process_check:
         message_html: Nenhum processo Sidekiq em execução para a(s) fila(s) %{value}. Reveja a configuração do seu Sidekiq
+      upload_check_privacy_error:
+        action: Verifique aqui para mais informações
+        message_html: "<strong>O seu servidor web está mal configurado. A privacidade dos seus utilizadores está em risco.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Verifique aqui para mais informações
+        message_html: "<strong>O seu armazenamento de objetos está mal configurado. A privacidade dos seus utilizadores está em risco.</strong>"
     tags:
       review: Estado da revisão
       updated_msg: 'Definições de #etiquetas correctamente actualizadas'
@@ -802,6 +840,7 @@ pt-PT:
           other: Partilhado por %{count} pessoas na última semana
         title: Ligações em alta
         usage_comparison: Partilhado %{today} vezes hoje, em comparação com %{yesterday} ontem
+      not_allowed_to_trend: Não permitido para tendência
       only_allowed: Apenas permitidos
       pending_review: Pendente de revisão
       preview_card_providers:
@@ -933,6 +972,7 @@ pt-PT:
   applications:
     created: Aplicação correctamente criada
     destroyed: Aplicação correctamente eliminada
+    logout: Sair
     regenerate_token: Regenerar token de acesso
     token_regenerated: Token de acesso regenerado com sucesso
     warning: Cuidado com estes dados. Não partilhar com ninguém!
@@ -940,6 +980,8 @@ pt-PT:
   auth:
     apply_for_account: Solicitar uma conta
     change_password: Palavra-passe
+    confirmations:
+      wrong_email_hint: Se esse endereço de e-mail não estiver correto, você pode alterá-lo nas configurações da conta.
     delete_account: Eliminar conta
     delete_account_html: Se deseja eliminar a sua conta, pode <a href="%{path}">continuar aqui</a>. Uma confirmação será solicitada.
     description:
@@ -967,6 +1009,8 @@ pt-PT:
     resend_confirmation: Reenviar instruções de confirmação
     reset_password: Criar nova palavra-passe
     rules:
+      accept: Aceitar
+      back: Retroceder
       preamble: Estas são definidas e aplicadas pelos moderadores de %{domain}.
       title: Algumas regras básicas.
     security: Alterar palavra-passe
@@ -1114,7 +1158,7 @@ pt-PT:
   featured_tags:
     add_new: Adicionar nova
     errors:
-      limit: Já atingiste o limite máximo de etiquetas
+      limit: Já destacou o número máximo de hashtags permitido
     hint_html: "<strong>O que são etiquetas em destaque?</strong> Exibidas de forma bem visível no seu perfil público, permitem que as pessoas consultem as suas publicações públicas especificamente sob essas etiquetas. São uma óptima ferramenta para dar seguimento a trabalhos criativos ou projectos de longo prazo."
   filters:
     contexts:
@@ -1158,8 +1202,6 @@ pt-PT:
       index:
         hint: Este filtro aplica-se a publicações individuais selecionadas independentemente de outros critérios. Pode adicionar mais publicações a este filtro através da interface web.
         title: Publicações filtradas
-  footer:
-    trending_now: Em alta neste momento
   generic:
     all: Tudo
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ pt-PT:
     validation_errors:
       one: Algo não está correcto. Por favor analise o erro abaixo
       other: Algo não está bem. Queira analisar os %{count} erros abaixo
-  html_validator:
-    invalid_markup: 'contém marcação HTML inválida: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Arquivo CSV inválido. Erro: %{error}'
@@ -1226,7 +1266,7 @@ pt-PT:
     title: Convidar pessoas
   lists:
     errors:
-      limit: Número máximo de listas alcançado
+      limit: Atingiu o número máximo de listas permitido
   login_activities:
     authentication_methods:
       otp: aplicação de autenticação em duas etapas
@@ -1367,7 +1407,11 @@ pt-PT:
       unrecognized_emoji: não é um emoji reconhecido
   relationships:
     activity: Atividade da conta
+    confirm_follow_selected_followers: Tem a certeza que deseja seguir os seguidores selecionados?
+    confirm_remove_selected_followers: Tem a certeza que deseja seguir os seguidores selecionados?
+    confirm_remove_selected_follows: Tem certeza que deseja remover os seguidores selecionados?
     dormant: Inativo
+    follow_failure: Não foi possível seguir algumas das contas selecionadas.
     follow_selected_followers: Seguir seguidores selecionados
     followers: Seguidores
     following: A seguir
@@ -1407,6 +1451,7 @@ pt-PT:
       electron: Electron
       firefox: Firefox
       generic: Navegador desconhecido
+      huawei_browser: Navegador Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Navegador Nokia S40 Ovi
@@ -1416,6 +1461,7 @@ pt-PT:
       qq: QQ Browser
       safari: Safari
       uc_browser: Navegador UC
+      unknown_browser: Navegador Desconhecido
       weibo: Weibo
     current_session: Sessão atual
     description: "%{browser} em %{platform}"
@@ -1428,9 +1474,10 @@ pt-PT:
       chrome_os: ChromeOS
       firefox_os: SO Firefox
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: plataforma desconhecida
+      unknown_platform: Plataforma Desconhecida
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1543,7 +1590,7 @@ pt-PT:
       '7889238': 3 meses
     min_age_label: Limite de idade
     min_favs: Manter pelo menos as publicações dos marcadores
-    min_favs_hint: Não apaga nenhuma das suas publicações que tenha recebido mais do que esta quantidade de favoritos. Deixe em branco para apagar as publicações, independentemente do número de favoritos
+    min_favs_hint: Não elimina nenhuma das suas publicações que tenha recebido pelo menos este número de favoritos. Deixe em branco para eliminar publicações, independentemente do seu número de favoritos
     min_reblogs: Manter as publicações reforçadas mais de
     min_reblogs_hint: Não apaga nenhuma das suas publicações que tenha sido partilhada mais do que este número de vezes. Deixe em branco para apagar as publicações, independentemente do número de partilhas
   stream_entries:
@@ -1595,22 +1642,22 @@ pt-PT:
       title: Arquivo de ficheiros
     suspicious_sign_in:
       change_password: alterar a sua palavra-passe
-      details: 'Aqui estão os detalhes do inicio de sessão:'
-      explanation: Detetamos um inicio de sessão na sua conta a partir de um novo endereço IP.
+      details: 'Eis os pormenores do início de sessão:'
+      explanation: Detectámos um início de sessão na sua conta a partir dum endereço IP novo.
       further_actions_html: Se não foi você, recomendamos que %{action} imediatamente e ative a autenticação de dois fatores para manter a sua conta segura.
-      subject: A sua conta foi acessada a partir de um novo endereço IP
-      title: Novo inicio de sessão
+      subject: A sua conta foi acedida a partir dum endereço IP novo
+      title: Um início de sessão novo
     warning:
       appeal: Submeter um recurso
-      appeal_description: Se acredita que isso é um erro, pode submeter um recurso para a equipa de %{instance}.
+      appeal_description: Se acha que isso é um erro, pode submeter um recurso para a equipa de %{instance}.
       categories:
         spam: Spam
         violation: O conteúdo infringe as seguintes diretrizes da comunidade
       explanation:
-        delete_statuses: Algumas das suas publicações foram consideradas como a infringir uma ou mais diretrizes da comunidade e foram subsequentemente removidas pelos moderadores de %{instance}.
-        disable: Não pode mais utilizar a sua conta, mas o seu perfil e outros dados permanecem intactos. Pode solicitar uma cópia dos seus dados, alterar as definições da conta ou apagá-la.
-        mark_statuses_as_sensitive: Algumas das suas publicações foram marcadas como sensíveis pelos moderadores de %{instance}. Isto significa que as pessoas terão de clicar na media nas publicações, antes de ser exibida uma pré-visualização. No futuro, quando publicar, lembre-se que pode você mesmo marcar a media como sensível.
-        sensitive: A partir de agora, todos os ficheiros de media que carregue serão marcados como sensíveis e escondidos atrás de um aviso de "clicar-para-continuar".
+        delete_statuses: Algumas das suas publicações foram consideradas como infractoras duma ou mais diretrizes da comunidade, e subsequentemente removidas pelos moderadores de %{instance}.
+        disable: Já não pode utilizar a sua conta, mas o seu perfil e outros dados permanecem intactos. Pode solicitar uma cópia dos seus dados, alterar as definições da conta, ou que a sua conta seja apagada.
+        mark_statuses_as_sensitive: Algumas das suas publicações foram marcadas como problemáticos pelos moderadores de %{instance}. Isto significa que as pessoas terão de tocar nas publicações para que possa ser apresentada uma pré-visualização. No futuro, quando publicar, lembre-se que pode você próprio marcar os media como problemáticos.
+        sensitive: A partir de agora, todos os ficheiros de media que carregue serão marcados como problemáticos e escondidos sob um aviso pedido para tocar para os ver.
         silence: Pode ainda utilizar a sua conta mas apenas as pessoas que já o seguem poderão ver as suas mensagens neste servidor, e poderá ser excluído de várias funcionalidades de divulgação. No entanto, outros poderão ainda segui-lo manualmente.
         suspend: Não pode mais utilizar a sua conta, e o seu perfil e outros dados já não se encontram acessíveis. Poderá ainda iniciar sessão para solicitar uma cópia dos seus dados até os mesmos serem totalmente removidos em cerca de 30 dias, porém reteremos alguns dados básicos para o impedir evitar a suspensão.
       reason: 'Motivo:'
@@ -1618,17 +1665,17 @@ pt-PT:
       subject:
         delete_statuses: As suas publicações em %{acct} foram removidas
         disable: A tua conta %{acct} foi congelada
-        mark_statuses_as_sensitive: As suas publicações em %{acct} foram marcadas como sensíveis
+        mark_statuses_as_sensitive: As suas publicações em %{acct} foram marcadas como problemáticas
         none: Aviso para %{acct}
-        sensitive: As suas publicações em %{acct} serão a partir de agora marcadas como sensíveis
-        silence: A tua conta %{acct} foi limitada
-        suspend: A tua conta %{acct} foi suspensa
+        sensitive: A partir de agora, as suas publicações em %{acct} serão marcadas como problemáticas
+        silence: A sua conta %{acct} foi limitada
+        suspend: A sua conta %{acct} foi suspensa
       title:
         delete_statuses: Publicações removidas
         disable: Conta congelada
-        mark_statuses_as_sensitive: Publicações marcadas como sensíveis
+        mark_statuses_as_sensitive: Publicações marcadas como problemáticas
         none: Aviso
-        sensitive: Conta marcada como sensível
+        sensitive: Conta marcada como problemática
         silence: Conta limitada
         suspend: Conta suspensa
     welcome:
@@ -1638,32 +1685,33 @@ pt-PT:
       final_action: Começar a publicar
       final_step: 'Comece a publicar! Mesmo sem seguidores, as suas mensagens públicas podem ser vistas por outros, como por exemplo na cronologia local e em etiquetas. Pense em apresentar-se usando a etiqueta #apresentações.'
       full_handle: O seu nome completo
-      full_handle_hint: Isto é o que tem de facultar aos seus amigos para que eles lhe possam enviar mensagens ou seguir a partir de outra instância.
+      full_handle_hint: Isto é o que tem de facultar aos seus amigos para que eles lhe possam enviar mensagens ou segui-lo a partir de outra instância.
       subject: Bem-vindo ao Mastodon
       title: Bem-vindo a bordo, %{name}!
   users:
-    follow_limit_reached: Não podes seguir mais do que %{limit} pessoas
+    follow_limit_reached: Não pode seguir mais do que %{limit} pessoas
+    go_to_sso_account_settings: Ir para as definições de conta do seu fornecedor de identidade
     invalid_otp_token: Código de autenticação inválido
-    otp_lost_help_html: Se perdeu acesso a ambos, pode entrar em contacto com %{email}
+    otp_lost_help_html: Se perdeu o acesso a ambos, pode entrar em contacto com %{email}
     seamless_external_login: Tu estás ligado via um serviço externo. Por isso, as configurações da palavra-passe e do e-mail não estão disponíveis.
     signed_in_as: 'Registado como:'
   verification:
-    explanation_html: 'Pode <strong>comprovar que é o dono dos links nos metadados do seu perfil</strong>. Para isso, o website para o qual o link aponta tem de conter um link para o seu perfil do Mastodon. Esse link <strong>tem</strong> de ter um atributo <code>rel="me"</code>. O conteúdo do texto não é relevante. Aqui está um exemplo:'
+    explanation_html: 'Pode <strong>comprovar que é o dono das hiperligações nos metadados do seu perfil</strong>. Para isso, a página para a qual a hiperligação aponta tem de conter uma outra para o seu perfil do Mastodon. Após adicionar a hiperligação, poderá ter de voltar aqui e voltar a guardar o seu perfil para que a verificação produza efeito. Essa hiperligação <strong>tem</strong> de ter um atributo <code>rel="me"</code>. O conteúdo do texto não é importante. Eis um exemplo:'
     verification: Verificação
   webauthn_credentials:
     add: Adicionar nova chave de segurança
     create:
       error: Ocorreu um problema ao adicionar sua chave de segurança. Tente novamente.
-      success: A sua chave de segurança foi adicionada com sucesso.
+      success: A sua chave de segurança foi correctamente adicionada.
     delete: Eliminar
     delete_confirmation: Tem a certeza de que pretende eliminar esta chave de segurança?
-    description_html: Se você ativar a <strong>autenticação com chave de segurança</strong>, para aceder à sua conta será necessário que utilize uma das suas chaves de segurança.
+    description_html: Se activar a <strong>autenticação com chave de segurança</strong>, vai passar a precisar de usar uma das suas chaves de segurança para aceder à sua conta.
     destroy:
       error: Ocorreu um problema ao remover a sua chave de segurança. Tente novamente.
-      success: A sua chave de segurança foi eliminada com sucesso.
+      success: A sua chave de segurança foi correctamente eliminada.
     invalid_credential: Chave de segurança inválida
-    nickname_hint: Introduza o apelido da sua nova chave de segurança
+    nickname_hint: Introduza a alcunha da sua nova chave de segurança
     not_enabled: Ainda não ativou o WebAuthn
-    not_supported: Este navegador não suporta chaves de segurança
+    not_supported: Este navegador não funciona com chaves de segurança
     otp_required: Para usar chaves de segurança, por favor ative primeiro a autenticação em duas etapas.
     registered_on: Registado em %{date}
diff --git a/config/locales/ro.yml b/config/locales/ro.yml
index 48cf790b7..32b0bdd2f 100644
--- a/config/locales/ro.yml
+++ b/config/locales/ro.yml
@@ -576,9 +576,6 @@ ro:
       expires_at: Expiră
       uses: Utilizări
     title: Invită persoane
-  lists:
-    errors:
-      limit: Ați atins valoarea maximă a listelor
   media_attachments:
     validations:
       images_and_video: Nu se poate atașa un videoclip la o stare care conține deja imagini
@@ -752,5 +749,4 @@ ro:
     seamless_external_login: Sunteți autentificat prin intermediul unui serviciu extern, astfel încât parola și setările de e-mail nu sunt disponibile.
     signed_in_as: 'Conectat ca:'
   verification:
-    explanation_html: 'Poți <strong>să te verifici ca proprietar al link-urilor din metadatele profilului tău</strong>. Pentru aceasta, site-ul web asociat trebuie să conțină un link înapoi la profilul tău. Link-ul înapoi <strong>trebuie</strong> să aibă un atribut <code>rel="me"</code>. Conținut textului link-ului nu contează. Iată un exemplu:'
     verification: Verificare
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index 9e0b3d54a..de248d76d 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -95,6 +95,7 @@ ru:
       moderation:
         active: Действующие
         all: Все
+        disabled: Отключено
         pending: В ожидании
         silenced: Ограниченные
         suspended: Заблокированные
@@ -139,6 +140,7 @@ ru:
       search: Поиск
       search_same_email_domain: Другие пользователи с тем же доменом электронной почты
       search_same_ip: Другие пользователи с таким же IP
+      security: Безопасность
       security_measures:
         only_password: Только пароль
         password_and_2fa: Пароль и 2FA
@@ -443,6 +445,7 @@ ru:
         resolve: Проверить домен
         title: Новая блокировка по домену
       no_email_domain_block_selected: Блоки домена электронной почты не были изменены, так как не были выбраны
+      not_permitted: Не разрешено
       resolved_dns_records_hint_html: Доменное имя устраняется на следующие MX-домены, которые в конечном итоге отвечают за прием электронной почты. Блокировка MX-домена будет блокировать регистрации с любого адреса электронной почты, который использует тот же MX-домен, даже если видимое доменное имя отличается от него. <strong>Будьте осторожны, чтобы не блокировать основных поставщиков электронной почты</strong>
       resolved_through_html: Разрешено через %{domain}
       title: Блокировка e-mail доменов
@@ -457,6 +460,7 @@ ru:
         private_comment_description_html: 'Чтобы помочь вам отслеживать откуда импортируются блокировка, импортированные блокировки будут созданы со следующим приватным комментарием: <q>%{comment}</q>'
         private_comment_template: Импортировано из %{source} %{date}
         title: Импорт доменных блокировок
+      invalid_domain_block: 'Один или несколько доменных блокировок были пропущены из-за следующих ошибок: %{error}'
       new:
         title: Импорт доменных блокировок
       no_file: Файл не выбран
@@ -492,6 +496,7 @@ ru:
       content_policies:
         comment: Внутренняя заметка
         description_html: Вы можете определить политики контента, которые будут применяться ко всем учетным записям этого домена и любого из его субдоменов.
+        limited_federation_mode_description_html: Вы можете выбрать, разрешать ли объединение с этим доменом.
         policies:
           reject_media: Отклонить медиа
           reject_reports: Отклонять жалобы
@@ -599,19 +604,23 @@ ru:
         mark_as_sensitive_description_html: Весь медиаконтент в обжалованных сообщениях будет отмечен как чувствительный, а претензия - записана, чтобы помочь вам в решении конфликтов при повторных нарушениях со стороны того же аккаунта.
         other_description_html: Посмотрите больше настроек для контроля поведения учётной записи и наладьте связь с аккаунтом с жалобой.
         resolve_description_html: Никаких действий не будет выполнено относительно доложенного содержимого. Жалоба будет закрыта.
-        silence_description_html: Профиль будет просматриваем только пользователями, которые уже подписаны на него, либо открыли его вручную. Это действие можно отменить в любой момент.
-        suspend_description_html: Профиль и всё опубликованное в нём содержимое станут недоступны, пока в конечном итоге учётная запись не будет удалена. Пользователи не смогут взаимодействовать с этой учётной записью. Это действие можно отменить в течение 30 дней.
+        silence_description_html: Учетная запись будет видна только тем пользователям, которые уже подписаны на неё, либо открыли его вручную. Это действие можно отменить в любой момент, и отменяет все жалобы против аккаунта.
+        suspend_description_html: Аккаунт и все его содержимое будут недоступны и в конечном итоге удалены, и взаимодействие с ним будет невозможно. Это действие можно отменить в течение 30 дней. Отменяет все жалобы против этого аккаунта.
       actions_description_html: Выберите действие, чтобы решить данную жалобу. Если вы выберите наказание против аккаунта, вы получите уведомление по E-Mail, проверяйте папку <strong>Спам</strong>.
+      actions_description_remote_html: Решите вопрос о том, какие меры необходимо принять для урегулирования этой жалобы. Это повлияет только на то, как <strong>ваш</strong> сервер взаимодействует с этим удаленным аккаунтом и обрабатывает его содержимое.
       add_to_report: Прикрепить ещё
       are_you_sure: Вы уверены?
       assign_to_self: Назначить себе
       assigned: Назначенный модератор
       by_target_domain: Домен объекта жалобы
+      cancel: Отменить
       category: Категория
       category_description_html: Причина, по которой были доложены этот пользователь или содержимое, будет указана при коммуникации с фигурирующим в жалобе пользователем
       comment:
         none: Нет
       comment_description_html: 'В дополнение, %{name} добавил(а) следующий комментарий:'
+      confirm: Подтвердить
+      confirm_action: Произвести модерацию учётной записи %{acct}
       created_at: Создана
       delete_and_resolve: Удалить посты
       forwarded: Переслано
@@ -628,6 +637,7 @@ ru:
         placeholder: Опишите, какие действия были приняты, или любые другие подробности…
         title: Примечания
       notes_description_html: Просмотрите или оставьте примечания для остальных модераторов и себя в будущем
+      processed_msg: 'Жалоба #%{id} успешно обработана'
       quick_actions_description_html: 'Выберите действие или прокрутите вниз, чтобы увидеть контент с жалобой:'
       remote_user_placeholder: удаленный пользователь из %{instance}
       reopen: Переоткрыть жалобу
@@ -640,9 +650,24 @@ ru:
       status: Статус
       statuses: Содержимое относящееся к жалобе
       statuses_description_html: Нарушающее правила содержимое будет процитировано при коммуникации с фигурирующим в жалобе аккаунтом
+      summary:
+        action_preambles:
+          delete_html: 'Вы собираетесь <strong>удалить</strong> некоторые посты <strong>@%{acct}</strong>. В результате этого:'
+          mark_as_sensitive_html: 'Вы собираетесь <strong>удалить</strong> некоторые посты <strong>@%{acct}</strong>. В результате этого:'
+          silence_html: 'Вы собираетесь <strong>заморозить</strong> учетную запись <strong>@%{acct}</strong>. В результате этого:'
+          suspend_html: 'Вы собираетесь <strong>заморозить</strong> учетную запись <strong>@%{acct}</strong>. В результате этого:'
+        actions:
+          delete_html: Удалить оскорбительные сообщения
+          mark_as_sensitive_html: Пометить медиа-оскорбительные сообщения как чувствительные
+        close_report: 'Отметить жалобу #%{id} как решённую'
+        close_reports_html: Отметить <strong>все</strong> жалобы на <strong>@%{acct}</strong> как разрешённые
+        delete_data_html: Удалить профиль и контент <strong>@%{acct}</strong> через 30 дней, если за это время они не будут разблокированы
+        preview_preamble_html: "<strong>@%{acct}</strong> получит предупреждение со следующим содержанием:"
+        send_email_html: Отправить <strong>@%{acct}</strong> предупреждение на почту
       target_origin: Происхождение объекта жалобы
       title: Жалобы
       unassign: Снять назначение
+      unknown_action_msg: 'Неизвестное действие: %{action}'
       unresolved: Нерешённые
       updated_at: Обновлена
       view_profile: Открыть профиль
@@ -741,6 +766,8 @@ ru:
         preamble: Наблюдение интересного контента играет важную роль при открытии новых пользователей, которые могут не знать ни одного Mastodon. Контролируйте как работают различные функции обнаружения на вашем сервере.
         profile_directory: Каталог профилей
         public_timelines: Публичные ленты
+        publish_discovered_servers: Публикация списка обнаруженных узлов
+        publish_statistics: Опубликовать стаитстику
         title: Обзор
         trends: Популярное
       domain_blocks:
@@ -795,6 +822,7 @@ ru:
         suspend: "%{name} приостановил аккаунт %{target}"
       appeal_approved: Обжаловано
       appeal_pending: Обжалование в обработке
+      appeal_rejected: Апелляция отклонена
     system_checks:
       database_schema_check:
         message_html: Есть отложенные миграции базы данных. Запустите их, чтобы убедиться, что приложение работает должным образом
@@ -808,6 +836,11 @@ ru:
         message_html: Вы не определили правила сервера.
       sidekiq_process_check:
         message_html: Ни один Sidekiq не запущен для %{value} очереди(-ей). Пожалуйста, просмотрите настройки Sidekiq
+      upload_check_privacy_error:
+        action: Нажмите сюда, чтобы узнать подробности
+      upload_check_privacy_error_object_storage:
+        action: Нажмите сюда, чтобы узнать подробности
+        message_html: "<strong>Ваше хранилище объектов неправильно настроено. Безопасность ваших пользователей находится под угрозой</strong>"
     tags:
       review: Состояние проверки
       updated_msg: Настройки хэштега обновлены
@@ -832,6 +865,7 @@ ru:
           other: Поделился %{count} человек за последнюю неделю
         title: Актуальные ссылки
         usage_comparison: Поделились %{today} раз сегодня, по сравнению с %{yesterday} вчера
+      not_allowed_to_trend: Не допущено в популярное
       only_allowed: Только разрешенное
       pending_review: Ожидает рассмотрения
       preview_card_providers:
@@ -937,6 +971,7 @@ ru:
         title: Популярные посты
       new_trending_tags:
         no_approved_tags: На данный момент популярные подтвержденные хэштеги отсутствуют.
+        requirements: 'Каждый из этих кандидатов может превысить #%{rank} одобренных популярных хештегов. Сейчас это #%{lowest_tag_name} с числом %{lowest_tag_score}.'
         title: Популярные хэштеги
       subject: Новые тренды для проверки на %{instance}
   aliases:
@@ -968,6 +1003,7 @@ ru:
   applications:
     created: Приложение успешно создано
     destroyed: Приложение успешно удалено
+    logout: Выйти
     regenerate_token: Повторно сгенерировать токен доступа
     token_regenerated: Токен доступа успешно сгенерирован
     warning: Будьте очень внимательны с этими данными. Не делитесь ими ни с кем!
@@ -975,6 +1011,8 @@ ru:
   auth:
     apply_for_account: Запросить аккаунт
     change_password: Пароль
+    confirmations:
+      wrong_email_hint: Если этот адрес электронной почты неверен, вы можете изменить его в настройках аккаунта.
     delete_account: Удалить учётную запись
     delete_account_html: Удалить свою учётную запись <a href="%{path}">можно в два счёта здесь</a>, но прежде у вас будет спрошено подтверждение.
     description:
@@ -1002,6 +1040,8 @@ ru:
     resend_confirmation: Повторить отправку инструкции для подтверждения
     reset_password: Сбросить пароль
     rules:
+      accept: Принять
+      back: Назад
       preamble: Они устанавливаются и применяются модераторами %{domain}.
       title: Несколько основных правил.
     security: Безопасность
@@ -1199,8 +1239,6 @@ ru:
       index:
         hint: Этот фильтр применяется для выбора отдельных постов, независимо от других критериев. Вы можете добавить больше записей в этот фильтр из веб-интерфейса.
         title: Отфильтрованные посты
-  footer:
-    trending_now: Актуально сейчас
   generic:
     all: Любой
     all_items_on_page_selected_html:
@@ -1231,8 +1269,6 @@ ru:
       many: Что-то здесь не так! Пожалуйста, прочитайте о %{count} ошибках ниже
       one: Что-то здесь не так! Пожалуйста, прочитайте об ошибке ниже
       other: Что-то здесь не так! Пожалуйста, прочитайте о %{count} ошибках ниже
-  html_validator:
-    invalid_markup: 'невалидная разметка HTML: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Неверный файл CSV. Ошибка: %{error}'
@@ -1277,7 +1313,7 @@ ru:
     title: Пригласить людей
   lists:
     errors:
-      limit: Вы достигли максимального числа списков
+      limit: Вы достигли максимального количества пользователей
   login_activities:
     authentication_methods:
       otp: приложение двухфакторной аутентификации
@@ -1418,7 +1454,11 @@ ru:
       unrecognized_emoji: не является распознанным эмодзи
   relationships:
     activity: Активность учётной записи
+    confirm_follow_selected_followers: Вы уверены, что хотите подписаться на выбранных подписчиков?
+    confirm_remove_selected_followers: Вы уверены, что хотите удалить выбранных подписчиков?
+    confirm_remove_selected_follows: Вы уверены, что хотите удалить выбранные подписки?
     dormant: Заброшенная
+    follow_failure: Не удалось подписаться за некоторыми из выбранных аккаунтов.
     follow_selected_followers: Подписаться на выбранных подписчиков
     followers: Подписчики
     following: Подписки
@@ -1458,6 +1498,7 @@ ru:
       electron: Electron
       firefox: Firefox
       generic: Неизвестный браузер
+      huawei_browser: Huawei Browser
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1467,6 +1508,7 @@ ru:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Неизвестный браузер
       weibo: Weibo
     current_session: Текущая сессия
     description: "%{browser} на %{platform}"
@@ -1479,9 +1521,10 @@ ru:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: OS Кай
       linux: Linux
       mac: Mac
-      other: неизвестной платформе
+      unknown_platform: Неизвестная платформа
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1606,7 +1649,7 @@ ru:
       '7889238': 3 месяца
     min_age_label: Возрастной порог
     min_favs: Порог отметок «избранного»
-    min_favs_hint: Не удаляет ваши посты, у которых количество отметок «избранного» достигло указанного выше значения. Оставьте поле пустым, чтобы удалять посты независимо от количества отметок.
+    min_favs_hint: Не удалять ваши посты, у которых количество отметок «избранного» достигло указанного выше значения. Оставьте поле пустым, чтобы удалять посты независимо от количества отметок
     min_reblogs: Порог продвижений
     min_reblogs_hint: Не удаляет ваши посты, количество продвижений которых достигло указанного выше значения. Оставьте поле пустым, чтобы удалять посты независимо от количества продвижений.
   stream_entries:
@@ -1706,6 +1749,7 @@ ru:
       title: Добро пожаловать на борт, %{name}!
   users:
     follow_limit_reached: Вы не можете подписаться больше, чем на %{limit} человек
+    go_to_sso_account_settings: Перейти к настройкам сторонних аккаунтов учетной записи
     invalid_otp_token: Введен неверный код двухфакторной аутентификации
     otp_lost_help_html: Если Вы потеряли доступ к обоим, свяжитесь с %{email}
     seamless_external_login: Вы залогинены через сторонний сервис, поэтому настройки e-mail и пароля недоступны.
diff --git a/config/locales/sa.yml b/config/locales/sa.yml
index 229e4568c..f8c64a785 100644
--- a/config/locales/sa.yml
+++ b/config/locales/sa.yml
@@ -6,7 +6,5 @@ sa:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/sc.yml b/config/locales/sc.yml
index 3c6149be1..307a44758 100644
--- a/config/locales/sc.yml
+++ b/config/locales/sc.yml
@@ -640,8 +640,6 @@ sc:
     storage: Immagasinamentu
   featured_tags:
     add_new: Agiunghe noa
-    errors:
-      limit: As giai evidentziadu sa cantidade màssima de etichetas
     hint_html: "<strong>Ite sunt is etichetas in evidèntzia?</strong> Sunt ammustradas in evidèntzia in su profilu pùblicu tuo e permitint a sa gente de navigare is messàgios pùblicos tuos in cussas etichetas ispetzìficas. Sunt unu traste perfetu pro tènnere unu registru de òperas creativas o progetos longos."
   filters:
     contexts:
@@ -660,8 +658,6 @@ sc:
       title: Filtros
     new:
       title: Agiunghe unu filtru nou
-  footer:
-    trending_now: Est tendèntzia immoe
   generic:
     all: Totus
     changes_saved_msg: Modìficas sarvadas.
@@ -672,8 +668,6 @@ sc:
     validation_errors:
       one: Calicuna cosa ancora no est andende. Bide sa faddina in bàsciu
       other: Calicuna cosa ancora no est andende. Bide is %{count} faddinas in bàsciu
-  html_validator:
-    invalid_markup: 'cuntenet etichetas HTML non vàlidas: %{error}'
   imports:
     errors:
       over_rows_processing_limit: cuntenet prus de %{count} filas
@@ -713,9 +707,6 @@ sc:
       expires_at: Iscadit
       uses: Impreos
     title: Invita gente
-  lists:
-    errors:
-      limit: Cantidade màssima de listas cròmpida
   media_attachments:
     validations:
       images_and_video: Non si podet allegare unu vìdeu in una publicatzione chi cuntenet giai immàgines
@@ -885,7 +876,6 @@ sc:
       ios: iOS
       linux: Linux
       mac: macOS
-      other: prataforma disconnota
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1030,7 +1020,6 @@ sc:
     seamless_external_login: As abertu sa sessione pro mèdiu de unu servìtziu esternu, e pro custa resone is cunfiguratziones de sa crae de intrada e de posta eletrònica non sunt a disponimentu.
     signed_in_as: 'Sessione aberta comente:'
   verification:
-    explanation_html: 'Ti podes <strong>verificare a sa sola comente mere de is ligòngios in is metadatos de su profilu tuo</strong>. Pro ddu fàghere su situ ligadu depet cuntènnere unu ligòngiu chi torret a su profilu de Mastodon tuo. Su ligòngiu in su situ <strong>depet</strong> tènnere un''atributu <code>rel="me"</code>. Su testu cuntenutu in su ligòngiu no est de importu. Custu est un''esèmpiu:'
     verification: Verìfica
   webauthn_credentials:
     add: Agiunghe una crae de seguresa noa
diff --git a/config/locales/sco.yml b/config/locales/sco.yml
index 23b905754..55d8e09a4 100644
--- a/config/locales/sco.yml
+++ b/config/locales/sco.yml
@@ -573,8 +573,6 @@ sco:
         mark_as_sensitive_description_html: The media in the clyped on posts will be mairked as sensitive an a strike wull be recordit fir tae help ye escalate on future infractions bi the same accoont.
         other_description_html: See mair options fir controllin the accoont's behaviour an customize communication tae the clyped on accoont.
         resolve_description_html: Nae action wull be taen aginst the clyped on accoont, nae strike recordit, an the clype wull be shut.
-        silence_description_html: The profile wull be visible ainly tae thaim thit awriddy follae it or luik it up bi haun, severely limitin its reach. This kin aye be revertit.
-        suspend_description_html: The profile an aw its contents wull become inaccessible tae it is eventually deletit. Interactin wi the accoont wullnae be possible. Reversible athin 30 days.
       actions_description_html: Decide whit action fir tae tak fir tae resolve this clype. If ye tak a punitive action agin the accoont thit wis clyped on, a email note wull be sent tae them, except whan the <strong>Mince</strong> caitegory is selectit.
       add_to_report: Add mair tae yer clype
       are_you_sure: Ye sure?
@@ -1105,8 +1103,6 @@ sco:
     storage: Media storage
   featured_tags:
     add_new: Add new
-    errors:
-      limit: Ye'v awriddy featurt the maximum amoont o hashtags
     hint_html: "<strong>Whit's featurt hashtags?</strong> They get pit prominently on yer public profile an alloo fowk fir tae broose yer public posts specifically unner thae hashtags. They'r a braw tuil fir keepin track o creative warks or lang-term projects."
   filters:
     contexts:
@@ -1150,8 +1146,6 @@ sco:
       index:
         hint: This filter applies tae select individual posts regairdless o ither criteria. Ye kin add mair posts tae this filter fae the wab interface.
         title: Filtert posts
-  footer:
-    trending_now: Trendin the noo
   generic:
     all: Aw
     all_items_on_page_selected_html:
@@ -1174,8 +1168,6 @@ sco:
     validation_errors:
       one: Somethin isnae quite richt yit! Please luik ower the error ablow
       other: Somethin isnae quite richt yit! Please review %{count} errors ablow
-  html_validator:
-    invalid_markup: 'contains invalid HTML mairkup: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Invalid CSV file. Error: %{error}'
@@ -1216,9 +1208,6 @@ sco:
       expires_at: Expires
       uses: Uises
     title: Invite fowk
-  lists:
-    errors:
-      limit: Ye'v hit the maximum amoont o lists
   login_activities:
     authentication_methods:
       otp: twa-factor authentication app
@@ -1422,7 +1411,6 @@ sco:
       ios: iOS
       linux: Linux
       mac: macOS
-      other: unkent platform
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1535,7 +1523,6 @@ sco:
       '7889238': 3 month
     min_age_label: Age threshaud
     min_favs: Keep posts favouritit at least
-    min_favs_hint: Disnae delete onie o yer posts thit's got at least this amoont o favourites. Lea blank fir tae delete posts regairdless o their nummer o favourites
     min_reblogs: Keep posts heezed at least
     min_reblogs_hint: Disnae delete onie o yer posts thit's been heezed at least this nummer o times. Lea blank fir tae delete posts regairdless o their number o heezes
   stream_entries:
@@ -1640,7 +1627,6 @@ sco:
     seamless_external_login: Ye'r logged in via a external service, sae passwird an email settins urnae available.
     signed_in_as: 'Signt in as:'
   verification:
-    explanation_html: 'Ye kin <strong>verify yersel as the ainer o the links in yer profile metadata</strong>. Fir that, the linkt wabsteid haes tae contain a link back tae yer Mastodon profile. The link back <strong>haes tae</strong> hae a <code>rel="me"</code> attribute. The text content o the link disnae maitter. Here a example:'
     verification: Verification
   webauthn_credentials:
     add: Add new security key
diff --git a/config/locales/si.yml b/config/locales/si.yml
index 37f8a8d7e..c34df2986 100644
--- a/config/locales/si.yml
+++ b/config/locales/si.yml
@@ -522,8 +522,6 @@ si:
         mark_as_sensitive_description_html: වාර්තා කරන ලද පළ කිරීම් වල මාධ්‍ය සංවේදී ලෙස සලකුණු කරනු ලබන අතර එම ගිණුම මගින් අනාගත උල්ලංඝනයන් උත්සන්න කිරීමට ඔබට උපකාර කිරීමට වර්ජනයක් වාර්තා කරනු ඇත.
         other_description_html: ගිණුමේ හැසිරීම පාලනය කිරීම සහ වාර්තා කළ ගිණුමට සන්නිවේදනය අභිරුචිකරණය කිරීම සඳහා තවත් විකල්ප බලන්න.
         resolve_description_html: වාර්තා කරන ලද ගිණුමට එරෙහිව කිසිදු ක්‍රියාමාර්ගයක් නොගනු ඇත, වැඩ වර්ජනයක් වාර්තා නොකෙරේ, වාර්තාව වසා දමනු ඇත.
-        silence_description_html: පැතිකඩ දෘශ්‍යමාන වනු ඇත්තේ දැනටමත් එය අනුගමනය කරන හෝ අතින් බලන අයට පමණක් වන අතර, එහි ළඟාවීම දැඩි ලෙස සීමා කරයි. සෑම විටම ආපසු හැරවිය හැක.
-        suspend_description_html: එය අවසානයේ මකා දමන තුරු පැතිකඩ සහ එහි සියලුම අන්තර්ගතයන් ප්‍රවේශ විය නොහැකි වනු ඇත. ගිණුම සමඟ අන්තර් ක්රියා කිරීම කළ නොහැකි වනු ඇත. දින 30 ක් ඇතුළත ආපසු හැරවිය හැකිය.
       actions_description_html: මෙම වාර්තාව විසඳීමට ගත යුතු ක්‍රියාමාර්ගය තීරණය කරන්න. ඔබ වාර්තා කරන ලද ගිණුමට එරෙහිව දණ්ඩනීය ක්‍රියාමාර්ගයක් ගන්නේ නම්, <strong>Spam</strong> කාණ්ඩය තෝරාගත් විට හැර, ඔවුන්ට විද්‍යුත් තැපෑලෙන් දැනුම්දීමක් යවනු ලැබේ.
       add_to_report: වාර්තා කිරීමට තවත් එක් කරන්න
       are_you_sure: ඔබට විශ්වාසද?
@@ -933,8 +931,6 @@ si:
     storage: මාධ්‍ය ගබඩාව
   featured_tags:
     add_new: අලුතින් එකතු කරන්න
-    errors:
-      limit: ඔබ දැනටමත් උපරිම හෑෂ් ටැග් ප්‍රමාණය විශේෂාංග කර ඇත
     hint_html: "<strong>විශේෂාංගගත හැෂ් ටැග් මොනවාද?</strong> ඒවා ඔබේ පොදු පැතිකඩෙහි ප්‍රමුඛව ප්‍රදර්ශනය වන අතර එම හැෂ් ටැග් යටතේ ඔබේ පොදු පළ කිරීම් බ්‍රවුස් කිරීමට මිනිසුන්ට ඉඩ සලසයි. නිර්මාණාත්මක කෘති හෝ දිගු කාලීන ව්යාපෘති පිළිබඳ වාර්තාවක් තබා ගැනීම සඳහා ඔවුන් විශිෂ්ට මෙවලමක් වේ."
   filters:
     contexts:
@@ -963,8 +959,6 @@ si:
     new:
       save: නව පෙරහන සුරකින්න
       title: නව පෙරහනක් එකතු කරන්න
-  footer:
-    trending_now: දැන් ප්‍රවණතාවය
   generic:
     all: සියල්ල
     changes_saved_msg: වෙනස්කම් සාර්ථකව සුරකින ලදී!
@@ -977,8 +971,6 @@ si:
     validation_errors:
       one: යමක් තවමත් හරි නැත! කරුණාකර පහත දෝෂය සමාලෝචනය කරන්න
       other: යමක් තවමත් හරි නැත! කරුණාකර පහත දෝෂ %{count} ක් සමාලෝචනය කරන්න
-  html_validator:
-    invalid_markup: 'වලංගු නොවන HTML සලකුණු අඩංගු වේ: %{error}'
   imports:
     errors:
       over_rows_processing_limit: පේළි %{count} කට වඩා අඩංගු වේ
@@ -1018,9 +1010,6 @@ si:
       expires_at: කල් ඉකුත් වේ
       uses: භාවිතා කරයි
     title: මිනිසුන්ට ආරාධනා කරන්න
-  lists:
-    errors:
-      limit: ඔබ උපරිම ලැයිස්තු ප්‍රමාණයට ළඟා වී ඇත
   login_activities:
     authentication_methods:
       otp: ද්වි-සාධක සත්‍යාපන යෙදුම
@@ -1215,7 +1204,6 @@ si:
       ios: අයිඕඑස්
       linux: ලිනක්ස්
       mac: මැක්ඕඑස්
-      other: නොදන්නා වේදිකාව
       windows: වින්ඩෝස්
       windows_mobile: වින්ඩෝස් මොබයිල්
       windows_phone: වින්ඩෝස් පෝන්
@@ -1328,7 +1316,6 @@ si:
       '7889238': මාස 3 යි
     min_age_label: වයස් සීමාව
     min_favs: අඩුම තරමින් පෝස්ට් ප්‍රිය කරන ලෙස තබා ගන්න
-    min_favs_hint: අවම වශයෙන් මෙම ප්‍රියතම ප්‍රමාණය ලබා ඇති ඔබේ පළ කිරීම් කිසිවක් මකන්නේ නැත. ඔවුන්ගේ ප්‍රියතමයන් ගණන නොතකා පළ කිරීම් මැකීමට හිස්ව තබන්න
     min_reblogs: අඩුම තරමේ පෝස්ට් බූස්ට් කරගෙන තියාගන්න
     min_reblogs_hint: අඩුම තරමින් මෙම වාර ගණන වැඩි කර ඇති ඔබගේ පළ කිරීම් කිසිවක් මකා නොදමන්න. බූස්ට් ගණන නොතකා පළ කිරීම් මැකීමට හිස්ව තබන්න
   stream_entries:
@@ -1426,7 +1413,6 @@ si:
     seamless_external_login: ඔබ බාහිර සේවාවක් හරහා ලොග් වී ඇත, එබැවින් මුරපදය සහ ඊමේල් සැකසුම් නොමැත.
     signed_in_as: 'මෙසේ පුරනය වී ඇත:'
   verification:
-    explanation_html: 'ඔබගේ පැතිකඩ පාරදත්ත</strong>හි ඇති සබැඳි වල හිමිකරු ලෙස ඔබට <strong>සත්‍යාපනය කළ හැක. ඒ සඳහා, සම්බන්ධිත වෙබ් අඩවියේ ඔබේ Mastodon පැතිකඩ වෙත ආපසු සබැඳියක් තිබිය යුතුය. සබැඳිය ආපසු <strong></code> </strong> <code>යුතුය. සබැඳියේ පෙළ අන්තර්ගතය වැදගත් නොවේ. මෙන්න උදාහරණයක්:'
     verification: සත්යාපනය
   webauthn_credentials:
     add: නව ආරක්ෂක යතුර එක් කරන්න
diff --git a/config/locales/simple_form.an.yml b/config/locales/simple_form.an.yml
index 1ff16cca5..10671134d 100644
--- a/config/locales/simple_form.an.yml
+++ b/config/locales/simple_form.an.yml
@@ -18,8 +18,8 @@ an:
           disable: Priva que l'usuario utilice la suya cuenta, pero no elimina ni amagada los suyos contenius.
           none: Utilizar esto pa ninviar una alvertencia a l'usuario, sin enchegar garra atra acción.
           sensitive: Forzar que totz los fichers multimedia d'este usuario sían marcaus como sensibles.
-          silence: Privar que l'usuario pueda publicar con visibilidat publica, amagada los suyos mensaches y notificacions a personas que no lo siguen.
-          suspend: Privar qualsequier interacción dende u enta esta cuenta y eliminar lo suyo conteniu. Reversible en un plazo de 30 días.
+          silence: Privar que l'usuario pueda publicar con visibilidat publica, amagada los suyos mensaches y notificacions a personas que no lo siguen. Zarra totz los informes contra esta cuenta.
+          suspend: Priva qualsequier interacción dende u enta esta cuenta y borra lo suyo conteniu. Reversible en 30 días. Zarra totz los informes contra esta cuenta.
         warning_preset_id: Opcional. Encara puede anyadir texto personalizau a la fin d'a configuración predefinida
       announcement:
         all_day: Quan ye triau nomás s'amostrarán las calendatas d'o rango de tiempo
@@ -74,6 +74,7 @@ an:
           hide: Amagar completament lo conteniu filtrau, comportando-se como si no existise
           warn: Amagar lo conteniu filtrau dezaga d'una alvertencia mencionando lo titol d'o filtro
       form_admin_settings:
+        activity_api_enabled: Conteyo de publicacions locals, usuarios activos y nuevos rechistros en periodos semanals
         backups_retention_period: Mantener los fichers d'usuario cheneraus entre lo numero de días especificau.
         bootstrap_timeline_accounts: Estas cuentas amaneixerán en a parte superior d'as recomendacions d'os nuevos usuarios.
         closed_registrations_message: Amostrau quan los rechistros son zarraus
@@ -81,6 +82,7 @@ an:
         custom_css: Puetz aplicar estilos personalizaus a la versión web de Mastodon.
         mascot: Reemplaza la ilustración en a interficie web abanzada.
         media_cache_retention_period: Los fichers multimedia descargaus s'eliminarán dimpués d'o numero especificau de días quan s'estableixca una valor positiva, y se redescargarán baixo demanda.
+        peers_api_enabled: Una lista de nombres de dominio que este servidor ha trobau en o Fediverso. Aquí no s'incluye garra dato sobre si federas con un servidor determinau, nomás que lo tuyo servidor lo conoixe. Esto ye emplegau per los servicios que replegan estatisticas sobre la federación en un sentiu cheneral.
         profile_directory: Lo directorio de perfils lista a totz los usuarios que han optado per que la suya cuenta pueda estar descubierta.
         require_invite_text: Quan los rechistros requieren aprebación manual, fa obligatoria la dentrada de texto "Per qué quiers unir-te?" en cuenta d'opcional
         site_contact_email: Cómo la chent puede meter-se en contacto con tu pa consultas legals u d'aduya.
@@ -229,6 +231,7 @@ an:
           hide: Amagar completament
           warn: Amagar con una alvertencia
       form_admin_settings:
+        activity_api_enabled: Publicar estatisticas agregadas sobre l'actividat d'o usuario en l'API
         backups_retention_period: Periodo de retención d'o fichero d'usuario
         bootstrap_timeline_accounts: Recomendar siempre estas cuentas a nuevos usuarios
         closed_registrations_message: Mensache personalizau quan los rechistros no son disponibles
@@ -236,6 +239,7 @@ an:
         custom_css: CSS personalizau
         mascot: Mascota personalizada (legado)
         media_cache_retention_period: Periodo de retención de caché multimedia
+        peers_api_enabled: Publicar la lista de servidors descubiertos en l'API
         profile_directory: Habilitar directorio de perfils
         registrations_mode: Quí puede rechistrar-se
         require_invite_text: Requerir una razón pa unir-se
diff --git a/config/locales/simple_form.ar.yml b/config/locales/simple_form.ar.yml
index 21d24af51..1863db702 100644
--- a/config/locales/simple_form.ar.yml
+++ b/config/locales/simple_form.ar.yml
@@ -18,8 +18,8 @@ ar:
           disable: منع المستخدم من استخدام حسابه، ولكن لا تَقم بحذف أو إخفاء محتواه.
           none: استخدم هذه لإرسال تحذير للمستخدم، دون تشغيل أو إثارة أي إجراء آخر.
           sensitive: إجبار جميع مرفقات الوسائط لهذا المستخدم على أن تكون حساسة.
-          silence: منع المستخدم من أن يكون قادراً على النشر للعامة، وإخفاء مشاركاته وإشعاراته من الذين لا يُتابعِونه.
-          suspend: منع أي تفاعل من أو إلى هذا الحساب، وحذف محتوياته، يمكن الرجوع عن هذا القرار في غضون 30 يوماً.
+          silence: منع المستخدم من القدرة على النشر للعامة، وإخفاء مشاركاته وإشعاراته لمن لا يتبعونه. إغلاق جميع التقارير المقترنة بهذا الحساب.
+          suspend: منع أي تفاعل من هذا الحساب أو إليه وحذف محتوياته. يمكن التراجع عنه في غضون 30 يوما. إغلاق جميع التقارير المفتوحة ضد هذا الحساب.
         warning_preset_id: اختياري. يمكنك إضافة نص مخصص إلى نهاية النموذج
       announcement:
         all_day: إن أختير، سيتم عرض تواريخ النطاق الزمني فقط
@@ -49,7 +49,7 @@ ar:
         phrase: سوف يتم العثور عليه مهما كان نوع النص أو حتى و إن كان داخل الويب فيه تحذير عن المحتوى
         scopes: ما هي المجالات المسموح بها في التطبيق ؟ إن قمت باختيار أعلى المجالات فيمكنك الاستغناء عن الخَيار اليدوي.
         setting_aggregate_reblogs: لا تقم بعرض المشارَكات الجديدة لمنشورات قد قُمتَ بمشاركتها سابقا (هذا الإجراء يعني المشاركات الجديدة فقط التي تلقيتَها)
-        setting_always_send_emails: عادة لن ترسل الإشعارات إلى بريدك الإلكتروني عندما تكون نشط على ماستدون
+        setting_always_send_emails: عادة لن تُرسَل إليك إشعارات البريد الإلكتروني عندما تكون نشطًا على ماستدون
         setting_default_sensitive: تُخفى الوسائط الحساسة تلقائيا ويمكن اظهارها عن طريق النقر عليها
         setting_display_media_default: إخفاء الوسائط المُعيَّنة كحساسة
         setting_display_media_hide_all: إخفاء كافة الوسائط دائمًا
@@ -74,6 +74,7 @@ ar:
           hide: إخفاء المحتويات التي تم تصفيتها، والتصرف كما لو أنها غير موجودة
           warn: إخفاء المحتوى الذي تم تصفيته خلف تحذير يذكر عنوان الفلتر
       form_admin_settings:
+        activity_api_enabled: عدد المنشورات المحلية و المستخدمين الناشطين و التسجيلات الأسبوعية الجديدة
         backups_retention_period: الاحتفاظ بأرشيف المستخدم الذي تم إنشاؤه لعدد محدد من الأيام.
         bootstrap_timeline_accounts: سيتم تثبيت هذه الحسابات على قمة التوصيات للمستخدمين الجدد.
         closed_registrations_message: ما سيعرض عند إغلاق التسجيلات
@@ -84,16 +85,18 @@ ar:
         profile_directory: دليل الملف الشخصي يسرد جميع المستخدمين الذين اختاروا الدخول ليكونوا قابلين للاكتشاف.
         require_invite_text: عندما تتطلب التسجيلات الموافقة اليدوية، اجعل إدخال النص "لماذا تريد الانضمام ؟" إلزاميا بدلا من اختياري
         site_contact_email: كيف يمكن للأشخاص أن يصلوا إليك للحصول على استفسارات قانونية أو استفسارات دعم.
-        site_contact_username: كيف يمكن للناس أن يصلوا إليك في ماستدون.
+        site_contact_username: كيف يمكن للناس أن يتصلوا بك في ماستدون.
         site_extended_description: أي معلومات إضافية قد تكون مفيدة للزوار والمستخدمين. يمكن تنظيمها مع بناء بنية Markdown.
         site_short_description: وصف قصير للمساعدة في التعرف على الخادم الخاص بك. من يقوم بتشغيله، ولمن ؟
         site_terms: استخدم سياسة الخصوصية الخاصة بك أو اتركها فارغة لاستخدام الافتراضي. يمكن هيكلتها مع بناء الجملة المصغرة مارك داون.
         site_title: كيف يمكن للناس الرجوع إلى الخادم الخاص بك إلى جانب اسم النطاق.
+        status_page_url: العنوان التشعبي حيث يمكن للناس رؤية صفحة حالة هذا الخادم عند حدوث عطب ما
         theme: الشكل الذي يشاهده الزوار الجدد و الغير مسجلين الدخول.
         thumbnail: عرض حوالي 2:1 صورة إلى جانب معلومات الخادم الخاص بك.
         timeline_preview: الزوار الذين سجلوا خروجهم سيكونون قادرين على تصفح أحدث المشاركات العامة المتاحة على الخادم.
         trendable_by_default: تخطي مراجعة المحتوى التريند اليدوي. لا يزال من الممكن الإزالة اللاحقة للعناصر الفردية من التريندات.
         trends: تظهر التريندز أي المشاركات وعلامات وقصص الأخبار التي تجذب الانتباه على الخادم الخاص بك.
+        trends_as_landing_page: إظهار المحتوى المتداوَل للمستخدمين والزوار غير المسجلين بدلاً من وصف هذا الخادم. يتطلب هذا تفعيل المتداولة.
       form_challenge:
         current_password: إنك بصدد الدخول إلى منطقة آمنة
       imports:
@@ -229,6 +232,7 @@ ar:
           hide: إخفاء بالكامل
           warn: إخفاء بتحذير
       form_admin_settings:
+        activity_api_enabled: نشر مُجمل الإحصائيات عن نشاط المستخدمين في واجهة برمجة التطبيقات API
         backups_retention_period: فترة الاحتفاظ بأرشيف المستخدم
         bootstrap_timeline_accounts: أوصي دائما بهذه الحسابات للمستخدمين الجدد
         closed_registrations_message: رسالة مخصصة عندما يكون التسجيل غير متاح
@@ -236,6 +240,7 @@ ar:
         custom_css: سي أس أس CSS مخصص
         mascot: جالب حظ مخصص (قديم)
         media_cache_retention_period: مدة الاحتفاظ بالتخزين المؤقت للوسائط
+        peers_api_enabled: نشر قائمة للخوادم المكتشَفة في واجهة برمجة التطبيقات API
         profile_directory: تفعيل دليل الصفحات التعريفية
         registrations_mode: من يمكنه التسجيل
         require_invite_text: يتطلب سببا للانضمام
@@ -247,11 +252,13 @@ ar:
         site_short_description: وصف الخادم
         site_terms: سياسة الخصوصية
         site_title: اسم الخادم
+        status_page_url: الرابط التشعبي لصفحة حالة الخادم
         theme: الحُلَّة الإفتراضية
         thumbnail: الصورة المصغرة للخادم
         timeline_preview: السماح بالوصول غير الموثق إلى الخيوط الزمنية العامة
         trendable_by_default: السماح للوسوم بالظهور على المتداوَلة دون مراجعة مسبقة
         trends: تمكين المتداوَلة
+        trends_as_landing_page: استخدام المُتداوَلة كصفحة ترحيب
       interactions:
         must_be_follower: حظر الإشعارات القادمة من حسابات لا تتبعك
         must_be_following: حظر الإشعارات القادمة من الحسابات التي لا تتابعها
diff --git a/config/locales/simple_form.ast.yml b/config/locales/simple_form.ast.yml
index 6a0dc22c4..9e97d51f2 100644
--- a/config/locales/simple_form.ast.yml
+++ b/config/locales/simple_form.ast.yml
@@ -3,10 +3,14 @@ ast:
   simple_form:
     hints:
       admin_account_action:
-        text_html: Opcional. Pues usar la sintaxis de los artículos ya amestar <a href="%{path}">amestar preconfiguraes</a> p'aforrar tiempu
+        text_html: Opcional. Pues usar la sintaxis de los artículos ya amestar <a href="%{path}">alvertencies preconfiguraes</a> p'aforrar tiempu
+        type_html: Escueyi qué facer con <strong>%{acct}</strong>
+        types:
+          none: Usa esta opción pa unviar una alvertencia al perfil, ensin aicionar nenguna otra aición.
       announcement:
         all_day: Al marcar la opción, namás apaecen les dates del intervalu de tiempu
         ends_at: Opcional. L'anunciu dexa de tar espublizáu na data qu'indiques
+        scheduled_at: Dexa esti campu como ta pa espublizar l'anunciu nel intre
         starts_at: Opcional. En casu de que l'anunciu tea arreyáu a un intervalu de tiempu específicu
         text: Pues usar la sintaxis de los artículos. Ten en cuenta l'espaciu que l'anunciu va ocupar na pantalla del usuariu/a
       defaults:
@@ -20,11 +24,17 @@ ast:
         irreversible: Los artículos peñeraos desapaecen de forma irreversible, magar que la peñera se quite dempués
         locale: La llingua de la interfaz, los mensaxes per corréu electrónicu ya los avisos push
         locked: Controla manualmente quién pue siguite pente l'aprobación de les solicitúes de siguimientu
+        password: Usa polo menos 8 caráuteres
+        setting_aggregate_reblogs: Nun amuesa los artículos compartíos nuevos que xá se compartieren de recién (namás afeuta a los artículos compartíos d'agora)
+        setting_always_send_emails: Los avisos nun se suelen unviar per corréu electrónicu si uses activamente Mastodon
+        setting_default_sensitive: El conteníu multimedia sensible anúbrese por defeutu ya pue amosase al calcar nelli
         setting_display_media_default: Anubrilu cuando se marque como sensible
         setting_display_media_hide_all: Anubrilu siempres
         setting_display_media_show_all: Amosalu siempres
         setting_hide_network: Les persones que sigas ya les que te sigan nun van apaecer nel to perfil
+        setting_noindex: Afeuta al perfil públicu ya a les páxines de los artículos
         setting_show_application: L'aplicación qu'uses pa espublizar apaez na vista detallada de los tos artículos
+        setting_use_blurhash: Los dilíos básense nos colores del conteníu multimedia anubríu mas desenfonca los detalles
         username: 'El nome d''usuariu va ser únicu en: %{domain}'
       featured_tag:
         name: 'Equí tán dalgunes de les etiquetes qu''usesti apocayá:'
@@ -39,6 +49,7 @@ ast:
         site_extended_description: Cualesquier tipu d'información adicional que pueda ser útil pa visitantes ya pa perfiles rexistraos. El testu pue estructurase cola sintaxis de Mastodon.
         site_short_description: Un descripción curtia qu'ayuda a identificar de forma única al sirvidor. ¿Quién lu lleva?, ¿pa quién ye?
         theme: L'estilu que los visitantes ya los perfiles nuevos usen.
+        trends: Les tendencies amuesen artículos, etiquetes ya noticies que tean ganando popularidá nesti sirvidor.
       form_challenge:
         current_password: Tas entrando a una área segura
       imports:
@@ -46,8 +57,14 @@ ast:
       invite_request:
         text: Esto va ayudanos a revisar la to solicitú
       ip_block:
-        comment: Opcional. Un recordatoriu de por qué amestesti esta regla.
+        comment: Opcional. Un recordatoriu de por qué amestesti esta norma.
         expires_in: Les direiciones IP son un recursu finitu, suelen compartise ya cambiar de manes. Por esti motivu, nun s'aconseyen los bloqueos indefiníos de direiciones IP.
+        severities:
+          no_access: Bloquia l'accesu a tolos recursos
+          sign_up_block: Fai que nun se puedan rexistrar cuentes nueves
+          sign_up_requires_approval: Fai que se tengan de revisar les cuentes rexistraes nueves
+      user:
+        chosen_languages: Namás los artículos de les llingües que marques son los que van apaecer nes llinies de tiempu públiques
     labels:
       account:
         fields:
@@ -58,8 +75,10 @@ ast:
       admin_account_action:
         include_statuses: Incluyir nel mensaxe los artículos de los que s'informó
         send_email_notification: Avisar al perfil per corréu electrónicu
+        text: Alvertencia personalizada
         type: Aición
         types:
+          sensitive: Marcar como cuenta sensible
           suspend: Suspender
       announcement:
         all_day: Eventu de tol día
@@ -92,18 +111,26 @@ ast:
         phrase: Pallabra clave o fras
         setting_advanced_layout: Activar la interfaz web avanzada
         setting_aggregate_reblogs: Agrupar los artículos compartíos nes llinies de tiempu
+        setting_always_send_emails: Unviar siempres los avisos per corréu electrónicu
         setting_auto_play_gif: Reproducir automáticamente los GIFs
         setting_boost_modal: Amosar el diálogu de confirmación enantes de compartir un artículu
+        setting_crop_images: Recortar les imáxenes de los artículos ensin espander a la proporción 16:9
         setting_default_language: Llingua de los artículos
         setting_default_privacy: Privacidá de los artículos
+        setting_default_sensitive: Marcar siempres tol conteníu como sensible
         setting_delete_modal: Amosar el diálogu de confirmación enantes de desaniciar un artículu
+        setting_disable_swiping: Desactivar el movimientu de desplazamientu
         setting_display_media: Conteníu multimedia
+        setting_expand_spoilers: Espander siempres los artículos marcaos con alvertencies de conteníu
+        setting_hide_network: Anubrir les cuentes que sigas ya te sigan
+        setting_noindex: Arrenunciar a apaecer nos índices de los motores de busca
         setting_reduce_motion: Amenorgar el movimientu de les animaciones
         setting_show_application: Dicir les aplicaciones que s'usen pa unviar artículos
         setting_system_font_ui: Usar la fonte predeterminada del sistema
         setting_theme: Estilu del sitiu
         setting_trends: Amosar les tendencies de güei
         setting_unfollow_modal: Amosar el diálogu de confirmación enantes de dexar de siguir a daquién
+        setting_use_blurhash: Facer que'l conteníu multimedia anubríu tenga dilíos coloríos
         setting_use_pending_items: Mou lentu
         severity: Gravedá
         sign_in_token_attempt: Códigu de seguranza
@@ -128,6 +155,9 @@ ast:
         site_title: Nome del sirvidor
         theme: Estilu predetermináu
         thumbnail: Miniatura del sirvidor
+        timeline_preview: Permitir l'accesu ensin autenticar a les llinies de tiempu públiques
+        trendable_by_default: Permitir tendencies ensin revisión previa
+        trends: Activar les tendencies
       interactions:
         must_be_follower: Bloquiar los avisos de los perfiles que nun te siguen
         must_be_following: Bloquiar los avisos de los perfiles que nun sigues
@@ -135,7 +165,12 @@ ast:
       invite_request:
         text: "¿Por qué quies xunite?"
       ip_block:
+        comment: Comentariu
         ip: IP
+        severities:
+          no_access: Bloquiar l'accesu
+          sign_up_block: Bloquiar el rexistru de cuentes nueves
+          sign_up_requires_approval: Llendar les cuentes rexistraes nueves
       notification_emails:
         favourite: Daquién marcó como favoritu'l to artículu
         follow: Daquién te sigue
@@ -145,15 +180,24 @@ ast:
         reblog: Daquién compartió'l to artículu
         report: Unvióse un informe nuevu
         trending_tag: Una tendencia rique una revisión
+      rule:
+        text: Norma
       tag:
+        listable: Permitir qu'esta etiqueta apaeza nes busques ya nes suxerencies
         name: Etiqueta
+        trendable: Permitir qu'esta etiqueta apaeza nes tendencies
+        usable: Permitir que los artículos usen esta etiqueta
       user:
         role: Rol
       user_role:
         name: Nome
         permissions_as_keys: Permisos
         position: Prioridá
+      webhook:
+        events: Eventos activos
     'no': Non
+    not_recommended: Nun s'aconseya
+    recommended: Aconséyase
     required:
       mark: "*"
     'yes': Sí
diff --git a/config/locales/simple_form.be.yml b/config/locales/simple_form.be.yml
index 54651ca66..4a2c532ce 100644
--- a/config/locales/simple_form.be.yml
+++ b/config/locales/simple_form.be.yml
@@ -18,8 +18,8 @@ be:
           disable: Перадухіліць выкарыстанне акаунтаў, але не выдаляць і не хаваць іх змесціва.
           none: Выкарыстоўвай гэта для папярэджвання карыстальнікаў але без іншых наступстваў.
           sensitive: Прымусова адмячаць усе медыя карыстальніка як дэлікатныя.
-          silence: Забараніць карыстальніку пасты з публічнай бачнасцю, схаваць ягоныя допісы і апавяшчэнні ад людзей, якія на яго не падпісаныя.
-          suspend: Забараніць любыя ўзаемадзеянні ад ці з гэтым уліковым запісам і выдаліць ягонае змесціва. Можна адрабіць цягам 30 дзён.
+          silence: Забараніць карыстальніку пасты з публічнай бачнасцю, схаваць ягоныя допісы і апавяшчэнні ад людзей, якія на яго не падпісаныя. Закрывае ўсе скаргі на гэты ўліковы запіс.
+          suspend: Забараніць любыя ўзаемадзеянні ад ці з гэтым уліковым запісам і выдаліць ягонае змесціва. Можна адрабіць цягам 30 дзён. Закрывае ўсе скаргі на гэты ўліковы запіс.
         warning_preset_id: Неабавязкова. Вы можаце дадаць уласны тэкст напрыканцы шаблону
       announcement:
         all_day: Калі пазначана, будуць паказаны толькі даты з пазначанага прамежку часу
@@ -74,6 +74,7 @@ be:
           hide: Поўнасцю схаваць адфільтраванае змесціва, дзейнічаць, нібы яго не існуе
           warn: Схаваць адфільтраваны кантэнт за папярэджаннем з назвай фільтру
       form_admin_settings:
+        activity_api_enabled: Падлік лакальна апублікаваных пастоў, актыўных карыстальнікаў і новых рэгістрацый у тыдзень
         backups_retention_period: Захоўваць створаныя архівы карыстальніка адзначаную колькасць дзён.
         bootstrap_timeline_accounts: Гэтыя ўліковыя запісы будуць замацаваны ў топе рэкамендацый для новых карыстальнікаў.
         closed_registrations_message: Паказваецца, калі рэгістрацыя закрытая
@@ -81,6 +82,7 @@ be:
         custom_css: Вы можаце прымяняць карыстальніцкія стылі ў вэб-версіі Mastodon.
         mascot: Замяняе ілюстрацыю ў пашыраным вэб-інтэрфейсе.
         media_cache_retention_period: Спампаваныя медыя будуць выдаляцца пасля выстаўленай колькасці дзён, калі выстаўлены станоўчы лік, і спампоўвацца нанова па запыце.
+        peers_api_enabled: Спіс даменных імён, з якімі сутыкнуўся гэты сервер у федэсвеце. Дадзеныя аб тым, ці знаходзіцеся вы з пэўным серверам у федэрацыі, не ўключаныя, ёсць толькі тое, што ваш сервер ведае пра гэта. Гэта выкарыстоўваецца сэрвісамі, якія збіраюць статыстыку па федэрацыі ў агульным сэнсе.
         profile_directory: Дырэкторыя профіляў змяшчае спіс усіх карыстальнікаў, якія вырашылі быць бачнымі.
         require_invite_text: Калі рэгістрацыя патрабуе ручнога пацвержання, зрабіце поле "Чаму вы хочаце далучыцца?" абавязковым
         site_contact_email: Як людзі могуць звязацца з вамі па юрыдычных запытах або пытаннях падтрымкі.
@@ -89,11 +91,13 @@ be:
         site_short_description: Кароткае апісанне, каб дапамагчы адназначна ідэнтыфікаваць ваш сервер. Хто яго падтрымлівае, для каго ён?
         site_terms: Апішыце ўласную палітыку прыватнасці альбо пакіньце поле пустым, калі хочаце скарыстацца прадвызначанай. Можна карыстацца сінтаксісам Markdown каб структураваць тэкст.
         site_title: Як людзі могуць звяртацца да вашага серверу акрамя яго даменнага імя.
+        status_page_url: URL старонкі, дзе людзі могуць бачыць стан гэтага сервера падчас збою
         theme: Тэма, што бачаць новыя карыстальнікі ды наведвальнікі, якія выйшлі.
         thumbnail: Выява памерамі прыкладна 2:1, якая паказваецца побач з інфармацыяй пра ваш сервер.
         timeline_preview: Наведвальнікі, якія выйшлі, змогуць праглядаць апошнія публічныя допісы на серверы.
         trendable_by_default: Прапусціць ручны агляд трэндавага змесціва. Асобныя элементы ўсё яшчэ можна будзе выдаліць з трэндаў пастфактум.
         trends: Трэнды паказваюць, якія допісы, хэштэгі і навіны набываюць папулярнасць на вашым серверы.
+        trends_as_landing_page: Паказваць папулярнае змесціва карыстальнікам, якія выйшлі з сістэмы, і наведвальнікам, замест апісання гэтага сервера. Патрабуецца ўключэнне трэндаў.
       form_challenge:
         current_password: Вы ўваходзіце ў бяспечную зону
       imports:
@@ -229,6 +233,7 @@ be:
           hide: Схаваць цалкам
           warn: Схаваць з папярэджаннем
       form_admin_settings:
+        activity_api_enabled: Апублікаваць зводную статыстыку аб актыўнасці карыстальнікаў API
         backups_retention_period: Працягласць захавання архіву карыстальніка
         bootstrap_timeline_accounts: Заўсёды раіць гэтыя ўліковыя запісы новым карыстальнікам
         closed_registrations_message: Уласнае паведамленне, калі рэгістрацыя немагчымая
@@ -236,6 +241,7 @@ be:
         custom_css: CSS карыстальніка
         mascot: Уласны маскот(спадчына)
         media_cache_retention_period: Працягласць захавання кэшу для медыя
+        peers_api_enabled: Апублікаваць спіс знойдзеных сервераў у API
         profile_directory: Уключыць каталог профіляў
         registrations_mode: Хто можа зарэгістравацца
         require_invite_text: Каб далучыцца, патрэбна прычына
@@ -247,11 +253,13 @@ be:
         site_short_description: Апісанне сервера
         site_terms: Палітыка канфідэнцыйнасці
         site_title: Назва сервера
+        status_page_url: URL старонкі статусу
         theme: Тэма па змаўчанні
         thumbnail: Мініяцюра сервера
         timeline_preview: Дазволіць неаўтэнтыфікаваны доступ да публічных стужак
         trendable_by_default: Дазваляць трэнды без папярэдняй праверкі
         trends: Уключыць трэнды
+        trends_as_landing_page: Выкарыстоўваць трэнды ў якасці лэндзінга
       interactions:
         must_be_follower: Заблакіраваць апавяшчэнні ад непадпісаных людзей
         must_be_following: Заблакіраваць апавяшчэнні ад людзей на якіх вы не падпісаны
diff --git a/config/locales/simple_form.bg.yml b/config/locales/simple_form.bg.yml
index 36741108f..e84edc50d 100644
--- a/config/locales/simple_form.bg.yml
+++ b/config/locales/simple_form.bg.yml
@@ -13,26 +13,26 @@ bg:
         include_statuses: Потребителят ще може да види кои публикации са довели до действието от страна на модератора или до предупреждението
         send_email_notification: Потребителят ще получи обяснение какво се е случило с неговия акаунт
         text_html: По избор. Можете да използвате синтаксисът на публикация. Можете да <a href="%{path}">добавите предварително настроени предупреждения</a>, за да спестите време
-        type_html: Изберете какво да сторите с <strong>%{acct}</strong>
+        type_html: Изберете какво ще правите с <strong>%{acct}</strong>
         types:
-          disable: Забранете на потребител да достъпва акаунта си, без да изтривате или скривате съдържанието на този акаунт.
-          none: Служи за изпращане на предупреждение до потребител, без да се активира друго действие.
+          disable: Предотвратяване на потребител да употребява акаунта си, но без да се изтрива или скрива съдържанието му.
+          none: Използвайте това за изпращане на предупреждение до потребител, без задействане на друго действие.
           sensitive: Принудително отбелязване на прикачената от потребителя мултимедия като чувствителна.
-          silence: Забраняване на потребителя да публикува публично, скриване на неговите публикации и известия от други потребители, които не го следват.
-          suspend: Забрана на всякаква интеракция с този акаунт и изтриване на съдържанието му. Обратимо в рамките на 30 дни.
+          silence: Предотвратяване на потребителя да може да публикува с обществена видимост, скриване на публикациите му и известията от хората, които не го следват. Затваря всички доклади срещу този акаунт.
+          suspend: Предотвратяване на всякакво взаимодействие от или към този акаунт и изтриване на съдържанието му. Обратимо в рамките на 30 дни. Затваря всички доклади срещу този акаунт.
         warning_preset_id: По избор. Можете да добавите допълнително текст накрая на предварително настроения текст
       announcement:
         all_day: Когато е избрана настройката, само датите от времевия диапазон ще бъдат показани
         ends_at: По избор. В зададеното време обявлението ще бъде автоматично премахнато
-        scheduled_at: Ако се остави празно, обявлението ще се публикува незабавно
+        scheduled_at: Оставете празно, за да публикувате оповестяването веднага
         starts_at: По избор. В случай, че обявлението е ограничено до определен времеви диапазон
-        text: Можете да използвате синтаксиса на обикновени публикации. Не забравяйте, че обявлението ще заеме известно място от екрана на потребителя
+        text: Може да употребявате синтаксиса на публикации. Имайте предвид, че оповестяването ще заема известно място от екрана на потребителя
       appeal:
-        text: Можете да възразите срещу провинение само веднъж
+        text: Може да възразите срещу провинение само веднъж
       defaults:
         autofollow: Хората, които се регистрират чрез поканата, автоматично ще ви последват
-        avatar: PNG, GIF или JPG. До %{size}. Ще бъде смалена до %{dimensions} пиксела
-        bot: Покажете на останалите потребители, че акаунтът извършва предимно автоматизирани действия, които не се следят
+        avatar: PNG, GIF или JPG. До най-много %{size}. Ще се смали до %{dimensions} пиксела
+        bot: Сигнализиране до другите, че акаунтът изпълнява предимно автоматизирани деяния и може да не се наблюдава
         context: Един или повече контексти, към които да се приложи филтърът
         current_password: От съображения за сигурност, въведете паролата на текущия акаунт
         current_username: Въведете потребителското име на текущия профил, за да потвърдите
@@ -40,29 +40,29 @@ bg:
         discoverable: Позволяване на странници да откриват вашия акаунт чрез препоръки, нашумели и други неща
         email: Ще ви се изпрати имейл за потвърждение
         fields: Може да добавите до 4 елемента в таблицата към профила си
-        header: PNG, GIF или JPG. До %{size}. Ще бъде смалена до %{dimensions} пиксела
+        header: PNG, GIF или JPG. До най-много %{size}. Ще се смали до %{dimensions} пиксела
         inbox_url: Копирайте URL адреса на заглавната страница на предаващия сървър, който искат да използвате
         irreversible: Филтрираните публикации ще изчезнат безвъзвратно, дори филтърът да бъде премахнат по-късно
         locale: Езикът на потребителския интерфейс, известиятата по имейл и насочените известия
-        locked: Изисква ръчно одобрение на последователите. По подразбиране, публикациите са достъпни само до последователи.
+        locked: Ръчно управляване кой може да ви последва, одобрявайки заявките за последване
         password: Използвайте поне 8 символа
         phrase: Ще съвпадне без значение дали са главни или малки букви, или ако е предупреждение към публикация
         scopes: Указва до кои API има достъп приложението. Ако изберете диапазон от най-високо ниво, няма нужда да избирате индивидуални.
         setting_aggregate_reblogs: Без показване на нови подсилвания за публикации, които са неотдавна подсилени (засяга само новополучени подсилвания)
-        setting_always_send_emails: Обикновено известия по имейл няма да бъдат изпратени при активно използване на Mastodon
+        setting_always_send_emails: Обикновено известията по имейл няма да са изпратени при дейна употреба на Mastodon
         setting_default_sensitive: Деликатната мултимедия е скрита по подразбиране и може да се разкрие с едно щракване
         setting_display_media_default: Скриване на мултимедия отбелязана като деликатна
-        setting_display_media_hide_all: Винаги да се скрива мултимедията
-        setting_display_media_show_all: Винаги да се показва мултимедията
+        setting_display_media_hide_all: Винаги скриване на мултимедията
+        setting_display_media_show_all: Винаги показване на мултимедията
         setting_hide_network: В профила ви ще бъде скрито кой може да последвате и кой може да ви последва
         setting_noindex: Засяга вашите публикации и публичен профил
         setting_show_application: Приложението, което ползвате за публикуване, ще се показва в подробностите на публикацията ви
-        setting_use_blurhash: Преливането се определя от цветовете на скритите изображения, но детайлите остават скрити
+        setting_use_blurhash: Преливането е въз основа на цветовете на скритите визуализации, но се замъгляват подробностите
         setting_use_pending_items: Да се показват обновявания на часовата ос само след щракване вместо автоматично превъртане на инфоканала
-        username: Вашето потребителско име ще е неповторим в %{domain}
-        whole_word: Ако ключовата дума/фраза е стриктно буквеноцифрена, ще бъде приложена само, ако съвпадне с цялата дума
+        username: Вашето потребителско име ще е неповторимо в %{domain}
+        whole_word: Ако ключовата дума или фраза е само буквеноцифрена, то ще се приложи само, ако съвпадне с цялата дума
       domain_allow:
-        domain: Този домейн ще може да извлече данни от този сървър и входящите данни от него ще бъдат обработени и запазени
+        domain: Домейнът ще може да извлича данни от този сървър и входящите данни от него ще се обработят и съхранят
       email_domain_block:
         domain: Това може да е името на домейна, който се съдържа в имейл адреса или MX записа, който той използва. Ще бъдат проверени при регистрация.
         with_dns_records: Ще има опит за преобразуване на DNS записите за дадения домейн и резултатите също ще бъдат блокирани
@@ -72,28 +72,32 @@ bg:
         action: Изберете кое действие да се извърши, прецеждайки съвпаденията на публикацията
         actions:
           hide: Напълно скриване на филтрираното съдържание, сякаш не съществува
-          warn: Скриване на филтрираното съдържание зад предупреждение, включващо името на филтъра
+          warn: Скриване на филтрираното съдържание зад предупреждение, споменавайки заглавието на филтъра
       form_admin_settings:
-        backups_retention_period: Запазване на генерираните потребителски архиви за посочения брой дни.
+        activity_api_enabled: Броят на местните публикувани публикации, дейни потребители и нови регистрации в седмични кофи
+        backups_retention_period: Задържане на породените потребителски архиви за определения брой дни.
         bootstrap_timeline_accounts: Тези акаунти ще се закачат в горния край на препоръките за следване на нови потребители.
         closed_registrations_message: Показва се, когато е затворено за регистрации
         content_cache_retention_period: Публикации от други сървъри ще се изтрият след определен брой дни при положително число. Действието може да е необратимо.
-        custom_css: Може да прилагате собствени стилове в уеб версията на Mastodon.
-        mascot: Можете да заместите илюстрацията в разширения уеб интерфейс.
-        media_cache_retention_period: Свалените мултимедийни файлове ще бъдат изтрити след посочения брой дни, когато броят е положително число, и ще бъдат свалени отново при поискване.
-        profile_directory: Указателят на профили съдържа всички потребители, които са се съгласили да бъдат откривани.
+        custom_css: Може да прилагате собствени стилове в уебверсията на Mastodon.
+        mascot: Замества илюстрацията в разширения уеб интерфейс.
+        media_cache_retention_period: Изтеглените мултимедийни файлове ще се изтрият след посочения брой дни, задавайки положително число, и ще се изтеглят пак при поискване.
+        peers_api_enabled: Списък от имена на домейни, с които сървърът се е свързал във федивселената. Тук не се включват данни за това дали федерирате с даден сървър, а само за това дали сървърът ви знае за него. Това се ползва от услуги, събиращи статистика за федерацията в общия смисъл.
+        profile_directory: Указателят на профили вписва всички потребители, избрали да бъдат откриваеми.
         require_invite_text: Когато регистрацията изисква ръчно одобрение, текстовото поле за това "Защо желаете да се присъедините?" ще бъде задължително, вместо по желание
         site_contact_email: Как могат хората да се свържат с вас относно правни запитвания или помощ.
         site_contact_username: Как хората могат да ви достигнат в Mastodon.
         site_extended_description: Всяка допълнителна информация, която може да е полезна за посетителите и потребителите ви. Може да се структурира със синтаксиса на Markdown.
         site_short_description: Кратък опис за помощ на неповторимата самоличност на сървъра ви. Кой го управлява, за кого е?
-        site_terms: Използвайте собствени правила за поверителност или оставете празно, за да използвате тези по подразбиране. Може да се структурира с Markdown синтаксис.
-        site_title: Как могат хората да наричат вашия сървър, освен името на домейна.
-        theme: Темата, която нови и невлезли потребители ще виждат.
-        thumbnail: Изображение в резолюция около 2:1, показвана до информацията за вашия сървър.
-        timeline_preview: Невлезлите потребители ще могат да преглеждат най-новите публични публикации, налични на сървъра.
-        trendable_by_default: Прескачане на ръчния преглед на нашумяло съдържание. Отделни елементи могат да бъдат премахвани от нашумели в последствие.
-        trends: В секцията Нашумели се показват публикации, хаштагове и новини, набрали популярност на вашия сървър.
+        site_terms: Употребявайте свои правила за поверителност или оставете празно, за да използвате стандартните правила. Може да се структурира със синтаксиса на Markdown.
+        site_title: Как могат хората да се обръщат към сървъра ви, освен по името на домейна.
+        status_page_url: Адресът на страницата, където хората могат да видят състоянието на този сървър по време на прекъсване
+        theme: Темата, която излизащи от системата посетители и нови потребители виждат.
+        thumbnail: Образ в съотношение около 2:1, показвано до информацията за сървъра ви.
+        timeline_preview: Излизащите от системата посетители ще може да разглеждат най-новите публични публикации, налични на сървъра.
+        trendable_by_default: Прескачане на ръчния преглед на изгряващо съдържание. Отделни елементи още могат да се премахват от изгряващи постфактум.
+        trends: В раздел „Налагащо се“ се показват публикации, хаштагове и новини, набрали популярност на сървъра ви.
+        trends_as_landing_page: Показване на налагащото се съдържание за излизащите потребители и посетители вместо на описа на този сървър. Изисква налагащото се да бъде включено.
       form_challenge:
         current_password: Влизате в сигурна зона
       imports:
@@ -117,12 +121,12 @@ bg:
       tag:
         name: Можете да смените само употребата на големи/малки букви, например, за да е по-четимо
       user:
-        chosen_languages: Само публикации на езиците с отметка ще бъдат показвани в публичните инфопотоци
+        chosen_languages: Само публикации на отметнатите езици ще се показват в публичните часови оси
         role: Ролите управляват какви права има потребителят
       user_role:
         color: Цветът, използван за ролите в потребителския интерфейс, като RGB в шестнадесетичен формат
         highlighted: Това прави ролята публично видима
-        name: Публично име за ролята, ако ролята е настроена да се показва като значка
+        name: Публично име на ролята, ако ролята е зададена да се показва като значка
         permissions_as_keys: Потребители с тази роля ще имат достъп до...
         position: По-висшата роля може да разреши конфликти в някои ситуации. Някои действия могат да бъдат извършени само за роли с по-нисък приоритет
       webhook:
@@ -138,7 +142,7 @@ bg:
       account_migration:
         acct: Потребителско име на новия акаунт
       account_warning_preset:
-        text: Предварително настроен текст
+        text: Зададен текст
         title: Заглавие
       admin_account_action:
         include_statuses: Включване на докладваните публикации в имейла
@@ -151,7 +155,7 @@ bg:
           sensitive: Деликатно
           silence: Ограничение
           suspend: Спиране
-        warning_preset_id: Употреба на преднастройка за предупреждение
+        warning_preset_id: Употреба на зададено предупреждение
       announcement:
         all_day: Целодневно събитие
         ends_at: Край на събитието
@@ -167,7 +171,7 @@ bg:
         chosen_languages: Филтриране на езиците
         confirm_new_password: Потвърждаване на новата парола
         confirm_password: Потвърдете паролата
-        context: Филтриране на контекста
+        context: Прецеждане на контекста
         current_password: Текуща парола
         data: Данни
         discoverable: Предложете акаунта на други
@@ -181,7 +185,7 @@ bg:
         irreversible: Премахване, вместо скриване
         locale: Език на интерфейса
         locked: Направи акаунта поверителен
-        max_uses: Максимален брой използвания
+        max_uses: Макс брой употреби
         new_password: Нова парола
         note: Биогр.
         otp_attempt: Двуфакторен код
@@ -203,13 +207,13 @@ bg:
         setting_display_media_hide_all: Скриване на всичко
         setting_display_media_show_all: Показване на всичко
         setting_expand_spoilers: Винаги разширяване на публикации, отбелязани с предупреждения за съдържание
-        setting_hide_network: Скриване на социалното ви графосвързване
+        setting_hide_network: Скриване на социалния ви свързан граф
         setting_noindex: Отказване от индексирането от търсачки
         setting_reduce_motion: Обездвижване на анимациите
         setting_show_application: Разкриване на приложението, изпращащо публикации
         setting_system_font_ui: Употреба на стандартния шрифт на системата
         setting_theme: Тема на сайта
-        setting_trends: Показване на днешните нашумели
+        setting_trends: Показване на днешното налагащо се
         setting_unfollow_modal: Показване на прозорче за потвърждение преди прекратяване следването на някого
         setting_use_blurhash: Показване на цветни преливки за скрита мултимедия
         setting_use_pending_items: Бавен режим
@@ -229,13 +233,15 @@ bg:
           hide: Напълно скриване
           warn: Скриване зад предупреждение
       form_admin_settings:
+        activity_api_enabled: Публикуване на агрегирани статиски относно потребителската дейност в API
         backups_retention_period: Период за съхранение на потребителския архив
         bootstrap_timeline_accounts: Винаги да се препоръчват следните акаунти на нови потребители
-        closed_registrations_message: Съобщение, показвано, когато записвания не са възможни
+        closed_registrations_message: Съобщение при неналична регистрация
         content_cache_retention_period: Период на съхранение на кеша за съдържание
         custom_css: Персонализиран CSS
         mascot: Талисман по избор (остаряла настройка)
         media_cache_retention_period: Период на запазване на мултимедията в кеш паметта
+        peers_api_enabled: Публикуване на списъка с открити сървъри в API
         profile_directory: Показване на директорията от профили
         registrations_mode: Кой може да се регистрира
         require_invite_text: Изисква се причина за присъединяване
@@ -247,11 +253,13 @@ bg:
         site_short_description: Опис на сървъра
         site_terms: Политика за поверителност
         site_title: Име на сървъра
+        status_page_url: URL адрес на страница със състоянието
         theme: Стандартна тема
         thumbnail: Миниобраз на сървъра
         timeline_preview: Позволяване на неупълномощен достъп до публични часови оси
-        trendable_by_default: Без преглед на нашумели
+        trendable_by_default: Без преглед на налагащото се
         trends: Включване на налагащи се
+        trends_as_landing_page: Употреба на налагащото се като целева страница
       interactions:
         must_be_follower: Блокирай известия от не-последователи
         must_be_following: Блокиране на известия от неследваните
@@ -275,7 +283,7 @@ bg:
         follow: Някой ви последва
         follow_request: Някой пожела да ви последва
         mention: Някой ви спомена
-        pending_account: Новите акаунти трябва да се прегледат
+        pending_account: Новите акаунти се нуждаят от преглед
         reblog: Някой подсили ваша публикация
         report: Новият доклад е подаден
         trending_tag: Изискване на преглед за новонашумели
@@ -298,11 +306,11 @@ bg:
         events: Включване на събития
         url: URL адрес на крайната точка
     'no': Не
-    not_recommended: Не се препоръчва
-    recommended: Препоръчано
+    not_recommended: Непрепоръчително
+    recommended: Препоръчва се
     required:
       mark: "*"
-      text: задължително
+      text: изисквано
     title:
       sessions:
         webauthn: Употребете един от ключовете си за сигурност, за да влезете
diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml
index 8e5fd73fa..bcabca034 100644
--- a/config/locales/simple_form.ca.yml
+++ b/config/locales/simple_form.ca.yml
@@ -18,19 +18,19 @@ ca:
           disable: Evita que l'usuari faci servir el seu compte, però no n'esborris ni amaguis el contingut.
           none: Fes servir això per a enviar un avís a l'usuari sense desencadenar cap altra acció.
           sensitive: Obliga a marcar tots els fitxers multimèdia adjunts com a sensibles.
-          silence: Evita que l'usuari pugui publicar amb visibilitat pública, amagar els seus tuts i les notificacions d'usuaris que no el segueixin.
-          suspend: Evita qualsevol interacció des de o cap a aquest compte i esborra els seus continguts. Reversible en un termini de 30 dies.
+          silence: Evita que l'usuari sigui capaç de publicar amb visibilitat pública, amagar els seus tuts i les notificacions d'usuaris que no el segueixin. Tanca tots els informes contra aquest compte.
+          suspend: Evita qualsevol interacció de o cap a aquest compte i esborra'n el contingut. Reversible en un termini de trenta dies. Tanca tots els informes contra aquest compte.
         warning_preset_id: Opcional. Encara pots afegir text personalitzat al final de la configuració predefinida
       announcement:
         all_day: Si es marca, només es mostraran les dates de l'interval de temps
-        ends_at: Opcional. En aquest moment, l'anunci no es publicarà automàticament
+        ends_at: Opcional. En aquest moment, l’anunci deixarà automàticament d'estar publicat
         scheduled_at: Deixa-ho en blanc per a publicar l’anunci immediatament
         starts_at: Opcional. En cas que el teu anunci estigui vinculat a un interval de temps específic
         text: Pots fer servir la sintaxi dels tuts. Tingues en compte l’espai que l’anunci ocuparà a la pantalla de l’usuari
       appeal:
         text: Només pots emetre una apel·lació per cada acció
       defaults:
-        autofollow: Les persones que es registrin a través de la invitació et seguiran automàticament
+        autofollow: Qui es registri a través de la invitació et seguirà automàticament
         avatar: PNG, GIF o JPG de com a màxim %{size}. S'escalarà a %{dimensions}px
         bot: Notifica que aquest compte realitza principalment accions automatitzades i que pot no estar controlat per cap persona
         context: Un o diversos contextos en què s'ha d'aplicar el filtre
@@ -74,6 +74,7 @@ ca:
           hide: Ocultar completament el contingut filtrat, comportant-se com si no existís
           warn: Oculta el contingut filtrat darrere d'un avís mencionant el títol del filtre
       form_admin_settings:
+        activity_api_enabled: Contador de tuts publicats localment, usuaris actius i registres nous en períodes setmanals
         backups_retention_period: Manté els arxius d'usuari generats durant el nombre de dies especificats.
         bootstrap_timeline_accounts: Aquests comptes es fixaran en la part superior de les recomanacions de seguiment dels nous usuaris.
         closed_registrations_message: Mostrat quan el registres estan tancats
@@ -81,6 +82,7 @@ ca:
         custom_css: Pots aplicar estils personalitzats en la versió web de Mastodon.
         mascot: Anul·la la il·lustració en la interfície web avançada.
         media_cache_retention_period: Els fitxers multimèdia descarregats s'esborraran després del nombre de dies especificat quan el valor configurat és positiu, i tornats a descarregats sota demanda.
+        peers_api_enabled: Una llista de noms de domini que aquest servidor ha trobat al fedivers. No inclou cap dada sobre si estàs federat amb un servidor determinat, només si el teu en sap res. La fan servir, en un sentit general, serveis que recoŀlecten estadístiques sobre la federació.
         profile_directory: El directori de perfils llista tots els usuaris que tenen activat ser descoberts.
         require_invite_text: Quan el registre requereix aprovació manual, fes que sigui obligatori en lloc d'opcional escriure el text de la sol·licitud d'invitació "Per què vols unir-te?"
         site_contact_email: Com pot la gent comunicar amb tu per a consultes legals o de recolzament.
@@ -89,11 +91,13 @@ ca:
         site_short_description: Una descripció curta per a ajudar a identificar de manera única el teu servidor. Qui el fa anar, per a qui és?
         site_terms: Fes servir la teva pròpia política de privacitat o deixa-ho en blanc per a la per defecte. Es pot estructurar amb format Markdown.
         site_title: Com pot la gent referir-se al teu servidor a part del seu nom de domini.
+        status_page_url: URL de la pàgina on els usuaris poden veure l'estat d'aquest servidor durant una interrupció del servei
         theme: El tema que els visitants i els nous usuaris veuen.
         thumbnail: Una imatge d'aproximadament 2:1 que es mostra al costat la informació del teu servidor.
         timeline_preview: Els visitants amb sessió no iniciada seran capaços de navegar per els tuts més recents en el teu servidor.
         trendable_by_default: Omet la revisió manual del contingut en tendència. Els articles individuals poden encara ser eliminats després del fet.
         trends: Les tendències mostren quins tuts, etiquetes i notícies estan guanyant força en el teu servidor.
+        trends_as_landing_page: Mostra el contingut en tendència als usuaris i visitants no autenticats enlloc de la descripció d'aquest servidor. Requereix que les tendències estiguin activades.
       form_challenge:
         current_password: Estàs entrant en una àrea segura
       imports:
@@ -229,6 +233,7 @@ ca:
           hide: Oculta completament
           warn: Oculta amb un avís
       form_admin_settings:
+        activity_api_enabled: Publica a l'API estadístiques agregades sobre l'activitat dels usuaris
         backups_retention_period: Període de retenció del arxiu d'usuari
         bootstrap_timeline_accounts: Recomana sempre aquests comptes als nous usuaris
         closed_registrations_message: Missatge personalitzat quan el registre està tancat
@@ -236,6 +241,7 @@ ca:
         custom_css: CSS personalitzat
         mascot: Mascota personalitzada (llegat)
         media_cache_retention_period: Període de retenció del cau multimèdia
+        peers_api_enabled: Publica a l'API la llista de servidors descoberts
         profile_directory: Habilita el directori de perfils
         registrations_mode: Qui es pot registrar
         require_invite_text: Requereix un motiu per el registre
@@ -247,11 +253,13 @@ ca:
         site_short_description: Descripció del servidor
         site_terms: Política de Privacitat
         site_title: Nom del servidor
+        status_page_url: URL de la pàgina de l'estat
         theme: Tema per defecte
         thumbnail: Miniatura del servidor
         timeline_preview: Permet l'accés no autenticat a les línies de temps públiques
         trendable_by_default: Permet tendències sense revisió prèvia
         trends: Activa les tendències
+        trends_as_landing_page: Fer servir les tendències com a pàgina inicial
       interactions:
         must_be_follower: Bloqueja les notificacions de persones que no em segueixen
         must_be_following: Bloqueja les notificacions de persones no seguides
diff --git a/config/locales/simple_form.cs.yml b/config/locales/simple_form.cs.yml
index 7c30b53a5..4669cdd02 100644
--- a/config/locales/simple_form.cs.yml
+++ b/config/locales/simple_form.cs.yml
@@ -18,8 +18,8 @@ cs:
           disable: Zabránit uživateli používat svůj účet, ale nemazat ani neskrývat jejich obsah.
           none: Toto použijte pro zaslání varování uživateli, bez vyvolání jakékoliv další akce.
           sensitive: Vynutit označení všech mediálních příloh tohoto uživatele jako citlivých.
-          silence: Zamezit uživateli odesílat veřejné příspěvky, schovat jejich příspěvky a notifikace před lidmi, kteří je nesledují.
-          suspend: Zamezit jakékoliv interakci z nebo do tohoto účtu a smazat jeho obsah. Vratné do 30 dnů.
+          silence: Zamezit uživateli odesílat veřejné příspěvky, schovat jejich příspěvky a notifikace před lidmi, kteří jej nesledují. Uzavře všechna hlášení proti tomuto účtu.
+          suspend: Zamezit jakékoliv interakci z nebo s tímto účtem a smazat jeho obsah. Lze vrátit zpět do 30 dnů. Uzavře všechna hlášení proti tomuto účtu.
         warning_preset_id: Volitelné. Na konec předlohy můžete stále vložit vlastní text
       announcement:
         all_day: Po vybrání budou zobrazeny jen dny z daného časového období
@@ -74,6 +74,7 @@ cs:
           hide: Úplně schovat filtrovaný obsah tak, jako by neexistoval
           warn: Schovat filtrovaný obsah za varováním zmiňujicím název filtru
       form_admin_settings:
+        activity_api_enabled: Počty lokálně publikovaných příspěvků, aktivních uživatelů a nových registrací v týdenních intervalech
         backups_retention_period: Zachovat generované uživatelské archivy pro zadaný počet dní.
         bootstrap_timeline_accounts: Tyto účty budou připnuty na vrchol nových uživatelů podle doporučení.
         closed_registrations_message: Zobrazeno při zavření registrace
@@ -81,6 +82,7 @@ cs:
         custom_css: Můžete použít vlastní styly ve verzi Mastodonu.
         mascot: Přepíše ilustraci v pokročilém webovém rozhraní.
         media_cache_retention_period: Stažené mediální soubory budou po zadaném počtu dní odstraněny, pokud je nastavena kladná hodnota, a na požádání znovu staženy.
+        peers_api_enabled: Seznam názvů domén se kterými se tento server setkal ve fediversu. Neobsahuje žádná data o tom, zda jste federovali s daným serverem, pouze že o něm váš server ví. Toto je využíváno službami, které sbírají o federování statistiku v obecném smyslu.
         profile_directory: Adresář profilu obsahuje seznam všech uživatelů, kteří se přihlásili, aby mohli být nalezeni.
         require_invite_text: Pokud přihlášení vyžaduje ruční schválení, měl by být textový vstup „Proč se chcete připojit?“ povinný spíše než volitelný
         site_contact_email: Jak vás mohou lidé kontaktovat v případě právních dotazů nebo dotazů na podporu.
@@ -89,11 +91,13 @@ cs:
         site_short_description: Krátký popis, který pomůže jednoznačně identifikovat váš server. Kdo ho provozuje, pro koho je určen?
         site_terms: Použijte vlastní zásady ochrany osobních údajů nebo ponechte prázdné pro použití výchozího nastavení. Může být strukturováno pomocí Markdown syntaxe.
         site_title: Jak mohou lidé odkazovat na váš server kromě názvu domény.
+        status_page_url: URL stránky, kde mohou lidé vidět stav tohoto serveru během výpadku
         theme: Vzhled stránky, který vidí noví a odhlášení uživatelé.
         thumbnail: Přibližně 2:1 obrázek zobrazený vedle informací o vašem serveru.
         timeline_preview: Odhlášení uživatelé budou moci procházet nejnovější veřejné příspěvky na serveru.
         trendable_by_default: Přeskočit manuální kontrolu populárního obsahu. Jednotlivé položky mohou být odstraněny z trendů později.
         trends: Trendy zobrazují, které příspěvky, hashtagy a zprávy získávají na serveru pozornost.
+        trends_as_landing_page: Zobrazit populární obsah odhlášeným uživatelům a návštěvníkům místo popisu tohoto serveru. Vyžaduje povolení trendů.
       form_challenge:
         current_password: Vstupujete do zabezpečeného prostoru
       imports:
@@ -229,6 +233,7 @@ cs:
           hide: Zcela skrýt
           warn: Skrýt s varováním
       form_admin_settings:
+        activity_api_enabled: Zveřejnit souhrnné statistiky o aktivitě uživatele v API
         backups_retention_period: Doba uchovávání archivu uživatelů
         bootstrap_timeline_accounts: Vždy doporučovat tyto účty novým uživatelům
         closed_registrations_message: Vlastní zpráva, když přihlášení není k dispozici
@@ -236,6 +241,7 @@ cs:
         custom_css: Vlastní CSS
         mascot: Vlastní maskot (zastaralé)
         media_cache_retention_period: Doba uchovávání mezipaměti médií
+        peers_api_enabled: Zveřejnit seznam nalezených serverů v API
         profile_directory: Povolit adresář profilů
         registrations_mode: Kdo se může přihlásit
         require_invite_text: Požadovat důvod pro připojení
@@ -247,11 +253,13 @@ cs:
         site_short_description: Popis serveru
         site_terms: Ochrana osobních údajů
         site_title: Název serveru
+        status_page_url: URL stránky se stavem
         theme: Výchozí motiv
         thumbnail: Miniatura serveru
         timeline_preview: Povolit neověřený přístup k veřejným časovým osám
         trendable_by_default: Povolit trendy bez předchozí revize
         trends: Povolit trendy
+        trends_as_landing_page: Použít trendy jako vstupní stránku
       interactions:
         must_be_follower: Blokovat oznámení od lidí, kteří vás nesledují
         must_be_following: Blokovat oznámení od lidí, které nesledujete
diff --git a/config/locales/simple_form.csb.yml b/config/locales/simple_form.csb.yml
new file mode 100644
index 000000000..0de706e41
--- /dev/null
+++ b/config/locales/simple_form.csb.yml
@@ -0,0 +1 @@
+csb:
diff --git a/config/locales/simple_form.cy.yml b/config/locales/simple_form.cy.yml
index 48357b4bc..542d06fa2 100644
--- a/config/locales/simple_form.cy.yml
+++ b/config/locales/simple_form.cy.yml
@@ -18,8 +18,8 @@ cy:
           disable: Yn atal y defnyddiwr rhag defnyddio ei gyfrif, ond peidio â dileu neu guddio ei gynnwys.
           none: Defnyddio hwn i anfon rhybudd at y defnyddiwr, heb ysgogi unrhyw gamau eraill.
           sensitive: Gorfodi holl atodiadau cyfryngau'r defnyddiwr hwn i gael eu nodi fel rhai sensitif.
-          silence: Atal y defnyddiwr rhag gallu postio gyda gwelededd cyhoeddus, cuddio ei bostiadau a'i hysbysiadau rhag pobl nad ydyn nhw'n eu dilyn.
-          suspend: Atal unrhyw ryngweithio o neu i'r cyfrif hwn a dileu ei gynnwys. Mae modd ei ddadwneud o fewn 30 diwrnod.
+          silence: Atal y defnyddiwr rhag gallu postio gyda gwelededd cyhoeddus, cuddio ei bostiadau a'i hysbysiadau rhag pobl nad ydyn nhw'n eu dilyn. Yn cau pob adroddiad yn erbyn y cyfrif hwn.
+          suspend: Atal unrhyw ryngweithio o neu i'r cyfrif hwn a dileu ei gynnwys. Dychwelyd o fewn 30 diwrnod. Yn cau pob adroddiad yn erbyn y cyfrif hwn.
         warning_preset_id: Yn ddewisol. Gallwch dal ychwanegu testun cyfaddas i ddiwedd y rhagosodiad
       announcement:
         all_day: Pan gaiff ei wirio, dim ond dyddiadau'r ystod amser fydd yn cael eu harddangos
@@ -74,6 +74,7 @@ cy:
           hide: Cuddiwch y cynnwys wedi'i hidlo'n llwyr, gan ymddwyn fel pe na bai'n bodoli
           warn: Cuddiwch y cynnwys wedi'i hidlo y tu ôl i rybudd sy'n sôn am deitl yr hidlydd
       form_admin_settings:
+        activity_api_enabled: Cyfrif o bostiadau a gyhoeddir yn lleol, defnyddwyr gweithredol, a chofrestriadau newydd mewn bwcedi wythnosol
         backups_retention_period: Cadw archifau defnyddwyr a gynhyrchwyd am y nifer penodedig o ddyddiau.
         bootstrap_timeline_accounts: Bydd y cyfrifon hyn yn cael eu pinio i frig argymhellion dilynol defnyddwyr newydd.
         closed_registrations_message: Yn cael eu dangos pan fydd cofrestriadau wedi cau
@@ -81,6 +82,7 @@ cy:
         custom_css: Gallwch gymhwyso arddulliau cyfaddas ar fersiwn gwe Mastodon.
         mascot: Yn diystyru'r darlun yn y rhyngwyneb gwe uwch.
         media_cache_retention_period: Bydd ffeiliau cyfryngau wedi'u llwytho i lawr yn cael eu dileu ar ôl y nifer penodedig o ddyddiau pan gânt eu gosod i werth cadarnhaol, a'u hail-lwytho i lawr ar alw.
+        peers_api_enabled: Rhestr o enwau parth y mae'r gweinydd hwn wedi dod ar eu traws yn y ffediws. Nid oes unrhyw ddata wedi'i gynnwys yma ynghylch a ydych chi'n ffedereiddio â gweinydd penodol, dim ond bod eich gweinydd yn gwybod amdano. Defnyddir hwn gan wasanaethau sy'n casglu ystadegau ar ffedereiddio mewn ystyr cyffredinol.
         profile_directory: Mae'r cyfeiriadur proffil yn rhestru'r holl ddefnyddwyr sydd wedi dewis i fod yn ddarganfyddiadwy.
         require_invite_text: Pan fydd angen cymeradwyaeth â llaw ar gyfer cofrestriadau, gwnewch y “Pam ydych chi am ymuno?” mewnbwn testun yn orfodol yn hytrach na dewisol
         site_contact_email: Sut y gall pobl gysylltu â chi ar gyfer ymholiadau cyfreithiol neu gymorth.
@@ -89,11 +91,13 @@ cy:
         site_short_description: Disgrifiad byr i helpu i adnabod eich gweinydd yn unigryw. Pwy sy'n ei redeg, ar gyfer pwy mae e?
         site_terms: Defnyddiwch eich polisi preifatrwydd eich hun neu gadewch yn wag i ddefnyddio'r rhagosodiad. Mae modd ei strwythuro gyda chystrawen Markdown.
         site_title: Sut y gall pobl gyfeirio at eich gweinydd ar wahân i'w enw parth.
+        status_page_url: URL tudalen lle gall pobl weld statws y gweinydd hwn yn ystod cyfnod o doriad gwasanaeth
         theme: Thema sy'n allgofnodi ymwelwyr a defnyddwyr newydd yn gweld.
         thumbnail: Delwedd tua 2:1 yn cael ei dangos ochr yn ochr â manylion eich gweinydd.
         timeline_preview: Bydd ymwelwyr sydd wedi allgofnodi yn gallu pori drwy'r postiadau cyhoeddus diweddaraf sydd ar gael ar y gweinydd.
         trendable_by_default: Hepgor adolygiad llaw o gynnwys sy'n tueddu. Gall eitemau unigol gael eu tynnu o dueddiadau o hyd ar ôl y ffaith.
         trends: Mae pynciau llosg yn dangos y postiadau, hashnodau, a newyddion sy'n denu sylw ar eich gweinydd.
+        trends_as_landing_page: Dangos cynnwys tueddiadol i ddefnyddwyr ac ymwelwyr sydd wedi allgofnodi yn lle disgrifiad o'r gweinydd hwn. Mae angen galluogi tueddiadau.
       form_challenge:
         current_password: Rydych chi'n mynd i mewn i ardal ddiogel
       imports:
@@ -204,7 +208,7 @@ cy:
         setting_display_media_show_all: Dangos popeth
         setting_expand_spoilers: Dangos postiadau wedi'u marcio â rhybudd cynnwys bob tro
         setting_hide_network: Cuddio eich graff cymdeithasol
-        setting_noindex: Peidio mynegeio peiriannau chwilio
+        setting_noindex: Eithrio rhag gael eich mynegeio gan beiriannau chwilio
         setting_reduce_motion: Lleihau mudiant mewn animeiddiadau
         setting_show_application: Datgelu rhaglen a ddefnyddir i anfon postiadau
         setting_system_font_ui: Defnyddio ffont rhagosodedig y system
@@ -229,6 +233,7 @@ cy:
           hide: Cuddio'n llwyr
           warn: Cuddio â rhybudd
       form_admin_settings:
+        activity_api_enabled: Cyhoeddi ystadegau cyfanredol am weithgarwch defnyddwyr yn yr API
         backups_retention_period: Cyfnod cadw archif defnyddwyr
         bootstrap_timeline_accounts: Argymhellwch y cyfrifon hyn i ddefnyddwyr newydd bob amser
         closed_registrations_message: Neges bersonol pan nad yw cofrestriadau ar gael
@@ -236,6 +241,7 @@ cy:
         custom_css: CSS cyfaddas
         mascot: Mascot cyfaddas (hen)
         media_cache_retention_period: Cyfnod cadw storfa cyfryngau
+        peers_api_enabled: Cyhoeddi rhestr o weinyddion a ddarganfuwyd yn yr API
         profile_directory: Galluogi cyfeiriadur proffil
         registrations_mode: Pwy all gofrestru
         require_invite_text: Gofyn am reswm i ymuno
@@ -247,11 +253,13 @@ cy:
         site_short_description: Disgrifiad y gweinydd
         site_terms: Polisi Preifatrwydd
         site_title: Enw'r gweinydd
+        status_page_url: URL tudalen statws
         theme: Thema ragosodedig
-        thumbnail: Lluniau bach gweinydd
+        thumbnail: Bawdlun y gweinydd
         timeline_preview: Caniatáu mynediad heb ei ddilysu i linellau amser cyhoeddus
         trendable_by_default: Caniatáu pynciau llosg heb adolygiad
         trends: Galluogi pynciau llosg
+        trends_as_landing_page: Defnyddio tueddiadau fel y dudalen gartref
       interactions:
         must_be_follower: Blocio hysbysiadau o bobl nad ydynt yn eich dilyn
         must_be_following: Blocio hysbysiadau o bobl nad ydych yn eu dilyn
diff --git a/config/locales/simple_form.da.yml b/config/locales/simple_form.da.yml
index ddeebaed0..b5ee9ee04 100644
--- a/config/locales/simple_form.da.yml
+++ b/config/locales/simple_form.da.yml
@@ -18,8 +18,8 @@ da:
           disable: Forhindre brugeren i at bruge sin konto, men slet eller skjul ikke vedkommendes indhold.
           none: Brug dette til at sende en advarsel til brugeren uden at udløse nogen anden handling.
           sensitive: Gennemtving sensitivmarkering af alle denne brugers medievedhæftninger.
-          silence: Forhindre brugeren i at kunne skrive offentligt synlige indlæg, skjule deres indlæg og notifikationer fra personer, som ikke følger dem.
-          suspend: Forhindre enhver interaktion fra eller til denne konto og slet dens indhold. Reversibelt inden for 30 dage.
+          silence: Forhindr brugeren i at udgive offentligt synlige indlæg, skjul vedkommendes indlæg samt notifikationer fra ikke-følgere. Lukker alle indrapporteringer af kontoen.
+          suspend: Forhindr alle interaktion fra/til kontoen og slet dens indhold. Kan omgøres inden for 30 dage. Lukker alle indrapporteringer af kontoen.
         warning_preset_id: Valgfri. Du kan stadig tilføje tilpasset tekst til slutningen af forvalgene
       announcement:
         all_day: Hvis markeret, vil kun datoerne for tidsintervallet blive vist
@@ -38,7 +38,7 @@ da:
         current_username: For at bekræfte, angiv brugernavnet for den aktuelle konto
         digest: Sendes kun efter en lang inaktivitetsperiode, og kun hvis du har modtaget personlige beskeder under fraværet
         discoverable: Tillad kontoen at blive fundet af fremmede via anbefalinger og øvrige funktioner
-        email: En bekræftelsese-mail fremsendes
+        email: En bekræftelses-e-mail fremsendes
         fields: Profilen kan have op til 4 elementer vist som en tabel
         header: PNG, GIF eller JPG. Maks. %{size}. Auto-nedskaleres til %{dimensions}px
         inbox_url: Kopiér URL'en fra forsiden af den videreformidler, der skal anvendes
@@ -74,6 +74,7 @@ da:
           hide: Skjul filtreret indhold helt (adfærd som om, det ikke fandtes)
           warn: Skjul filtreret indhold bag en advarsel, der nævner filterets titel
       form_admin_settings:
+        activity_api_enabled: Antal lokalt opslåede indlæg, aktive brugere samt nye tilmeldinger i ugentlige opdelinger
         backups_retention_period: Behold genererede brugerarkiver i det angivne antal dage.
         bootstrap_timeline_accounts: Disse konti fastgøres øverst på nye brugeres følg-anbefalinger.
         closed_registrations_message: Vises, når tilmeldinger er lukket
@@ -81,6 +82,7 @@ da:
         custom_css: Man kan anvende tilpassede stilarter på Mastodon-webversionen.
         mascot: Tilsidesætter illustrationen i den avancerede webgrænseflade.
         media_cache_retention_period: Downloadede mediefiler slettes efter det angivne antal dage, når sat til en positiv værdi, og gendownloades på forlangende.
+        peers_api_enabled: En liste med domænenavne, som denne server har stødt på i fediverset. Ingen data inkluderes her om, hvorvidt der fødereres med en given server, blot at din server kender til det. Dette bruges af tjenester, som indsamler generelle føderationsstatistikker.
         profile_directory: Profilmappen oplister alle brugere, som har valgt at kunne opdages.
         require_invite_text: Når tilmelding kræver manuel godkendelse, så gør “Hvorfor ønsker du at deltage?” tekstinput obligatorisk i stedet for valgfrit
         site_contact_email: Hvordan folk kan opnå kontakt ifm. juridiske eller supportforespørgsler.
@@ -89,11 +91,13 @@ da:
         site_short_description: En kort beskrivelse mhp. entydigt at kunne identificere denne server. Hvem kører den, hvem er den for?
         site_terms: Brug egen fortrolighedspolitik eller lad stå tomt for standardpolitikken. Kan struktureres med Markdown-syntaks.
         site_title: Hvordan folk kan henvise til serveren udover domænenavnet.
+        status_page_url: URL'en til en side, hvor status for denne server kan ses under en afbrydelse
         theme: Tema, som udloggede besøgende og nye brugere ser.
         thumbnail: Et ca. 2:1 billede vist sammen med serveroplysningerne.
         timeline_preview: Udloggede besøgende kan gennemse serverens seneste offentlige indlæg.
         trendable_by_default: Spring manuel gennemgang af trendindhold over. Individuelle elementer kan stadig fjernes fra trends efter kendsgerningen.
         trends: Tendenser viser, hvilke indlæg, hashtags og nyheder opnår momentum på serveren.
+        trends_as_landing_page: Vis tendensindhold til udloggede brugere og besøgende i stedet for en beskrivelse af denne server. Kræver, at tendenser er aktiveret.
       form_challenge:
         current_password: Du bevæger dig ind på et sikkert område
       imports:
@@ -229,6 +233,7 @@ da:
           hide: Skjul helt
           warn: Skjul bag en advarsel
       form_admin_settings:
+        activity_api_enabled: Offentliggør samlede statistikker vedr. brugeraktivitet i API'en
         backups_retention_period: Brugerarkivs opbevaringsperiode
         bootstrap_timeline_accounts: Anbefal altid disse konti til nye brugere
         closed_registrations_message: Tilpasset besked, når tilmelding er utilgængelig
@@ -236,6 +241,7 @@ da:
         custom_css: Tilpasset CSS
         mascot: Tilpasset maskot (ældre funktion)
         media_cache_retention_period: Media-cache opbevaringsperiode
+        peers_api_enabled: Udgiv liste over fundne server i API'en
         profile_directory: Aktivér profiloversigt
         registrations_mode: Hvem, der kan tilmelde sig
         require_invite_text: Kræv tilmeldingsbegrundelse
@@ -247,11 +253,13 @@ da:
         site_short_description: Serverbeskrivelse
         site_terms: Fortrolighedspolitik
         site_title: Servernavn
+        status_page_url: Statusside-URL
         theme: Standardtema
         thumbnail: Serverminiaturebillede
         timeline_preview: Tillad ikke-godkendt adgang til offentlige tidslinjer
         trendable_by_default: Tillad ikke-reviderede tendenser
         trends: Aktivér trends
+        trends_as_landing_page: Brug tendenser som destinationssiden
       interactions:
         must_be_follower: Blokér notifikationer fra ikke-følgere
         must_be_following: Blokér notifikationer fra folk, som ikke følges
diff --git a/config/locales/simple_form.de.yml b/config/locales/simple_form.de.yml
index 6f22876cf..bc2983063 100644
--- a/config/locales/simple_form.de.yml
+++ b/config/locales/simple_form.de.yml
@@ -8,7 +8,7 @@ de:
         acct: Gib profilname@domain des Kontos an, zu dem du umziehen möchtest
       account_warning_preset:
         text: Du kannst Beitragssyntax verwenden, wie z. B. URLs, Hashtags und Erwähnungen
-        title: Optional. Für den Empfänger nicht sichtbar
+        title: Optional. Für Empfänger*in nicht sichtbar
       admin_account_action:
         include_statuses: Die Person sieht, welche Beiträge die Moderationsmaßnahme oder Warnung verursacht haben
         send_email_notification: Benutzer*in wird eine Erklärung erhalten, was mit dem Konto geschehen ist
@@ -16,10 +16,10 @@ de:
         type_html: Wähle aus, wie mit <strong>%{acct}</strong> vorgegangen werden soll
         types:
           disable: Benutzer*in daran hindern, das Konto verwenden zu können, aber die Inhalte nicht löschen oder ausblenden.
-          none: Verwende dies, um dem Account eine Warnung zu schicken, ohne dabei eine andere Aktion vorzunehmen.
+          none: Dem Account eine Verwarnung zuschicken, ohne dabei eine andere Aktion vorzunehmen.
           sensitive: Erzwingen, dass alle Medienanhänge dieses Profils mit einer Inhaltswarnung versehen werden.
-          silence: Verhindern, dass der Benutzer in der Lage ist, mit der öffentlichen Sichtbarkeit zu posten und seine Beiträge und Benachrichtigungen vor Personen zu verbergen, die ihm nicht folgen.
-          suspend: Verhindert jegliche Interaktion von oder zu diesem Konto und löscht dessen Inhalt. Kann innerhalb von 30 Tagen rückgängig gemacht werden.
+          silence: Verhindert, dass dieses Profil öffentlich sichtbare Beiträge verfassen kann, und verbirgt alle Beiträge und Benachrichtigungen vor Personen, die diesem Profil nicht folgen. Alle Meldungen zu diesem Konto werden geschlossen.
+          suspend: Verhindert jegliche Interaktion von oder zu diesem Konto und löscht dessen Inhalt. Dies kann innerhalb von 30 Tagen rückgängig gemacht werden. Alle Meldungen zu diesem Konto werden geschlossen.
         warning_preset_id: Optional. Du kannst immer noch eigenen Text an das Ende der Vorlage hinzufügen
       announcement:
         all_day: Falls aktiviert, werden nur der Tag bzw. die Tage innerhalb des Zeitraums angezeigt
@@ -28,7 +28,7 @@ de:
         starts_at: Optional. Nur für den Fall, dass deine Ankündigung an einen bestimmten Zeitraum gebunden ist
         text: Du kannst die reguläre Syntax wie für Beiträge verwenden, also auch Profile erwähnen und Hashtags nutzen. Bitte beachte den Platz, den die Ankündigung auf dem Bildschirm der Benutzer*innen einnehmen wird
       appeal:
-        text: Du kannst nur einmal Einspruch gegen einen Verstoß einlegen
+        text: Du kannst nur einmal Einspruch gegen eine Maßnahme einlegen
       defaults:
         autofollow: Personen, die sich über deine Einladung registrieren, folgen automatisch deinem Profil
         avatar: PNG, GIF oder JPG. Höchstens %{size} groß. Wird auf %{dimensions} px verkleinert
@@ -50,9 +50,9 @@ de:
         scopes: Welche Schnittstellen der Applikation erlaubt sind. Wenn du einen Top-Level-Scope auswählst, dann musst du nicht jeden einzelnen darunter auswählen.
         setting_aggregate_reblogs: Zeige denselben Beitrag nicht nochmal an, wenn er erneut geteilt wurde (dies betrifft nur neulich erhaltene erneut geteilte Beiträge)
         setting_always_send_emails: Normalerweise werden Benachrichtigungen nicht per E-Mail verschickt, wenn du gerade auf Mastodon aktiv bist
-        setting_default_sensitive: Medien, die mit einer Inhaltswarnung versehen worden sind, werden erst nach einem zusätzlichen Klick angezeigt
-        setting_display_media_default: Medien mit Inhaltswarnung verbergen
-        setting_display_media_hide_all: Medien immer verbergen
+        setting_default_sensitive: Medien, die mit einer Inhaltswarnung versehen wurden, werden erst nach einem zusätzlichen Klick angezeigt
+        setting_display_media_default: Medien mit Inhaltswarnung ausblenden
+        setting_display_media_hide_all: Medien immer ausblenden
         setting_display_media_show_all: Medien mit Inhaltswarnung immer anzeigen
         setting_hide_network: Wem du folgst und wer dir folgt, wird in deinem Profil nicht angezeigt
         setting_noindex: Betrifft alle öffentlichen Daten deines Profils, z. B. deine Beiträge, Account-Empfehlungen und „Über mich“
@@ -74,13 +74,15 @@ de:
           hide: Den gefilterten Beitrag vollständig ausblenden, als hätte er nie existiert
           warn: Den gefilterten Beitrag hinter einer Warnung, die den Filtertitel beinhaltet, ausblenden
       form_admin_settings:
+        activity_api_enabled: Anzahl der wöchentlichen Beiträge, aktiven Profile und Registrierungen auf diesem Server
         backups_retention_period: Behalte die Archive, die von den Benutzer*innen erstellt worden sind, für die angegebene Anzahl an Tagen.
-        bootstrap_timeline_accounts: Diese Profile werden bei den Follower-Empfehlungen für neu registrierte Nutzer*innen oben angeheftet.
+        bootstrap_timeline_accounts: Diese Konten werden bei den Follower-Empfehlungen für neu registrierte Nutzer*innen oben angeheftet.
         closed_registrations_message: Wird angezeigt, wenn Registrierungen deaktiviert sind
         content_cache_retention_period: Beiträge von anderen Servern werden nach der angegebenen Anzahl von Tagen, wenn sie auf einen positiven Wert gesetzt werden, gelöscht. Dies kann eventuell nicht rückgängig gemacht werden.
         custom_css: Du kannst benutzerdefinierte Stile auf die Web-Version von Mastodon anwenden.
         mascot: Überschreibt die Abbildung in der erweiterten Weboberfläche.
         media_cache_retention_period: Von anderen Instanzen übertragene Mediendateien werden nach der angegebenen Anzahl an Tagen – sofern das Feld eine positive Zahl enthält – aus dem Cache gelöscht und bei Bedarf erneut heruntergeladen.
+        peers_api_enabled: Eine Liste von Domainnamen, die diesem Server im Fediverse begegnet sind. Hier werden keine Angaben darüber gemacht, ob du mit einem bestimmten Server föderierst, sondern nur, dass dein Server davon weiß. Dies wird von Diensten verwendet, die Statistiken über Föderation im allgemeinen Sinne sammeln.
         profile_directory: Das Profilverzeichnis zeigt alle Benutzer*innen an, die sich dafür entschieden haben, entdeckt zu werden.
         require_invite_text: Wenn Registrierungen eine manuelle Genehmigung erfordern, dann werden Nutzer einen Grund für ihre Registrierung angeben müssen
         site_contact_email: Wie man dich bei rechtlichen oder Support-Anfragen erreichen kann.
@@ -89,15 +91,17 @@ de:
         site_short_description: Eine kurze Beschreibung zur eindeutigen Identifizierung des Servers. Wer betreibt ihn, für wen ist er bestimmt?
         site_terms: Verwende eine eigene Datenschutzerklärung oder lasse das Feld leer, um die allgemeine Vorlage zu verwenden. Kann mit der Markdown-Syntax formatiert werden.
         site_title: Wie Personen neben dem Domainnamen auf deinen Server verweisen können.
+        status_page_url: URL einer Seite, auf der der Serverstatus während eines Ausfalls angezeigt wird
         theme: Das Design, das abgemeldete Besucher und neue Benutzer sehen.
         thumbnail: Ein Bild ungefähr im 2:1-Format, das neben den Server-Informationen angezeigt wird.
         timeline_preview: Besucher*innen und ausgeloggte Benutzer*innen können die neuesten öffentlichen Beiträge dieses Servers aufrufen.
         trendable_by_default: Manuelles Überprüfen angesagter Inhalte überspringen. Einzelne Elemente können später noch aus den Trends entfernt werden.
         trends: Trends zeigen, welche Beiträge, Hashtags und Nachrichten auf deinem Server immer beliebter werden.
+        trends_as_landing_page: Zeigt Trendinhalte abgemeldeter Benutzer und Besucher anstelle einer Beschreibung dieses Servers an. Erfordert, dass Trends aktiviert sind.
       form_challenge:
-        current_password: Du betrittst einen gesicherten Bereich
+        current_password: Du betrittst einen sicheren Bereich
       imports:
-        data: CSV-Datei, exportiert von einem anderen Mastodon-Server
+        data: CSV-Datei, die von einem Mastodon-Server exportiert worden ist
       invite_request:
         text: Dies wird uns bei der Überprüfung deiner Anmeldung behilflich sein
       ip_block:
@@ -105,15 +109,15 @@ de:
         expires_in: IP-Adressen sind eine begrenzte Ressource. Sie können außerdem auf viele Computer aufgeteilt sein und auch die Zuordnungen ändern sich. Deshalb werden unbestimmte IP-Blöcke nicht empfohlen.
         ip: Gib eine IPv4- oder IPv6-Adresse an. Du kannst ganze Bereiche mit der CIDR-Syntax blockieren. Achte darauf, dass du dich nicht selbst aussperrst!
         severities:
-          no_access: Blockiere Zugriff auf alle Ressourcen
+          no_access: Zugriff auf alle Ressourcen blockieren
           sign_up_block: Neue Registrierungen werden nicht möglich sein
           sign_up_requires_approval: Neue Registrierungen müssen genehmigt werden
         severity: Wähle aus, was mit Anfragen von dieser IP-Adresse geschehen soll
       rule:
         text: Führe eine Regel oder Bedingung für Benutzer*innen auf diesem Server ein. Bleib dabei kurz und knapp
       sessions:
-        otp: 'Gib den Zwei-Faktor-Code von deinem Telefon ein oder benutze einen deiner Wiederherstellungscodes:'
-        webauthn: Wenn es sich um einen USB-Schlüssel handelt, stelle sicher, dass du ihn einsteckst und ihn antippst.
+        otp: 'Gib den Zwei-Faktor-Code von deinem Smartphone ein oder benutze einen deiner Wiederherstellungscodes:'
+        webauthn: Wenn es sich um einen USB-Schlüssel handelt, vergewissere dich, dass du ihn einsteckst und – falls erforderlich – antippst.
       tag:
         name: Du kannst zum Beispiel nur die Groß- und Kleinschreibung der Buchstaben ändern, um es lesbarer zu machen
       user:
@@ -126,31 +130,31 @@ de:
         permissions_as_keys: Benutzer*innen mit dieser Rolle haben Zugriff auf...
         position: Höhere Rollen entscheiden über Konfliktlösungen zu gewissen Situationen. Bestimmte Aktionen können nur mit geringfügigeren Rollen durchgeführt werden
       webhook:
-        events: Wähle die zu sendenden Termine
-        url: Wo Ereignisse hingesendet werden
+        events: Zu sendende Ereignisse auswählen
+        url: Wohin Ereignisse geschickt werden
     labels:
       account:
         fields:
-          name: Bezeichnung
+          name: Beschriftung
           value: Inhalt
       account_alias:
-        acct: Betreiber des alten Kontos
+        acct: Profilname des alten Kontos
       account_migration:
-        acct: Betreiber des neuen Kontos
+        acct: Adresse des neuen Kontos
       account_warning_preset:
         text: Vorlagentext
         title: Titel
       admin_account_action:
-        include_statuses: Beitragsmeldungen in die E-Mail mit anfügen
-        send_email_notification: Nutzer*in per E-Mail benachrichtigen
-        text: Benutzerdefinierte Warnung
+        include_statuses: Gemeldete Beiträge der E-Mail beifügen
+        send_email_notification: Benachrichtigung per E-Mail
+        text: Benutzerdefinierte Verwarnung
         type: Aktion
         types:
           disable: Einfrieren
-          none: Warnung senden
+          none: Nur Verwarnung
           sensitive: Inhaltswarnung
-          silence: Einschränkung
-          suspend: Sperren
+          silence: Stummschalten
+          suspend: Sperre
         warning_preset_id: Warnungsvorlage verwenden
       announcement:
         all_day: Ganztägiges Ereignis
@@ -159,18 +163,18 @@ de:
         starts_at: Beginn der Ankündigung
         text: Ankündigung
       appeal:
-        text: Teile mit, warum diese Entscheidung zurückgenommen werden soll
+        text: Begründe, weshalb diese Entscheidung zurückgenommen werden sollte
       defaults:
-        autofollow: Lade ein, um deinem Konto zu folgen
+        autofollow: Meinem Profil automatisch folgen
         avatar: Profilbild
-        bot: Dies ist ein Bot-Konto
+        bot: Dieses Profil ist ein Bot
         chosen_languages: Sprachen einschränken
         confirm_new_password: Neues Passwort bestätigen
         confirm_password: Passwort bestätigen
-        context: Filter nach Bereichen
+        context: Nach Bereichen filtern
         current_password: Derzeitiges Passwort
         data: Daten
-        discoverable: Konto für andere empfehlen
+        discoverable: Dieses Konto anderen empfehlen
         display_name: Anzeigename
         email: E-Mail-Adresse
         expires_in: Läuft ab
@@ -179,16 +183,16 @@ de:
         honeypot: "%{label} (nicht ausfüllen)"
         inbox_url: Inbox-URL des Relais
         irreversible: Endgültig, nicht nur temporär ausblenden
-        locale: Sprache des Webinterface
+        locale: Sprache des Webinterfaces
         locked: Geschütztes Profil
-        max_uses: Maximale Anzahl der Nutzer
+        max_uses: Maximale Verwendungen
         new_password: Neues Passwort
-        note: Über mich
+        note: Biografie
         otp_attempt: Zwei-Faktor-Authentisierung
         password: Passwort
         phrase: Wort oder Formulierung
         setting_advanced_layout: Erweitertes Webinterface verwenden
-        setting_aggregate_reblogs: Boosts in der Timeline gruppieren
+        setting_aggregate_reblogs: Geteilte Beiträge in den Timelines gruppieren
         setting_always_send_emails: Benachrichtigungen immer senden
         setting_auto_play_gif: Animierte GIFs automatisch abspielen
         setting_boost_modal: Bestätigung vor dem Teilen einholen
@@ -200,7 +204,7 @@ de:
         setting_disable_swiping: Wischgesten deaktivieren
         setting_display_media: Medien-Anzeige
         setting_display_media_default: Standard
-        setting_display_media_hide_all: Alle Medien verstecken
+        setting_display_media_hide_all: Alle Medien ausblenden
         setting_display_media_show_all: Alle Medien anzeigen
         setting_expand_spoilers: Beiträge mit Inhaltswarnung immer ausklappen
         setting_hide_network: Deine Follower und „Folge ich“ nicht anzeigen
@@ -213,10 +217,10 @@ de:
         setting_unfollow_modal: Bestätigungsdialog anzeigen, bevor jemandem entfolgt wird
         setting_use_blurhash: Farbverlauf für verborgene Medien anzeigen
         setting_use_pending_items: Langsamer Modus
-        severity: Schweregrad
-        sign_in_token_attempt: Sicherheitscode
+        severity: Einschränkung
+        sign_in_token_attempt: Sicherheitsschlüssel
         title: Titel
-        type: Art des Imports
+        type: Typ
         username: Profilname
         username_or_email: Profilname oder E-Mail
         whole_word: Phrasensuche mit exakter Zeichenfolge erzwingen
@@ -226,9 +230,10 @@ de:
         name: Hashtag
       filters:
         actions:
-          hide: Komplett ausblenden
-          warn: Mit einer Warnung ausblenden
+          hide: Vollständig ausblenden
+          warn: Mit einer Inhaltswarnung ausblenden
       form_admin_settings:
+        activity_api_enabled: Aggregierte Nutzungsdaten über die API veröffentlichen
         backups_retention_period: Aufbewahrungsfrist für Archive
         bootstrap_timeline_accounts: Neuen Nutzern immer diese Konten empfehlen
         closed_registrations_message: Nachricht, falls Registrierungen deaktiviert sind
@@ -236,22 +241,25 @@ de:
         custom_css: Eigenes CSS
         mascot: Benutzerdefiniertes Maskottchen (Legacy)
         media_cache_retention_period: Aufbewahrungsfrist für den Medien-Cache
+        peers_api_enabled: Über die API die bekanntgewordenen Fediverse-Server veröffentlichen
         profile_directory: Profilverzeichnis aktivieren
         registrations_mode: Wer darf ein neues Konto registrieren?
-        require_invite_text: Grund für den Beitritt verlangen
+        require_invite_text: Begründung für Beitritt verlangen
         show_domain_blocks: Anzeigen, welche Domains gesperrt wurden
         show_domain_blocks_rationale: Anzeigen, weshalb Domains gesperrt wurden
         site_contact_email: E-Mail-Adresse
-        site_contact_username: Kontakt Benutzername
-        site_extended_description: Detaillierte Beschreibung
+        site_contact_username: Profilname
+        site_extended_description: Erweiterte Beschreibung
         site_short_description: Serverbeschreibung
         site_terms: Datenschutzerklärung
         site_title: Servername
+        status_page_url: URL der Statusseite
         theme: Standard-Design
         thumbnail: Vorschaubild des Servers
         timeline_preview: Nicht-authentifizierten Zugriff auf die öffentliche Timeline gestatten
         trendable_by_default: Trends ohne vorherige Überprüfung erlauben
         trends: Trends aktivieren
+        trends_as_landing_page: Trends als Landingpage verwenden
       interactions:
         must_be_follower: Benachrichtigungen von Profilen verbergen, die mir nicht folgen
         must_be_following: Benachrichtigungen von Profilen verbergen, denen ich nicht folge
@@ -259,7 +267,7 @@ de:
       invite:
         comment: Kommentar
       invite_request:
-        text: Warum möchtest du beitreten?
+        text: Weshalb möchtest du beitreten?
       ip_block:
         comment: Kommentar
         ip: IP-Adresse
@@ -272,7 +280,7 @@ de:
         appeal: wenn jemand einer Moderationsentscheidung widerspricht
         digest: Zusammenfassung senden
         favourite: wenn jemand meinen Beitrag favorisiert
-        follow: wenn mir jemand folgt
+        follow: wenn mir jemand Neues gefolgt ist
         follow_request: wenn mir jemand folgen möchte
         mention: wenn mich jemand erwähnt
         pending_account: wenn ein neues Konto überprüft werden muss
@@ -282,10 +290,10 @@ de:
       rule:
         text: Regel
       tag:
-        listable: Erlaube diesem Hashtag, im Profilverzeichnis zu erscheinen
+        listable: Erlaube, dass dieser Hashtag in Suchen und Empfehlungen erscheint
         name: Hashtag
-        trendable: Erlaube es, diesen Hashtag in den Trends erscheinen zu lassen
-        usable: Beiträgen erlauben, diesen Hashtag zu verwenden
+        trendable: Erlaube, dass dieser Hashtag in den Trends erscheint
+        usable: Erlaube, dass dieser Hashtag in Beiträgen erscheint
       user:
         role: Rolle
       user_role:
diff --git a/config/locales/simple_form.el.yml b/config/locales/simple_form.el.yml
index a936c9143..f56be1d24 100644
--- a/config/locales/simple_form.el.yml
+++ b/config/locales/simple_form.el.yml
@@ -18,8 +18,8 @@ el:
           disable: Αποτρέψτε το χρήστη από τη χρήση του λογαριασμού του, αλλά όχι διαγραφή ή απόκρυψη των περιεχομένων του.
           none: Χρησιμοποιήστε αυτό για να στείλετε μια προειδοποίηση στον χρήστη, χωρίς να ενεργοποιήσετε οποιαδήποτε άλλη ενέργεια.
           sensitive: Εξαναγκάστε όλα τα συνημμένα πολυμέσα αυτού του χρήστη να επισημαίνονται ως ευαίσθητα.
-          silence: Αποτρέψτε στο χρήστη να μπορεί να δημοσιεύει δημόσια, να αποκρύπτει τις δημοσιεύσεις και τις ειδοποιήσεις του από άτομα που δεν τις ακολουθούν.
-          suspend: Αποτρέψτε οποιαδήποτε αλληλεπίδραση από ή προς αυτόν τον λογαριασμό και διαγράψτε τα περιεχόμενά του. Αναστρέψιμη εντός 30 ημερών.
+          silence: Αποτρέψτε τον χρήστη από το να μπορεί να δημοσιεύει με δημόσια ορατότητα, να αποκρύπτει τις δημοσιεύσεις του και τις ειδοποιήσεις του από άτομα που δεν τις ακολουθούν. Κλείνει όλες τις αναφορές εναντίον αυτού του λογαριασμού.
+          suspend: Αποτροπή οποιασδήποτε αλληλεπίδρασης από ή προς αυτόν τον λογαριασμό και διαγραφή των περιεχομένων του. Αναστρέψιμο εντός 30 ημερών. Κλείνει όλες τις αναφορές εναντίον αυτού του λογαριασμού.
         warning_preset_id: Προαιρετικό. Μπορείς να προσθέσεις επιπλέον κείμενο μετά το προκαθορισμένο κείμενο
       announcement:
         all_day: Όταν είναι επιλεγμένο, θα εμφανίζονται μόνο οι ημερομηνίες εντός της χρονικής διάρκειας
@@ -27,6 +27,8 @@ el:
         scheduled_at: Αν μείνει κενό, η ενημέρωση θα δημοσιευτεί αμέσως
         starts_at: Προαιρετικό, για την περίπτωση που η ανακοίνωση αφορά συγκεκριμένη χρονική διάρκεια
         text: Δεκτό το συντακτικό των τουτ. Παρακαλούμε έχε υπόψιν σου το χώρο που θα καταλάβει η ανακοίνωση στην οθόνη του χρήστη
+      appeal:
+        text: Μπορείτε να κάνετε έφεση σε μια ποινή μόνο μία φορά
       defaults:
         autofollow: Όσοι εγγραφούν μέσω της πρόσκλησης θα σε ακολουθούν αυτόματα
         avatar: PNG, GIF ή JPG. Έως %{size}. Θα περιοριστεί σε διάσταση %{dimensions}px
@@ -35,6 +37,7 @@ el:
         current_password: Για λόγους ασφαλείας παρακαλώ γράψε τον κωδικό του τρέχοντος λογαριασμού
         current_username: Για επιβεβαίωση, παρακαλώ γράψε το όνομα χρήστη του τρέχοντος λογαριασμού
         digest: Αποστέλλεται μόνο μετά από μακρά περίοδο αδράνειας και μόνο αν έχεις λάβει προσωπικά μηνύματα κατά την απουσία σου
+        discoverable: Επιτρέψτε στον λογαριασμό σας να ανακαλυφθεί από αγνώστους μέσω συστάσεων, τάσεων και άλλων χαρακτηριστικών
         email: Θα σου σταλεί email επιβεβαίωσης
         fields: Μπορείς να έχεις έως 4 σημειώσεις σε μορφή πίνακα στο προφίλ σου
         header: PNG, GIF ή JPG. Έως %{size}. Θα περιοριστεί σε διάσταση %{dimensions}px
@@ -50,7 +53,7 @@ el:
         setting_default_sensitive: Τα ευαίσθητα πολυμέσα είναι κρυμμένα και εμφανίζονται με ένα κλικ
         setting_display_media_default: Απόκρυψη ευαίσθητων πολυμέσων
         setting_display_media_hide_all: Μόνιμη απόκρυψη όλων των πολυμέσων
-        setting_display_media_show_all: Μόνιμη εμφάνιση ευαίσθητων πολυμέσων
+        setting_display_media_show_all: Πάντα εμφάνιση πολυμέσων
         setting_hide_network: Δε θα εμφανίζεται στο προφίλ σου ποιους ακολουθείς και ποιοι σε ακολουθούν
         setting_noindex: Επηρεάζει το δημόσιο προφίλ και τις δημοσιεύσεις σου
         setting_show_application: Η εφαρμογή που χρησιμοποιείς για να στέλνεις τα τουτ σου θα εμφανίζεται στις αναλυτικές λεπτομέρειες τους
@@ -61,9 +64,40 @@ el:
       domain_allow:
         domain: Ο τομέας αυτός θα επιτρέπεται να ανακτά δεδομένα από αυτό τον διακομιστή και τα εισερχόμενα δεδομένα θα επεξεργάζονται και θα αποθηκεύονται
       email_domain_block:
+        domain: Αυτό μπορεί να είναι το όνομα τομέα που εμφανίζεται στη διεύθυνση email ή η εγγραφή MX που χρησιμοποιεί. Θα ελέγχονται κατά την εγγραφή.
         with_dns_records: Θα γίνει απόπειρα ανάλυσης των εγγραφών DNS του τομέα και τα αποτελέσματα θα μπουν και αυτά σε μαύρη λίστα
       featured_tag:
         name: 'Εδώ είναι μερικά από τα hashtags που χρησιμοποιήσατε περισσότερο πρόσφατα:'
+      filters:
+        action: Επιλέξτε ποια ενέργεια θα εκτελεστεί όταν μια δημοσίευση ταιριάζει με το φίλτρο
+        actions:
+          hide: Πλήρης αποκρυψη του φιλτραρισμένου περιεχομένου, συμπεριφέρεται σαν να μην υπήρχε
+          warn: Απόκρυψη φιλτραρισμένου περιεχομένου πίσω από μια προειδοποίηση που αναφέρει τον τίτλο του φίλτρου
+      form_admin_settings:
+        activity_api_enabled: Καταμέτρηση τοπικά δημοσιευμένων δημοσιεύσεων, ενεργών χρηστών και νέων εγγραφών σε εβδομαδιαία πακέτα
+        backups_retention_period: Διατήρηση αρχείων χρηστών που δημιουργήθηκαν για τον καθορισμένο αριθμό ημερών.
+        bootstrap_timeline_accounts: Αυτοί οι λογαριασμοί θα καρφιτσωθούν στην κορυφή των νέων χρηστών που ακολουθούν τις συστάσεις.
+        closed_registrations_message: Εμφανίζεται όταν κλείνουν οι εγγραφές
+        content_cache_retention_period: Αναρτήσεις από άλλους διακομιστές θα διαγραφούν μετά τον καθορισμένο αριθμό ημερών όταν οριστεί μια θετική τιμή. Αυτό μπορεί να είναι μη αναστρέψιμο.
+        custom_css: Μπορείς να εφαρμόσεις προσαρμοσμένα στυλ στην έκδοση ιστοσελίδας του Mastodon.
+        mascot: Παρακάμπτει την εικονογραφία στην προηγμένη διεπαφή ιστού.
+        media_cache_retention_period: Τα ληφθέντα αρχεία πολυμέσων θα διαγραφούν μετά τον καθορισμένο αριθμό ημερών, όταν οριστεί σε θετική τιμή, και να γίνει εκ νέου λήψη κατά απαίτηση.
+        peers_api_enabled: Μια λίστα με ονόματα τομέα που συνάντησε αυτός ο διακομιστής στο fediverse. Δεν περιλαμβάνονται δεδομένα εδώ για το αν συναλλάσσετε με ένα συγκεκριμένο διακομιστή, μόνο ότι ο διακομιστής σας το ξέρει. Χρησιμοποιείται από υπηρεσίες που συλλέγουν στατιστικά στοιχεία για την συναλλαγή με γενική έννοια.
+        profile_directory: Ο κατάλογος προφίλ παραθέτει όλους τους χρήστες που έχουν επιλέξει να είναι ανακαλύψιμοι.
+        require_invite_text: 'Όταν η εγγραφή απαιτεί χειροκίνητη έγκριση, κάνε το πεδίο κειμένου: «Γιατί θέλετε να συμμετάσχετε;» υποχρεωτικό αντί για προαιρετικό'
+        site_contact_email: Πώς μπορούν να σε προσεγγίσουν για νομικές ή υποστηρικτικές έρευνες.
+        site_contact_username: Πώς μπορούν να σε προσεγγίσουν στο Mastodon.
+        site_extended_description: Οποιεσδήποτε πρόσθετες πληροφορίες μπορεί να είναι χρήσιμες για τους επισκέπτες και τους χρήστες σας. Μπορεί να δομηθεί με σύνταξη Markdown.
+        site_short_description: Μια σύντομη περιγραφή για να ξεχωρίζει ο διακομιστής σου. Ποιος τον έχει, για ποιον είναι;
+        site_terms: Χρησιμοποίησε τη δική σου πολιτική απορρήτου ή άσε το κενό για να χρησιμοποιήσεις την προεπιλεγμένη. Μπορεί να δομηθεί με σύνταξη Markdown.
+        site_title: Πώς μπορεί κάποιος να αναφέρεται στο διακομιστή σου εκτός από το όνομα τομέα του.
+        status_page_url: Το URL μιας σελίδας όπου κάποιος μπορεί να δει την κατάσταση αυτού του διακομιστή κατά τη διάρκεια μιας διακοπής λειτουργίας
+        theme: Θέμα που βλέπουν αποσυνδεδεμένοι επισκέπτες ή νέοι χρήστες.
+        thumbnail: Μια εικόνα περίπου 2:1 που εμφανίζεται παράλληλα με τις πληροφορίες του διακομιστή σου.
+        timeline_preview: Αποσυνδεδεμένοι επισκέπτες θα μπορούν να περιηγηθούν στις πιο πρόσφατες δημόσιες δημοσιεύσεις που είναι διαθέσιμες στο διακομιστή.
+        trendable_by_default: Παράλειψη χειροκίνητης αξιολόγησης του περιεχομένου σε τάση. Μεμονωμένα στοιχεία μπορούν ακόμα να αφαιρεθούν από τις τάσεις μετέπειτα.
+        trends: Τάσεις δείχνουν ποιες δημοσιεύσεις, ετικέτες και ειδήσεις προκαλούν έλξη στο διακομιστή σας.
+        trends_as_landing_page: Εμφάνιση περιεχομένου σε τάση σε αποσυνδεδεμένους χρήστες και επισκέπτες αντί για μια περιγραφή αυτού του διακομιστή. Απαιτεί οι τάσεις να έχουν ενεργοποιηθεί.
       form_challenge:
         current_password: Μπαίνεις σε ασφαλή περιοχή
       imports:
@@ -88,6 +122,16 @@ el:
         name: Μπορείς να αλλάξεις μόνο το πλαίσιο των χαρακτήρων, για παράδειγμα για να γίνει περισσότερο ευανάγνωστο
       user:
         chosen_languages: Όταν ενεργοποιηθεί, στη δημόσια ροή θα εμφανίζονται τουτ μόνο από τις επιλεγμένες γλώσσες
+        role: Ο ρόλος ελέγχει ποια δικαιώματα έχει ο χρήστης
+      user_role:
+        color: Το χρώμα που θα χρησιμοποιηθεί για το ρόλο σε ολόκληρη τη διεπαφή, ως RGB σε δεκαεξαδική μορφή
+        highlighted: Αυτό καθιστά το ρόλο δημόσια ορατό
+        name: Δημόσιο όνομα του ρόλου, εάν ο ρόλος έχει οριστεί να εμφανίζεται ως σήμα
+        permissions_as_keys: Οι χρήστες με αυτόν τον ρόλο θα έχουν πρόσβαση σε...
+        position: Ανώτεροι ρόλοι αποφασίζει την επίλυση συγκρούσεων σε ορισμένες περιπτώσεις. Ορισμένες ενέργειες μπορούν να εκτελεστούν μόνο σε ρόλους με χαμηλότερη προτεραιότητα
+      webhook:
+        events: Επιλέξτε συμβάντα για αποστολή
+        url: Πού θα σταλούν τα γεγονότα
     labels:
       account:
         fields:
@@ -118,6 +162,8 @@ el:
         scheduled_at: Προγραμματισμένη δημοσίευση
         starts_at: Έναρξη γεγονότος
         text: Ανακοίνωση
+      appeal:
+        text: Εξηγήστε γιατί αυτή η απόφαση πρέπει να αντιστραφεί
       defaults:
         autofollow: Προσκάλεσε για να ακολουθήσουν το λογαριασμό σου
         avatar: Αβατάρ
@@ -187,10 +233,33 @@ el:
           hide: Πλήρης απόκρυψη
           warn: Απόκρυψη με προειδοποίηση
       form_admin_settings:
+        activity_api_enabled: Δημοσίευση συγκεντρωτικών στατιστικών σχετικά με τη δραστηριότητα του χρήστη στο API
+        backups_retention_period: Περίοδος αρχειοθέτησης του χρήστη
+        bootstrap_timeline_accounts: Πρότεινε πάντα αυτούς τους λογαριασμούς σε νέους χρήστες
+        closed_registrations_message: Προσαρμοσμένο μήνυμα όταν οι εγγραφές δεν είναι διαθέσιμες
+        content_cache_retention_period: Περίοδος διατήρησης προσωρινής μνήμης περιεχομένου
         custom_css: Προσαρμοσμένο CSS
+        mascot: Προσαρμοσμένη μασκότ (απαρχαιωμένο)
+        media_cache_retention_period: Περίοδος διατήρησης προσωρινής μνήμης πολυμέσων
+        peers_api_enabled: Δημοσίευση λίστας των εντοπισμένων διακομιστών στο API
+        profile_directory: Ενεργοποίηση καταλόγου προφίλ
         registrations_mode: Ποιος μπορεί να εγγραφεί
+        require_invite_text: Απαίτησε έναν λόγο για να γίνει κάποιος μέλος
+        show_domain_blocks: Εμφάνιση αποκλεισμένων τομέων
+        show_domain_blocks_rationale: Εμφάνιση γιατί αποκλείστηκαν οι τομείς
         site_contact_email: E-mail επικοινωνίας
         site_contact_username: Όνομα χρήστη επικοινωνίας
+        site_extended_description: Εκτεταμένη περιγραφή
+        site_short_description: Περιγραφή διακομιστή
+        site_terms: Πολιτική Απορρήτου
+        site_title: Όνομα διακομιστή
+        status_page_url: URL σελίδας κατάστασης
+        theme: Προεπιλεγμένο θέμα
+        thumbnail: Μικρογραφία διακομιστή
+        timeline_preview: Να επιτρέπεται μη πιστοποιημένη πρόσβαση σε δημόσια χρονολόγια
+        trendable_by_default: Επίτρεψε τις τάσεις χωρίς προηγούμενη αξιολόγηση
+        trends: Ενεργοποίηση τάσεων
+        trends_as_landing_page: Χρήση των τάσεων ως σελίδα προορισμού
       interactions:
         must_be_follower: Μπλόκαρε τις ειδοποιήσεις από όσους δεν σε ακολουθούν
         must_be_following: Μπλόκαρε τις ειδοποιήσεις από όσους δεν ακολουθείς
@@ -208,6 +277,7 @@ el:
           sign_up_requires_approval: Περιορισμός εγγραφών
         severity: Κανόνας
       notification_emails:
+        appeal: Κάποιος κάνει έφεση σε απόφαση συντονιστή
         digest: Αποστολή συνοπτικών email
         favourite: Αποστολή email όταν κάποιος σημειώνει ως αγαπημένη τη δημοσίευσή σου
         follow: Αποστολή email όταν κάποιος σε ακολουθεί
@@ -215,6 +285,8 @@ el:
         mention: Αποστολή email όταν κάποιος σε αναφέρει
         pending_account: Αποστολή email όταν υπάρχει νέος λογαριασμός για επιθεώρηση
         reblog: Αποστολή email όταν κάποιος προωθεί τη δημοσίευση σου
+        report: Υποβλήθηκε νέα αναφορά
+        trending_tag: Νέο περιεχόμενο προς τάση απαιτεί αξιολόγηση
       rule:
         text: Κανόνας
       tag:
@@ -224,6 +296,15 @@ el:
         usable: Χρήση της ετικέτας σε τουτ
       user:
         role: Ρόλος
+      user_role:
+        color: Χρώμα εμβλήματος
+        highlighted: Εμφάνιση ρόλου ως σήμα στα προφίλ χρηστών
+        name: Όνομα
+        permissions_as_keys: Δικαιώματα
+        position: Προτεραιότητα
+      webhook:
+        events: Ενεργοποιημένα συμβάντα
+        url: Endpoint URL
     'no': Όχι
     required:
       mark: "*"
diff --git a/config/locales/simple_form.en-GB.yml b/config/locales/simple_form.en-GB.yml
index 6d216bb80..27aa80c42 100644
--- a/config/locales/simple_form.en-GB.yml
+++ b/config/locales/simple_form.en-GB.yml
@@ -18,8 +18,8 @@ en-GB:
           disable: Prevent the user from using their account, but do not delete or hide their contents.
           none: Use this to send a warning to the user, without triggering any other action.
           sensitive: Force all this user's media attachments to be flagged as sensitive.
-          silence: Prevent the user from being able to post with public visibility, hide their posts and notifications from people not following them.
-          suspend: Prevent any interaction from or to this account and delete its contents. Revertible within 30 days.
+          silence: Prevent the user from being able to post with public visibility, hide their posts and notifications from people not following them. Closes all reports against this account.
+          suspend: Prevent any interaction from or to this account and delete its contents. Revertible within 30 days. Closes all reports against this account.
         warning_preset_id: Optional. You can still add custom text to end of the preset
       announcement:
         all_day: When checked, only the dates of the time range will be displayed
@@ -74,6 +74,7 @@ en-GB:
           hide: Completely hide the filtered content, behaving as if it did not exist
           warn: Hide the filtered content behind a warning mentioning the filter's title
       form_admin_settings:
+        activity_api_enabled: Counts of locally published posts, active users, and new registrations in weekly buckets
         backups_retention_period: Keep generated user archives for the specified number of days.
         bootstrap_timeline_accounts: These accounts will be pinned to the top of new users' follow recommendations.
         closed_registrations_message: Displayed when sign-ups are closed
@@ -81,6 +82,7 @@ en-GB:
         custom_css: You can apply custom styles on the web version of Mastodon.
         mascot: Overrides the illustration in the advanced web interface.
         media_cache_retention_period: Downloaded media files will be deleted after the specified number of days when set to a positive value, and re-downloaded on demand.
+        peers_api_enabled: A list of domain names this server has encountered in the fediverse. No data is included here about whether you federate with a given server, just that your server knows about it. This is used by services that collect statistics on federation in a general sense.
         profile_directory: The profile directory lists all users who have opted-in to be discoverable.
         require_invite_text: When sign-ups require manual approval, make the “Why do you want to join?” text input mandatory rather than optional
         site_contact_email: How people can reach you for legal or support inquiries.
@@ -89,10 +91,185 @@ en-GB:
         site_short_description: A short description to help uniquely identify your server. Who is running it, who is it for?
         site_terms: Use your own privacy policy or leave blank to use the default. Can be structured with Markdown syntax.
         site_title: How people may refer to your server besides its domain name.
+        status_page_url: URL of a page where people can see the status of this server during an outage
         theme: Theme that logged out visitors and new users see.
         thumbnail: A roughly 2:1 image displayed alongside your server information.
+        timeline_preview: Logged out visitors will be able to browse the most recent public posts available on the server.
+        trendable_by_default: Skip manual review of trending content. Individual items can still be removed from trends after the fact.
+        trends: Trends show which posts, hashtags and news stories are gaining traction on your server.
+        trends_as_landing_page: Show trending content to logged-out users and visitors instead of a description of this server. Requires trends to be enabled.
+      form_challenge:
+        current_password: You are entering a secure area
+      imports:
+        data: CSV file exported from another Mastodon server
+      invite_request:
+        text: This will help us review your application
+      ip_block:
+        comment: Optional. Remember why you added this rule.
+        expires_in: IP addresses are a finite resource, they are sometimes shared and often change hands. For this reason, indefinite IP blocks are not recommended.
+        ip: Enter an IPv4 or IPv6 address. You can block entire ranges using the CIDR syntax. Be careful not to lock yourself out!
+        severities:
+          no_access: Block access to all resources
+          sign_up_block: New sign-ups will not be possible
+          sign_up_requires_approval: New sign-ups will require your approval
+        severity: Choose what will happen with requests from this IP
+      rule:
+        text: Describe a rule or requirement for users on this server. Try to keep it short and simple
+      sessions:
+        otp: 'Enter the two-factor code generated by your phone app or use one of your recovery codes:'
+        webauthn: If it's an USB key be sure to insert it and, if necessary, tap it.
+      tag:
+        name: You can only change the casing of the letters, for example, to make it more readable
+      user:
+        chosen_languages: When checked, only posts in selected languages will be displayed in public timelines
+        role: The role controls which permissions the user has
+      user_role:
+        color: Color to be used for the role throughout the UI, as RGB in hex format
+        highlighted: This makes the role publicly visible
+        name: Public name of the role, if role is set to be displayed as a badge
+        permissions_as_keys: Users with this role will have access to...
+        position: Higher role decides conflict resolution in certain situations. Certain actions can only be performed on roles with a lower priority
+      webhook:
+        events: Select events to send
+        url: Where events will be sent to
     labels:
+      account:
+        fields:
+          name: Label
+          value: Content
+      account_alias:
+        acct: Handle of the old account
+      account_migration:
+        acct: Handle of the new account
+      account_warning_preset:
+        text: Preset text
+        title: Title
+      admin_account_action:
+        include_statuses: Include reported posts in the e-mail
+        send_email_notification: Notify the user per e-mail
+        text: Custom warning
+        type: Action
+        types:
+          disable: Freeze
+          none: Send a warning
+          sensitive: Sensitive
+          silence: Limit
+          suspend: Suspend
+        warning_preset_id: Use a warning preset
+      announcement:
+        all_day: All-day event
+        ends_at: End of event
+        scheduled_at: Schedule publication
+        starts_at: Start of event
+        text: Announcement
+      appeal:
+        text: Explain why this decision should be reversed
+      defaults:
+        autofollow: Invite to follow your account
+        avatar: Avatar
+        bot: This is a bot account
+        chosen_languages: Filter languages
+        confirm_new_password: Confirm new password
+        confirm_password: Confirm password
+        context: Filter contexts
+        current_password: Current password
+        data: Data
+        discoverable: Suggest account to others
+        display_name: Display name
+        email: E-mail address
+        expires_in: Expire after
+        fields: Profile metadata
+        header: Header
+        honeypot: "%{label} (do not fill in)"
+        inbox_url: URL of the relay inbox
+        irreversible: Drop instead of hide
+        locale: Interface language
+        locked: Require follow requests
+        max_uses: Max number of uses
+        new_password: New password
+        note: Bio
+        otp_attempt: Two-factor code
+        password: Password
+        phrase: Keyword or phrase
+        setting_advanced_layout: Enable advanced web interface
+        setting_aggregate_reblogs: Group boosts in timelines
+        setting_always_send_emails: Always send e-mail notifications
+        setting_auto_play_gif: Auto-play animated GIFs
+        setting_boost_modal: Show confirmation dialogue before boosting
+        setting_crop_images: Crop images in non-expanded posts to 16x9
+        setting_default_language: Posting language
+        setting_default_privacy: Posting privacy
+        setting_default_sensitive: Always mark media as sensitive
+        setting_delete_modal: Show confirmation dialogue before deleting a post
+        setting_disable_swiping: Disable swiping motions
+        setting_display_media: Media display
+        setting_display_media_default: Default
+        setting_display_media_hide_all: Hide all
+        setting_display_media_show_all: Show all
+        setting_expand_spoilers: Always expand posts marked with content warnings
+        setting_hide_network: Hide your social graph
+        setting_noindex: Opt-out of search engine indexing
+        setting_reduce_motion: Reduce motion in animations
+        setting_show_application: Disclose application used to send posts
+        setting_system_font_ui: Use system's default font
+        setting_theme: Site theme
+        setting_trends: Show today's trends
+        setting_unfollow_modal: Show confirmation dialog before unfollowing someone
+        setting_use_blurhash: Show colourful gradients for hidden media
+        setting_use_pending_items: Slow mode
+        severity: Severity
+        sign_in_token_attempt: Security code
+        title: Title
+        type: Import type
+        username: Username
+        username_or_email: Username or Email
+        whole_word: Whole word
+      email_domain_block:
+        with_dns_records: Include MX records and IPs of the domain
+      featured_tag:
+        name: Hashtag
+      filters:
+        actions:
+          hide: Hide completely
+          warn: Hide with a warning
+      form_admin_settings:
+        activity_api_enabled: Publish aggregate statistics about user activity in the API
+        backups_retention_period: User archive retention period
+        bootstrap_timeline_accounts: Always recommend these accounts to new users
+        closed_registrations_message: Custom message when sign-ups are not available
+        content_cache_retention_period: Content cache retention period
+        custom_css: Custom CSS
+        mascot: Custom mascot (legacy)
+        media_cache_retention_period: Media cache retention period
+        peers_api_enabled: Publish list of discovered servers in the API
+        profile_directory: Enable profile directory
+        registrations_mode: Who can sign-up
+        require_invite_text: Require a reason to join
+        show_domain_blocks: Show domain blocks
+        show_domain_blocks_rationale: Show why domains were blocked
+        site_contact_email: Contact e-mail
+        site_contact_username: Contact username
+        site_extended_description: Extended description
+        site_short_description: Server description
+        site_terms: Privacy Policy
+        site_title: Server name
+        status_page_url: Status page URL
+        theme: Default theme
+        thumbnail: Server thumbnail
+        timeline_preview: Allow unauthenticated access to public timelines
+        trendable_by_default: Allow trends without prior review
+        trends: Enable trends
+        trends_as_landing_page: Use trends as the landing page
+      interactions:
+        must_be_follower: Block notifications from non-followers
+        must_be_following: Block notifications from people you don't follow
+        must_be_following_dm: Block direct messages from people you don't follow
+      invite:
+        comment: Comment
+      invite_request:
+        text: Why do you want to join?
       ip_block:
+        comment: Comment
         ip: IP
         severities:
           no_access: Block access
@@ -121,6 +298,7 @@ en-GB:
         role: Role
       user_role:
         color: Badge colour
+        highlighted: Display role as badge on user profiles
         name: Name
         permissions_as_keys: Permissions
         position: Priority
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index 622300820..f9caef346 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -91,11 +91,13 @@ en:
         site_short_description: A short description to help uniquely identify your server. Who is running it, who is it for?
         site_terms: Use your own privacy policy or leave blank to use the default. Can be structured with Markdown syntax.
         site_title: How people may refer to your server besides its domain name.
+        status_page_url: URL of a page where people can see the status of this server during an outage
         theme: Theme that logged out visitors and new users see.
         thumbnail: A roughly 2:1 image displayed alongside your server information.
         timeline_preview: Logged out visitors will be able to browse the most recent public posts available on the server.
         trendable_by_default: Skip manual review of trending content. Individual items can still be removed from trends after the fact.
         trends: Trends show which posts, hashtags and news stories are gaining traction on your server.
+        trends_as_landing_page: Show trending content to logged-out users and visitors instead of a description of this server. Requires trends to be enabled.
       form_challenge:
         current_password: You are entering a secure area
       imports:
@@ -251,11 +253,13 @@ en:
         site_short_description: Server description
         site_terms: Privacy Policy
         site_title: Server name
+        status_page_url: Status page URL
         theme: Default theme
         thumbnail: Server thumbnail
         timeline_preview: Allow unauthenticated access to public timelines
         trendable_by_default: Allow trends without prior review
         trends: Enable trends
+        trends_as_landing_page: Use trends as the landing page
       interactions:
         must_be_follower: Block notifications from non-followers
         must_be_following: Block notifications from people you don't follow
diff --git a/config/locales/simple_form.en_GB.yml b/config/locales/simple_form.en_GB.yml
deleted file mode 100644
index 8752d81bb..000000000
--- a/config/locales/simple_form.en_GB.yml
+++ /dev/null
@@ -1,131 +0,0 @@
----
-en_GB:
-  simple_form:
-    hints:
-      account_warning_preset:
-        text: You can use toot syntax, such as URLs, hashtags and mentions
-      admin_account_action:
-        send_email_notification: The user will receive an explanation of what happened with their account
-        text_html: Optional. You can use toot syntax. You can <a href="%{path}">add warning presets</a> to save time
-        type_html: Choose what to do with <strong>%{acct}</strong>
-        warning_preset_id: Optional. You can still add custom text to end of the preset
-      defaults:
-        autofollow: People who sign up through the invite will automatically follow you
-        avatar: PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px
-        bot: This account mainly performs automated actions and might not be monitored
-        context: One or multiple contexts where the filter should apply
-        digest: Only sent after a long period of inactivity and only if you have received any personal messages in your absence
-        discoverable_html: The <a href="%{path}" target="_blank">directory</a> lets people find accounts based on interests and activity. Requires at least %{min_followers} followers
-        email: You will be sent a confirmation e-mail
-        fields: You can have up to 4 items displayed as a table on your profile
-        header: PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px
-        inbox_url: Copy the URL from the frontpage of the relay you want to use
-        irreversible: Filtered toots will disappear irreversibly, even if filter is later removed
-        locale: The language of the user interface, e-mails and push notifications
-        locked: Requires you to manually approve followers
-        password: Use at least 8 characters
-        phrase: Will be matched regardless of casing in text or content warning of a toot
-        scopes: Which APIs the application will be allowed to access. If you select a top-level scope, you don't need to select individual ones.
-        setting_aggregate_reblogs: Do not show new boosts for toots that have been recently boosted (only affects newly-received boosts)
-        setting_display_media_default: Hide media marked as sensitive
-        setting_display_media_hide_all: Always hide all media
-        setting_display_media_show_all: Always show media marked as sensitive
-        setting_hide_network: Who you follow and who follows you will not be shown on your profile
-        setting_noindex: Affects your public profile and status pages
-        setting_show_application: The application you use to toot will be displayed in the detailed view of your toots
-        username: Your username will be unique on %{domain}
-        whole_word: When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word
-      featured_tag:
-        name: 'You might want to use one of these:'
-      imports:
-        data: CSV file exported from another Mastodon server
-      sessions:
-        otp: 'Enter the two-factor code generated by your phone app or use one of your recovery codes:'
-      user:
-        chosen_languages: When checked, only toots in selected languages will be displayed in public timelines
-    labels:
-      account:
-        fields:
-          name: Label
-          value: Content
-      account_warning_preset:
-        text: Preset text
-      admin_account_action:
-        send_email_notification: Notify the user per e-mail
-        text: Custom warning
-        type: Action
-        types:
-          disable: Disable
-          none: Do nothing
-          silence: Silence
-          suspend: Suspend and irreversibly delete account data
-        warning_preset_id: Use a warning preset
-      defaults:
-        autofollow: Invite to follow your account
-        avatar: Avatar
-        bot: This is a bot account
-        chosen_languages: Filter languages
-        confirm_new_password: Confirm new password
-        confirm_password: Confirm password
-        context: Filter contexts
-        current_password: Current password
-        data: Data
-        discoverable: List this account on the directory
-        display_name: Display name
-        email: E-mail address
-        expires_in: Expire after
-        fields: Profile metadata
-        header: Header
-        inbox_url: URL of the relay inbox
-        irreversible: Drop instead of hide
-        locale: Interface language
-        locked: Lock account
-        max_uses: Max number of uses
-        new_password: New password
-        note: Bio
-        otp_attempt: Two-factor code
-        password: Password
-        phrase: Keyword or phrase
-        setting_aggregate_reblogs: Group boosts in timelines
-        setting_auto_play_gif: Auto-play animated GIFs
-        setting_boost_modal: Show confirmation dialog before boosting
-        setting_default_language: Posting language
-        setting_default_privacy: Post privacy
-        setting_default_sensitive: Always mark media as sensitive
-        setting_delete_modal: Show confirmation dialog before deleting a toot
-        setting_display_media: Media display
-        setting_display_media_default: Default
-        setting_display_media_hide_all: Hide all
-        setting_display_media_show_all: Show all
-        setting_expand_spoilers: Always expand toots marked with content warnings
-        setting_hide_network: Hide your network
-        setting_noindex: Opt-out of search engine indexing
-        setting_reduce_motion: Reduce motion in animations
-        setting_show_application: Disclose application used to send toots
-        setting_system_font_ui: Use system's default font
-        setting_theme: Site theme
-        setting_unfollow_modal: Show confirmation dialog before unfollowing someone
-        severity: Severity
-        type: Import type
-        username: Username
-        username_or_email: Username or Email
-        whole_word: Whole word
-      featured_tag:
-        name: Hashtag
-      interactions:
-        must_be_follower: Block notifications from non-followers
-        must_be_following: Block notifications from people you don't follow
-        must_be_following_dm: Block direct messages from people you don't follow
-      notification_emails:
-        digest: Send digest e-mails
-        favourite: Send e-mail when someone favourites your status
-        follow: Send e-mail when someone follows you
-        follow_request: Send e-mail when someone requests to follow you
-        mention: Send e-mail when someone mentions you
-        reblog: Send e-mail when someone boosts your status
-        report: Send e-mail when a new report is submitted
-    'no': 'No'
-    required:
-      mark: "*"
-      text: required
-    'yes': 'Yes'
diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml
index 846a671ff..2e9ec45e6 100644
--- a/config/locales/simple_form.eo.yml
+++ b/config/locales/simple_form.eo.yml
@@ -18,8 +18,8 @@ eo:
           disable: Malhelpi la uzanton uzi sian konton, sed ne forigi aŭ kaŝi ties enhavojn.
           none: Uzu ĉi tion por sendi averton al la uzanto, sen ekigi alian agon.
           sensitive: Devigi ĉiujn da plurmediaj aldonaĵoj esti markitaj kiel sentemaj.
-          silence: Malhelpi la uzanton povu afiŝi per publika videbleco, kaŝi ties afiŝojn kaj sciigojn de homoj kiuj ne sekvas rin.
-          suspend: Malhelpu ajnan interagon de aŭ al ĉi tiu konto kaj forigu ĝian enhavon. Returnebla ene de 30 tagoj.
+          silence: Malhelpu la uzanton povi afiŝi kun publika videbleco, kaŝu iliajn afiŝojn kaj sciigojn de homoj, kiuj ne sekvas ilin. Fermas ĉiujn raportojn kontraŭ ĉi tiu konto.
+          suspend: Malhelpu ajnan interagadon de aŭ al ĉi tiu konto kaj forigu ĝian enhavon. Revertebla ene de 30 tagoj. Fermas ĉiujn raportojn kontraŭ ĉi tiu konto.
         warning_preset_id: Malnepra. Vi povas ankoraŭ aldoni propran tekston al la fino de la antaŭagordo
       announcement:
         all_day: Kiam markita, nur la datoj de la tempointervalo estos montrataj
@@ -50,10 +50,10 @@ eo:
         scopes: Kiujn API-ojn la aplikaĵo permesiĝos atingi. Se vi elektas supran amplekson, vi ne bezonas elekti la individuajn.
         setting_aggregate_reblogs: Ne montri novajn plusendojn de mesaĝoj lastatempe plusenditaj (nur efikas al nove ricevitaj plusendoj)
         setting_always_send_emails: Normale, la sciigoj per retpoŝto ne estos senditaj kiam vi uzas Mastodon aktive
-        setting_default_sensitive: La tiklaj aŭdovidaĵoj estas kaŝitaj implicite, kaj povas esti montritaj per klako
-        setting_display_media_default: Kaŝi aŭdovidaĵojn markitajn kiel tikla
-        setting_display_media_hide_all: Ĉiam kaŝi la aŭdovidaĵojn
-        setting_display_media_show_all: Ĉiam montri la aŭdovidaĵojn
+        setting_default_sensitive: Tiklaj plurmedioj estas kaŝitaj implicite, kaj povas esti montritaj per klako
+        setting_display_media_default: Kaŝi plurmediojn markitajn kiel tiklaj
+        setting_display_media_hide_all: Ĉiam kaŝi la plurmediojn
+        setting_display_media_show_all: Ĉiam montri la plurmediojn
         setting_hide_network: Tiuj kiujn vi sekvas, kaj tiuj kiuj sekvas vin estos kaŝitaj en via profilo
         setting_noindex: Influas vian publikan profilon kaj afiŝajn paĝojn
         setting_show_application: La aplikaĵo, kiun vi uzas por afiŝi, estos montrita en la detala vido de viaj afiŝoj
@@ -74,6 +74,7 @@ eo:
           hide: Tute kaŝigi la filtritajn enhavojn, kvazau ĝi ne ekzistis
           warn: Kaŝi la enhavon filtritan malantaŭ averto mencianta la nomon de la filtro
       form_admin_settings:
+        activity_api_enabled: Nombroj de loke publikigitaj afiŝoj, aktivaj uzantoj kaj novaj registradoj en semajnaj siteloj
         backups_retention_period: Konservi generitajn uzantoarkivojn por la kvanto de tagoj.
         bootstrap_timeline_accounts: Ĉi tiuj kontoj pinglitas al la supro de sekvorekomendoj de novaj uzantoj.
         closed_registrations_message: Montrita kiam registroj fermitas
@@ -81,6 +82,7 @@ eo:
         custom_css: Vi povas meti propajn stilojn en la retversio de Mastodon.
         mascot: Anstatauigi la ilustraĵon en la altnivela retinterfaco.
         media_cache_retention_period: Elŝutitaj audovidaĵojn forigotas post la kvanto de tagoj kiam fiksitas al pozitiva nombro.
+        peers_api_enabled: Listo de domajnaj nomoj kiujn ĉi tiu servilo renkontis en la fediverso. Neniuj datumoj estas inkluditaj ĉi tie pri ĉu vi federacias kun donita servilo, nur ke via servilo scias pri ĝi. Ĉi tio estas uzata de servoj kiuj kolektas statistikojn pri federacio en ĝenerala signifo.
         profile_directory: La profilujo listigas ĉiujn uzantojn kiu volonte malkovrebli.
         require_invite_text: Kiam registroj bezonas permanan aprobon, igi la "Kial vi volas aliĝi?" tekstoenigon deviga anstau nedeviga
         site_contact_email: Kiel personoj povas kontakti vin por juraj au subtenaj demandoj.
@@ -89,11 +91,13 @@ eo:
         site_short_description: Mallonga priskribo por helpi unike identigi vian servilon. Kiu faras, por kiu?
         site_terms: Uzu vian sian privatecan politekon au ignoru por uzi la defaulton.
         site_title: Kiel personoj voki vian servilon anstatau ĝia domajnnomo.
+        status_page_url: URL de paĝo kie homoj povas vidi la staton de ĉi tiu servilo dum malfunkcio
         theme: Etoso kiun elsalutitaj vizitantoj kaj novaj uzantoj vidas.
         thumbnail: Ĉirkaua 2:1 bildo montritas kun via servilinformo.
         timeline_preview: Elsalutitaj vizitantoj povos vidi la plej lastajn publikajn mesaĝojn disponeblaj en la servilo.
         trendable_by_default: Ignori permanan kontrolon de tendenca enhavo.
         trends: Tendencoj montras kiu mesaĝoj, kradvortoj kaj novaĵoj populariĝas en via servilo.
+        trends_as_landing_page: Montru tendencan enhavon al elsalutitaj uzantoj kaj vizitantoj anstataŭ priskribo de ĉi tiu servilo. Necesas ke tendencoj estu ebligitaj.
       form_challenge:
         current_password: Vi eniras sekuran areon
       imports:
@@ -195,10 +199,10 @@ eo:
         setting_crop_images: Stuci bildojn en negrandigitaj mesaĝoj al 16x9
         setting_default_language: Publikada lingvo
         setting_default_privacy: Privateco de afiŝado
-        setting_default_sensitive: Ĉiam marki aŭdovidaĵojn tiklaj
+        setting_default_sensitive: Ĉiam marki plurmediojn kiel tiklaj
         setting_delete_modal: Montri konfirman fenestron antaŭ ol forigi mesaĝon
         setting_disable_swiping: Malebligi svingajn movojn
-        setting_display_media: Aŭdovidaĵa montrado
+        setting_display_media: Montrado de plurmedioj
         setting_display_media_default: Implicita
         setting_display_media_hide_all: Kaŝi ĉiujn
         setting_display_media_show_all: Montri ĉiujn
@@ -229,6 +233,7 @@ eo:
           hide: Kaŝi komplete
           warn: Kaŝi malantaŭ averto
       form_admin_settings:
+        activity_api_enabled: Publikigi entutajn statistikojn pri uzantagado en la API
         backups_retention_period: Uzantoarkivretendauro
         bootstrap_timeline_accounts: Ĉiam rekomendi ĉi tiujn kontojn al novaj uzantoj
         closed_registrations_message: Kutima mesaĝo kiam registroj ne estas disponeblaj
@@ -236,7 +241,8 @@ eo:
         custom_css: Propa CSS
         mascot: Propa maskoto
         media_cache_retention_period: Audovidaĵkaŝaĵretendauro
-        profile_directory: Ebligi la profilujon
+        peers_api_enabled: Eldonu liston de malkovritaj serviloj en la API
+        profile_directory: Ŝalti la profilujon
         registrations_mode: Kiu povas krei konton
         require_invite_text: Bezoni kialon por aliĝi
         show_domain_blocks: Montri domajnblokojn
@@ -247,11 +253,13 @@ eo:
         site_short_description: Priskribo de servilo
         site_terms: Privateca politiko
         site_title: Nomo de la servilo
+        status_page_url: URL de la paĝo de stato
         theme: Implicita etoso
         thumbnail: Bildeto de servilo
         timeline_preview: Permesi la neaŭtentigitan aliron al la publikaj templinioj
         trendable_by_default: Permesi tendencojn sen deviga kontrolo
         trends: Ŝalti furorojn
+        trends_as_landing_page: Uzu tendencojn kiel la landpaĝon
       interactions:
         must_be_follower: Bloki sciigojn de nesekvantoj
         must_be_following: Bloki sciigojn de homoj, kiujn vi ne sekvas
diff --git a/config/locales/simple_form.es-AR.yml b/config/locales/simple_form.es-AR.yml
index d15ddaa49..e70232479 100644
--- a/config/locales/simple_form.es-AR.yml
+++ b/config/locales/simple_form.es-AR.yml
@@ -18,8 +18,8 @@ es-AR:
           disable: Evitar que el usuario use su cuenta, pero no elimina ni oculta sus contenidos.
           none: Usá esto para enviarle una advertencia al usuario, sin ejecutar ninguna otra acción.
           sensitive: Forzar a que todos los adjuntos de medios de este usuario sean marcados como sensibles.
-          silence: Evitar que el usuario pueda publicar mensajes, ocultando sus publicaciones y notificaciones a cuentas que no lo siguen.
-          suspend: Evitar cualquier interacción desde o hacia esta cuenta, y eliminar su contenido. Reversible en un plazo de 30 días.
+          silence: Evitá que la cuenta pueda enviar mensajes con visibilidad pública, ocultá sus mensajes y notificaciones a personas que no la siguen. Esto cierra todas las denuncias contra esta cuenta.
+          suspend: Evitá cualquier interacción desde o hacia esta cuenta y eliminá su contenido. Revertible en 30 días. Esto cierra todas las denuncias contra esta cuenta.
         warning_preset_id: Opcional. Todavía podés agregar texto personalizado al final del preajuste
       announcement:
         all_day: Cuando esté seleccionado, sólo se mostrarán las fechas del rango de tiempo
@@ -74,6 +74,7 @@ es-AR:
           hide: Ocultar completamente el contenido filtrado, comportándose como si no existiera
           warn: Ocultar el contenido filtrado detrás de una advertencia mencionando el título del filtro
       form_admin_settings:
+        activity_api_enabled: Conteos de mensajes publicados localmente, cuentas activas y nuevos registros en tandas semanales
         backups_retention_period: Conservar los archivos historiales generados por el usuario durante el número de días especificado.
         bootstrap_timeline_accounts: Estas cuentas serán fijadas a la parte superior de las recomendaciones de cuentas a seguir para nuevos usuarios.
         closed_registrations_message: Mostrado cuando los registros están cerrados
@@ -81,6 +82,7 @@ es-AR:
         custom_css: Podés aplicar estilos personalizados a la versión web de Mastodon.
         mascot: Reemplaza la ilustración en la interface web avanzada.
         media_cache_retention_period: Los archivos de medios descargados se eliminarán después del número especificado de días cuando se establezca un valor positivo, y se volverán a descargar a pedido.
+        peers_api_enabled: Una lista de nombres de dominio que este servidor ha encontrado en el Fediverso. Acá no se incluye ningún dato sobre si federás con un servidor determinado, sólo que tu servidor lo conoce. Esto es usado por los servicios que recopilan estadísticas sobre la federación en un sentido general.
         profile_directory: El directorio de perfiles lista a todos los usuarios que han optado a que su cuenta pueda ser descubierta.
         require_invite_text: Cuando registros aprobación manual, hacé que la solicitud de invitación "¿Por qué querés unirte?" sea obligatoria, en vez de opcional
         site_contact_email: Cómo la gente puede estar en contacto con vos para consultas legales o de ayuda.
@@ -89,11 +91,13 @@ es-AR:
         site_short_description: Una breve descripción para ayudar a identificar individualmente a tu servidor. ¿Quién lo administra, a quién va dirigido?
         site_terms: Usá tu propia política de privacidad o dejala en blanco para usar la predeterminada. Puede estructurarse con sintaxis Markdown.
         site_title: Cómo la gente puede referirse a tu servidor además de su nombre de dominio.
+        status_page_url: Dirección web de una página donde la gente puede ver el estado de este servidor durante un apagón
         theme: El tema que los visitantes no registrados y los nuevos usuarios ven.
         thumbnail: Una imagen de aproximadamente 2:1 se muestra junto a la información de tu servidor.
         timeline_preview: Los visitantes no registrados podrán navegar por los mensajes públicos más recientes disponibles en el servidor.
         trendable_by_default: Omití la revisión manual del contenido en tendencia. Los elementos individuales aún podrán eliminarse de las tendencias.
         trends: Las tendencias muestran qué mensajes, etiquetas y noticias están ganando tracción en tu servidor.
+        trends_as_landing_page: Mostrar contenido en tendencia para usuarios que no iniciaron sesión y visitantes, en lugar de una descripción de este servidor. Requiere que las tendencias estén habilitadas.
       form_challenge:
         current_password: Estás ingresando en un área segura
       imports:
@@ -229,6 +233,7 @@ es-AR:
           hide: Ocultar completamente
           warn: Ocultar con una advertencia
       form_admin_settings:
+        activity_api_enabled: Publicar estadísticas agregadas sobre la actividad de la cuenta en la API
         backups_retention_period: Período de retención del archivo historial del usuario
         bootstrap_timeline_accounts: Siempre recomendar estas cuentas a usuarios nuevos
         closed_registrations_message: Mensaje personalizado cuando los registros no están disponibles
@@ -236,6 +241,7 @@ es-AR:
         custom_css: CSS personalizado
         mascot: Mascota personalizada (legado)
         media_cache_retention_period: Período de retención de la caché de medios
+        peers_api_enabled: Publicar lista de servidores descubiertos en la API
         profile_directory: Habilitar directorio de perfiles
         registrations_mode: Quién puede registrarse
         require_invite_text: Requerir un motivo para unirse
@@ -247,11 +253,13 @@ es-AR:
         site_short_description: Descripción del servidor
         site_terms: Política de privacidad
         site_title: Nombre del servidor
+        status_page_url: Dirección web de la página de estado
         theme: Tema predeterminado
         thumbnail: Miniatura del servidor
         timeline_preview: Permitir el acceso no autenticado a las líneas temporales públicas
         trendable_by_default: Permitir tendencias sin revisión previa
         trends: Habilitar tendencias
+        trends_as_landing_page: Usar las tendencias como la página de destino
       interactions:
         must_be_follower: Bloquear notificaciones de cuentas que no te siguen
         must_be_following: Bloquear notificaciones de cuentas que no seguís
diff --git a/config/locales/simple_form.es-MX.yml b/config/locales/simple_form.es-MX.yml
index b08403426..52d8974f2 100644
--- a/config/locales/simple_form.es-MX.yml
+++ b/config/locales/simple_form.es-MX.yml
@@ -18,8 +18,8 @@ es-MX:
           disable: Evitar que el usuario utilice su cuenta, pero no eliminar ni ocultar sus contenidos.
           none: Utilizar esto para enviar una advertencia al usuario, sin poner en marcha ninguna otra acción.
           sensitive: Forzar que todos los archivos multimedia de este usuario sean marcados como sensibles.
-          silence: Evitar que el usuario pueda publicar con visibilidad pública, oculta sus mensajes y notificaciones a personas que no lo siguen.
-          suspend: Evitar cualquier interacción desde o hacia esta cuenta y eliminar su contenido. Reversible en un plazo de 30 días.
+          silence: Evita que el usuario pueda publicar con visibilidad pública, y oculta sus publicaciones y notificaciones a personas que no lo siguen. Cierra todas las denuncias contra esta cuenta.
+          suspend: Evita cualquier interacción desde o hacia esta cuenta y elimina su contenido. Reversible en 30 días. Cierra todas las denuncias contra esta cuenta.
         warning_preset_id: Opcional. Aún puede añadir texto personalizado al final de la configuración predefinida
       announcement:
         all_day: Cuando está seleccionado solo se mostrarán las fechas del rango de tiempo
@@ -74,6 +74,7 @@ es-MX:
           hide: Ocultar completamente el contenido filtrado, comportándose como si no existiera
           warn: Ocultar el contenido filtrado detrás de una advertencia mencionando el título del filtro
       form_admin_settings:
+        activity_api_enabled: Conteo de publicaciones publicadas localmente, usuarios activos, y nuevos registros en periodos semanales
         backups_retention_period: Mantener los archivos de usuario generados durante el número de días especificado.
         bootstrap_timeline_accounts: Estas cuentas aparecerán en la parte superior de las recomendaciones de los nuevos usuarios.
         closed_registrations_message: Mostrado cuando los registros están cerrados
@@ -81,6 +82,7 @@ es-MX:
         custom_css: Puedes aplicar estilos personalizados a la versión web de Mastodon.
         mascot: Reemplaza la ilustración en la interfaz web avanzada.
         media_cache_retention_period: Los archivos multimedia descargados se eliminarán después del número especificado de días cuando se establezca un valor positivo, y se redescargarán bajo demanda.
+        peers_api_enabled: Una lista de nombres de dominio que este servidor ha encontrado en el fediverso. Aquí no se incluye ningún dato sobre si usted federa con un servidor determinado, sólo que su servidor lo sabe. Esto es utilizado por los servicios que recopilan estadísticas sobre la federación en un sentido general.
         profile_directory: El directorio de perfiles lista a todos los usuarios que han optado por que su cuenta pueda ser descubierta.
         require_invite_text: Cuando los registros requieren aprobación manual, hace obligatoria la entrada de texto "¿Por qué quieres unirte?" en lugar de opcional
         site_contact_email: Cómo la gente puede ponerse en contacto contigo para consultas legales o de ayuda.
@@ -89,11 +91,13 @@ es-MX:
         site_short_description: Una breve descripción para ayudar a identificar su servidor de forma única. ¿Quién lo administra, a quién va dirigido?
         site_terms: Utiliza tu propia política de privacidad o déjala en blanco para usar la predeterminada Puede estructurarse con formato Markdown.
         site_title: Cómo puede referirse la gente a tu servidor además de por el nombre de dominio.
+        status_page_url: URL de una página donde las personas pueden ver el estado de este servidor durante una interrupción
         theme: El tema que los visitantes no registrados y los nuevos usuarios ven.
         thumbnail: Una imagen de aproximadamente 2:1 se muestra junto a la información de tu servidor.
         timeline_preview: Los visitantes no registrados podrán navegar por los mensajes públicos más recientes disponibles en el servidor.
         trendable_by_default: Omitir la revisión manual del contenido en tendencia. Los elementos individuales aún podrán eliminarse de las tendencias.
         trends: Las tendencias muestran qué mensajes, etiquetas y noticias están ganando tracción en tu servidor.
+        trends_as_landing_page: Mostrar contenido en tendencia para usuarios y visitantes desconectados en lugar de una descripción de este servidor. Requiere tendencias para ser habilitado.
       form_challenge:
         current_password: Estás entrando en un área segura
       imports:
@@ -229,6 +233,7 @@ es-MX:
           hide: Ocultar completamente
           warn: Ocultar con una advertencia
       form_admin_settings:
+        activity_api_enabled: Publicar estadísticas locales acerca de la actividad de usuario en la API
         backups_retention_period: Período de retención del archivo de usuario
         bootstrap_timeline_accounts: Recomendar siempre estas cuentas a nuevos usuarios
         closed_registrations_message: Mensaje personalizado cuando los registros no están disponibles
@@ -236,6 +241,7 @@ es-MX:
         custom_css: CSS personalizado
         mascot: Mascota personalizada (legado)
         media_cache_retention_period: Período de retención de caché multimedia
+        peers_api_enabled: Publicar lista de servidores descubiertos en la API
         profile_directory: Habilitar directorio de perfiles
         registrations_mode: Quién puede registrarse
         require_invite_text: Requerir una razón para unirse
@@ -247,11 +253,13 @@ es-MX:
         site_short_description: Descripción del servidor
         site_terms: Política de Privacidad
         site_title: Nombre del servidor
+        status_page_url: URL de página de estado
         theme: Tema por defecto
         thumbnail: Miniatura del servidor
         timeline_preview: Permitir el acceso no autenticado a las líneas de tiempo públicas
         trendable_by_default: Permitir tendencias sin revisión previa
         trends: Habilitar tendencias
+        trends_as_landing_page: Usar tendencias como página de destino
       interactions:
         must_be_follower: Bloquear notificaciones de personas que no te siguen
         must_be_following: Bloquear notificaciones de personas que no sigues
diff --git a/config/locales/simple_form.es.yml b/config/locales/simple_form.es.yml
index d5ae3be7f..593416aca 100644
--- a/config/locales/simple_form.es.yml
+++ b/config/locales/simple_form.es.yml
@@ -18,8 +18,8 @@ es:
           disable: Evita que el usuario utilice su cuenta, pero no elimina ni oculta sus contenidos.
           none: Utilizar esto para enviar una advertencia al usuario, sin poner en marcha ninguna otra acción.
           sensitive: Forzar que todos los archivos multimedia de este usuario sean marcados como sensibles.
-          silence: Evitar que el usuario pueda publicar con visibilidad pública, oculta sus mensajes y notificaciones a personas que no lo siguen.
-          suspend: Evitar cualquier interacción desde o hacia esta cuenta y eliminar su contenido. Reversible en un plazo de 30 días.
+          silence: Evita que el usuario pueda publicar con visibilidad pública, y oculta sus mensajes y notificaciones a personas que no lo siguen. Cierra todos los informes contra esta cuenta.
+          suspend: Evita cualquier interacción desde o hacia esta cuenta y elimina su contenido. Reversible en 30 días. Cierra todos los informes contra esta cuenta.
         warning_preset_id: Opcional. Aún puede añadir texto personalizado al final de la configuración predefinida
       announcement:
         all_day: Cuando está seleccionado solo se mostrarán las fechas del rango de tiempo
@@ -74,6 +74,7 @@ es:
           hide: Ocultar completamente el contenido filtrado, comportándose como si no existiera
           warn: Ocultar el contenido filtrado detrás de una advertencia mencionando el título del filtro
       form_admin_settings:
+        activity_api_enabled: Conteo de publicaciones publicadas localmente, usuarios activos y registros nuevos cada semana
         backups_retention_period: Mantener los archivos de usuario generados durante el número de días especificado.
         bootstrap_timeline_accounts: Estas cuentas aparecerán en la parte superior de las recomendaciones de los nuevos usuarios.
         closed_registrations_message: Mostrado cuando los registros están cerrados
@@ -81,6 +82,7 @@ es:
         custom_css: Puedes aplicar estilos personalizados a la versión web de Mastodon.
         mascot: Reemplaza la ilustración en la interfaz web avanzada.
         media_cache_retention_period: Los archivos multimedia descargados se eliminarán después del número especificado de días cuando se establezca un valor positivo, y se redescargarán bajo demanda.
+        peers_api_enabled: Una lista de nombres de dominio que este servidor ha encontrado en el Fediverso. Aquí no se incluye ningún dato sobre si federas con un servidor determinado, solo que tu servidor lo conoce. Esto es utilizado por los servicios que recopilan estadísticas sobre la federación en un sentido general.
         profile_directory: El directorio de perfiles lista a todos los usuarios que han optado por que su cuenta pueda ser descubierta.
         require_invite_text: Cuando los registros requieren aprobación manual, hace obligatoria la entrada de texto "¿Por qué quieres unirte?" en lugar de opcional
         site_contact_email: Cómo la gente puede ponerse en contacto contigo para consultas legales o de ayuda.
@@ -89,11 +91,13 @@ es:
         site_short_description: Una breve descripción para ayudar a identificar su servidor de forma única. ¿Quién lo administra, a quién va dirigido?
         site_terms: Utiliza tu propia política de privacidad o déjala en blanco para usar la predeterminada Puede estructurarse con formato Markdown.
         site_title: Cómo puede referirse la gente a tu servidor además de por el nombre de dominio.
+        status_page_url: URL de la página donde la gente puede ver el estado de este servidor durante una incidencia
         theme: El tema que los visitantes no registrados y los nuevos usuarios ven.
         thumbnail: Una imagen de aproximadamente 2:1 se muestra junto a la información de tu servidor.
         timeline_preview: Los visitantes no registrados podrán navegar por los mensajes públicos más recientes disponibles en el servidor.
         trendable_by_default: Omitir la revisión manual del contenido en tendencia. Los elementos individuales aún podrán eliminarse de las tendencias.
         trends: Las tendencias muestran qué mensajes, etiquetas y noticias están ganando tracción en tu servidor.
+        trends_as_landing_page: Mostrar contenido en tendencia para usuarios y visitantes en lugar de una descripción de este servidor. Requiere que las tendencias estén habilitadas.
       form_challenge:
         current_password: Estás entrando en un área segura
       imports:
@@ -229,6 +233,7 @@ es:
           hide: Ocultar completamente
           warn: Ocultar con una advertencia
       form_admin_settings:
+        activity_api_enabled: Publicar estadísticas agregadas sobre la actividad del usuario con la API
         backups_retention_period: Período de retención del archivo de usuario
         bootstrap_timeline_accounts: Recomendar siempre estas cuentas a nuevos usuarios
         closed_registrations_message: Mensaje personalizado cuando los registros no están disponibles
@@ -236,6 +241,7 @@ es:
         custom_css: CSS personalizado
         mascot: Mascota personalizada (legado)
         media_cache_retention_period: Período de retención de caché multimedia
+        peers_api_enabled: Publicar lista de servidores descubiertos en la API
         profile_directory: Habilitar directorio de perfiles
         registrations_mode: Quién puede registrarse
         require_invite_text: Requerir una razón para unirse
@@ -247,11 +253,13 @@ es:
         site_short_description: Descripción del servidor
         site_terms: Política de Privacidad
         site_title: Nombre del servidor
+        status_page_url: URL de página de estado
         theme: Tema por defecto
         thumbnail: Miniatura del servidor
         timeline_preview: Permitir el acceso no autenticado a las líneas de tiempo públicas
         trendable_by_default: Permitir tendencias sin revisión previa
         trends: Habilitar tendencias
+        trends_as_landing_page: Usar tendencias como la página de inicio
       interactions:
         must_be_follower: Bloquear notificaciones de personas que no te siguen
         must_be_following: Bloquear notificaciones de personas que no sigues
diff --git a/config/locales/simple_form.et.yml b/config/locales/simple_form.et.yml
index 025905aa1..8a999ce2f 100644
--- a/config/locales/simple_form.et.yml
+++ b/config/locales/simple_form.et.yml
@@ -18,8 +18,8 @@ et:
           disable: Keela kasutajal konto kasutamine, sisu kustutamata või varjamata.
           none: Hoiatuse saatmine kasutajale, ilma täiendavate tegevuste käivitamiseta.
           sensitive: Kogu kasutaja meediasisu märgitakse kui tundlik sisu.
-          silence: Takistab kasutajal avaliku nähtavusega postitamist, peidab tema postitused ja märguanded inimeste eest, kes teda ei jälgi.
-          suspend: Takistamaks suhtlust selle kontoga ja kustutamaks kogu sisu. Tagasivõetav 30 päeva jooksul.
+          silence: Välistab kasutaja võimaluse postitada avalikult, peita postitusi ning teavitusi mitte-jälgijate eest. Lõpetab kõik selle konto kohta esitatud kaebused.
+          suspend: Välistab igasuguse suhtluse selle kontoga või sellelt kontolt ning kustutab kogu konto sisu. Tagasipööratav 30 päeva jooksul. Lõpetab kõik selle konto vastu esitatud kaebused.
         warning_preset_id: Valikuline. Saad ikka lisada mis tahes teksti eelseadistuse lõppu
       announcement:
         all_day: Kui valitud, kuvatakse vaid selle ajavahemiku kuupäevi
@@ -30,7 +30,7 @@ et:
       appeal:
         text: Otsust on võimalik vaidlustada vaid 1 kord
       defaults:
-        autofollow: Inimesed, kes loovad konto selle kutse läbi, automaatselt jälgivad Teid
+        autofollow: Inimesed, kes loovad konto selle kutse läbi, automaatselt jälgivad sind
         avatar: PNG, GIF või JPG. Kõige rohkem %{size}. Vähendatakse %{dimensions} pikslini
         bot: Teavita teisi, et see konto teeb enamjaolt automatiseeritud tegevusi ja ei pruugi olla järelvalve all
         context: Üks või mitu konteksti, mille vastu see filter peaks rakenduma
@@ -54,7 +54,7 @@ et:
         setting_display_media_default: Peida tundlikuks märgitud meedia
         setting_display_media_hide_all: Alati peida kõik meedia
         setting_display_media_show_all: Alati näita tundlikuks märgistatud meedia
-        setting_hide_network: Profiilil ei kuvata Keda sa jälgid ja kes jälgib sind
+        setting_hide_network: Profiilil ei kuvata, keda sa jälgid ja kes sind jälgib
         setting_noindex: Mõjutab avalikku profiili ja postituste lehekülgi
         setting_show_application: Postitamiseks kasutatud rakenduse infot kuvatakse postituse üksikasjavaates
         setting_use_blurhash: Värvid põhinevad peidetud visuaalidel, kuid hägustavad igasuguseid detaile
@@ -74,6 +74,7 @@ et:
           hide: Filtreeritud sisu täielik peitmine, nagu seda polekski üldse olemas
           warn: Varja filtreeritud sisu hoiatusega, nimetades filtri pealkirja
       form_admin_settings:
+        activity_api_enabled: Kohalike postituste, aktiivsete kasutajate ja uute registreerumistr arv nädala kaupa grupeeritult
         backups_retention_period: Talleta genereeritud kasutajaarhiivid määratud arv päevi.
         bootstrap_timeline_accounts: Need kasutajad kinnitatakse uute kasutajate jälgimissoovituste esiritta.
         closed_registrations_message: Kuvatakse, kui liitumised pole võimalikud
@@ -81,6 +82,7 @@ et:
         custom_css: Kohandatud stiile on võimalik kasutada Mastodoni veebiliideses.
         mascot: Asendab kohandatud veebiliidese illustratsiooni.
         media_cache_retention_period: Positiivse väärtuse korral kustutatakse allalaetud meediafailid määratud päevade möödudes. Vajadusel laaditakse need uuesti alla.
+        peers_api_enabled: Domeeninimede loetelu, mida see server on Fediversumis kohanud. Mitte mingeid andmeid selle serveri födereerumise kohta antud serverite pole, vaid üksnes info, et sellest serverist ollakse teadlik. Seda kasutavad teenused, mis koguvad üldist födereerumise statistikat.
         profile_directory: Kasutajate kataloog kuvab nimekirja kasutajatest, kes on seda lubanud.
         require_invite_text: Kui liitumisi on tarvis kinnitada, oleks "Miks soovid liituda?" vastus vajalik
         site_contact_email: Kui peaks tekkima vajadus ühendust võtta täiendavate küsimuste osas.
@@ -89,11 +91,13 @@ et:
         site_short_description: Lühikirjeldus serveri unikaalseks identifitseerimiseks. Kes haldab, kellele mõeldud?
         site_terms: Lisa siia serveri isikuandmete kaitse põhimõtted või jäta tühjaks, et kasutada geneerilisi. Tekstis on lubatud Markdowni süntaks.
         site_title: Kuidas inimesed saavad serverile viidata, lisaks domeeninimele.
+        status_page_url: Lehe URL, kus saab serveri maas oleku ajal näha serveri olekut
         theme: Teema, mida näevad sisenemata ning uued kasutajad.
         thumbnail: Umbes 2:1 mõõdus pilt serveri informatsiooni kõrval.
         timeline_preview: Sisenemata külastajatel on võimalik sirvida viimaseid avalikke postitusi serveril.
         trendable_by_default: Populaarse sisu ülevaatuse vahele jätmine. Pärast seda on siiski võimalik üksikuid üksusi trendidest eemaldada.
         trends: Populaarsuse suunad näitavad millised postitused, sildid ja uudislood koguvad sinu serveris tähelepanu.
+        trends_as_landing_page: Näitab välja logitud kasutajatele ja külalistele serveri kirjelduse asemel populaarset sisu. Populaarne sisu (trendid) peab selleks olema sisse lülitatud.
       form_challenge:
         current_password: Turvalisse alasse sisenemine
       imports:
@@ -229,6 +233,7 @@ et:
           hide: Peida täielikult
           warn: Peida hoiatusega
       form_admin_settings:
+        activity_api_enabled: Avalda agregeeritud statistika kasutajaaktiivsuse kohta API-s
         backups_retention_period: Kasutajate arhiivi talletusperiood
         bootstrap_timeline_accounts: Alati soovita neid kontosid uutele kasutajatele
         closed_registrations_message: Kohandatud teade, kui liitumine pole võimalik
@@ -236,6 +241,7 @@ et:
         custom_css: Kohandatud CSS
         mascot: Kohandatud maskott (kunagine)
         media_cache_retention_period: Meediapuhvri talletusperiood
+        peers_api_enabled: Avalda avastatud serverite loetelu API kaudu
         profile_directory: Luba kasutajate kataloog
         registrations_mode: Kes saab liituda
         require_invite_text: Nõua liitumiseks põhjendust
@@ -247,11 +253,13 @@ et:
         site_short_description: Serveri lühikirjeldus
         site_terms: Isikuandmete kaitse
         site_title: Serveri nimi
+        status_page_url: Oleku lehe URL
         theme: Vaikmisi teema
         thumbnail: Serveri pisipilt
         timeline_preview: Luba autentimata ligipääs avalikele ajajoontele
         trendable_by_default: Luba trendid eelneva ülevaatuseta
         trends: Luba trendid
+        trends_as_landing_page: Kasuta maabumislehena lehte Populaarne
       interactions:
         must_be_follower: Keela teavitused mittejälgijatelt
         must_be_following: Keela teavitused kasutajatelt, keda sa ei jälgi
@@ -273,8 +281,8 @@ et:
         digest: Saada ülevaatlike e-kirju
         favourite: Saada e-kiri, kui keegi lisab su postituse lemmikuks
         follow: Saada e-kiri, kui keegi alustab jälgimist
-        follow_request: Saada e-kiri, kui keegi soovib Teid jälgida
-        mention: Saada e-kiri, kui keegi mainib Teid
+        follow_request: Saada e-kiri, kui keegi soovib sind jälgida
+        mention: Saada e-kiri, kui keegi mainib sind
         pending_account: Saada e-kiri, kui uus konto vajab ülevaatlust
         reblog: Keegi jagas postitust
         report: Esitatud on uus raport
diff --git a/config/locales/simple_form.eu.yml b/config/locales/simple_form.eu.yml
index c39fac3c0..a6ebedc3a 100644
--- a/config/locales/simple_form.eu.yml
+++ b/config/locales/simple_form.eu.yml
@@ -18,8 +18,8 @@ eu:
           disable: Erabiltzaileari bere kontua erabiltzea eragotzi, baina ez ezabatu edo ezkutatu bere edukiak.
           none: Erabili hau erabiltzaileari abisu bat bidaltzeko, beste ekintzarik abiarazi gabe.
           sensitive: Behartu erabiltzaile honen multimedia eranskin guztiak hunkigarri gisa markatzea.
-          silence: Eragotzi erabiltzaileak ikusgaitasun publikoarekin argitaratzea, ezkutatu bere bidalketa eta jakinarazpenak jarraitzen ez duten pertsonei.
-          suspend: Eragotzi kontu honek inolako interakziorik izatea eta ezabatu bere edukiak. Atzera bota daiteke 30 egun igaro aurretik.
+          silence: Eragotzi erabiltzaileak ikusgaitasun publikoarekin argitaratzea, ezkutatu bere bidalketa eta jakinarazpenak jarraitzen ez duten pertsonei. Kontu honen aurkako txosten guztiak ixten ditu.
+          suspend: Eragotzi kontu honek inolako interakziorik izatea eta ezabatu bere edukiak. Atzera bota daiteke 30 egun igaro aurretik. Kontu honen aurkako txosten guztiak ixten ditu.
         warning_preset_id: Aukerakoa. Zure testua gehitu dezakezu aurre-ezarpenaren ostean
       announcement:
         all_day: Markatutakoan soilik denbora barrutiko datak erakutsiko dira
@@ -74,6 +74,7 @@ eu:
           hide: Ezkutatu erabat iragazitako edukia, existituko ez balitz bezala
           warn: Ezkutatu iragazitako edukia iragazkiaren izenburua duen abisu batekin
       form_admin_settings:
+        activity_api_enabled: Lokalki argitaratutako bidalketak, erabiltzaile aktiboak, eta izen-emateen kopuruak astero zenbatzen ditu
         backups_retention_period: Mantendu sortutako erabiltzailearen artxiboa zehazturiko egun kopuruan.
         bootstrap_timeline_accounts: Kontu hauek erabiltzaile berrien jarraitzeko gomendioen goiko aldean ainguratuko dira.
         closed_registrations_message: Izen-ematea itxia dagoenean bistaratua
@@ -81,6 +82,7 @@ eu:
         custom_css: Estilo pertsonalizatuak aplikatu ditzakezu Mastodonen web bertsioan.
         mascot: Web interfaze aurreratuko ilustrazioa gainidazten du.
         media_cache_retention_period: Balio positibo bat ezarriz gero, egun kopuru horretara iristean beste zerbitzarietatik deskargatutako multimedia fitxategiak ezabatuko dira. Ondoren, eskatu ahala deskargatuko dira berriz.
+        peers_api_enabled: Zerbitzari honek fedibertsoan ikusi dituen zerbitzarien domeinu-izenen zerrenda. Ez da daturik ematen zerbitzari jakin batekin federatzearen ala ez federatzearen inguruan, zerbitzariak haien berri duela soilik. Federazioari buruzko estatistika orokorrak biltzen dituzten zerbitzuek erabiltzen dute hau.
         profile_directory: Profilen direktorioan ikusgai egotea aukeratu duten erabiltzaile guztiak zerrendatzen dira.
         require_invite_text: Izen emateak eskuz onartu behar direnean, "Zergatik elkartu nahi duzu?" testu sarrera derrigorrezko bezala ezarri, ez hautazko
         site_contact_email: Jendeak kontsulta legalak egin edo laguntza eskatzeko bidea.
@@ -89,11 +91,13 @@ eu:
         site_short_description: Zure zerbitzaria identifikatzen laguntzen duen deskribapen laburra. Nork du ardura? Nori zuzendua dago?
         site_terms: Erabili zure pribatutasun politika edo hutsik utzi lehenetsia erabiltzeko. Markdown sintaxiarekin egituratu daiteke.
         site_title: Jendeak nola deituko dion zure zerbitzariari, domeinu-izenaz gain.
+        status_page_url: Kanporatua dagoen jendeak zerbitzari honen egoera ikus dezaten gune baten URL
         theme: Saioa hasi gabeko erabiltzaileek eta berriek ikusiko duten gaia.
         thumbnail: Zerbitzariaren informazioaren ondoan erakusten den 2:1 inguruko irudia.
         timeline_preview: Saioa hasi gabeko erabiltzaileek ezingo dituzte arakatu zerbitzariko bidalketa publiko berrienak.
         trendable_by_default: Saltatu joeretako edukiaren eskuzko berrikuspena. Ondoren elementuak banan-bana kendu daitezke joeretatik.
         trends: Joeretan zure zerbitzarian bogan dauden bidalketa, traola eta albisteak erakusten dira.
+        trends_as_landing_page: Saioa hasita ez duten erabiltzaileei eta bisitariei pil-pilean dagoen edukia erakutsi zerbitzari honen deskribapena erakutsi ordez. Joerak aktibatuak edukitzea beharrezkoa da.
       form_challenge:
         current_password: Zonalde seguruan sartzen ari zara
       imports:
@@ -229,6 +233,7 @@ eu:
           hide: Ezkutatu guztiz
           warn: Ezkutatu ohar batekin
       form_admin_settings:
+        activity_api_enabled: Argitaratu erabiltzaile-jardueraren guztizko estatistikak APIan
         backups_retention_period: Erabiltzailearen artxiboa gordetzeko epea
         bootstrap_timeline_accounts: Gomendatu beti kontu hauek erabiltzaile berriei
         closed_registrations_message: Izen-emateak itxita daudenerako mezu pertsonalizatua
@@ -236,6 +241,7 @@ eu:
         custom_css: CSS pertsonalizatua
         mascot: Maskota pertsonalizatua (zaharkitua)
         media_cache_retention_period: Multimediaren cachea atxikitzeko epea
+        peers_api_enabled: Argitaratu aurkitutako zerbitzarien zerrenda APIan
         profile_directory: Gaitu profil-direktorioa
         registrations_mode: Nork eman dezake izena
         require_invite_text: Eskatu arrazoi bat batzeko
@@ -247,11 +253,13 @@ eu:
         site_short_description: Zerbitzariaren deskribapena
         site_terms: Pribatutasun politika
         site_title: Zerbitzariaren izena
+        status_page_url: Egoera-orriaren URL
         theme: Lehenetsitako gaia
         thumbnail: Zerbitzariaren koadro txikia
         timeline_preview: Onartu autentifikatu gabeko sarbidea denbora lerro publikoetara
         trendable_by_default: Onartu joerak aurrez berrikusi gabe
         trends: Gaitu joerak
+        trends_as_landing_page: Joerak erabili helburuko orri gisa
       interactions:
         must_be_follower: Blokeatu jarraitzaile ez direnen jakinarazpenak
         must_be_following: Blokeatu zuk jarraitzen ez dituzu horien jakinarazpenak
diff --git a/config/locales/simple_form.fa.yml b/config/locales/simple_form.fa.yml
index e2a95c118..20409ec6c 100644
--- a/config/locales/simple_form.fa.yml
+++ b/config/locales/simple_form.fa.yml
@@ -3,9 +3,9 @@ fa:
   simple_form:
     hints:
       account_alias:
-        acct: نشانی username@domain را برای حسابی که می‌خواهید از آن منتقل شوید بنویسید
+        acct: مشخّص کردن username@domain حسابی که می‌خواهید از آن منتقل شوید
       account_migration:
-        acct: نشانی username@domain را برای حسابی که می‌خواهید به آن منتقل شوید بنویسید
+        acct: مشخّص کردن username@domain حسابی که می‌خواهید به آن منتقل شوید
       account_warning_preset:
         text: می‌توانید مانند فرسته‌های معمولی کاربران دیگر را نام ببرید یا پیوند و برچسب بگذارید
         title: اختیاری. برای گیرنده قابل مشاهده نیست
@@ -18,8 +18,8 @@ fa:
           disable: از استفادهٔ کاربر از حسابش جلوگیری می‌کند، ولی محتوایش را حذف یا پنهان نمی‌کند.
           none: برای فرستادن هشداری به کاربر، بدون هیچ کنش دیگری استفاده کنید.
           sensitive: اجبار همهٔ پیوست‌های رسانه‌ای این کاربر برای نشانه‌گذاری به عنوان حساس.
-          silence: جلوگیری از توانایی کاربر برای فرستادن با نمایانی عمومی، نهفتن فرسته‌ها و آگاهی‌هایش از افرادی که دنبالش نمی‌کنند.
-          suspend: جلوگیری از هر برهم‌کنشی از یا به این حساب و حذف محتواهایش. قابل بازگشت در عرض ۳۰ روز.
+          silence: جلوگیری از توانایی کاربر برای فرستادن با نمایانی عمومی، نهفتن فرسته‌ها و آگاهی‌هایش از افرادی که دنبالش نمی‌کنند. تمامی گزارش‌ها در مورد این حساب را می‌بندد.
+          suspend: جلوگیری از هر برهم‌کنشی از یا به این حساب و حذف محتواهایش. قابل بازگشت در عرض ۳۰ روز. تمامی گزارش‌ها در مورد این حساب را می‌بندد.
         warning_preset_id: اختیاری. همچنان می‌توانید در پایان متن آماده چیزی بیفزایید
       announcement:
         all_day: هنگام گزینش، تنها تاریخ‌های بازهٔ زمانی نمایش داده خواهند شد
@@ -49,6 +49,7 @@ fa:
         phrase: مستقل از کوچکی و بزرگی حروف، با متن اصلی یا هشدار محتوای فرسته‌ها مقایسه می‌شود
         scopes: واسط‌های برنامه‌نویسی که این برنامه به آن دسترسی دارد. اگر بالاترین سطح دسترسی را انتخاب کنید، دیگر نیازی به انتخاب سطح‌های پایینی ندارید.
         setting_aggregate_reblogs: برای تقویت‌هایی که به تازگی برایتان نمایش داده شده‌اند، تقویت‌های بیشتر را نمایش نده (فقط روی تقویت‌های اخیر تأثیر می‌گذارد)
+        setting_always_send_emails: در حالت عادی آگاهی‌های رایانامه‌ای هنگامی که فعّالانه از ماستودون استفاده می‌کنید فرستاده نمی‌شوند
         setting_default_sensitive: تصاویر حساس به طور پیش‌فرض پنهان هستند و می‌توانند با یک کلیک آشکار شوند
         setting_display_media_default: تصویرهایی را که به عنوان حساس علامت زده شده‌اند پنهان کن
         setting_display_media_hide_all: همیشه همهٔ عکس‌ها و ویدیوها را پنهان کن
@@ -65,6 +66,15 @@ fa:
       email_domain_block:
         domain: این می‌تواند نام دامنه‌ای باشد که در نشانی رایانامه یا رکورد MX استفاده می‌شود. پس از ثبت نام بررسی خواهند شد.
         with_dns_records: تلاشی برای resolve کردن رکوردهای ساناد دامنهٔ داده‌شده انجام شده و نتیجه نیز مسدود خواهد شد
+      featured_tag:
+        name: 'این‌ها برخی از برچسب‌هایی هستند که به تازگی استفاده کرده‌اید:'
+      filters:
+        action: گزینش کنشی که هنگام تطابق فرسته‌ای با پالایه انجام شود
+        actions:
+          hide: نهفتن کامل محتوای پالوده، گویی وجود ندارد
+          warn: نهفتن محتوای پالوده پشت هشداری که به عنوان پالایه اشاره می‌کند
+      form_admin_settings:
+        backups_retention_period: نگه داشتن بایگانی‌های کاربری برای روزهای مشخّص شده.
       form_challenge:
         current_password: شما در حال ورود به یک منطقهٔ‌ حفاظت‌شده هستید
       imports:
@@ -174,6 +184,7 @@ fa:
         setting_use_pending_items: حالت آهسته
         severity: شدت
         sign_in_token_attempt: کد امنیتی
+        title: عنوان
         type: نوع درون‌ریزی
         username: نام کاربری (لاتین)
         username_or_email: نام کاربری یا ایمیل
@@ -182,6 +193,17 @@ fa:
         with_dns_records: شامل رکوردهای MX و‌IPهای دامنه
       featured_tag:
         name: برچسب
+      filters:
+        actions:
+          hide: نهفتن کامل
+          warn: نهفتن با هشدار
+      form_admin_settings:
+        backups_retention_period: دورهٔ نگه‌داری بایگانی کاربری
+        theme: زمینهٔ پیش‌گزیده
+        thumbnail: بندانگشتی کارساز
+        timeline_preview: اجازهٔ دسترسی بدون تأیید هویت به خط‌زمانی‌های عمومی
+        trendable_by_default: اجازهٔ پرطرفدار شدن بدون بازبینی پیشین
+        trends: به کار انداختن پرطرفدارها
       interactions:
         must_be_follower: انسداد آگاهی‌ها از ناپی‌گیران
         must_be_following: انسداد آگاهی‌ها از افرادی که پی نمی‌گیرید
@@ -195,6 +217,7 @@ fa:
         ip: آی‌پی
         severities:
           no_access: بن کردن دسترسی
+          sign_up_block: مسدود کردن ثبت‌نام‌ها
           sign_up_requires_approval: محدود کردن ثبت نام‌ها
         severity: قانون
       notification_emails:
@@ -215,6 +238,17 @@ fa:
         name: برچسب
         trendable: بگذارید که این برچسب در موضوعات پرطرفدار دیده شود
         usable: بگذارید که این برچسب در فرسته‌ها به کار بروند
+      user:
+        role: نقش
+      user_role:
+        color: رنگ نشان
+        highlighted: نمایش نقش به شکل نشان روی نمایهً کاربری
+        name: نام
+        permissions_as_keys: اجازه‌ها
+        position: اولویت
+      webhook:
+        events: رویدادهای به کار افتاده
+        url: نشانی نقطهٔ پایانی
     'no': خیر
     required:
       mark: "*"
diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml
index fc40b2855..434924fa8 100644
--- a/config/locales/simple_form.fi.yml
+++ b/config/locales/simple_form.fi.yml
@@ -8,7 +8,7 @@ fi:
         acct: Määrittele käyttäjän käyttäjänimi@verkkotunnus, johon haluat siirtyä
       account_warning_preset:
         text: Voit käyttää julkaisun syntaksia, kuten URL-osoitteita, aihetunnisteita ja mainintoja
-        title: Vapaaehtoinen. Ei näytetä vastaanottajalle
+        title: Valinnainen. Ei näy vastaanottajalle
       admin_account_action:
         include_statuses: Käyttäjä näkee mitkä viestit johtivat toimenpiteeseen tai varoitukseen
         send_email_notification: Käyttäjä saa selityksen mitä tapahtui hänen tililleen
@@ -18,8 +18,8 @@ fi:
           disable: Estä käyttäjää käyttämästä tiliään, mutta älä poista tai piilota sen sisältöä.
           none: Käytä tätä lähettääksesi varoituksen käyttäjälle käynnistämättä mitään muita toimintoja.
           sensitive: Pakota kaikki tämän käyttäjän mediatiedostot arkaluontoisiksi.
-          silence: Estä käyttäjää pystymästä julkaisemaan julkisessa näkyvyydessä, piilota käyttäjän viestit ja ilmoitukset ihmisiltä, jotka eivät seuraa käyttäjää.
-          suspend: Estä vuorovaikutus tililtä tai tilille ja poista sen sisältö. Palautettavissa 30 päivän kuluessa.
+          silence: Estä käyttäjää lähettämästä viestejä julkisesti, piilota hänen viestinsä ja ilmoituksensa ihmisiltä, jotka eivät seuraa häntä. Sulkee kaikki tämän tilin raportit.
+          suspend: Estä kaikki vuorovaikutus tältä -tai tälle tilille ja poista sen kaikki sisältö. Päätös voidaan peruuttaa 30 päivän aikana. Sulkee kaikki raportit tätä tiliä vasten.
         warning_preset_id: Valinnainen. Voit silti lisätä mukautetun tekstin esiasetuksen loppuun
       announcement:
         all_day: Kun valittu, vain valittu aikaväli näytetään
@@ -67,13 +67,14 @@ fi:
         domain: Tämä voi olla se verkkotunnus, joka näkyy sähköpostiosoitteessa tai MX tietueessa jota se käyttää. Ne tarkistetaan rekisteröitymisen yhteydessä.
         with_dns_records: Annetun verkkotunnuksen DNS-tietueet yritetään ratkaista ja tulokset myös estetään
       featured_tag:
-        name: 'Tässä muutamia aihetunnisteita, joita käytit viime aikoina:'
+        name: 'Tässä muutamia hiljattain käyttämiäsi aihetunnisteita:'
       filters:
         action: Valitse, mikä toiminto suoritetaan, kun viesti vastaa suodatinta
         actions:
           hide: Piilota suodatettu sisältö kokonaan ja käyttäydy ikään kuin sitä ei olisi olemassa
           warn: Piilota suodatettu sisältö varoituksen taakse, jossa mainitaan suodattimen otsikko
       form_admin_settings:
+        activity_api_enabled: Paikallisesti julkaistujen julkaisujen, aktiivisten käyttäjien ja rekisteröitymisten viikoittainen määrä
         backups_retention_period: Säilytä luodut arkistot määritetyn määrän päiviä.
         bootstrap_timeline_accounts: Nämä tilit kiinnitetään uusien käyttäjien suositusten yläpuolelle.
         closed_registrations_message: Näkyy, kun ilmoittautuminen on suljettu
@@ -81,6 +82,7 @@ fi:
         custom_css: Voit käyttää mukautettuja tyylejä Mastodonin verkkoversiossa.
         mascot: Ohittaa kuvituksen edistyneessä käyttöliittymässä.
         media_cache_retention_period: Ladatut mediatiedostot poistetaan määritetyn määrän päiviä jälkeen, kun arvo on positiivinen ja ladataan uudelleen pyynnöstä.
+        peers_api_enabled: Lista verkkotunnuksista, joita tämä palvelin on kohdannut fediversessä. Täällä ei ole tietoja siitä, oletko liitossa tiettyjen palvelinten kanssa, vaan ainoastaan luettelo niistä verkkotunnuksista, joista palvelimesi on ylipäätään tietoinen. Tätä tietoa käytetään palveluissa, jotka keräävät liittotilastoja laajassa merkityksessä.
         profile_directory: Profiilihakemisto lueteloi kaikki käyttäjät, jotka ovat ilmoittaneet olevansa löydettävissä.
         require_invite_text: Kun kirjautuminen vaatii manuaalisen hyväksynnän, tee ”Miksi haluat liittyä?” teksti syötetään pakolliseksi eikä vapaaehtoiseksi
         site_contact_email: Kuinka ihmiset voivat tavoittaa sinut oikeudellisissa tai tukikysymyksissä.
@@ -89,11 +91,13 @@ fi:
         site_short_description: Lyhyt kuvaus auttaa yksilöimään palvelimesi. Kuka sitä johtaa, kenelle se on tarkoitettu?
         site_terms: Käytä omaa tietosuojakäytäntöä tai jätä tyhjäksi, jos haluat käyttää oletusta. Voidaan jäsentää Markdown-syntaksilla.
         site_title: Kuinka ihmiset voivat viitata palvelimeen sen verkkotunnuksen lisäksi.
+        status_page_url: URL-osoite sivulle, jonka kautta tämän palvelimen tila voidaan ongelmatilanteissa tarkastaa
         theme: Teema, jonka uloskirjautuneet vierailijat ja uudet käyttäjät näkevät.
         thumbnail: Noin 2:1 kuva näytetään palvelimen tietojen rinnalla.
         timeline_preview: Uloskirjautuneet vierailijat voivat selata uusimpia julkisia viestejä, jotka ovat saatavilla palvelimella.
         trendable_by_default: Ohita suositun sisällön manuaalinen tarkistus. Yksittäisiä kohteita voidaan edelleen poistaa jälkikäteen.
         trends: Trendit osoittavat, mitkä julkaisut, aihetunnisteet ja uutiset ovat saamassa vetoa palvelimellasi.
+        trends_as_landing_page: Näytä vierailijoille ja uloskirjautuneille käyttäjille suosittu sisältö palvelininstanssin kuvaustekstin sijaan. Edellytyksenä on, että suosittu sisältö -ominaisuus on käytössä.
       form_challenge:
         current_password: Olet menossa suojatulle alueelle
       imports:
@@ -188,13 +192,13 @@ fi:
         password: Salasana
         phrase: Avainsana tai lause
         setting_advanced_layout: Ota käyttöön edistynyt selainkäyttöliittymä
-        setting_aggregate_reblogs: Ryhmitä boostaukset aikajanalla
+        setting_aggregate_reblogs: Ryhmitä tehostukset aikajanalla
         setting_always_send_emails: Lähetä aina sähköposti-ilmoituksia
         setting_auto_play_gif: Toista GIF-animaatiot automaattisesti
-        setting_boost_modal: Kysy vahvistusta ennen buustausta
+        setting_boost_modal: Kysy vahvistus ennen tehostusta
         setting_crop_images: Rajaa kuvat avaamattomissa tuuttauksissa 16:9 kuvasuhteeseen
         setting_default_language: Julkaisujen kieli
-        setting_default_privacy: Julkaisun näkyvyys
+        setting_default_privacy: Viestin näkyvyys
         setting_default_sensitive: Merkitse media aina arkaluontoiseksi
         setting_delete_modal: Kysy vahvistusta ennen viestin poistamista
         setting_disable_swiping: Poista pyyhkäisyt käytöstä
@@ -229,6 +233,7 @@ fi:
           hide: Piilota kokonaan
           warn: Piilota varoituksella
       form_admin_settings:
+        activity_api_enabled: Julkaise yhteenlasketut tilastot käyttäjätoiminnasta rajapinnassa
         backups_retention_period: Käyttäjän arkiston säilytysaika
         bootstrap_timeline_accounts: Suosittele aina näitä tilejä uusille käyttäjille
         closed_registrations_message: Mukautettu viesti, kun kirjautumisia ei ole saatavilla
@@ -236,6 +241,7 @@ fi:
         custom_css: Mukautettu CSS
         mascot: Mukautettu maskotti (legacy)
         media_cache_retention_period: Median välimuistin säilytysaika
+        peers_api_enabled: Julkaise löydettyjen instanssien luettelo ohjelmointirajapinnassa
         profile_directory: Ota profiilihakemisto käyttöön
         registrations_mode: Kuka voi rekisteröityä
         require_invite_text: Vaadi syy liittyä
@@ -247,11 +253,13 @@ fi:
         site_short_description: Palvelimen kuvaus
         site_terms: Tietosuojakäytäntö
         site_title: Palvelimen nimi
+        status_page_url: Tilasivun URL-osoite
         theme: Oletusteema
         thumbnail: Palvelimen pikkukuva
         timeline_preview: Salli todentamaton pääsy julkiselle aikajanalle
         trendable_by_default: Salli trendit ilman ennakkotarkastusta
         trends: Trendit käyttöön
+        trends_as_landing_page: Käytä suosittua sisältöä aloitussivuna
       interactions:
         must_be_follower: Estä ilmoitukset käyttäjiltä, jotka eivät seuraa sinua
         must_be_following: Estä ilmoitukset käyttäjiltä, joita et seuraa
@@ -276,7 +284,7 @@ fi:
         follow_request: Lähetä sähköposti, kun joku pyytää seurata sinua
         mention: Lähetä sähköposti, kun sinut mainitaan
         pending_account: Uusi tili tarvitsee tarkastusta
-        reblog: Lähetä sähköposti, kun joku buustaa julkaisusi
+        reblog: Lähetä sähköposti, kun joku tehosti viestiäsi
         report: Uusi raportti on lähetetty
         trending_tag: Uusi trendi vaatii tarkastelua
       rule:
diff --git a/config/locales/simple_form.fo.yml b/config/locales/simple_form.fo.yml
index 84a695aca..2713d1f89 100644
--- a/config/locales/simple_form.fo.yml
+++ b/config/locales/simple_form.fo.yml
@@ -18,8 +18,8 @@ fo:
           disable: Forða brúkaranum at brúka teirra kontu, men lat vera við at strika ella fjala teirra innihald.
           none: Brúka hetta at senda eina ávaring til brúkaran, uttan at íverkseta nakra aðra atgerð.
           sensitive: Flagga øll framtíðar miðlaviðfesti hjá brúkaranum sum viðkvom.
-          silence: Forða brúkaranum at posta alment, fjal teirra postar og fráboðanir frá fólki, sum ikki fylgja teimum.
-          suspend: Forða alt samskifti frá og til hesa kontuna og strika innihaldið hjá henni. Kann angrast innan 30 dagar.
+          silence: Forða brúkaranum at posta alment, fjal teirra postar og fráboðanir frá fólki, sum ikki fylgja teimum. Lukkar allar rapporteringar um hesa kontuna.
+          suspend: Forða alt samskifti frá og til hesa kontuna og strika innihaldið hjá henni. Kann angrast innan 30 dagar. Lukkar allar rapporteringar um hesa kontuna.
         warning_preset_id: Valfrítt. Tú kanst enn leggja serskildan tekst afturat undanstillingini
       announcement:
         all_day: Tá hetta er valt, verða einans dagfestingarnar í tíðarbilinum vístar
@@ -74,6 +74,7 @@ fo:
           hide: Fjal filtreraða innihaldið fullkomiliga, ber seg at sum at tað ikki fanst
           warn: Fjal filtreraða innihaldið aftan fyri eina ávaring, sum nevnir heitið á filtrinum
       form_admin_settings:
+        activity_api_enabled: Tal av lokalt útgivnum postum, virknum brúkarum og nýggjum skrásetingum býtt vikuliga
         backups_retention_period: Varðveit gjørd brúkarasøvn í ásetta talið av døgum.
         bootstrap_timeline_accounts: Hesar kontur verða festar ovast á listanum yvir brúkarar, sum tey nýggju verða mælt til at fylgja.
         closed_registrations_message: Víst tá stongt er fyri tilmeldingum
@@ -81,6 +82,7 @@ fo:
         custom_css: Tú kanst seta títt egna snið upp í net-útgávuni av Mastodon.
         mascot: Skúgvar til viks myndprýðingina í framkomna vev-markamótinum.
         media_cache_retention_period: Miðlafílur, sum eru tiknar niður, verða strikaðar eftir ásetta talið av døgum, tá hetta er sett til eitt positivt virði, og takast niður umaftur eftir ynski.
+        peers_api_enabled: Ein listi við navnaøkjum, sum hesin ambætarin er komin framat í fediversinum. Ongar dátur eru tiknar við her um tú er sameind/ur við ein givnan ambætara, einans at tín ambætari veit um hann. Hetta verður brúkt av tænastum, sum gera hagtøl um sameining yvirhøvur.
         profile_directory: Vangaskráin listar allar brúkarar, sum hava valt at kunna uppdagast.
         require_invite_text: Tá tilmeldingar krevja serskilda góðkenning, set so "Hví vil tú vera við?" tekstateigin til at vera kravdan heldur enn valfrían
         site_contact_email: Hvussu fólk kunnu røkka teg við løgfrøðisligum og brúks-fyrispurningum.
@@ -89,11 +91,13 @@ fo:
         site_short_description: Ein stutt lýsing at eyðmerkja ambætaran hjá tær. Hvør rekur hann og hvønn er hann til?
         site_terms: Brúka tín egna privatlívspolitikk ella lat vera blankt fyri at brúka tann sjálvsetta. Kann skrivast við Markdown syntaksi.
         site_title: Hvussu fólk kunnu vísa til ambætaran hjá tær útyvir at brúka navnaøkið.
+        status_page_url: Slóð til eina síðu, har ið fólk kunnu síggja støðuna á hesum ambætaranum í sambandi við streymslit
         theme: Uppsetingareyðkenni, sum vitjandi, ið ikki eru ritaði inn, og nýggir brúkarar síggja.
         thumbnail: Ein mynd í lutfallinum 2:1, sum verður víst saman við ambætaraupplýsingunum hjá tær.
         timeline_preview: Vitjandi, sum eru ritaði út, fara at kunna blaða ígjøgnum nýggjastu almennu postarnar, sum eru tøkir á ambætaranum.
         trendable_by_default: Loyp uppum serskilda eftirkannan av tilfari, sum er vælumtókt. Einstakir lutir kunnu framvegis strikast frá listum við vælumtóktum tilfari seinni.
         trends: Listar við vælumtóktum tilfari vísa, hvørjir postar, frámerki og tíðindasøgur hava framburð á tínum ambætara.
+        trends_as_landing_page: Vís vitjandi og brúkarum, sum ikki eru innritaðir, rák í staðin fyri eina lýsing av ambætaranum. Krevur at rák eru virkin.
       form_challenge:
         current_password: Tú ert á veg til eitt trygt øki
       imports:
@@ -229,6 +233,7 @@ fo:
           hide: Fjal fullkomiliga
           warn: Fjal við eini ávaring
       form_admin_settings:
+        activity_api_enabled: Útgev samantald hagtøl um brúkaravirksemi í API'num
         backups_retention_period: Hvussu leingi verða brúkarasøvn goymd
         bootstrap_timeline_accounts: Mæl altíð nýggjum brúkarum at fylgja hesar kontur
         closed_registrations_message: Serskild boð, tá tað ikki er møguligt at tilmelda seg
@@ -236,6 +241,7 @@ fo:
         custom_css: Serskilt CSS
         mascot: Serskildur maskottur (arvur)
         media_cache_retention_period: Tíðarskeið, har miðlagoymslur verða varðveittar
+        peers_api_enabled: Kunnger lista við uppdagaðum ambætarum í API'num
         profile_directory: Ger vangaskrá virkna
         registrations_mode: Hvør kann tilmelda seg
         require_invite_text: Krev eina orsøk at luttaka
@@ -247,11 +253,13 @@ fo:
         site_short_description: Ambætaralýsing
         site_terms: Privatlívspolitikkur
         site_title: Ambætaranavn
+        status_page_url: Slóð til støðusíðu
         theme: Sjálvvalt uppsetingareyðkenni
         thumbnail: Ambætarasmámynd
         timeline_preview: Loyv teimum, sum ikki eru ritaði inn, atgongd til almennar tíðarlinjur
         trendable_by_default: Loyv vælumtóktum tilfari uttan at viðgera tað fyrst
         trends: Loyv ráki
+        trends_as_landing_page: Brúka rák sum lendingarsíðu
       interactions:
         must_be_follower: Blokera fráboðanum frá teimum, sum ikki fylgja tær
         must_be_following: Blokera fráboðanum frá teimum, tú ikki fylgir
diff --git a/config/locales/simple_form.fr-QC.yml b/config/locales/simple_form.fr-QC.yml
index 11c050354..541bc8be6 100644
--- a/config/locales/simple_form.fr-QC.yml
+++ b/config/locales/simple_form.fr-QC.yml
@@ -18,8 +18,8 @@ fr-QC:
           disable: Empêcher l’utilisateur·rice d’utiliser son compte, mais ne pas supprimer ou masquer son contenu.
           none: Utilisez ceci pour envoyer un avertissement à l’utilisateur·rice, sans déclencher aucune autre action.
           sensitive: Forcer toutes les pièces jointes de cet·te utilisateur·rice à être signalées comme sensibles.
-          silence: Empêcher l’utilisateur·rice de poster avec une visibilité publique, cacher ses messages et ses notifications aux personnes qui ne les suivent pas.
-          suspend: Empêcher toute interaction depuis ou vers ce compte et supprimer son contenu. Réversible dans les 30 jours.
+          silence: Empêcher l'utilisateur⋅rice de publier des messages en visibilité publique et cacher tous ses messages et notifications aux comptes non abonnés. Cloture tous les signalements concernant ce compte.
+          suspend: Empêcher toute interaction depuis ou vers ce compte et supprimer son contenu. Réversible dans les 30 jours. Cloture tous les signalements concernant ce compte.
         warning_preset_id: Facultatif. Vous pouvez toujours ajouter un texte personnalisé à la fin de la présélection
       announcement:
         all_day: Coché, seules les dates de l’intervalle de temps seront affichées
@@ -74,6 +74,7 @@ fr-QC:
           hide: Cacher complètement le contenu filtré, faire comme s'il n'existait pas
           warn: Cacher le contenu filtré derrière un avertissement mentionnant le nom du filtre
       form_admin_settings:
+        activity_api_enabled: Nombre de messages publiés localement, de comptes actifs et de nouvelles inscriptions par tranche hebdomadaire
         backups_retention_period: Conserve les archives générées par l'utilisateur selon le nombre de jours spécifié.
         bootstrap_timeline_accounts: Ces comptes seront épinglés en tête de liste des recommandations pour les nouveaux utilisateurs.
         closed_registrations_message: Affiché lorsque les inscriptions sont fermées
@@ -81,6 +82,7 @@ fr-QC:
         custom_css: Vous pouvez appliquer des styles personnalisés sur la version Web de Mastodon.
         mascot: Remplace l'illustration dans l'interface Web avancée.
         media_cache_retention_period: Les fichiers multimédias téléchargés seront supprimés après le nombre de jours spécifiés lorsque la valeur est positive, et seront téléchargés à nouveau sur demande.
+        peers_api_enabled: Une liste de noms de domaine que ce serveur a rencontrés dans le fédiverse. Aucune donnée indiquant si vous vous fédérez ou non avec un serveur particulier n'est incluse ici, seulement l'information que votre serveur connaît un autre serveur. Cette option est utilisée par les services qui collectent des statistiques sur la fédération en général.
         profile_directory: L'annuaire des profils répertorie tous les utilisateurs qui ont opté pour être découverts.
         require_invite_text: Lorsque les inscriptions nécessitent une approbation manuelle, rendre le texte de l’invitation "Pourquoi voulez-vous vous inscrire ?" obligatoire plutôt que facultatif
         site_contact_email: Comment les personnes peuvent vous joindre pour des demandes de renseignements juridiques ou d'assistance.
@@ -89,11 +91,13 @@ fr-QC:
         site_short_description: Une courte description pour aider à identifier de manière unique votre serveur. Qui l'exécute, à qui il est destiné ?
         site_terms: Utilisez votre propre politique de confidentialité ou laissez vide pour utiliser la syntaxe par défaut. Peut être structurée avec la syntaxe Markdown.
         site_title: Comment les personnes peuvent se référer à votre serveur en plus de son nom de domaine.
+        status_page_url: URL d'une page où les gens peuvent voir l'état de ce serveur en cas de panne
         theme: Thème que verront les utilisateur·rice·s déconnecté·e·s ainsi que les nouveaux·elles utilisateur·rice·s.
         thumbnail: Une image d'environ 2:1 affichée à côté des informations de votre serveur.
         timeline_preview: Les visiteurs déconnectés pourront parcourir les derniers messages publics disponibles sur le serveur.
         trendable_by_default: Ignorer l'examen manuel du contenu tendance. Des éléments individuels peuvent toujours être supprimés des tendances après coup.
         trends: Les tendances montrent quelles publications, hashtags et actualités sont en train de gagner en traction sur votre serveur.
+        trends_as_landing_page: Afficher le contenu tendance au lieu d'une description de ce serveur pour les comptes déconnectés et les non-inscrit⋅e⋅s. Nécessite que les tendances soient activées.
       form_challenge:
         current_password: Vous entrez une zone sécurisée
       imports:
@@ -229,6 +233,7 @@ fr-QC:
           hide: Cacher complètement
           warn: Cacher derrière un avertissement
       form_admin_settings:
+        activity_api_enabled: Publie des statistiques agrégées sur l'activité des utilisateur⋅rice⋅s dans l'API
         backups_retention_period: Période d'archivage utilisateur
         bootstrap_timeline_accounts: Toujours recommander ces comptes aux nouveaux utilisateurs
         closed_registrations_message: Message personnalisé lorsque les inscriptions ne sont pas disponibles
@@ -236,6 +241,7 @@ fr-QC:
         custom_css: CSS personnalisé
         mascot: Mascotte personnalisée (héritée)
         media_cache_retention_period: Durée de rétention des médias dans le cache
+        peers_api_enabled: Publie la liste des serveurs découverts dans l'API
         profile_directory: Activer l’annuaire des profils
         registrations_mode: Qui peut s’inscrire
         require_invite_text: Exiger une raison pour s’inscrire
@@ -247,11 +253,13 @@ fr-QC:
         site_short_description: Description du serveur
         site_terms: Politique de confidentialité
         site_title: Nom du serveur
+        status_page_url: URL de la page de l'état du serveur
         theme: Thème par défaut
         thumbnail: Miniature du serveur
         timeline_preview: Autoriser l’accès non authentifié aux fils publics
         trendable_by_default: Autoriser les tendances sans révision préalable
         trends: Activer les tendances
+        trends_as_landing_page: Utiliser les tendances comme page d'accueil
       interactions:
         must_be_follower: Bloquer les notifications des personnes qui ne vous suivent pas
         must_be_following: Bloquer les notifications des personnes que vous ne suivez pas
diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml
index 53a2d708c..4a026084c 100644
--- a/config/locales/simple_form.fr.yml
+++ b/config/locales/simple_form.fr.yml
@@ -3,7 +3,7 @@ fr:
   simple_form:
     hints:
       account_alias:
-        acct: Spécifiez l’identifiant@domaine du compte que vous souhaitez faire migrer
+        acct: Spécifiez l’identifiant@domaine du compte à partir duquel vous souhaitez migrer
       account_migration:
         acct: Spécifiez l’identifiant@domaine du compte vers lequel vous souhaitez migrer
       account_warning_preset:
@@ -18,8 +18,8 @@ fr:
           disable: Empêcher l’utilisateur·rice d’utiliser son compte, mais ne pas supprimer ou masquer son contenu.
           none: Utilisez ceci pour envoyer un avertissement à l’utilisateur·rice, sans déclencher aucune autre action.
           sensitive: Forcer toutes les pièces jointes de cet·te utilisateur·rice à être signalées comme sensibles.
-          silence: Empêcher l’utilisateur·rice de poster avec une visibilité publique, cacher ses messages et ses notifications aux personnes qui ne les suivent pas.
-          suspend: Empêcher toute interaction depuis ou vers ce compte et supprimer son contenu. Réversible dans les 30 jours.
+          silence: Empêcher l'utilisateur⋅rice de publier des messages en visibilité publique et cacher tous ses messages et notifications aux comptes non abonnés. Cloture tous les signalements concernant ce compte.
+          suspend: Empêcher toute interaction depuis ou vers ce compte et supprimer son contenu. Réversible dans les 30 jours. Cloture tous les signalements concernant ce compte.
         warning_preset_id: Facultatif. Vous pouvez toujours ajouter un texte personnalisé à la fin de la présélection
       announcement:
         all_day: Coché, seules les dates de l’intervalle de temps seront affichées
@@ -35,13 +35,13 @@ fr:
         bot: Signale aux autres que ce compte exécute principalement des actions automatisées et pourrait ne pas être surveillé
         context: Un ou plusieurs contextes où le filtre devrait s’appliquer
         current_password: Par mesure de sécurité, veuillez saisir le mot de passe de ce compte
-        current_username: Pour confirmer, veuillez saisir le nom d'utilisateur de ce compte
+        current_username: Pour confirmer, veuillez saisir l’identifiant de ce compte
         digest: Uniquement envoyé après une longue période d’inactivité en cas de messages personnels reçus pendant votre absence
         discoverable: Permet à votre compte d’être découvert par des inconnus par le biais de recommandations, de tendances et autres fonctionnalités
         email: Vous recevrez un courriel de confirmation
         fields: Vous pouvez avoir jusqu’à 4 éléments affichés en tant que tableau sur votre profil
         header: Au format PNG, GIF ou JPG. %{size} maximum. Sera réduit à %{dimensions}px
-        inbox_url: Copiez l’URL depuis la page d’accueil du relai que vous souhaitez utiliser
+        inbox_url: Copiez l’URL depuis la page d’accueil du relais que vous souhaitez utiliser
         irreversible: Les messages filtrés disparaîtront irrévocablement, même si le filtre est supprimé plus tard
         locale: La langue de l’interface, des courriels et des notifications
         locked: Nécessite que vous approuviez manuellement chaque abonné·e
@@ -59,7 +59,7 @@ fr:
         setting_show_application: Le nom de l’application que vous utilisez pour publier sera affichée dans la vue détaillée de vos messages
         setting_use_blurhash: Les dégradés sont basés sur les couleurs des images cachées mais n’en montrent pas les détails
         setting_use_pending_items: Cacher les mises à jour des fils d’actualités derrière un clic, au lieu de les afficher automatiquement
-        username: Votre nom d’utilisateur sera unique sur %{domain}
+        username: Votre identifiant sera unique sur %{domain}
         whole_word: Si le mot-clé ou la phrase est alphanumérique, alors le filtre ne sera appliqué que s’il correspond au mot entier
       domain_allow:
         domain: Ce domaine pourra récupérer des données de ce serveur et les données entrantes seront traitées et stockées
@@ -74,26 +74,30 @@ fr:
           hide: Cacher complètement le contenu filtré, faire comme s'il n'existait pas
           warn: Cacher le contenu filtré derrière un avertissement mentionnant le nom du filtre
       form_admin_settings:
+        activity_api_enabled: Nombre de messages publiés localement, de comptes actifs et de nouvelles inscriptions par tranche hebdomadaire
         backups_retention_period: Conserve les archives générées par l'utilisateur selon le nombre de jours spécifié.
         bootstrap_timeline_accounts: Ces comptes seront épinglés en tête de liste des recommandations pour les nouveaux utilisateurs.
         closed_registrations_message: Affiché lorsque les inscriptions sont fermées
-        content_cache_retention_period: Les publications depuis d'autres serveurs seront supprimées après un nombre de jours spécifiés lorsque défini sur une valeur positive. Cela peut être irréversible.
+        content_cache_retention_period: Lorsque la valeur est positive, les messages publiés depuis d'autres serveurs seront supprimés après le nombre de jours défini. Cela peut être irréversible.
         custom_css: Vous pouvez appliquer des styles personnalisés sur la version Web de Mastodon.
         mascot: Remplace l'illustration dans l'interface Web avancée.
-        media_cache_retention_period: Les fichiers multimédias téléchargés seront supprimés après le nombre de jours spécifiés lorsque la valeur est positive, et seront téléchargés à nouveau sur demande.
-        profile_directory: L'annuaire des profils répertorie tous les utilisateurs qui ont opté pour être découverts.
+        media_cache_retention_period: Lorsque la valeur est positive, les fichiers multimédias téléchargés seront supprimés après le nombre de jours défini et pourront être à nouveau téléchargés sur demande.
+        peers_api_enabled: Une liste de noms de domaine que ce serveur a rencontrés dans le fédiverse. Aucune donnée indiquant si vous vous fédérez ou non avec un serveur particulier n'est incluse ici, seulement l'information que votre serveur connaît un autre serveur. Cette option est utilisée par les services qui collectent des statistiques sur la fédération en général.
+        profile_directory: L'annuaire des profils répertorie tous les comptes qui choisi d'être découvrables.
         require_invite_text: Lorsque les inscriptions nécessitent une approbation manuelle, rendre le texte de l’invitation "Pourquoi voulez-vous vous inscrire ?" obligatoire plutôt que facultatif
-        site_contact_email: Comment les personnes peuvent vous joindre pour des demandes de renseignements juridiques ou d'assistance.
+        site_contact_email: Comment l'on peut vous joindre pour des requêtes d'assistance ou d'ordre juridique.
         site_contact_username: Comment les gens peuvent vous contacter sur Mastodon.
-        site_extended_description: Toute information supplémentaire qui peut être utile aux visiteurs et à vos utilisateurs. Peut être structurée avec la syntaxe Markdown.
-        site_short_description: Une courte description pour aider à identifier de manière unique votre serveur. Qui l'exécute, à qui il est destiné ?
-        site_terms: Utilisez votre propre politique de confidentialité ou laissez vide pour utiliser la syntaxe par défaut. Peut être structurée avec la syntaxe Markdown.
-        site_title: Comment les personnes peuvent se référer à votre serveur en plus de son nom de domaine.
+        site_extended_description: Toute information supplémentaire qui peut être utile aux non-inscrit⋅e⋅s et à vos utilisateur⋅rice⋅s. Peut être structurée avec la syntaxe Markdown.
+        site_short_description: Une description brève pour aider à faire connaître votre serveur de manière unique. Qui s'en occupe ? à qui est-il destiné ?
+        site_terms: Utilisez votre propre politique de confidentialité ou laissez vide pour utiliser les conditions définies par défaut. Peut être structurée avec la syntaxe Markdown.
+        site_title: Comment l'on peut faire référence à votre serveur, autrement que par le nom de domaine.
+        status_page_url: URL d'une page où les gens peuvent voir l'état de ce serveur en cas de panne
         theme: Thème que verront les utilisateur·rice·s déconnecté·e·s ainsi que les nouveaux·elles utilisateur·rice·s.
         thumbnail: Une image d'environ 2:1 affichée à côté des informations de votre serveur.
-        timeline_preview: Les visiteurs déconnectés pourront parcourir les derniers messages publics disponibles sur le serveur.
+        timeline_preview: Les utilisateur⋅rice⋅s déconnecté⋅e⋅s pourront parcourir les derniers messages publics disponibles sur le serveur.
         trendable_by_default: Ignorer l'examen manuel du contenu tendance. Des éléments individuels peuvent toujours être supprimés des tendances après coup.
-        trends: Les tendances montrent quelles publications, hashtags et actualités sont en train de gagner en traction sur votre serveur.
+        trends: Les tendances montrent quelles publications, hashtags et actualités gagnent en ampleur sur votre serveur.
+        trends_as_landing_page: Afficher le contenu tendance au lieu d'une description de ce serveur pour les comptes déconnectés et les non-inscrit⋅e⋅s. Nécessite que les tendances soient activées.
       form_challenge:
         current_password: Vous entrez une zone sécurisée
       imports:
@@ -172,7 +176,7 @@ fr:
         data: Données
         discoverable: Suggérer ce compte aux autres
         display_name: Nom public
-        email: Adresse courriel
+        email: Adresse de courriel
         expires_in: Expire après
         fields: Métadonnées du profil
         header: Image d’en-tête
@@ -210,7 +214,7 @@ fr:
         setting_system_font_ui: Utiliser la police par défaut du système
         setting_theme: Thème du site
         setting_trends: Afficher les tendances du jour
-        setting_unfollow_modal: Afficher une fenêtre de confirmation avant de vous désabonner d’un compte
+        setting_unfollow_modal: Demander confirmation avant de vous désabonner d’un compte
         setting_use_blurhash: Afficher des dégradés colorés pour les médias cachés
         setting_use_pending_items: Mode lent
         severity: Sévérité
@@ -218,7 +222,7 @@ fr:
         title: Nom
         type: Type d’import
         username: Identifiant
-        username_or_email: Nom d’utilisateur·rice ou courriel
+        username_or_email: Identifiant ou adresse courriel
         whole_word: Mot entier
       email_domain_block:
         with_dns_records: Inclure les enregistrements MX et IP du domaine
@@ -229,29 +233,33 @@ fr:
           hide: Cacher complètement
           warn: Cacher derrière un avertissement
       form_admin_settings:
-        backups_retention_period: Période d'archivage utilisateur
-        bootstrap_timeline_accounts: Toujours recommander ces comptes aux nouveaux utilisateurs
+        activity_api_enabled: Publie des statistiques agrégées sur l'activité des utilisateur⋅rice⋅s dans l'API
+        backups_retention_period: Durée de rétention des archives utilisateur
+        bootstrap_timeline_accounts: Toujours recommander ces comptes aux nouveaux⋅elles utilisateur⋅rice⋅s
         closed_registrations_message: Message personnalisé lorsque les inscriptions ne sont pas disponibles
         content_cache_retention_period: Durée de rétention du contenu dans le cache
         custom_css: CSS personnalisé
         mascot: Mascotte personnalisée (héritée)
         media_cache_retention_period: Durée de rétention des médias dans le cache
+        peers_api_enabled: Publie la liste des serveurs découverts dans l'API
         profile_directory: Activer l’annuaire des profils
         registrations_mode: Qui peut s’inscrire
         require_invite_text: Exiger une raison pour s’inscrire
         show_domain_blocks: Afficher les blocages de domaines
         show_domain_blocks_rationale: Montrer pourquoi les domaines ont été bloqués
-        site_contact_email: E-mail de contact
-        site_contact_username: Nom d'utilisateur du contact
+        site_contact_email: Adresse de courriel de contact
+        site_contact_username: Identifiant du contact
         site_extended_description: Description étendue
         site_short_description: Description du serveur
         site_terms: Politique de confidentialité
         site_title: Nom du serveur
+        status_page_url: URL de la page de l'état du serveur
         theme: Thème par défaut
         thumbnail: Miniature du serveur
         timeline_preview: Autoriser l’accès non authentifié aux fils publics
         trendable_by_default: Autoriser les tendances sans révision préalable
         trends: Activer les tendances
+        trends_as_landing_page: Utiliser les tendances comme page d'accueil
       interactions:
         must_be_follower: Bloquer les notifications des personnes qui ne vous suivent pas
         must_be_following: Bloquer les notifications des personnes que vous ne suivez pas
@@ -271,12 +279,12 @@ fr:
       notification_emails:
         appeal: Une personne fait appel d'une décision des modérateur·rice·s
         digest: Envoyer des courriels récapitulatifs
-        favourite: Quelqu’un a ajouté mon message à ses favoris
-        follow: Quelqu’un vient de me suivre
-        follow_request: Quelqu’un demande à me suivre
-        mention: Quelqu’un me mentionne
+        favourite: Quelqu’un a ajouté votre message à ses favoris
+        follow: Quelqu’un vient de vous suivre
+        follow_request: Quelqu’un demande à vous suivre
+        mention: Quelqu’un vous a mentionné⋅e
         pending_account: Nouveau compte en attente d’approbation
-        reblog: Quelqu’un a partagé mon message
+        reblog: Quelqu’un a partagé votre message
         report: Nouveau signalement soumis
         trending_tag: Nouvelle tendance nécessitant supervision
       rule:
diff --git a/config/locales/simple_form.fy.yml b/config/locales/simple_form.fy.yml
index d45809a3a..be950189b 100644
--- a/config/locales/simple_form.fy.yml
+++ b/config/locales/simple_form.fy.yml
@@ -18,8 +18,8 @@ fy:
           disable: Foarkom dat de brûker harren account brûkt, mar wiskje of ferstopje de ynhâld net.
           none: Brûk dit om in warskôging nei de brûker te stjoeren, sûnder dat noch in oare maatregel nommen wurdt.
           sensitive: Forsearje dat alle mediabylagen fan dizze brûker as gefoelich markearre wurde.
-          silence: Foarkom dat de brûker iepenbiere berjochten ferstjoere kin, ferstopje harren berjochten en meldingen foar minsken dy’t dyjinge net folgje.
-          suspend: Alle ynteraksjes fan en mei dizze account opkeare, en de accountgegevens fuortsmite. Dit kin binnen 30 dagen weromdraaid wurde.
+          silence: Foarkom dat de brûker iepenbiere berjochten ferstjoere kin, ferstopje harren berjochten en meldingen foar minsken dy’t dyjinge net folgje. Dit slút alle rapportaazjes oer dizze account.
+          suspend: Alle ynteraksjes fan en mei dizze account opkeare, en de accountgegevens fuortsmite. Dit kin binnen 30 dagen weromdraaid wurde. Dit slút alle rapportaazjes oer dizze account.
         warning_preset_id: Opsjoneel. Jo kinne noch hieltyd hânmjittich tekst tafoegje oan it ein fan de foarynstelling
       announcement:
         all_day: Wannear dit oanfinkt is, wurde allinnich de datums binnen it tiidrek toand
@@ -74,6 +74,7 @@ fy:
           hide: Ferstopje de filtere ynhâld folslein, as oft it net bestiet
           warn: Ferstopje de filtere ynhâld efter in warskôging, mei de titel fan it filter as warskôgingstekst
       form_admin_settings:
+        activity_api_enabled: Tal lokaal publisearre artikelen, aktive brûkers en nije registraasjes yn wyklikse werjefte
         backups_retention_period: De oanmakke brûkersargiven foar it opjûne oantal dagen behâlde.
         bootstrap_timeline_accounts: Dizze accounts wurde boppe oan de oanrekommandaasjes oan nije brûkers toand. Meardere brûkersnammen troch komma’s skiede.
         closed_registrations_message: Werjûn wannear’t registraasje fan nije accounts útskeakele is
@@ -81,6 +82,7 @@ fy:
         custom_css: Jo kinne oanpaste CSS tapasse op de webferzje fan dizze Mastodon-server.
         mascot: Oerskriuwt de yllustraasje yn de avansearre webomjouwing.
         media_cache_retention_period: Mediabestannen dy’t fan oare servers download binne wurde nei it opjûne oantal dagen fuortsmiten en wurde op fersyk opnij download.
+        peers_api_enabled: In list mei domeinnammen, dêr’t dizze server yn fediverse kontakt hân mei hat. Hjir wurdt gjin data dield, oft jo mei in bepaalde server federearrest, mar alinnich, dat jo server dat wit. Dit wurdt foar tsjinsten brûkt, dy’t statistiken oer federaasje yn algemiene sin sammelet.
         profile_directory: De brûkersgids befettet in list fan alle brûkers dy¥t derfoar keazen hawwe om ûntdekt wurde te kinnen.
         require_invite_text: Meitsje it ynfoljen fan "Wêrom wolle jo jo hjir registrearje?" ferplicht yn stee fan opsjoneel, wannear’t registraasjes hânmjittich goedkard wurde moatte
         site_contact_email: Hoe minsken jo berikke kinne foar juridyske fragen of stipe.
@@ -89,11 +91,13 @@ fy:
         site_short_description: In koarte beskriuwing om it unike karakter fan jo server te toanen. Wa beheart de server, wat is de doelgroep?
         site_terms: Brûk jo eigen privacybeleid of lit leech om de standertwearde te brûken. Kin mei Markdown opmakke wurde.
         site_title: Hoe minsken bûten de domeinnamme nei jo server ferwize kinne.
+        status_page_url: URL fan in side dêr’t minsken de steat fan dizze server sjen kinne wylst in steuring
         theme: Tema dy’t ôfmelde besikers en nije brûkers sjen.
         thumbnail: In ôfbylding fan ûngefear in ferhâlding fan 2:1 dy’t njonken jo serverynformaasje toand wurdt.
         timeline_preview: Net oanmelde besikers kinne de meast resinte, op de server oanwêzige iepenbiere berjochten besjen.
         trendable_by_default: Hânmjittige beoardieling fan trends oerslaan. Yndividuele items kinne letter dochs noch ôfkard wurde.
         trends: Trends toane hokker berjochten, hashtags en nijsberjochten op jo server oan populariteit winne.
+        trends_as_landing_page: Toan trending ynhâld oan ôfmelde brûkers en besikers yn stee fan in beskriuwing fan dizze server. Fereasket dat trends ynskeakele binne.
       form_challenge:
         current_password: Jo betrêdzje in feilige omjouwing
       imports:
@@ -229,6 +233,7 @@ fy:
           hide: Folslein ferstopje
           warn: Mei in warskôging ferstopje
       form_admin_settings:
+        activity_api_enabled: Publyklik meitsjen fan sammele statistiken oer brûkersaktiviteiten yn de API
         backups_retention_period: Bewartermyn brûkersargyf
         bootstrap_timeline_accounts: Accounts dy’t altyd oan nije brûkers oanrekommandearre wurde
         closed_registrations_message: Oanpast berjocht wannear registraasje útskeakele is
@@ -236,6 +241,7 @@ fy:
         custom_css: Oanpaste CSS
         mascot: Oanpaste maskotte (legacy)
         media_cache_retention_period: Bewartermyn mediabuffer
+        peers_api_enabled: Publyklik meitsjen fan ûntdekte servers yn de API
         profile_directory: Brûkersgids ynskeakelje
         registrations_mode: Wa kin harren registrearje
         require_invite_text: Opjaan fan in reden is ferplicht
@@ -247,11 +253,13 @@ fy:
         site_short_description: Serveromskriuwing
         site_terms: Privacybelied
         site_title: Servernamme
+        status_page_url: URL fan steatside
         theme: Standerttema
         thumbnail: Serverthumbnail
         timeline_preview: Tagong ta de iepenbiere tiidlinen sûnder oan te melden tastean
         trendable_by_default: Trends goedkarre sûnder yn it foar geande beoardieling
         trends: Trends ynskeakelje
+        trends_as_landing_page: Lit trends op de startside sjen
       interactions:
         must_be_follower: Meldingen fan minsken dy’t jo net folgje blokkearje
         must_be_following: Meldingen fan minsken dy’t jo net folgje blokkearje
diff --git a/config/locales/simple_form.ga.yml b/config/locales/simple_form.ga.yml
index 997668a34..39190ad9b 100644
--- a/config/locales/simple_form.ga.yml
+++ b/config/locales/simple_form.ga.yml
@@ -13,12 +13,18 @@ ga:
         setting_display_media_hide_all: Folaítear meáin i gcónaí
         setting_display_media_show_all: Go dtaispeántar meáin i gcónaí
     labels:
+      account:
+        fields:
+          name: Lipéad
       account_warning_preset:
         title: Teideal
       admin_account_action:
         text: Rabhadh saincheaptha
         types:
+          disable: Reoigh
           none: Seol rabhadh
+          silence: Teorannaigh
+          suspend: Cuir ar fionraí
       announcement:
         text: Fógra
       defaults:
@@ -27,6 +33,7 @@ ga:
         display_name: Ainm taispeána
         email: Seoladh ríomhphoist
         header: Ceanntásc
+        new_password: Pasfhocal nua
         note: Beathaisnéis
         password: Pasfhocal
         setting_display_media_default: Réamhshocrú
@@ -47,14 +54,23 @@ ga:
         site_short_description: Cur síos freastalaí
         site_terms: Polasaí príobháideachais
         site_title: Ainm freastalaí
+      invite:
+        comment: Ráiteas
       ip_block:
+        comment: Ráiteas
         ip: IP
+        severity: Riail
       notification_emails:
         follow: Lean duine éigin tú
         reblog: Mhol duine éigin do phostáil
+      rule:
+        text: Riail
       tag:
         name: Haischlib
+      user:
+        role: Ról
       user_role:
         name: Ainm
+    recommended: Molta
     required:
       mark: "*"
diff --git a/config/locales/simple_form.gd.yml b/config/locales/simple_form.gd.yml
index 090fbc552..c86fc5a0c 100644
--- a/config/locales/simple_form.gd.yml
+++ b/config/locales/simple_form.gd.yml
@@ -18,8 +18,8 @@ gd:
           disable: Bac an cleachdaiche o chleachdadh a’ chunntais aca ach na sguab às no falaich an t-susbaint aca.
           none: Cleachd seo airson rabhadh a chur dhan chleachdaiche gun ghnìomh eile a ghabhail.
           sensitive: Èignich comharra gu bheil e frionasach air a h-uile ceanglachan meadhain a’ chleachdaiche seo.
-          silence: Bac an cleachdaiche o bhith a’ postadh le faicsinneachd poblach, falaich na postaichean aca agus brathan o na daoine nach eil ’ga leantainn.
-          suspend: Bac conaltradh sam bith leis a’ chunntas seo agus sguab às an t-susbaint aige. Gabhaidh seo a neo-dhèanamh am broinn 30 latha.
+          silence: Bac an cleachdaiche o bhith a’ postadh le faicsinneachd poblach, falaich na postaichean aca agus brathan o na daoine nach eil ’ga leantainn. Dùinidh seo gach gearan mun chunntas seo.
+          suspend: Bac eadar-ghnìomh sam bith leis a’ chunntas seo agus sguab às an t-susbaint aige. Gabhaidh seo a neo-dhèanamh am broinn 30 latha. Dùinidh seo gach gearan mun chunntas seo.
         warning_preset_id: Roghainneil. ’S urrainn dhut teacsa gnàthaichte a chur ri deireadh an ro-sheata fhathast
       announcement:
         all_day: Nuair a bhios cromag ris, cha nochd ach cinn-latha na rainse-ama
@@ -74,6 +74,7 @@ gd:
           hide: Falaich an t-susbaint chriathraichte uile gu lèir mar nach robh i ann idir
           warn: Falaich an t-susbaint chriathraichte air cùlaibh rabhaidh a dh’innseas tiotal na criathraige
       form_admin_settings:
+        activity_api_enabled: Cunntasan nam postaichean a chaidh fhoillseachadh gu h-ionadail, nan cleachdaichean gnìomhach ’s nan clàraidhean ùra an am bucaidean seachdaineil
         backups_retention_period: Cùm na tasg-lannan a chaidh a ghintinn dhan luchd-cleachdaidh rè an àireamh de làithean a shònraich thu.
         bootstrap_timeline_accounts: Thèid na cunntasan seo a phrìneachadh air bàrr nam molaidhean leantainn dhan luchd-cleachdaidh ùr.
         closed_registrations_message: Thèid seo a shealltainn nuair a bhios an clàradh dùinte
@@ -81,6 +82,7 @@ gd:
         custom_css: "’S urrainn dhut stoidhlean gnàthaichte a chur an sàs air an tionndadh-lìn de Mhastodon."
         mascot: Tar-àithnidh seo an sgead-dhealbh san eadar-aghaidh-lìn adhartach.
         media_cache_retention_period: Thèid na faidhlichean meadhain air an luchdadh a-nuas a sguabadh às às dèidh an àireamh de làithean a shònraich thu nuair a bhios luach dearbh air agus an ath-luachdadh nuair a thèid an iarraidh an uairsin.
+        peers_api_enabled: Seo liosta de dh’ainmean àrainne ris an do thachair am frithealaiche seo sa cho-shaoghal. Chan eil dàta sam bith ’ga ghabhail a-staigh an-seo mu a bheil thu co-naisgte ri frithealaiche sònraichte gus nach eil ach dìreach gu bheil am frithealaiche agad eòlach air. Thèid seo a chleachdadh le seirbheisean a chruinnicheas stadastaireachd air a’ cho-nasgadh san fharsaingeachd.
         profile_directory: Seallaidh eòlaire nam pròifil liosta dhen luchd-cleachdaidh a dh’aontaich gun gabh an rùrachadh.
         require_invite_text: Nuair a bhios aontachadh a làimh riatanach dhan chlàradh, dèan an raon teacsa “Carson a bu mhiann leat ballrachd fhaighinn?” riatanach seach roghainneil
         site_contact_email: Mar a ruigear thu le ceistean laghail no taice.
@@ -89,11 +91,13 @@ gd:
         site_short_description: Tuairisgeul goirid a chuidicheas le aithneachadh sònraichte an fhrithealaiche agad. Cò leis is cò dha a tha e?
         site_terms: Cleachd am poileasaidh prìobhaideachd agad fhèin no fàg bàn e gus am fear bunaiteach a chleachdadh. ’S urrainn dhut structar a chur air le co-chàradh Markdown.
         site_title: An t-ainm a tha air an fhrithealaiche agad seach ainm àrainne.
+        status_page_url: URL duilleige far am faicear staid an fhrithealaiche seo nuair a bhios e sìos
         theme: An t-ùrlar a chì na h-aoighean gun chlàradh a-staigh agus an luchd-cleachdaidh ùr.
         thumbnail: Dealbh mu 2:1 a thèid a shealltainn ri taobh fiosrachadh an fhrithealaiche agad.
         timeline_preview: "’S urrainn dha na h-aoighean gun chlàradh a-staigh na postaichean poblach as ùire a tha ri fhaighinn air an fhrithealaiche a bhrabhsadh."
         trendable_by_default: Geàrr leum thar lèirmheas a làimh na susbainte a’ treandadh. Gabhaidh nithean fa leth a thoirt far nan treandaichean fhathast an uairsin.
         trends: Seallaidh na treandaichean na postaichean, tagaichean hais is naidheachdan a tha fèill mhòr orra air an fhrithealaiche agad.
+        trends_as_landing_page: Seall susbaint a’ treandadh dhan fheadhainn nach do chlàraich a-steach is do dh’aoighean seach tuairisgeul an fhrithealaiche seo. Feumaidh treandaichean a bhith an comas airson sin.
       form_challenge:
         current_password: Tha thu a’ tighinn a-steach gu raon tèarainte
       imports:
@@ -103,7 +107,7 @@ gd:
       ip_block:
         comment: Roghainneil. Cùm an cuimhne carson an do chuir thu an riaghailt seo ris.
         expires_in: Tha an uiread de sheòlaidhean IP cuingichte is thèid an co-roinneadh aig amannan agus an gluasad do chuideigin eile gu tric. Air an adhbhar seo, cha mholamaid bacadh IP gun chrìoch.
-        ip: Cuir a-steach seòladh IPv4 no IPv6. ’S urrainn dhut rainsean gu lèir a bhacadh le co-chàradh CIDR. Thoir an aire nach gluais thu thu fhèin a-mach!
+        ip: Cuir a-steach seòladh IPv4 no IPv6. ’S urrainn dhut rainsean gu lèir a bhacadh le co-chàradh CIDR. Thoir an aire nach glais thu thu fhèin a-mach!
         severities:
           no_access: Bac inntrigeadh dha na goireasan uile
           sign_up_block: Cha bhi ùr-chlàradh ceadaichte
@@ -229,6 +233,7 @@ gd:
           hide: Falaich uile gu lèir
           warn: Falaich le rabhadh
       form_admin_settings:
+        activity_api_enabled: Foillsich agragaid dhen stadastaireachd mu ghnìomhachd nan cleachdaichean san API
         backups_retention_period: Ùine glèidhidh aig tasg-lannan an luchd-cleachdaidh
         bootstrap_timeline_accounts: Mol na cunntasan seo do chleachdaichean ùra an-còmhnaidh
         closed_registrations_message: Teachdaireachd ghnàthaichte nuair nach eil clàradh ri fhaighinn
@@ -236,6 +241,7 @@ gd:
         custom_css: CSS gnàthaichte
         mascot: Suaichnean gnàthaichte (dìleabach)
         media_cache_retention_period: Ùine glèidhidh aig tasgadan nam meadhanan
+        peers_api_enabled: Foillsich liosta nam frithealaichean a chaidh a rùrachadh san API
         profile_directory: Cuir eòlaire nam pròifil an comas
         registrations_mode: Cò dh’fhaodas clàradh
         require_invite_text: Iarr adhbhar clàraidh
@@ -247,11 +253,13 @@ gd:
         site_short_description: Tuairisgeul an fhrithealaiche
         site_terms: Poileasaidh prìobhaideachd
         site_title: Ainm an fhrithealaiche
+        status_page_url: URL duilleag na staide
         theme: An t-ùrlar bunaiteach
         thumbnail: Dealbhag an fhrithealaiche
         timeline_preview: Ceadaich inntrigeadh gun ùghdarrachadh air na loidhnichean-ama phoblach
         trendable_by_default: Ceadaich treandaichean gun lèirmheas ro làimh
         trends: Cuir na treandaichean an comas
+        trends_as_landing_page: Cleachd na treandaichean ’nan duilleag-laighe
       interactions:
         must_be_follower: Bac na brathan nach eil o luchd-leantainn
         must_be_following: Bac na brathan o dhaoine nach lean thu
diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml
index 714275ab5..d3d7a53c1 100644
--- a/config/locales/simple_form.gl.yml
+++ b/config/locales/simple_form.gl.yml
@@ -18,8 +18,8 @@ gl:
           disable: Evitar que a usuaria utilice a súa conta, mais non eliminala ou agochar o seu contido.
           none: Utiliza esto para darlle un aviso á usuaria, se activar ningunha outra acción.
           sensitive: Forzar que tódolos ficheiros multimedia das usuarias sexan marcadas como sensibles.
-          silence: Evitar que a usuaria poida publicar toots públicos, agocha os seus toots e notificacións para a xente que non a segue.
-          suspend: Evita calquera interacción con ou desta conta e elimina os seus contidos. Reversible durante 30 días.
+          silence: Evitar que a usuaria poida publicar de xeito público, agochar as súas publicacións e notificacións para a xente que non a sigue. Isto pecha tódalas denuncias acerca desta conta.
+          suspend: Evita calquera interacción con/ou desta conta e elimina os seus contidos. Reversible durante 30 días. Isto pecha tódalas denuncias acerca desta conta.
         warning_preset_id: Optativo. Poderás engadir texto personalizado ao final do preestablecido
       announcement:
         all_day: Cando se marca, só serán amosadas as datas do intre de tempo
@@ -74,6 +74,7 @@ gl:
           hide: Agochar todo o contido filtrado, facer coma se non existise
           warn: Agochar o contido filtrado tras un aviso que conteña o nome do filtro
       form_admin_settings:
+        activity_api_enabled: Conta do número de publicacións locais, usuarias activas, e novos rexistros en acumulados semanais
         backups_retention_period: Gardar os arquivos xerados pola usuaria durante o número de días indicado.
         bootstrap_timeline_accounts: Estas contas aparecerán fixas na parte superior das recomendacións para as usuarias.
         closed_registrations_message: Móstrase cando non se admiten novas usuarias
@@ -81,6 +82,7 @@ gl:
         custom_css: Podes aplicar deseños personalizados na versión web de Mastodon.
         mascot: Sobrescribe a ilustración na interface web avanzada.
         media_cache_retention_period: Os ficheiros multimedia descargados serán eliminados despois do número de días indicado ao establecer un valor positivo, e voltos a descargar baixo petición.
+        peers_api_enabled: Unha lista dos nomes de dominio que este servidor atopou no fediverso. Non se inclúen aquí datos acerca de se estás a federar con eles ou non, só que o teu servidor os recoñeceu. Ten utilidade para servizos que recollen estatísticas acerca da federación nun amplo senso.
         profile_directory: O directorio de perfís inclúe a tódalas usuarias que optaron por ser descubribles.
         require_invite_text: Cando os rexistros requiren aprobación manual, facer que o texto "Por que te queres rexistrar?" do convite sexa obrigatorio en lugar de optativo
         site_contact_email: De que xeito se pode contactar contigo para temas legais ou obter axuda.
@@ -89,11 +91,13 @@ gl:
         site_short_description: Breve descrición que axuda a identificar de xeito único o teu servidor. Quen o xestiona, a quen vai dirixido?
         site_terms: Escribe a túa propia política de privacidade ou usa o valor por defecto. Podes usar sintaxe Markdow.
         site_title: De que xeito se pode referir o teu servidor ademáis do seu nome de dominio.
+        status_page_url: URL dunha páxina onde se pode ver o estado deste servidor cando non está a funcionar
         theme: Decorado que verán visitantes e novas usuarias.
         thumbnail: Imaxe con proporcións 2:1 mostrada xunto á información sobre o servidor.
         timeline_preview: Visitantes e usuarias non conectadas poderán ver as publicacións públicas máis recentes do servidor.
         trendable_by_default: Omitir a revisión manual das tendencias. Poderás igualmente eliminar manualmente os elementos que vaian aparecendo.
         trends: As tendencias mostran publicacións, cancelos e novas historias que teñen popularidade no teu servidor.
+        trends_as_landing_page: Mostrar contidos en voga para as persoas sen sesión iniciada e visitantes no lugar dunha descrición deste servidor. Require ter activado Tendencias.
       form_challenge:
         current_password: Estás entrando nun área segura
       imports:
@@ -229,6 +233,7 @@ gl:
           hide: Agochar completamente
           warn: Agochar tras un aviso
       form_admin_settings:
+        activity_api_enabled: Publicar na API estatísticas agregadas acerca da actividade das usuarias
         backups_retention_period: Período de retención do arquivo da usuaria
         bootstrap_timeline_accounts: Recomendar sempre estas contas ás novas usuarias
         closed_registrations_message: Mensaxe personalizada para cando o rexistro está pechado
@@ -236,6 +241,7 @@ gl:
         custom_css: CSS personalizado
         mascot: Mascota propia (herdado)
         media_cache_retention_period: Período de retención da caché multimedia
+        peers_api_enabled: Publicar na API unha lista dos servidores descubertos
         profile_directory: Activar o directorio de perfís
         registrations_mode: Quen se pode rexistrar
         require_invite_text: Pedir unha razón para unirse
@@ -247,11 +253,13 @@ gl:
         site_short_description: Descrición do servidor
         site_terms: Política de Privacidade
         site_title: Nome do servidor
+        status_page_url: URL da páxina do estado
         theme: Decorado por omisión
         thumbnail: Icona do servidor
         timeline_preview: Permitir acceso á cronoloxía pública sen autenticación
         trendable_by_default: Permitir tendencias sen aprobación previa
         trends: Activar tendencias
+        trends_as_landing_page: Usar as tendencias como páxina de benvida
       interactions:
         must_be_follower: Bloquear as notificacións de non-seguidoras
         must_be_following: Bloquea as notificacións de persoas que non segues
diff --git a/config/locales/simple_form.he.yml b/config/locales/simple_form.he.yml
index 6c87e2bd6..8e6631e36 100644
--- a/config/locales/simple_form.he.yml
+++ b/config/locales/simple_form.he.yml
@@ -18,8 +18,8 @@ he:
           disable: מנעי מהמשתמש להשתמש בחשבונם, מבלי למחוק או להסתיר את תוכנו.
           none: השתמשי בזה כדי לשלוח למשתמש אזהרה, מבלי לגרור פעולות נוספות.
           sensitive: אלצי את כל קבצי המדיה המצורפים על ידי המשתמש להיות מסומנים כרגישים.
-          silence: מנעי מהמשתמש להיות מסוגל לחצרץ בנראות פומבית, החביאי את חצרוציהם והתראותיהם מאנשים שלא עוקבים אחריהם.
-          suspend: מנעי כל התקשרות עם חשבון זה ומחקי את תוכנו. ניתן לשחזור תוך 30 יום.
+          silence: מנעי מהמשתמש להיות מסוגל לחצרץ בנראות פומבית, החביאי את חצרוציהם והתראותיהם מאנשים שלא עוקבים אחריהם. פעולה זו תסגור את הטיפול בדיווחים נגד החשבון.
+          suspend: מנעי כל התקשרות עם חשבון זה ומחקי את תוכנו. ניתן לשחזור תוך 30 יום. פעולה זו תסגור את הטיפול בדיווחים נגד החשבון.
         warning_preset_id: אופציונלי. ניתן עדיין להוסיף טקסט ייחודי לסוף ההגדרה
       announcement:
         all_day: אם נבחר, רק התאריכים בטווח הזמן יוצגו
@@ -74,6 +74,7 @@ he:
           hide: הסתר את התוכן המסונן, כאילו לא היה קיים
           warn: הסתר את התוכן המסונן מאחורי אזהרה עם כותרת המסנן
       form_admin_settings:
+        activity_api_enabled: מספר ההודעות שפורסמו מקומית, משתמשים פעילים, והרשמות חדשות בדליים שבועיים
         backups_retention_period: לשמור ארכיון משתמש שנוצר למשך מספר הימים המצוין.
         bootstrap_timeline_accounts: חשבונות אלו יוצמדו לראש רשימת המלצות המעקב של משתמשים חדשים.
         closed_registrations_message: להציג כאשר הרשמות חדשות אינן מאופשרות
@@ -81,6 +82,7 @@ he:
         custom_css: ניתן לבחור ערכות סגנון אישיות בגרסת הדפדפן של מסטודון.
         mascot: בחירת ציור למנשק הווב המתקדם.
         media_cache_retention_period: קבצי מדיה שהורדו ימחקו אחרי מספר הימים שיצוינו אם נבחר מספר חיובי, או-אז יורדו שוב מחדש בהתאם לצורך.
+        peers_api_enabled: רשימת השרתים ששרת זה פגש בפדיוורס. לא כולל מידע לגבי קשר ישיר עם שרת נתון, אלא רק שידוע לשרת זה על קיומו. מידע זה משמש שירותים האוספים סטטיסטיקות כלליות על הפדרציה.
         profile_directory: מדריך הפרופילים מפרט את כל המשתמשים שביקשו להיות ניתנים לגילוי.
         require_invite_text: כאשר הרשמות דורשות אישור ידני, הפיכת טקסט ה"מדוע את/ה רוצה להצטרף" להכרחי במקום אופציונלי
         site_contact_email: מה היא הדרך ליצור איתך קשר לצורך תמיכה או לצורך תאימות עם החוק.
@@ -89,11 +91,13 @@ he:
         site_short_description: תיאור קצר שיעזור להבחין בייחודיות השרת שלך. מי מריץ אותו, למי הוא מיועד?
         site_terms: נסחו מדיניות פרטיות או השאירו ריק כדי להשתמש בברירת המחדל. ניתן לנסח בעזרת תחביר מארקדאון.
         site_title: כיצד יקרא השרת שלך על ידי הקהל מלבד שם המתחם.
-        theme: ערכת המראה שיראו משתמשים חדשים ולא מחוברים.
+        status_page_url: כתובת לבדיקת מצב שרת זה בעת תקלה
+        theme: ערכת המראה שיראו משתמשים חדשים ומשתמשים שאינם מחוברים.
         thumbnail: תמונה ביחס 2:1 בערך שתוצג ליד המידע על השרת שלך.
         timeline_preview: משתמשים מנותקים יוכלו לדפדף בהודעות ציר הזמן הציבורי שעל השרת.
         trendable_by_default: לדלג על בדיקה ידנית של התכנים החמים. פריטים ספציפיים עדיין ניתנים להסרה לאחר מעשה.
         trends: נושאים חמים יציגו אילו הודעות, תגיות וידיעות חדשות צוברות חשיפה על השרת שלך.
+        trends_as_landing_page: הצג למבקרים ולמשתמשים שאינם מחוברים את הנושאים החמים במקום את תיאור השרת. מחייב הפעלה של אפשרות הנושאים החמים.
       form_challenge:
         current_password: את.ה נכנס. ת לאזור מאובטח
       imports:
@@ -229,6 +233,7 @@ he:
           hide: הסתרה כוללת
           warn: הסתרה עם אזהרה
       form_admin_settings:
+        activity_api_enabled: פרסום סטטיסטיקות מקובצות עבור פעילות משתמשים באמצעות מנשק התוכנה
         backups_retention_period: תקופת השמירה של ארכיון המשתמש
         bootstrap_timeline_accounts: המלצה על חשבונות אלה למשתמשים חדשים
         closed_registrations_message: הודעה מיוחדת כשההרשמה לא מאופשרת
@@ -236,6 +241,7 @@ he:
         custom_css: CSS בהתאמה אישית
         mascot: סמל השרת (ישן)
         media_cache_retention_period: תקופת שמירת מטמון מדיה
+        peers_api_enabled: פרסם רשימה של שרתים שנתגלו באמצעות ה-API
         profile_directory: הפעלת ספריית פרופילים
         registrations_mode: מי יכולים לפתוח חשבון
         require_invite_text: לדרוש סיבה להצטרפות
@@ -247,11 +253,13 @@ he:
         site_short_description: תיאור השרת
         site_terms: מדיניות פרטיות
         site_title: שם השרת
+        status_page_url: URL של עמוד סטטוס חיצוני
         theme: ערכת נושא ברירת מחדל
         thumbnail: תמונה ממוזערת מהשרת
         timeline_preview: הרשאת גישה בלתי מאומתת לפיד הפומבי
         trendable_by_default: הרשאה לפריטים להופיע בנושאים החמים ללא אישור מוקדם
         trends: אפשר פריטים חמים (טרנדים)
+        trends_as_landing_page: דף הנחיתה יהיה "נושאים חמים"
       interactions:
         must_be_follower: חסימת התראות משאינם עוקבים
         must_be_following: חסימת התראות משאינם נעקבים
diff --git a/config/locales/simple_form.hu.yml b/config/locales/simple_form.hu.yml
index 871065d54..32f305ce9 100644
--- a/config/locales/simple_form.hu.yml
+++ b/config/locales/simple_form.hu.yml
@@ -18,8 +18,8 @@ hu:
           disable: A felhasználó nem fogja tudni használni a fiókját, de ettől még nem töröljük azt vagy rejtjük el a tartalmait.
           none: Ezt használd ahhoz, hogy a felhasználónak figyelmeztetést küldj bármilyen más következmény nélkül.
           sensitive: Ennek a felhasználónak minden médiatartalmát jelöljük meg kényesként.
-          silence: Megakadályozzuk, hogy ez a felhasználó nyilvános láthatóságú bejegyzést tegyen közzé, elrejtjük a bejegyzéseit és a róla szóló értesítéseket azok elől, akik nem közvetlen követői.
-          suspend: Minden interakciót megakadályozunk ezzel a fiókkal és töröljük a tartalmát. 30 napon belül még visszacsinálható.
+          silence: A felhasználó megakadályozása abban, hogy nyilvános bejegyzéseket tehessen közzé, bejegyzései és tőle származó értesítések elrejtése az őt követők számára. A fiók ellen indított minden bejelentés lezárása.
+          suspend: A fiókkal való minden kapcsolat megakadályozása és a tartalmak törlése. 30 napig visszaállítható. A fiók ellen indított minden bejelentés lezárása.
         warning_preset_id: Nem kötelező. A figyelmeztetés végére saját szöveget is írhatsz.
       announcement:
         all_day: Bejelölve csak a dátumok számítanak majd a megadott intervallumból
@@ -74,6 +74,7 @@ hu:
           hide: A szűrt tartalom teljes elrejtése, mintha nem is létezne
           warn: A szűrt tartalom a szűrő címét említő figyelmeztetés mögé rejtése
       form_admin_settings:
+        activity_api_enabled: Helyi bejegyzések, aktív felhasználók és új regisztrációk száma heti bontásban
         backups_retention_period: Az előállított felhasználói archívumok megtartása a megadott napokig.
         bootstrap_timeline_accounts: Ezek a fiókok ki lesznek tűzve az új felhasználók követési javaslatainak élére.
         closed_registrations_message: Akkor jelenik meg, amikor a regisztráció le van zárva
@@ -81,6 +82,7 @@ hu:
         custom_css: A Mastodon webes verziójában használhatsz egyedi stílusokat.
         mascot: Felülvágja a haladó webes felületen található illusztrációt.
         media_cache_retention_period: A letöltött médiafájlok megadott számú nap után törölve lesznek, ha pozitív értékre van állítva, és igény szerint újból le lesznek töltve.
+        peers_api_enabled: Azon domainek listája, melyekkel ez a kiszolgáló találkozott a fediverzumban. Nem csatolunk adatot arról, hogy föderált kapcsolatban vagy-e az adott kiszolgálóval, csak arról, hogy a kiszolgálód tud a másikról. Ezt olyan szolgáltatások használják, melyek általában a föderációról készítenek statisztikákat.
         profile_directory: A profilok jegyzéke minden olyan felhasználót felsorol, akik engedélyezték a felfedezhetőségüket.
         require_invite_text: Ha a regisztrációhoz manuális jóváhagyásra van szükség, akkor a „Miért akarsz csatlakozni?” válasz kitöltése legyen kötelező, és ne opcionális
         site_contact_email: Hogyan érhetnek el jogi vagy támogatási kérésekkel.
@@ -89,11 +91,13 @@ hu:
         site_short_description: Rövid leírás, amely segíthet a kiszolgálód egyedi azonosításában. Ki futtatja, kinek készült?
         site_terms: Használd a saját adatvédelmi irányelveidet, vagy hagyd üresen az alapértelmezett használatához. Markdown szintaxis használható.
         site_title: Hogyan hivatkozhatnak mások a kiszolgálódra a domain nevén kívül.
+        status_page_url: Annak az oldalnak az URL-je, melyen ennek a kiszolgálónak az állapotát látják az emberek egy leállás során
         theme: A téma, melyet a kijelentkezett látogatók és az új felhasználók látnak.
         thumbnail: Egy durván 2:1 arányú kép, amely a kiszolgálóinformációk mellett jelenik meg.
         timeline_preview: A kijelentkezett látogatók továbbra is böngészhetik a kiszolgáló legfrissebb nyilvános bejegyzéseit.
         trendable_by_default: Kézi felülvizsgálat kihagyása a felkapott tartalmaknál. Az egyes elemek utólag távolíthatók el a trendek közül.
         trends: A trendek azt mondják meg, hogy mely bejegyzések, hashtagek és hírbejegyzések felkapottak a kiszolgálódon.
+        trends_as_landing_page: Felkapott tartalmak mutatása a kijelentkezett felhasználók és látogatók számára ennek a kiszolgálónak a leírása helyett. Szükséges hozzá a trendek engedélyezése.
       form_challenge:
         current_password: Beléptél egy biztonsági térben
       imports:
@@ -229,6 +233,7 @@ hu:
           hide: Teljes elrejtés
           warn: Elrejtés figyelmeztetéssel
       form_admin_settings:
+        activity_api_enabled: Összesített statisztikák közzététele az API-ban a felhasználók aktivitásáról
         backups_retention_period: Felhasználói archívum megtartási időszaka
         bootstrap_timeline_accounts: Mindig javasoljuk ezeket a fiókokat az új felhasználók számára
         closed_registrations_message: A feliratkozáskor megjelenő egyéni üzenet nem érhető el
@@ -236,6 +241,7 @@ hu:
         custom_css: Egyéni CSS
         mascot: Egyéni kabala (örökölt)
         media_cache_retention_period: Média-gyorsítótár megtartási időszaka
+        peers_api_enabled: Felfedezett kiszolgálók listájának közzététele az API-ban
         profile_directory: Profiladatbázis engedélyezése
         registrations_mode: Ki regisztrálhat
         require_invite_text: Indok megkövetelése a csatlakozáshoz
@@ -247,11 +253,13 @@ hu:
         site_short_description: Kiszolgáló leírása
         site_terms: Adatvédelmi szabályzat
         site_title: Kiszolgáló neve
+        status_page_url: Állapotoldal URL-je
         theme: Alapértelmezett téma
         thumbnail: Kiszolgáló bélyegképe
         timeline_preview: A nyilvános idővonalak hitelesítés nélküli elérésének engedélyezése
         trendable_by_default: Trendek engedélyezése előzetes ellenőrzés nélkül
         trends: Trendek engedélyezése
+        trends_as_landing_page: Trendek használata nyitóoldalként
       interactions:
         must_be_follower: Nem követőidtől érkező értesítések tiltása
         must_be_following: Nem követettjeidtől érkező értesítések tiltása
diff --git a/config/locales/simple_form.id.yml b/config/locales/simple_form.id.yml
index 7923d9574..607d3bdd0 100644
--- a/config/locales/simple_form.id.yml
+++ b/config/locales/simple_form.id.yml
@@ -18,8 +18,6 @@ id:
           disable: Cegah pengguna menggunakan akun mereka, tetapi jangan menghapus atau menyembunyikan konten mereka.
           none: Gunakan ini untuk mengirim peringatan kepada pengguna, tanpa memicu tindakan lainnya.
           sensitive: Paksa semua lampiran media pengguna sebagai sensitif.
-          silence: Cegah pengguna agar tidak dapat memposting dengan visibilitas publik, sembunyikan postingan dan notifikasi mereka dari orang yang tidak mengikuti mereka.
-          suspend: Cegah interaksi apapun dari/ke akun ini dan hapus kontennya. Dapat dikembalikan selama 30 hari.
         warning_preset_id: Opsional. Anda tetap dapat menambahkan teks kustom pada akhir preset
       announcement:
         all_day: Saat dicentang, hanya tanggal dalam rentang waktu tertentu yang akan ditampilkan
diff --git a/config/locales/simple_form.io.yml b/config/locales/simple_form.io.yml
index 42c95a9ae..d6aff6afd 100644
--- a/config/locales/simple_form.io.yml
+++ b/config/locales/simple_form.io.yml
@@ -18,8 +18,6 @@ io:
           disable: Preventez uzanto de uzar olia konto ma ne efacez o celez olia kontenaji.
           none: Uzez co por sendar averto a la uzanto sen eventigar irga altra ago.
           sensitive: Koaktez omna mediiatachaji da ca uzanto markizesar quale sentoza.
-          silence: Preventez la uzanto de povar postar per publika videbleso, celez olia posti e avizi de personi quo ne sequas oli.
-          suspend: Preventez irga interago de o a ca konto e efacez ola kontenaji. Inversebla til 30 dii.
         warning_preset_id: Neobligata. Vu povas ankore insertar kustume texto a extremajo di fixito
       announcement:
         all_day: Kande kontrolesas, nur tempoporteodato montresos
diff --git a/config/locales/simple_form.is.yml b/config/locales/simple_form.is.yml
index a9c94717d..6c9e2e869 100644
--- a/config/locales/simple_form.is.yml
+++ b/config/locales/simple_form.is.yml
@@ -18,8 +18,8 @@ is:
           disable: Koma í veg fyrir að notandinn noti aðganginn sinn, en ekki eyða eða fela efnið þeirra.
           none: Nota þetta til að senda aðvörun til notandans, án þess að setja neina aðra aðgerð í gang.
           sensitive: Þvinga fram að öll myndefnisviðhengi þessa notanda verði flögguð sem viðkvæmt efni.
-          silence: Koma í veg fyrir að notandinn geti birt færslur opinberlega, fela færslur þeirra og tilkynningar fyrir fólki sem ekki er að fylgjast með notandanum.
-          suspend: Koma í veg fyrir öll samskipti til eða frá þessum aðgangi og eyða öllu efni hans. Afturkallanlegt innan 30 daga.
+          silence: Koma í veg fyrir að notandinn geti birt færslur opinberlega, fela færslur þeirra og tilkynningar fyrir fólki sem ekki er að fylgjast með notandanum. Lokar öllum kærum gagnvart þessum aðgangi.
+          suspend: Koma í veg fyrir öll samskipti til eða frá þessum aðgangi og eyða öllu efni hans. Afturkallanlegt innan 30 daga. Lokar öllum kærum gagnvart þessum aðgangi.
         warning_preset_id: Valkvætt. Þú getur ennþá bætt sérsniðnum texta við enda forstillinga
       announcement:
         all_day: Þegar merkt er við þetta, munu einungis birtast dagsetningar tímarammans
@@ -74,6 +74,7 @@ is:
           hide: Fela síað efni algerlega, rétt eins og það sé ekki til staðar
           warn: Fela síað efni á bakvið aðvörun sem tekur fram titil síunnar
       form_admin_settings:
+        activity_api_enabled: Fjöldi staðværra stöðufærslna, virkra notenda og nýskráninga í vikulegum skömmtum
         backups_retention_period: Halda safni notandans í tiltekinn fjölda daga.
         bootstrap_timeline_accounts: Þessir notendaaðgangar verða festir efst í meðmælum til nýrra notenda um að fylgjast með þeim.
         closed_registrations_message: Birtist þegar lokað er á nýskráningar
@@ -81,6 +82,7 @@ is:
         custom_css: Þú getur virkjað sérsniðna stíla í vefútgáfu Mastodon.
         mascot: Þetta tekyr yfir myndskreytinguna í ítarlega vefviðmótinu.
         media_cache_retention_period: Sóttu myndefni verður eytt eftir tiltekinn fjölda daga þegar þetta er jákvætt gildi og síðan sótt aftur eftir þörfum.
+        peers_api_enabled: Listi yfir þau lénaheiti sem þessi netþjónn hefur rekist á í skýjasambandinu. Engin gögn eru hér sem gefa til kynna hvort þú sért í sambandi við tiltekinn netþjón, bara að netþjónninn þinn viti um hann. Þetta er notað af þjónustum sem safna tölfræði um skýjasambönd á almennan hátt.
         profile_directory: Notendamappan telur upp alla þá notendur sem hafa valið að vera uppgötvanlegir.
         require_invite_text: Þegar nýskráningar krefjast handvirks samþykkis, þá skal gera textann í “Hvers vegna viltu taka þátt?” að kröfu en ekki valkvæðan
         site_contact_email: Hovernig fólk getur haft samband við þig til að fá aðstoð eða vegna lagalegra mála.
@@ -89,11 +91,13 @@ is:
         site_short_description: Stutt lýsing sem hjálpar til við að auðkenna netþjóninn þinn. Hver er að reka hann, fyrir hverja er hann?
         site_terms: Notaðu þína eigin persónuverndarstefnu eða skildu þetta eftir autt til að nota sjálfgefna stefnu. Má sníða með Markdown-málskipan.
         site_title: Hvað fólk kallar netþjóninn þinn annað en með heiti lénsins.
+        status_page_url: Slóð á síðu þar sem fólk getur séð ástand netþjónsins þegar vandræði koma upp
         theme: Þema sem útskráðir gestir og nýjir notendur sjá.
         thumbnail: Mynd um það bil 2:1 sem birtist samhliða upplýsingum um netþjóninn þinn.
         timeline_preview: Gestir sem ekki eru skráðir inn munu geta skoðað nýjustu opinberu færslurnar sem tiltækar eru á þjóninum.
         trendable_by_default: Sleppa handvirkri yfirferð á vinsælu efni. Áfram verður hægt að fjarlægja stök atriði úr vinsældarlistum.
         trends: Vinsældir sýna hvaða færslur, myllumerki og fréttasögur séu í umræðunni á netþjóninum þínum.
+        trends_as_landing_page: Sýna vinsælt efni til ekki-innskráðra notenda í stað lýsingar á þessum netþjóni. Krefst þess að vinsældir efnis sé virkjað.
       form_challenge:
         current_password: Þú ert að fara inn á öryggissvæði
       imports:
@@ -229,6 +233,7 @@ is:
           hide: Fela alveg
           warn: Fela með aðvörun
       form_admin_settings:
+        activity_api_enabled: Birta samantektartölfræði um virkni notanda í API-kerfisviðmótinu
         backups_retention_period: Tímalengd sem safni notandans er haldið eftir
         bootstrap_timeline_accounts: Alltaf mæla með þessum notendaaðgöngum fyrir nýja notendur
         closed_registrations_message: Sérsniðin skilaboð þegar ekki er hægt að nýskrá
@@ -236,6 +241,7 @@ is:
         custom_css: Sérsniðið CSS
         mascot: Sérsniðið gæludýr (eldra)
         media_cache_retention_period: Tímalengd sem myndefni haldið
+        peers_api_enabled: Birta lista yfir uppgötvaða netþjóna í API-kerfisviðmótinu
         profile_directory: Virkja notendamöppu
         registrations_mode: Hverjir geta nýskráð sig
         require_invite_text: Krefjast ástæðu fyrir þátttöku
@@ -247,11 +253,13 @@ is:
         site_short_description: Lýsing á vefþjóni
         site_terms: Persónuverndarstefna
         site_title: Heiti vefþjóns
+        status_page_url: Slóð á ástandssíðu
         theme: Sjálfgefið þema
         thumbnail: Smámynd vefþjóns
         timeline_preview: Leyfa óauðkenndan aðgang að opinberum tímalínum
         trendable_by_default: Leyfa vinsælt efni án undanfarandi yfirferðar
         trends: Virkja vinsælt
+        trends_as_landing_page: Nota vinsælasta sem upphafssíðu
       interactions:
         must_be_follower: Loka á tilkynningar frá þeim sem ekki eru fylgjendur
         must_be_following: Loka á tilkynningar frá þeim sem þú fylgist ekki með
diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml
index 5ce33b62d..a62699c3f 100644
--- a/config/locales/simple_form.it.yml
+++ b/config/locales/simple_form.it.yml
@@ -18,8 +18,8 @@ it:
           disable: Impedisce all'utente di utilizzare il suo account, ma non elimina o nasconde i suoi contenuti.
           none: Usa questo per inviare un avviso all'utente, senza eseguire altre azioni.
           sensitive: Forza tutti gli allegati multimediali di questo utente ad essere contrassegnati come sensibili.
-          silence: Impedisce all'utente di poter pubblicare con visibilità pubblica, nasconde i suoi post e notifiche a persone che non lo seguono.
-          suspend: Impedisce qualsiasi interazione da o per questo account ed elimina i suoi contenuti. Annullabile entro 30 giorni.
+          silence: Impedisce all'utente di poter pubblicare con visibilità pubblica, nasconde i suoi post e notifiche a persone che non lo seguono. Chiude tutte le segnalazioni relative a questo account.
+          suspend: Impedisce qualsiasi interazione da o verso questo account e cancellarne il contenuto. Annullabile entro 30 giorni. Chiude tutte le segnalazioni contro questo account.
         warning_preset_id: Opzionale. Puoi aggiungere un testo personalizzato alla fine di quello preimpostato
       announcement:
         all_day: Se selezionato, verranno visualizzate solo le date dell'intervallo di tempo
@@ -74,6 +74,7 @@ it:
           hide: Nascondi completamente il contenuto filtrato, come se non esistesse
           warn: Nascondi il contenuto filtrato e mostra invece un avviso, citando il titolo del filtro
       form_admin_settings:
+        activity_api_enabled: Conteggi di post pubblicati localmente, utenti attivi e nuove registrazioni in gruppi settimanali
         backups_retention_period: Conserva gli archivi utente generati per il numero di giorni specificato.
         bootstrap_timeline_accounts: Questi account verranno aggiunti in cima ai consigli da seguire dei nuovi utenti.
         closed_registrations_message: Visualizzato alla chiusura delle iscrizioni
@@ -81,6 +82,7 @@ it:
         custom_css: È possibile applicare stili personalizzati sulla versione web di Mastodon.
         mascot: Sostituisce l'illustrazione nell'interfaccia web avanzata.
         media_cache_retention_period: I file multimediali scaricati verranno eliminati dopo il numero di giorni specificato se impostati su un valore positivo e scaricati nuovamente su richiesta.
+        peers_api_enabled: Un elenco di nomi di dominio che questo server ha incontrato nel fediverse. Qui non sono inclusi dati sul fatto se si federano con un dato server, solo che il server ne è a conoscenza. Questo viene utilizzato dai servizi che raccolgono statistiche sulla federazione in senso generale.
         profile_directory: La directory del profilo elenca tutti gli utenti che hanno acconsentito ad essere individuabili.
         require_invite_text: 'Quando le iscrizioni richiedono l''approvazione manuale, rendi la domanda: "Perché vuoi unirti?" obbligatoria anziché facoltativa'
         site_contact_email: In che modo le persone possono contattarti per richieste legali o di supporto.
@@ -89,11 +91,13 @@ it:
         site_short_description: Una breve descrizione per aiutare a identificare in modo univoco il tuo server. Chi lo gestisce, a chi è rivolto?
         site_terms: Usa la tua politica sulla privacy o lascia vuoto per usare l'impostazione predefinita. Può essere strutturata con la sintassi Markdown.
         site_title: In che modo le persone possono fare riferimento al tuo server oltre al suo nome di dominio.
+        status_page_url: URL di una pagina in cui le persone possono visualizzare lo stato di questo server durante un disservizio
         theme: Tema visualizzato dai visitatori e dai nuovi utenti disconnessi.
         thumbnail: Un'immagine approssimativamente 2:1 visualizzata insieme alle informazioni del tuo server.
         timeline_preview: I visitatori disconnessi potranno sfogliare i post pubblici più recenti disponibili sul server.
         trendable_by_default: Salta la revisione manuale dei contenuti di tendenza. I singoli elementi possono ancora essere rimossi dalle tendenze dopo il fatto.
         trends: Le tendenze mostrano quali post, hashtag e notizie stanno guadagnando popolarità sul tuo server.
+        trends_as_landing_page: Mostra i contenuti di tendenza agli utenti disconnessi e ai visitatori, invece di una descrizione di questo server. Richiede l'abilitazione delle tendenze.
       form_challenge:
         current_password: Stai entrando in un'area sicura
       imports:
@@ -229,6 +233,7 @@ it:
           hide: Nascondi completamente
           warn: Nascondi con avviso
       form_admin_settings:
+        activity_api_enabled: Pubblica le statistiche aggregate sull'attività degli utenti nell'API
         backups_retention_period: Periodo di conservazione dell'archivio utente
         bootstrap_timeline_accounts: Consiglia sempre questi account ai nuovi utenti
         closed_registrations_message: Messaggio personalizzato quando le iscrizioni non sono disponibili
@@ -236,6 +241,7 @@ it:
         custom_css: Personalizza CSS
         mascot: Personalizza mascotte (legacy)
         media_cache_retention_period: Periodo di conservazione della cache multimediale
+        peers_api_enabled: Pubblica l'elenco dei server scoperti nell'API
         profile_directory: Abilita directory del profilo
         registrations_mode: Chi può iscriversi
         require_invite_text: Richiedi un motivo per unirsi
@@ -247,11 +253,13 @@ it:
         site_short_description: Descrizione del server
         site_terms: Politica sulla privacy
         site_title: Nome del server
+        status_page_url: URL della pagina di stato
         theme: Tema predefinito
         thumbnail: Miniatura del server
         timeline_preview: Consenti l'accesso non autenticato alle timeline pubbliche
         trendable_by_default: Consenti le tendenze senza revisione preventiva
         trends: Abilita le tendenze
+        trends_as_landing_page: Usa le tendenze come pagina di destinazione
       interactions:
         must_be_follower: Blocca notifiche da chi non ti segue
         must_be_following: Blocca notifiche dalle persone che non segui
diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml
index ea633e6af..5af0130d4 100644
--- a/config/locales/simple_form.ja.yml
+++ b/config/locales/simple_form.ja.yml
@@ -18,8 +18,8 @@ ja:
           disable: ユーザーが自分のアカウントを使用できないようにします。コンテンツを削除したり非表示にすることはありません。
           none: これを使用すると、他の操作をせずにユーザーに警告を送信できます。
           sensitive: このユーザーが添付したメディアを強制的に閲覧注意にする
-          silence: ユーザーが公開投稿できないようにし、フォローしていない人に投稿や通知が表示されないようにする。
-          suspend: このアカウントとのやりとりを止め、コンテンツを削除します。30日以内は取消可能です。
+          silence: ユーザーによる公開投稿を禁止し、フォローしていない人に投稿や通知が表示されないようにします。また、このアカウントに対するすべての通報をクローズします。
+          suspend: このアカウントによるすべての活動を禁止し、コンテンツを削除します。この操作は30日以内であれば取り消しが可能です。また、このアカウントに対するすべての通報をクローズします。
         warning_preset_id: オプションです。プリセット警告文の末尾に任意の文字列を追加することができます
       announcement:
         all_day: 有効化すると、対象期間の箇所に日付だけが表示されます
@@ -71,29 +71,33 @@ ja:
       filters:
         action: 投稿がフィルタに一致したときに実行するアクションを選択
         actions:
-          hide: フィルタリングしたコンテンツを完全に隠し、あたかも存在しないかのようにします
-          warn: フィルタリングされたコンテンツを、フィルタータイトルの警告の後ろに隠します。
+          hide: フィルタに一致した投稿を完全に非表示にします
+          warn: フィルタに一致した投稿を非表示にし、フィルタのタイトルを含む警告を表示します
       form_admin_settings:
+        activity_api_enabled: 週単位でローカルで公開された投稿数、アクティブユーザー数、新規登録者数を表示します
         backups_retention_period: 生成されたユーザーのアーカイブを指定した日数の間保持します。
-        bootstrap_timeline_accounts: これらのアカウントは、新しいユーザーのフォロー推奨リストの一番上にピン留めされます。
+        bootstrap_timeline_accounts: これらのアカウントは、新しいユーザー向けのおすすめユーザーの一番上にピン留めされます。
         closed_registrations_message: アカウント作成を停止している時に表示されます
         content_cache_retention_period: 正の値に設定されている場合、他のサーバーの投稿は指定された日数の後に削除されます。元に戻せません。
         custom_css: ウェブ版のMastodonでカスタムスタイルを適用できます。
-        mascot: 上級者向けWebインターフェースのイラストを上書きする。
+        mascot: 上級者向けWebインターフェースのイラストを上書きします。
         media_cache_retention_period: 正の値に設定されている場合、ダウンロードされたメディアファイルは指定された日数の後に削除され、リクエストに応じて再ダウンロードされます。
-        profile_directory: プロフィールディレクトリには、掲載するよう設定したすべてのユーザーが一覧表示されます。
-        require_invite_text: アカウント登録が承認制の場合、「意気込みをお聞かせください」のテキストの入力を必須にする
+        peers_api_enabled: このサーバーが Fediverse で遭遇したドメイン名のリストです。このサーバーが知っているだけで、特定のサーバーと連合しているかのデータは含まれません。これは一般的に Fediverse に関する統計情報を収集するサービスによって使用されます。
+        profile_directory: ディレクトリには、掲載する設定をしたすべてのユーザーが一覧表示されます。
+        require_invite_text: アカウント登録が承認制の場合、登録の際の申請事由の入力を必須にします
         site_contact_email: 法律またはサポートに関する問い合わせ先
         site_contact_username: マストドンでの連絡方法
         site_extended_description: 訪問者やユーザーに役立つかもしれない任意の追加情報。Markdownが使えます。
-        site_short_description: 誰が運営しているのか、誰に向けたものなのかなど、サーバーを特定する短い説明。
+        site_short_description: 運営している人や組織、想定しているユーザーなど、サーバーの特徴を説明する短いテキスト
         site_terms: 独自のプライバシーポリシーを使用するか空白にしてデフォルトのプライバシーポリシーを使用します。Markdownが使えます。
-        site_title: ドメイン名以外でサーバーを参照する方法です。
+        site_title: ドメイン名以外でサーバーを参照する方法
+        status_page_url: 障害発生時などにユーザーがサーバーの状態を確認できるページのURL
         theme: ログインしていない人と新規ユーザーに表示されるテーマ。
         thumbnail: サーバー情報と共に表示される、アスペクト比が約 2:1 の画像。
-        timeline_preview: ログアウトした人でも、サーバー上で利用可能な最新の公開投稿を閲覧することができます。
-        trendable_by_default: トレンドコンテンツの手動レビューをスキップする。個々のコンテンツは後でトレンドから削除できます。
-        trends: トレンドは、サーバー上でどの投稿、ハッシュタグ、ニュース記事が人気を集めているかを示します。
+        timeline_preview: ログインしていないユーザーがサーバー上の最新の公開投稿を閲覧できるようにします。
+        trendable_by_default: トレンドの審査を省略します。トレンドは掲載後でも個別に除外できます。
+        trends: トレンドは、サーバー上で人気を集めている投稿、ハッシュタグ、ニュース記事などが表示されます。
+        trends_as_landing_page: ログインしていないユーザーに対して、サーバーの説明の代わりにトレンドコンテンツを表示します。トレンドを有効にする必要があります。
       form_challenge:
         current_password: セキュリティ上重要なエリアにアクセスしています
       imports:
@@ -120,11 +124,11 @@ ja:
         chosen_languages: 選択すると、選択した言語の投稿のみが公開タイムラインに表示されるようになります
         role: このロールはユーザーが持つ権限を管理します
       user_role:
-        color: UI 全体で使用される色(RGB hex 形式)
+        color: UI 全体でロールの表示に使用される色(16進数RGB形式)
         highlighted: これによりロールが公開されます。
         name: ロールのバッジを表示する際の表示名
         permissions_as_keys: このロールを持つユーザーは次の機能にアクセスできます
-        position: 特定の状況では、より高いロールが競合の解決を決定します。特定のアクションは優先順位が低いロールでのみ実行できます。
+        position: 場合により、より高いロールのユーザーが紛争の解決を決定します。特定のアクションは優先度が低いロールでのみ実行できます。
       webhook:
         events: 送信するイベントを選択
         url: イベントの送信先
@@ -229,16 +233,18 @@ ja:
           hide: 完全に隠す
           warn: 警告付きで隠す
       form_admin_settings:
+        activity_api_enabled: APIでユーザーアクティビティに関する集計統計を公開する
         backups_retention_period: ユーザーアーカイブの保持期間
-        bootstrap_timeline_accounts: 新規ユーザーに必ずおすすめするアカウント
-        closed_registrations_message: サインアップできない場合のカスタムメッセージ
+        bootstrap_timeline_accounts: おすすめユーザーに常に表示するアカウント
+        closed_registrations_message: アカウント作成を停止している時のカスタムメッセージ
         content_cache_retention_period: コンテンツキャッシュの保持期間
         custom_css: カスタムCSS
         mascot: カスタムマスコット(レガシー)
         media_cache_retention_period: メディアキャッシュの保持期間
+        peers_api_enabled: 発見したサーバーのリストをAPIで公開する
         profile_directory: ディレクトリを有効にする
         registrations_mode: 新規登録が可能な人
-        require_invite_text: 意気込み理由の入力を必須にする。
+        require_invite_text: 申請事由の入力を必須にする
         show_domain_blocks: ドメインブロックを表示
         show_domain_blocks_rationale: ドメインがブロックされた理由を表示
         site_contact_email: 連絡先メールアドレス
@@ -247,11 +253,13 @@ ja:
         site_short_description: サーバーの説明
         site_terms: プライバシーポリシー
         site_title: サーバーの名前
+        status_page_url: ステータスページのURL
         theme: デフォルトテーマ
         thumbnail: サーバーのサムネイル
         timeline_preview: 公開タイムラインへの未認証のアクセスを許可する
-        trendable_by_default: 審査前のハッシュタグのトレンドへの表示を許可する
+        trendable_by_default: 審査前のトレンドの掲載を許可する
         trends: トレンドを有効にする
+        trends_as_landing_page: 新規登録画面にトレンドを表示する
       interactions:
         must_be_follower: フォロワー以外からの通知をブロック
         must_be_following: フォローしていないユーザーからの通知をブロック
diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml
index 9a78256e3..f340b207c 100644
--- a/config/locales/simple_form.ko.yml
+++ b/config/locales/simple_form.ko.yml
@@ -18,8 +18,8 @@ ko:
           disable: 사용자가 계정을 사용하는 것을 막지만, 그의 게시물을 삭제하거나 숨기지는 않습니다.
           none: 이것을 사용해서 어떤 동작도 하지 않고, 사용자에게 경고를 보냅니다.
           sensitive: 이 사용자의 모든 미디어 첨부를 민감함으로 강제 설정합니다.
-          silence: 이 사용자가 공개 설정으로 게시물을 작성할 수 없도록 하고, 그를 팔로우 하지 않는 사람에게는 이 사용자의 게시물과 알림을 숨깁니다.
-          suspend: 이 계정과의 모든 상호작용을 막고 모든 내용을 삭제합니다. 30일 이내에 되돌리기가 가능합니다.
+          silence: 이 사용자가 공개 설정으로 게시물을 작성할 수 없도록 하고, 그를 팔로우 하지 않는 사람에게는 이 사용자의 게시물과 알림을 숨깁니다. 이 계정에 대한 모든 신고를 닫습니다.
+          suspend: 이 계정과의 모든 상호작용을 막고 모든 내용을 삭제합니다. 30일 이내에 되돌리기가 가능합니다. 이 계정에 대한 모든 신고를 닫습니다.
         warning_preset_id: 선택사항. 틀의 마지막에 임의의 텍스트를 추가 할 수 있습니다
       announcement:
         all_day: 체크 되었을 경우, 그 시간에 속한 날짜들에만 표시됩니다
@@ -30,7 +30,7 @@ ko:
       appeal:
         text: 처벌에 대해 단 한 번만 이의제기를 할 수 있습니다
       defaults:
-        autofollow: 이 초대를 통해 가입하는 사람은 당신을 자동으로 팔로우 하게 됩니다
+        autofollow: 이 초대로 가입한 사람은 나를 팔로우하게 됩니다.
         avatar: PNG, GIF 혹은 JPG. 최대 %{size}. %{dimensions}px로 축소 됨
         bot: 이 계정이 대부분 자동으로 작업을 수행하고 잘 확인하지 않는다는 것을 알립니다.
         context: 필터를 적용 할 한 개 이상의 컨텍스트
@@ -39,7 +39,7 @@ ko:
         digest: 오랫동안 활동하지 않았을 때 받은 멘션들에 대한 요약 받기
         discoverable: 추천, 트렌드 및 기타 기능을 통해 낯선 사람이 내 계정을 발견할 수 있도록 허용합니다
         email: 당신은 확인 메일을 받게 됩니다
-        fields: 당신의 프로파일에 최대 4개까지 표 형식으로 나타낼 수 있습니다
+        fields: 프로필에 최대 4개의 항목을 표로 표시할 수 있습니다.
         header: PNG, GIF 혹은 JPG. 최대 %{size}. %{dimensions}px로 축소 됨
         inbox_url: 사용 할 릴레이 서버의 프론트페이지에서 URL을 복사합니다
         irreversible: 필터링 된 게시물은 나중에 필터가 사라지더라도 돌아오지 않게 됩니다
@@ -74,6 +74,7 @@ ko:
           hide: 필터에 걸러진 글을 처음부터 없었던 것처럼 완전히 가리기
           warn: 필터에 걸러진 글을 필터 제목과 함께 경고 뒤에 가리기
       form_admin_settings:
+        activity_api_enabled: 주별 로컬에 게시된 글, 활성 사용자 및 새로운 가입자 수
         backups_retention_period: 생성된 사용자 아카이브를 며칠동안 저장할 지.
         bootstrap_timeline_accounts: 이 계정들은 팔로우 추천 목록 상단에 고정됩니다.
         closed_registrations_message: 새 가입을 차단했을 때 표시됩니다
@@ -81,6 +82,7 @@ ko:
         custom_css: 사용자 지정 스타일을 웹 버전의 마스토돈에 지정할 수 있습니다.
         mascot: 고급 사용자 인터페이스에 있는 일러스트를 교체합니다.
         media_cache_retention_period: 양수로 설정된 경우 다운로드된 미디어 파일들은 지정된 일수가 지나면 삭제될 것이고 필요할 때 다시 다운로드 될 것입니다.
+        peers_api_enabled: 이 서버가 연합우주에서 만났던 서버들에 대한 도메인 네임의 목록입니다. 해당 서버와 어떤 연합을 했는지에 대한 정보는 전혀 포함되지 않고, 단순히 그 서버를 알고 있는지에 대한 것입니다. 이것은 일반적으로 연합에 대한 통계를 수집할 때 사용됩니다.
         profile_directory: 프로필 책자는 발견되기를 희망하는 모든 사람들의 목록을 나열합니다.
         require_invite_text: 가입이 수동 승인을 필요로 할 때, "왜 가입하려고 하나요?" 항목을 선택사항으로 두는 것보다는 필수로 두는 것이 낫습니다
         site_contact_email: 사람들이 법적이나 도움 요청을 위해 당신에게 연락할 방법.
@@ -89,17 +91,19 @@ ko:
         site_short_description: 이 서버를 특별하게 구분할 수 있는 짧은 설명. 누가 운영하고, 누구를 위한 것인가요?
         site_terms: 자신만의 개인정보 정책을 사용하거나 비워두는 것으로 기본값을 사용할 수 있습니다. 마크다운 문법을 사용할 수 있습니다.
         site_title: 사람들이 이 서버를 도메인 네임 대신에 부를 이름.
+        status_page_url: 이 서버가 중단된 동안 사람들이 서버의 상태를 볼 수 있는 페이지 URL
         theme: 로그인 하지 않은 사용자나 새로운 사용자가 보게 될 테마.
         thumbnail: 대략 2:1 비율의 이미지가 서버 정보 옆에 표시됩니다.
         timeline_preview: 로그아웃 한 사용자들이 이 서버에 있는 최신 공개글들을 볼 수 있게 합니다.
         trendable_by_default: 유행하는 콘텐츠에 대한 수동 승인을 건너뜁니다. 이 설정이 적용된 이후에도 각각의 항목들을 삭제할 수 있습니다.
         trends: 트렌드는 어떤 게시물, 해시태그 그리고 뉴스 기사가 이 서버에서 인기를 끌고 있는지 보여줍니다.
+        trends_as_landing_page: 로그아웃한 사용자와 방문자에게 서버 설명 대신 유행하는 내용을 보여줍니다. 유행 기능을 활성화해야 합니다.
       form_challenge:
         current_password: 당신은 보안 구역에 진입하고 있습니다
       imports:
-        data: 다른 마스토돈 서버에서 추출된 CSV 파일
+        data: 다른 마스토돈 서버에서 내보낸 CSV 파일
       invite_request:
-        text: 이 정보는 우리가 심사를 하는 데에 참고할 수 있습니다
+        text: 이 정보는 신청을 검토하는데 도움을 줄 수 있습니다.
       ip_block:
         comment: 필수 아님. 왜 이 규칙을 추가했는지 기억하세요.
         expires_in: IP 주소는 한정된 자원입니다, 이것들은 가끔 공유 되거나 자주 소유자가 바뀌기도 합니다. 이런 이유로 인해, IP 차단을 영구히 유지하는 것은 추천하지 않습니다.
@@ -112,7 +116,7 @@ ko:
       rule:
         text: 이 서버 사용자들이 지켜야 할 규칙과 요구사항을 설명해주세요. 짧고 간단하게 작성해주세요
       sessions:
-        otp: '휴대전화에서 생성 된 2단계 인증 코드를 입력하거나, 복구 코드 중 하나를 사용하세요:'
+        otp: '휴대전화에서 생성된 이중 인증 코드를 입력하거나, 복구 코드 중 하나를 사용하세요:'
         webauthn: USB 키라면 삽입했는지 확인하고, 필요하다면 누르세요.
       tag:
         name: 읽기 쉽게하기 위한 글자의 대소문자만 변경할 수 있습니다.
@@ -146,11 +150,11 @@ ko:
         text: 커스텀 경고
         type: 조치
         types:
-          disable: 비활성화
-          none: 아무 것도 하지 않기
+          disable: 동결
+          none: 경고 보내기
           sensitive: 민감함
-          silence: 침묵
-          suspend: 정지하고 되돌릴 수 없는 데이터 삭제
+          silence: 제한
+          suspend: 정지
         warning_preset_id: 경고 틀 사용하기
       announcement:
         all_day: 종일 일정
@@ -159,11 +163,11 @@ ko:
         starts_at: 이벤트 시작
         text: 공지사항
       appeal:
-        text: 이 결정이 왜 번복되어야 하는지에 대해 설명해주세요
+        text: 이 결정을 번복해야만 하는 이유가 무엇입니까
       defaults:
         autofollow: 초대를 통한 팔로우
         avatar: 아바타
-        bot: 이것은 봇 계정입니다
+        bot: 이것은 자동화된 계정입니다
         chosen_languages: 언어 필터링
         confirm_new_password: 암호 다시 입력
         confirm_password: 암호 다시 입력
@@ -171,7 +175,7 @@ ko:
         current_password: 현재 암호 입력
         data: 데이터
         discoverable: 계정을 다른 사람들에게 추천하기
-        display_name: 표시되는 이름
+        display_name: 표시 이름
         email: 이메일 주소
         expires_in: 만료시각
         fields: 프로필 메타데이터
@@ -183,8 +187,8 @@ ko:
         locked: 팔로우 요청 필요
         max_uses: 사용 횟수 제한
         new_password: 새로운 암호 입력
-        note: 자기소개
-        otp_attempt: 2단계 인증 코드
+        note: 소개
+        otp_attempt: 이중 인증 코드
         password: 암호
         phrase: 키워드 또는 문장
         setting_advanced_layout: 고급 웹 UI 활성화
@@ -202,15 +206,15 @@ ko:
         setting_display_media_default: 기본
         setting_display_media_hide_all: 모두 가리기
         setting_display_media_show_all: 모두 보이기
-        setting_expand_spoilers: 열람주의 툿을 항상 펼치기
+        setting_expand_spoilers: 열람주의 게시물을 항상 펼치기
         setting_hide_network: 내 인맥 숨기기
         setting_noindex: 검색엔진의 인덱싱을 거절
         setting_reduce_motion: 애니메이션 줄이기
-        setting_show_application: 툿 작성에 사용한 앱을 공개
+        setting_show_application: 게시물 작성에 쓰인 애플리케이션 공개
         setting_system_font_ui: 시스템의 기본 글꼴을 사용
         setting_theme: 사이트 테마
         setting_trends: 오늘의 유행 보이기
-        setting_unfollow_modal: 언팔로우 전 언팔로우 확인 표시
+        setting_unfollow_modal: 누군가를 언팔로우 할 때 확인란 표시하기
         setting_use_blurhash: 숨겨진 미디어에 대해 그라디언트 표시
         setting_use_pending_items: 느린 모드
         severity: 심각도
@@ -229,6 +233,7 @@ ko:
           hide: 완전히 숨기기
           warn: 경고와 함께 숨기기
       form_admin_settings:
+        activity_api_enabled: API에 유저 활동에 대한 통계 발행
         backups_retention_period: 사용자 아카이브 유지 기한
         bootstrap_timeline_accounts: 새로운 사용자들에게 추천할 계정들
         closed_registrations_message: 가입이 불가능 할 때의 사용자 지정 메시지
@@ -236,6 +241,7 @@ ko:
         custom_css: 사용자 정의 CSS
         mascot: 사용자 정의 마스코트 (legacy)
         media_cache_retention_period: 미디어 캐시 유지 기한
+        peers_api_enabled: API에 발견 된 서버들의 목록 발행
         profile_directory: 프로필 책자 활성화
         registrations_mode: 누가 가입할 수 있는지
         require_invite_text: 가입 하는 이유를 필수로 입력하게 하기
@@ -247,11 +253,13 @@ ko:
         site_short_description: 서버 설명
         site_terms: 개인정보 정책
         site_title: 서버 이름
+        status_page_url: 상태 페이지 URL
         theme: 기본 테마
         thumbnail: 서버 썸네일
         timeline_preview: 로그인 하지 않고 공개 타임라인에 접근하는 것을 허용
         trendable_by_default: 사전 리뷰 없이 트렌드에 오르는 것을 허용
         trends: 유행 활성화
+        trends_as_landing_page: 유행을 방문 페이지로 쓰기
       interactions:
         must_be_follower: 나를 팔로우 하지 않는 사람에게서 온 알림을 차단
         must_be_following: 내가 팔로우 하지 않는 사람에게서 온 알림을 차단
@@ -278,13 +286,13 @@ ko:
         pending_account: 새 계정이 심사가 필요할 때
         reblog: 누군가 내 게시물을 부스트 했을 때
         report: 새 신고가 접수되었을 때
-        trending_tag: 새 트렌드에 대한 리뷰가 필요할 때
+        trending_tag: 검토해야 할 새 유행
       rule:
         text: 규칙
       tag:
         listable: 이 해시태그가 검색과 추천에 보여지도록 허용
         name: 해시태그
-        trendable: 이 해시태그가 유행에 보여지도록 허용
+        trendable: 이 해시태그가 유행에 나타날 수 있도록 허용
         usable: 이 해시태그를 게시물에 사용 가능하도록 허용
       user:
         role: 역할
@@ -297,7 +305,7 @@ ko:
       webhook:
         events: 활성화된 이벤트
         url: 엔드포인트 URL
-    'no': 아니오
+    'no': 아니요
     required:
       mark: "*"
       text: 필수 항목
diff --git a/config/locales/simple_form.ku.yml b/config/locales/simple_form.ku.yml
index aaef8cf55..f7188152d 100644
--- a/config/locales/simple_form.ku.yml
+++ b/config/locales/simple_form.ku.yml
@@ -18,8 +18,6 @@ ku:
           disable: Nehêle bikarhêner ajimêrê xwe bi kar bîne lê naverokan jê nabe an jî veneşêre.
           none: Ji bo ku tu hişyariyekê ji bikarhêner re bişînî vê bi kar bîne, bêyî ku çalakiyeke din bikî.
           sensitive: Neçar bihêle ku ev bikarhêner hemû pêvekên medyayê hestiyar nîşan bike.
-          silence: Pêşî li bikarhêneran bigire ku bikarhêner bi herkesî ra xûyabarî neşîne, post û agahdarîyên xwe ji mirovên ku wan naşopîne veşêre.
-          suspend: Pêşîya hevbandorîya vî ajimêrê bigire û naveroka wê jê bibe. Di nava 30 rojan de tê vegerandin.
         warning_preset_id: Bi dilê xwe ye. Tu hîn jî dikarî dawîya pêşsazkirî de nivîsek teybet li zêde bikî
       announcement:
         all_day: Dema were nîşankirin, tenê dîrokên navbera demê dê werin nîşan kirin
diff --git a/config/locales/simple_form.lv.yml b/config/locales/simple_form.lv.yml
index 57762f821..ff727235e 100644
--- a/config/locales/simple_form.lv.yml
+++ b/config/locales/simple_form.lv.yml
@@ -18,8 +18,8 @@ lv:
           disable: Neļauj lietotājam izmantot savu kontu, bet neizdzēs vai neslēp tā saturu.
           none: Izmanto šo, lai nosūtītu lietotājam brīdinājumu, neradot nekādas citas darbības.
           sensitive: Piespiest visus šī lietotāja multivides pielikumus atzīmēt kā sensitīvus.
-          silence: Neļauj lietotājam publicēt ziņas publiski redzamā veidā, paslēp viņu ziņas un paziņojumus no cilvēkiem, kuri viņiem neseko.
-          suspend: Novērst jebkādu mijiedarbību ar šo kontu vai ar to un dzēst tā saturu. Atgriežams 30 dienu laikā.
+          silence: Neļauj lietotājam publicēt ziņas ar publisku redzamību, paslēp viņa ziņas un paziņojumus no personām, kas viņiem neseko. Tiek aizvērti visi šī konta pārskati.
+          suspend: Novērs jebkādu mijiedarbību no šī konta vai uz to un dzēs tā saturu. Atgriežams 30 dienu laikā. Tiek aizvērti visi šī konta pārskati.
         warning_preset_id: Neobligāts. Tu joprojām vari pievienot pielāgotu tekstu sākotnējās iestatīšanas beigās
       announcement:
         all_day: Atzīmējot šo opciju, tiks parādīti tikai laika diapazona datumi
@@ -74,6 +74,7 @@ lv:
           hide: Paslēp filtrēto saturu pilnībā, izturoties tā, it kā tas neeksistētu
           warn: Paslēp filtrēto saturu aiz brīdinājuma, kurā minēts filtra nosaukums
       form_admin_settings:
+        activity_api_enabled: Vietēji publicēto ziņu, aktīvo lietotāju un jauno reģistrāciju skaits nedēļas kopās
         backups_retention_period: Saglabā ģenerētos lietotāju arhīvus norādīto dienu skaitā.
         bootstrap_timeline_accounts: Šie konti tiks piesprausti jauno lietotāju ieteikumu augšdaļā.
         closed_registrations_message: Tiek rādīts, kad reģistrēšanās ir slēgta
@@ -81,6 +82,7 @@ lv:
         custom_css: Vari lietot pielāgotus stilus Mastodon tīmekļa versijā.
         mascot: Ignorē ilustrāciju uzlabotajā tīmekļa saskarnē.
         media_cache_retention_period: Lejupielādētie multivides faili tiks dzēsti pēc norādītā dienu skaita, kad tie būs iestatīti uz pozitīvu vērtību, un pēc pieprasījuma tiks lejupielādēti atkārtoti.
+        peers_api_enabled: Domēna vārdu saraksts, ar kuriem šis serveris ir saskāries fediversā. Šeit nav iekļauti dati par to, vai tu veic federāciju ar noteiktu serveri, tikai tavs serveris par to zina. To izmanto dienesti, kas apkopo statistiku par federāciju vispārīgā nozīmē.
         profile_directory: Profilu direktorijā ir uzskaitīti visi lietotāji, kuri ir izvēlējušies būt atklājami.
         require_invite_text: 'Ja pierakstīšanai nepieciešama manuāla apstiprināšana, izdari tā, lai teksta: “Kāpēc vēlaties pievienoties?” ievade ir obligāta, nevis opcionāla'
         site_contact_email: Kā cilvēki var sazināties ar tevi par juridiskiem vai atbalsta jautājumiem.
@@ -89,11 +91,13 @@ lv:
         site_short_description: Īss apraksts, kas palīdzēs unikāli identificēt tavu serveri. Kurš to darbina, kam tas paredzēts?
         site_terms: Izmanto pats savu konfidencialitātes politiku vai atstāj tukšu, lai izmantotu noklusējuma iestatījumu. Var strukturēt ar Markdown sintaksi.
         site_title: Kā cilvēki var atsaukties uz tavu serveri, izņemot tā domēna nosaukumu.
+        status_page_url: Tās lapas URL, kurā lietotāji var redzēt šī servera statusu pārtraukuma laikā
         theme: Tēma, kuru redz apmeklētāji, kuri ir atteikušies, un jaunie lietotāji.
         thumbnail: Aptuveni 2:1 attēls, kas tiek parādīts kopā ar tava servera informāciju.
         timeline_preview: Atteikušies apmeklētāji varēs pārlūkot jaunākās serverī pieejamās publiskās ziņas.
         trendable_by_default: Izlaist aktuālā satura manuālu pārskatīšanu. Atsevišķas preces joprojām var noņemt no tendencēm pēc fakta.
         trends: Tendences parāda, kuras ziņas, atsauces un ziņu stāsti gūst panākumus tavā serverī.
+        trends_as_landing_page: Šī servera apraksta vietā rādīt aktuālo saturu lietotājiem un apmeklētājiem, kuri ir atteikušies. Nepieciešams iespējot tendences.
       form_challenge:
         current_password: Tu ieej drošā zonā
       imports:
@@ -229,6 +233,7 @@ lv:
           hide: Paslēpt pilnībā
           warn: Paslēpt ar brīdinājumu
       form_admin_settings:
+        activity_api_enabled: Publicējiet apkopotu statistiku par lietotāju darbībām API
         backups_retention_period: Lietotāja arhīva glabāšanas periods
         bootstrap_timeline_accounts: Vienmēr iesaki šos kontus jaunajiem lietotājiem
         closed_registrations_message: Pielāgots ziņojums, ja reģistrēšanās nav pieejama
@@ -236,6 +241,7 @@ lv:
         custom_css: Pielāgots CSS
         mascot: Pielāgots talismans (mantots)
         media_cache_retention_period: Multivides kešatmiņas saglabāšanas periods
+        peers_api_enabled: Publicēt API atklāto serveru sarakstu
         profile_directory: Iespējot profila direktoriju
         registrations_mode: Kurš drīkst pieteikties
         require_invite_text: Pieprasīt pievienošanās iemeslu
@@ -247,11 +253,13 @@ lv:
         site_short_description: Servera apraksts
         site_terms: Privātuma Politika
         site_title: Servera nosaukums
+        status_page_url: Statusa lapas URL
         theme: Noklusētā tēma
         thumbnail: Servera sīkbilde
         timeline_preview: Atļaut neautentificētu piekļuvi publiskajām ziņu lentām
         trendable_by_default: Atļaut tendences bez iepriekšējas pārskatīšanas
         trends: Iespējot tendences
+        trends_as_landing_page: Izmantojiet tendences kā galveno lapu
       interactions:
         must_be_follower: Bloķēt paziņojumus no ne-sekotājiem
         must_be_following: Bloķēt paziņojumus no cilvēkiem, kuriem tu neseko
diff --git a/config/locales/simple_form.ms.yml b/config/locales/simple_form.ms.yml
index fb892181d..57aff248b 100644
--- a/config/locales/simple_form.ms.yml
+++ b/config/locales/simple_form.ms.yml
@@ -18,8 +18,6 @@ ms:
           disable: Halang pengguna daripada menggunakan akaun mereka, tetapi jangan hapus atau sorokkan kandungan mereka.
           none: Gunakan ini untuk menghantar amaran kepada pengguna, tanpa mencetuskan sebarang tindakan lain.
           sensitive: Memaksa semua lampiran media pengguna ini ditandakan sebagai sensitif.
-          silence: Halang pengguna daripada membuat hantaran secara awam, menyorokkan hantaran dan pemberitahuan mereka daripada orang yang tidak mengikuti mereka.
-          suspend: Halang sebarang interaksi daripada atau kepada akaun ini dan menghapuskan kandungannya. Boleh dikembalikan dalam 30 hari.
         warning_preset_id: Pilihan. Anda masih boleh menambah teks tersuai pada akhir praset
       announcement:
         all_day: Apabila diperiksa, hanya tarikh dalam julat masa yang akan dipaparkan
diff --git a/config/locales/simple_form.my.yml b/config/locales/simple_form.my.yml
index 5e1fc6bee..5f1624819 100644
--- a/config/locales/simple_form.my.yml
+++ b/config/locales/simple_form.my.yml
@@ -1 +1,309 @@
+---
 my:
+  simple_form:
+    hints:
+      account_warning_preset:
+        text: URLs၊ hashtags နှင့် mentions များကဲ့သို့ ပို့စ်ကို သုံးနိုင်သည်
+        title: လက်ခံသူမှ မြင်နိုင်၊ မမြင်နိုင် ရွေးချယ်ခွင့်
+      admin_account_action:
+        include_statuses: အသုံးပြုသူသည် မည်သည့်ပို့စ်များမှာ စိစစ်ထားခြင်း သို့မဟုတ် သတိပေးထားခြင်းဖြစ်စေသည်ကို တွေ့မြင်နိုင်ပါသည်
+        send_email_notification: အသုံးပြုသူသည် ၎င်းတို့၏အကောင့်နှင့် ဖြစ်ပျက်ခဲ့သော ရှင်းလင်းချက်ကို လက်ခံရရှိမည်ဖြစ်သည်
+        text_html: Post Syntax ကို သုံးနိုင်သည်။ အချိန်ကုန်သက်သာစေရန်အတွက် <a href="%{path}">ကြိုတင်သတိပေးချက်များ ထည့်နိုင်သည်</a>
+        type_html: "<strong>%{acct}</strong> နှင့် ဘာလုပ်ရမည်ကို ရွေးပါ။"
+        types:
+          disable: အသုံးပြုသူကို ၄င်း၏အကောင့်အား သုံးစွဲခြင်းမှ ဟန့်တားပါ။ သို့သော် ၄င်းဖန်တီးထားသည့် အချက်အလက်များကို မဖျက်ပါ။ မဖျောက်ပါ။
+          none: အခြားလုပ်ဆောင်ချက်တစ်ခုမှ မလုပ်ဘဲ အသုံးပြုသူထံ သတိပေးချက်ပေးပို့ရန် ၎င်းကို အသုံးပြုပါ။
+          sensitive: ဤအသုံးပြုသူ၏ မီဒီယာပူးတွဲပါဖိုင်များအားလုံးကို သတိထားရသောမီဒီယာအဖြစ် သတ်မှတ်ရန် တိုက်တွန်းပါ။
+          silence: စောင့်ကြည့်မထားသူများ၏ ပို့စ်များနှင့် အသိပေးချက်များကို ဖျောက်ထားနိုင်ပြီး အသုံးပြုသူအား အများမြင်မည့်ပို့စ် တင်ခြင်းကို တားမြစ်နိုင်သည်။ ဤအကောင့်နှင့်ပတ်သက်သည့် အစီရင်ခံစာအားလုံးကို ပိတ်ထားနိုင်သည်။
+          suspend: ဤအကောင့်မှ သို့မဟုတ် ဤအကောင့်နှင့် အပြန်အလှန်တုံ့ပြန်မှုကို တားဆီးပြီး အကြောင်းအရာများကို ဖျက်ပါ။ ရက်ပေါင်း ၃၀ အတွင်း ပြန်ပြောင်းနိုင်သည်။ ဤအကောင့်နှင့်ပတ်သက်သည့် အစီရင်ခံစာအားလုံးကို ပိတ်ရန်။
+        warning_preset_id: ကြိုတင်သတ်မှတ်မှုအဆုံးတွင် စိတ်ကြိုက်စာသားကို ထည့်နိုင်သည်
+      announcement:
+        all_day: အချိန်အပိုင်းအခြား ရက်စွဲများကိုသာ ပြသပါမည်
+        ends_at: ကြေညာချက်ထုတ်ပြန်ခြင်းကို ဤအချိန်တွင် အလိုအလျောက် ရပ်တန့်လိုက်ပါမည်
+        scheduled_at: ကြေညာချက်ချက်ချင်းထုတ်ပြန်ရန်အတွက် နေရာလွတ်ချန်ထားပါ
+        starts_at: သင့်ကြေညာချက်သည် အချိန်အပိုင်းအခြားတစ်ခုနှင့် ဆက်စပ်နိုင်ပါသည်
+        text: Post Syntax ကို သုံးနိုင်သည်။ အသုံးပြုသူ၏ဖန်သားပြင်တွင် ပေါ်လာမည့် ကြေညာချက်နေရာကို သတိထားပါ
+      appeal:
+        text: လုပ်ဆောင်ချက်တစ်ကြိမ်သာ အသုံးပြုနိုင်သည်
+      defaults:
+        autofollow: ဖိတ်ကြားချက်မှတစ်ဆင့် အကောင့်ဖွင့်သူများမှာ သင့်ကို အလိုအလျောက်စောင့်ကြည့်မည်ဖြစ်ပါသည်
+        avatar: PNG၊ GIF သို့မဟုတ် JPG။ အများဆုံး %{size}။ %{dimensions}px သို့ လျှော့ချပါမည်။
+        bot: အကောင့်သည် အဓိကအားဖြင့် အလိုအလျောက် လုပ်ဆောင်ချက်များ ဆောင်ရွက်နိုင်ပြီး စောင့်ကြည့်ခြင်းမပြုနိုင်ကြောင်း အခြားသူများအား အသိပေးပါ
+        context: စစ်ထုတ်သင့်သည့် အကြောင်းအရာ တစ်ခု သို့မဟုတ် များစွာ
+        current_password: လုံခြုံရေးအတွက် ကျေးဇူးပြုပြီး လက်ရှိအကောင့်၏ စကားဝှက်ကို ထည့်ပါ
+        current_username: အတည်ပြုရန်အတွက် လက်ရှိအကောင့်၏ အသုံးပြုသူအမည်ကို ရိုက်ထည့်ပါ
+        digest: အချိန်အတော်ကြာ အသုံးမပြုသည့်သည့်နောက်တွင်သာ ပေးပို့ပြီး အသုံးမပြုသည့်ကာလအတွင်း ကိုယ်ရေးကိုယ်တာစာများသာ လက်ခံရန်
+        discoverable: အကြံပြုချက်များ၊ လက်ရှိခေတ်စားနေသောပို့စ်များနှင့် အခြားအကြောင်းအရာများမှတစ်ဆင့် သင့်အကောင့်ကို တခြားသူများက တွေ့ရှိနိုင်စေရန် ခွင့်ပြုပါ။
+        email: သင့်ထံ အတည်ပြုချက်အီးမေးလ်တစ်စောင် ပေးပို့ပါမည်
+        fields: သင့်ပရိုဖိုင်တွင် ၄ ခုအထိ ပြသထားနိုင်သည်
+        header: PNG၊ GIF သို့မဟုတ် JPG။ အများဆုံး %{size}။ %{dimensions}px သို့ လျှော့ချပါမည်
+        inbox_url: သင်အသုံးပြုလိုသော relay ၏ ရှေ့စာမျက်နှာမှ URL ကို ကူးယူပါ
+        irreversible: စစ်ထုတ်ခြင်းကိုဖယ်ရှားလိုက်ပါက စစ်ထုတ်ထားသော ပို့စ်များမှာ ပျောက်ကွယ်သွားပါမည်
+        locale: အသုံးပြုသူမှ လက်ရှိသုံးနေသည့်ဘာသာစကား၊ အီးမေးလ်များနှင့် ရရှိစေရန်ပေးပို့သည့် အသိပေးချက်များ
+        locked: စောင့်ကြည့်ရန်အတွက် တောင်းဆိုမှုများကို အတည်ပြုခြင်းဖြင့် စောင့်ကြည့်သူများကို ထိန်းချုပ်နိုင်သည်
+        password: အနည်းဆုံး စာလုံး ၈ လုံး အသုံးပြုပါ။
+        scopes: API အက်ပလီကေးရှင်းများကို ဝင်ရောက်ကြည့်ရှုခွင့်ပြုပါမည်။ ထိပ်တန်းအဆင့်နယ်ပယ်ကို ရွေးချယ်ထားပါက တစ်ခုချင်းစီရွေးချယ်ရန် မလိုအပ်တော့ပါ။
+        setting_aggregate_reblogs: မကြာသေးခင်က Boost လုပ်ထားသောပို့စ်များအတွက် Boost  အသစ်များကို မပြပါနှင့် (အသစ်ရရှိထားသော Boost များကိုသာ ပြသပါရန်)
+        setting_always_send_emails: Mastodon ကို လက်ရှိအသုံးပြုနေချိန်တွင် ပုံမှန်အားဖြင့် အီးမေးလ်အသိပေးချက်များကို ပေးပို့မည်မဟုတ်ပါ
+        setting_default_sensitive: သတိထားရသောမီဒီယာကို မူလအားဖြင့် ဖျောက်ထားနိုင်ပြီး ကလစ်တစ်ချက်နှိပ်ရုံဖြင့် ပြန်လည်ဖော်ပြနိုင်သည်
+        setting_display_media_default: သတိထားရသောမီဒီယာအဖြစ် သတ်မှတ်ထားမှုအား ဖျောက်ပါ
+        setting_display_media_hide_all: မီဒီယာကို အမြဲတမ်းဖျောက်ထားပါ
+        setting_display_media_show_all: မီဒီယာကို အမြဲတမ်းပြပါ
+        setting_hide_network: သင်စောင့်ကြည့်မည့်သူများနှင့် သင့်ကိုစောင့်ကြည့်မည့်သူများကို သင့်ပရိုဖိုင်တွင် ဖျောက်ထားနိုင်ပါသည်
+        setting_noindex: သင်၏ အများမြင်သည့်ပရိုဖိုင်နှင့် ပို့စ်စာမျက်နှာများကို အကျိုးသက်ရောက်သည်
+        setting_show_application: ပို့စ်တင်ရန်အတွက် သင်အသုံးပြုသည့် အက်ပလီကေးရှင်းကို သင့်ပို့စ်များ၏ အသေးစိတ်ကြည့်ရှုမှုတွင် ပြသမည်ဖြစ်သည်
+        username: "%{domain} ရှိ သင့်အသုံးပြုသူအမည်မှာ တူညီ၍မရပါ"
+        whole_word: အဓိကစကားလုံး သို့မဟုတ် စကားစုသည် အက္ခရာဂဏန်းများသာဖြစ်ပါကစကားလုံးတစ်ခုလုံးနှင့် ကိုက်ညီမှသာ အသုံးပြုနိုင်မည်ဖြစ်သည်
+      domain_allow:
+        domain: ဤဒိုမိန်းသည် ဤဆာဗာမှ အချက်အလက်များကို ရယူနိုင်မည်ဖြစ်ပြီး ဝင်လာသောအချက်အလက်များကို စီမံဆောင်ရွက်ပေးပြီး သိမ်းဆည်းသွားမည်ဖြစ်သည်
+      email_domain_block:
+        domain: "၎င်းမှာ အီးမေးလ်လိပ်စာ သို့မဟုတ် အသုံးပြုသည့် MX မှတ်တမ်းတွင် ပေါ်လာသည့် ဒိုမိန်းအမည် ဖြစ်နိုင်သည်။ အကောင့်ဖွင့်ပြီးပါက စစ်ဆေးနိုင်မည်ဖြစ်သည်။"
+        with_dns_records: ပေးထားသော ဒိုမိန်း၏ DNS မှတ်တမ်းများကို ဖြေရှင်းရန် ကြိုးပမ်းမှု ပြုလုပ်မည်ဖြစ်ပြီး ရလဒ်များကိုလည်း ပိတ်ဆို့သွားမည်ဖြစ်သည်။
+      featured_tag:
+        name: ဤသည်မှာ သင်မကြာသေးမီက အသုံးပြုခဲ့သော hashtag အချို့ဖြစ်သည် -
+      filters:
+        action: ပို့စ်တစ်ခုသည် စစ်ထုတ်မှုနှင့် ကိုက်ညီချိန်တွင် မည်သည့်လုပ်ဆောင်ချက် ဆောင်ရွက်မည်ကို ရွေးချယ်ပါ
+        actions:
+          hide: စစ်ထုတ်ထားသော အကြောင်းအရာကို လုံးဝဖျောက်ထားပြီး မရှိသကဲ့သို့ ပြပါ
+          warn: စစ်ထုတ်မှုခေါင်းစဉ်ကိုဖော်ပြသည့်သတိပေးချက်နောက်တွင် စစ်ထုတ်ထားသောအကြောင်းအရာကို ဖျောက်ထားပါ
+      form_admin_settings:
+        activity_api_enabled: အပတ်စဉ် စာရင်းများတွင် ဒေသတွင်းတင်ထားသောပို့စ်များ၊ လက်ရှိအသုံးပြုသူများနှင့် စာရင်းသွင်းမှုအသစ်များ
+        backups_retention_period: သတ်မှတ်ထားသော ရက်အရေအတွက်အလိုက် အသုံးပြုသူမှတ်တမ်းများကို သိမ်းဆည်းပါ။
+        bootstrap_timeline_accounts: ဤအကောင့်များကို အသုံးပြုသူအသစ်များ၏ စောင့်ကြည့်မှု အကြံပြုချက်များ၏ထိပ်ဆုံးတွင် ပင်ချိတ်ထားပါမည်။
+        closed_registrations_message: အကောင့်ဖွင့်ခြင်းများကို ပိတ်ထားသည့်အခါတွင် ပြသထားသည်
+        content_cache_retention_period: သတ်မှတ်ထားသောရက်များပြီးနောက် အခြားဆာဗာများမှ ပို့စ်များကို ဖျက်လိုက်ပါမည်။ ပြန်လည်ပြင်ဆင်၍မရပါ။
+        custom_css: Mastodon ဝဘ်ဗားရှင်းတွင် စိတ်ကြိုက်စတိုင်များကို အသုံးပြုနိုင်ပါသည်။
+        mascot: အဆင့်မြင့် ဝဘ်ပုံစံတွင်တွင် ရုပ်ပုံဖြင့်ဖော်ပြထားသည်။
+        media_cache_retention_period: သတ်မှတ်ထားသောရက်များပြီးနောက် ဒေါင်းလုဒ်လုပ်ထားသော မီဒီယာဖိုင်များကို ဖျက်လိုက်ပါမည်။ တောင်းဆိုပါက ပြန်လည်ဒေါင်းလုဒ် လုပ်ခွင့်ရှိသည်။
+        profile_directory: ပရိုဖိုင်လမ်းညွှန်တွင် ရှာဖွေ‌နိုင်သည့်အသုံးပြုသူအားလုံးကို စာရင်းပြုစုထားသည်။
+        require_invite_text: အကောင့်ဖွင့်ရာတွင် လူကိုယ်တိုင်ခွင့်ပြုချက်လိုအပ်သောအခါ “ဘာကြောင့်ပါဝင်ချင်သလဲ” ဟုလုပ်ပါ။ စိတ်ကြိုက်ရွေးချယ်မည့်အစား စာသားထည့်သွင်းရန်မဖြစ်မနေထည့်သွင်းပါ။
+        site_contact_email: ဥပဒေရေးရာ သို့မဟုတ် အကူအညီ စုံစမ်းမေးမြန်းရန် လူများက သင့်ထံ မည်သို့ဆက်သွယ်နိုင်မည်နည်း။
+        site_contact_username: Mastodon တွင် အခြားသူများက သင့်ကို မည်သို့သိရှိနိုင်မည်နည်း။
+        site_extended_description: ဝင်ရောက်ကြည့်ရှုသူများနှင့် အသုံးပြုသူများအတွက် အသုံးဝင်မည့် နောက်ထပ်အချက်အလက်များကို Markdown စာကြောင်းများဖြင့် ရေးသားနိုင်သည်။
+        site_short_description: သင်၏ဆာဗာအား သိသားထင်ရှားစွာ အမှတ်အသားပြုနိုင်မည့် အကျဉ်းရုံးဖော်ပြချက်။ မည်သူမှ ဆာဗာအား ထိန်းကြောင်းလည်ပတ်နေသည်၊ မည်သူ့အတွက် ဖြစ်သည်?
+        site_terms: သင့်ကိုယ်ပိုင် ကိုယ်ရေးအချက်အလက်မူဝါဒကို အသုံးပြုပါ သို့မဟုတ် မူလသတ်မှတ်ချက်ကို အသုံးပြုရန်အတွက် ကွက်လပ်ထားပါ။ Markdown စာများဖြင့် ရေးသားနိုင်သည်။
+        site_title: ဒိုမိန်းအမည်မှတပါး သင့်ဆာဗာကို လူများက မည်သို့သိရှိနိုင်မည်နည်း။
+        status_page_url: ပြတ်တောက်နေစဉ်အတွင်း လူများက ဤဆာဗာအခြေအနေကို မြင်နိုင်မည့် စာမျက်နှာ URL
+        theme: အကောင့်မှထွက်ပြီး အသုံးပြုသူအသစ်များနှင့် ဝင်ကြည့်မည့်သူများအတွက် မြင်ရမည့်ပုံစံ။
+        thumbnail: သင့်ဆာဗာအချက်အလက်နှင့်အတူ အကြမ်းဖျင်းအားဖြင့် ၂:၁ ဖြင့် ပြသထားသောပုံတစ်ပုံ။
+        timeline_preview: အကောင့်မှထွက်ထားသူများသည် ဆာဗာပေါ်ရှိ လတ်တလော အများမြင်ပို့စ်များကို ရှာဖွေကြည့်ရှုနိုင်မည်ဖြစ်သည်။
+        trendable_by_default: ခေတ်စားနေသော အကြောင်းအရာများ၏ ကိုယ်တိုင်သုံးသပ်ချက်ကို ကျော်ပါ။ နောက်ပိုင်းတွင် အချက်အလက်တစ်ခုချင်းစီကို ခေတ်စားနေသောအကြောင်းအရာများကဏ္ဍမှ ဖယ်ရှားနိုင်ပါသေးသည်။
+        trends: လက်ရှိခေတ်စားနေသာပို့စ်များ၊ hashtag များနှင့် သတင်းဇာတ်လမ်းများကို သင့်ဆာဗာပေါ်တွင် တွေ့မြင်နိုင်ပါမည်။
+        trends_as_landing_page: ဤဆာဗာဖော်ပြချက်အစား အကောင့်မှ ထွက်ထားသူများနှင့် ဝင်ရောက်ကြည့်ရှုသူများအတွက် ခေတ်စားနေသော အကြောင်းအရာများကို ပြသပါ။ ခေတ်စားနေသောပို့စ်များကို ဖွင့်ထားရန် လိုအပ်သည်။
+      form_challenge:
+        current_password: သင်သည် လုံခြုံသောနေရာသို့ ဝင်ရောက်နေပါသည်
+      imports:
+        data: အခြား Mastodon ဆာဗာမှ CSV ဖိုင်ကို ပို့ထားသည်
+      invite_request:
+        text: "၎င်းသည် သင့်အက်ပလီကေးရှင်းကို ပြန်လည်သုံးသပ်ရန်အတွက် ကူညီပေးနိုင်ပါသည်"
+      ip_block:
+        comment: ဤစည်းမျဉ်း ဘာကြောင့်ထည့်ခဲ့သည်အား သတိရရန်။
+        expires_in: အိုင်ပီလိပ်စာများမှာ အကန့်အသတ်ရှိသည့် အရင်းအမြစ်ဖြစ်သည်။ တခါတရံ လိပ်စာများအား မျှဝေသုံးစွဲကြသည်။ အခါအားလျှော်စွာ လက်လွှဲသုံးစွဲမှုများလည်း ရှီတတ်သည်။ ထို့အတွက်ကြောင့် အကန့်အသတ်မဲ့ အိုင်ပီကန့်သတ်ထိန်းချုပ်မှု လုပ်ဆောင်ခြင်းကို အားမပေးပါ။
+        ip: IPv4 သို့မဟုတ် IPv6 လိပ်စာကို ထည့်ပါ။ CIDR syntax ကို အသုံးပြု၍ အတိုင်းအတာတစ်ခုလုံးကို ပိတ်ဆို့နိုင်သည်။ ကိုယ်တိုင် လော့ခ်မချမိစေရန် သတိထားပါ။
+        severities:
+          no_access: အရင်းအမြစ်များအားလုံးသို့ ဝင်ရောက်ခွင့်ကို ပိတ်ပါ
+          sign_up_block: အကောင့်အသစ်များ မဖွင့်နိုင်တော့ပါ
+          sign_up_requires_approval: အကောင့်အသစ်များသည် သင့်ခွင့်ပြုချက်လိုအပ်ပါသည်
+        severity: ဤ IP မှ တောင်းဆိုမှုများဖြင့် ဖြစ်ပေါ်လာမည့်အရာ ရွေးချယ်ပါ
+      rule:
+        text: ဤဆာဗာအသုံးပြုသူများအတွက် စည်းမျဉ်း သို့မဟုတ် လိုအပ်ချက် ဖော်ပြပါ။ လိုရင်းတိုရှင်းဖြစ်ပါစေ။
+      sessions:
+        otp: သင့်ဖုန်းအက်ပ်မှထုတ်ပေးသောနှစ်ဆင့်ခံလုံခြုံရေးကုဒ်ကို ထည့်ပါ သို့မဟုတ် ပြန်လည်ရယူရေးကုဒ်များထဲမှ တစ်ခုကို အသုံးပြုပါ -
+        webauthn: USB ကီးဖြစ်ပါက ထည့်သွင်းပါ။ လိုအပ်ပါက နှိပ်ပါ။
+      tag:
+        name: ဥပမာအားဖြင့် စာလုံးများကို ပိုမိုဖတ်ရှုနိုင်စေရန်မှာ သင်သာ ပြောင်းလဲနိုင်သည်။
+      user:
+        chosen_languages: အမှန်ခြစ် ရွေးချယ်ထားသော ဘာသာစကားများဖြင့်သာ ပို့စ်များကို အများမြင်စာမျက်နှာတွင် ပြသပါမည်
+        role: အသုံးပြုသူ၏ ခွင့်ပြုချက်ကဏ္ဍကို ထိန်းချုပ်ထားသည်
+      user_role:
+        color: hex ပုံစံ RGB အဖြစ် UI တစ်လျှောက်လုံး အခန်းကဏ္ဍအတွက် အသုံးပြုရမည့်အရောင်
+        highlighted: ယင်းက အခန်းကဏ္ဍကို အများမြင်အောင် ဖွင့်ပေးထားသည်။
+        name: အကယ်၍ အခန်းကဏ္ဍကို သင်္ကေတတစ်ခုအဖြစ်ပြသရန် သတ်မှတ်ထားပါက အခန်းကဏ္ဍကို အများမြင်မည့်အမည်
+        permissions_as_keys: ဤအခန်းကဏ္ဍဖြင့် အသုံးပြုသူများသာ အသုံးပြုခွင့်ရှိပါမည်...
+        position: အခန်းကဏ္ဍအဆင့်မြင့်သူအနေဖြင့် အချို့သောအခြေအနေများတွင် ပဋိပက္ခများကို ဖြေရှင်းနိုင်သည်။ အချို့လုပ်ဆောင်ချက်များမှာ အဆင့်နိမ့်အခန်းကဏ္ဍများတွင်သာ လုပ်ဆောင်နိုင်သည်။
+      webhook:
+        events: ပို့မည့်အကြောင်းအရာများကို ရွေးချယ်ပါ
+        url: အကြောင်းအရာများကို ဘယ်ကို ပို့မလဲ။
+    labels:
+      account:
+        fields:
+          name: အညွှန်း
+          value: အကြောင်းအရာ
+      account_alias:
+        acct: အကောင့်ဟောင်းကို ကိုင်တွယ်ပါ။
+      account_migration:
+        acct: အကောင့်သစ်ကို ကိုင်တွယ်ပါ။
+      account_warning_preset:
+        text: ကြိုရေးထားသောစာ
+        title: ခေါင်းစဥ်
+      admin_account_action:
+        include_statuses: အီးမေးလ်တွင် တိုင်ကြားထားသောပို့စ်များကို ထည့်သွင်းပါ
+        send_email_notification: အသုံးပြုသူတစ်ယောက်တွင် အီးမေးလ်တစ်ခုသာ အသုံးပြုရပါမည်
+        text: စိတ်ကြိုက်သတိပေးချက်
+        type: လုပ်ဆောင်ချက်
+        types:
+          disable: ရပ်တန့်
+          none: သတိပေးချက်ပေးပို့ပါ
+          sensitive: သတိထားရသော
+          silence: ကန့်သတ်
+          suspend: ရပ်ဆိုင်းရန်
+        warning_preset_id: ကြိုတင်သတိပေးချက်ကို အသုံးပြုပါ
+      announcement:
+        all_day: တစ်နေ့တာလုပ်ငန်းစဉ်
+        ends_at: အကြောင်းအရာ၏အဆုံး
+        scheduled_at: ထုတ်ပြန်မည့်အချိန်
+        starts_at: အကြောင်းအရာ၏အစ
+        text: ကြေညာချက်
+      appeal:
+        text: ဆုံးဖြတ်ချက် ပြောင်းပြန်ချသင့်သောအကြောင်းရင်းအား ရှင်းပြပါ။
+      defaults:
+        autofollow: သင့်အကောင့်စောင့်ကြည့်ရန် ဖိတ်ခေါ်ပါ
+        avatar: ကိုယ်စားပြုရုပ်ပုံ
+        bot: ဤသည်မှာ ဘော့တ်အကောင့်တစ်ခုဖြစ်သည်။
+        chosen_languages: ဘာသာစကားများကို စစ်ထုတ်ထားခြင်း
+        confirm_new_password: စကားဝှက်အသစ်ကို အတည်ပြုပါ
+        confirm_password: စကားဝှက်ကို အတည်ပြုပါ။
+        context: အကြောင်းအရာများကို စစ်ထုတ်ပါ။
+        current_password: လက်ရှိစကားဝှက်
+        data: အချက်အလက်
+        discoverable: အကောင့်ကို အခြားသူများအား အကြံပြုပါ
+        display_name: ဖော်ပြမည့်အမည်
+        email: အီးမေးလ်လိပ်စာ
+        expires_in: သက်တမ်းကုန်ဆုံးမည့်ရက်
+        fields: ပရိုဖိုင်မီတာဒေတာ
+        header: မျက်နှာဖုံးပုံ
+        honeypot: "%{label} (မဖြည့်ပါနှင့်)"
+        inbox_url: ထပ်ဆင့်ဝင်စာ၏ URL
+        irreversible: ဖျောက်မယ့်အစား လွှတ်လိုက်ပါ။
+        locale: ဘာသာစကား
+        locked: စောင့်ကြည့်တောင်းဆိုမှုများ လိုအပ်သည်
+        max_uses: အများဆုံးအသုံးပြုမှုအရေအတွက်
+        new_password: စကားဝှက်အသစ်
+        note: ကိုယ်ရေးအကျဉ်း
+        otp_attempt: နှစ်ဆင့်ခံလုံခြုံရေးကုဒ်
+        password: စကားဝှက်
+        phrase: အဓိကစကားလုံး သို့မဟုတ် စကားစု
+        setting_advanced_layout: အဆင့်မြှင့်ထားသည့်ဝဘ်ကို ဖွင့်ပါ
+        setting_aggregate_reblogs: စာမျက်နှာများရှိ အဖွဲ့လိုက် Boost များ
+        setting_always_send_emails: အီးမေးလ်သတိပေးချက်များကို အမြဲပို့ပါ
+        setting_auto_play_gif: ကာတွန်း GIF များကို အလိုအလျောက်ဖွင့်ပါ
+        setting_boost_modal: Boost မလုပ်မီ အတည်ပြုချက်ပြပါ
+        setting_crop_images: အကျယ်မချဲ့ထားသော စာစုများတွင် ပုံများကို ၁၆း၉ အရွယ် ဖြတ်တောက်ပါ။
+        setting_default_language: ပို့စ်တင်မည့်ဘာသာစကား
+        setting_default_privacy: ပို့စ်ကို ဘယ်သူမြင်နိုင်မလဲ
+        setting_default_sensitive: သတိထားရသောမီဒီယာအဖြစ် အမြဲအမှတ်အသားပြုပါ
+        setting_delete_modal: ပို့စ်တစ်ခုမဖျက်မီ အတည်ပြုချက်ပြပါ။
+        setting_disable_swiping: ပွတ်ဆွဲခြင်းများကို ပိတ်ပါ
+        setting_display_media: မီဒီယာဖော်ပြမှု
+        setting_display_media_default: မူလသတ်မှတ်ချက်
+        setting_display_media_hide_all: အားလုံးကို ဖျောက်ပါ
+        setting_display_media_show_all: အားလုံးပြရန်
+        setting_expand_spoilers: အကြောင်းအရာသတိပေးချက်များဖြင့် အမှတ်အသားပြုထားသော ပို့စ်များကို အမြဲချဲ့ပါ
+        setting_hide_network: သင့် Social Graph ကို ဖျောက်ထားပါ
+        setting_noindex: ရှာဖွေရေးအညွှန်းမှ ဖယ်ထုတ်ပါ
+        setting_reduce_motion: ကာတွန်းများတွင် လှုပ်ရှားမှုကို လျှော့ချပါ
+        setting_show_application: ပို့စ်များ ပေးပို့ရာတွင် အသုံးပြုသည့် အက်ပလီကေးရှင်း
+        setting_system_font_ui: စနစ်ရှိ နဂိုမူလ စာလုံးပုံစံကို အသုံးပြုပါ
+        setting_theme: ဆိုက်အပြင်အဆင်
+        setting_trends: ယနေ့ ရေပန်းစားသည်များကို ပြပါ
+        setting_unfollow_modal: တစ်စုံတစ်ဦးကို မစောင့်ကြည့်မီ အတည်ပြုချက် ဒိုင်ယာလော့ခ်ကို ပြပါ
+        setting_use_blurhash: ဖုံးကွယ်ထားသော ရုပ်ပုံ၊ဗီဒီယိုများအား အရောင်စုံ ရောင်ပြေးအနေနှင့် ပြသပါ။
+        setting_use_pending_items: အနှေးပြကွက်
+        severity: ပြင်းထန်မှု
+        sign_in_token_attempt: လုံခြုံရေးကုဒ်
+        title: ခေါင်းစဥ်
+        type: ထည့်သွင်းမှုအမျိုးအစား
+        username: အသုံးပြုသူအမည်
+        username_or_email: အသုံးပြုသူအမည် သို့မဟုတ် အီးမေးလ်
+        whole_word: စကားလုံးဖြင့်သာ
+      email_domain_block:
+        with_dns_records: ဒိုမိန်း၏ MX မှတ်တမ်းများနှင့် IP များ ထည့်သွင်းပါ
+      featured_tag:
+        name: Hashtag
+      filters:
+        actions:
+          hide: လုံးဝဖျောက်ထားပါ
+          warn: သတိပေးချက်ဖြင့် ဖျောက်ပါ
+      form_admin_settings:
+        activity_api_enabled: API ရှိ အသုံးပြုသူလုပ်ဆောင်ချက်စာရင်းများကို ထုတ်ပြန်ပါ
+        backups_retention_period: အသုံးပြုသူ၏ မှတ်တမ်းကာလ
+        bootstrap_timeline_accounts: ဤအကောင့်များကို အသုံးပြုသူအသစ်များအတွက် အကြံပြုပေးပါ
+        closed_registrations_message: အကောင့်ဖွင့်ခြင်းများ မရတော့သောအခါ စိတ်ကြိုက်မက်ဆေ့ချ်ပို့ခြင်း
+        content_cache_retention_period: အကြောင်းအရာ ကက်ရှ်ထိန်းသိမ်းသည့်ကာလ
+        custom_css: စိတ်ကြိုက်ပြုလုပ်ထားသော CSS
+        mascot: စိတ်ကြိုက်ပြုလုပ်ထားသော mascot (legacy)
+        media_cache_retention_period: မီဒီယာကက်ရှ် ထိန်းသိမ်းသည့်ကာလ
+        peers_api_enabled: API တွင် ရှာဖွေတွေ့ရှိထားသော ဆာဗာများစာရင်းကို ထုတ်ပြန်ပါ
+        profile_directory: ပရိုဖိုင်လမ်းညွှန်ကို ဖွင့်ပါ
+        registrations_mode: ဘယ်သူတွေ အကောင့်ဖွင့်နိုင်မလဲ
+        require_invite_text: ပါဝင်ရန် အကြောင်းပြချက်တစ်ခု လိုအပ်ပါသည်
+        show_domain_blocks: ဒိုမိန်းပိတ်ပင်ထားမှုများကိုပြရန်
+        show_domain_blocks_rationale: ဒိုမိန်းများကို ဘာကြောင့် ပိတ်ဆို့ထားရကြောင်း ပြရန်
+        site_contact_email: ဆက်သွယ်ရမည့် အီးမေးလ်
+        site_contact_username: ဆက်သွယ်ရမည့် အသုံးပြုသူအမည်
+        site_extended_description: တိုးချဲ့ဖော်ပြချက်
+        site_short_description: ဆာဗာဖော်ပြချက်
+        site_terms: ကိုယ်ရေးအချက်အလက်မူဝါဒ
+        site_title: ဆာဗာအမည်
+        status_page_url: အခြေအနေပြစာမျက်နှာ URL
+        theme: မူလသတ်မှတ်ထားသည့် အပြင်အဆင်
+        thumbnail: ဆာဗာ ပုံသေး
+        timeline_preview: အများမြင်စာမျက်နှာများသို့ အထောက်အထားမရှိဘဲ ဝင်ရောက်ခွင့်ပြုပါ
+        trendable_by_default: ကြိုမသုံးသပ်ဘဲ ခေတ်စားနေသောအကြောင်းအရာများကို ခွင့်ပြုပါ
+        trends: လက်ရှိခေတ်စားနေမှုများကိုပြပါ
+        trends_as_landing_page: ခေတ်စားနေသောပို့စ်များကို landing စာမျက်နှာအဖြစ် အသုံးပြုပါ
+      interactions:
+        must_be_follower: စောင့်ကြည့်မနေသူများထံမှ အသိပေးချက်များကို ပိတ်ပါ
+        must_be_following: သင် စောင့်ကြည့်မထားသူများထံမှ အသိပေးချက်များကို ပိတ်ပါ
+        must_be_following_dm: သင် စောင့်ကြည့်မထားသူများထံမှ တိုက်ရိုက်မက်ဆေ့ချ်များကို ပိတ်ပါ
+      invite:
+        comment: မှတ်ချက်
+      invite_request:
+        text: သင် ဘာကြောင့် ပါဝင်ချင်တာလဲ။
+      ip_block:
+        comment: မှတ်ချက်
+        ip: IP
+        severities:
+          no_access: ဝင်ရောက်ခွင့်ကို ပိတ်ပါ
+          sign_up_block: အကောင့်ဖွင့်ခြင်းများကို ပိတ်ပါ
+          sign_up_requires_approval: အကောင့်ဖွင့်ခြင်းများကို ကန့်သတ်ပါ
+        severity: စည်းမျဉ်း
+      notification_emails:
+        appeal: တစ်စုံတစ်ယောက်က စိစစ်ဆုံးဖြတ်ခြင်းကို တောင်းခံနေသည်
+        digest: အီးမေးလ်အကျဉ်းချုပ်များပို့ရန်
+        favourite: တစ်စုံတစ်ဦးက သင့်ပို့စ်ကို နှစ်သက်ခဲ့သည်။
+        follow: တစ်စုံတစ်ဦးက သင့်ကို စောင့်ကြည့်ခဲ့သည်
+        follow_request: တစ်စုံတစ်ဦးက သင့်ကို စောင့်ကြည့်ရန် တောင်းဆိုခဲ့သည်
+        mention: တစ်စုံတစ်ဦးက သင့်ကို ဖော်ပြခဲ့သည်
+        pending_account: အကောင့်အသစ်ကို ပြန်လည်သုံးသပ်ရန် လိုအပ်သည်
+        reblog: တစ်ယောက်က သင့်ပို့စ်ကို Boost လုပ်ခဲ့သည်
+        report: အစီရင်ခံစာအသစ် တင်သွင်းထားသည်
+        trending_tag: လက်ရှိခေတ်စားနေသောပို့စ်များကို ပြန်လည်သုံးသပ်ရန် လိုသည်
+      rule:
+        text: စည်းမျဉ်း
+      tag:
+        listable: ရှာဖွေမှုများနှင့် အကြံပြုချက်များတွင် ဤ hashtag ပေါ်လာစေရန် ခွင့်ပြုပါ
+        name: Hashtag
+        trendable: ခေတ်စားနေသောအကြောင်းအရာများအောက်တွင် ဤ hashtag ပေါ်လာစေရန် ခွင့်ပြုပါ
+        usable: ပို့စ်များကို ဤ hashtag သုံးခွင့်ပြုပါ
+      user:
+        role: အခန်းကဏ္ဍ
+      user_role:
+        color: သင်္ကေတအရောင်
+        highlighted: အသုံးပြုသူပရိုဖိုင်များတွင် သင်္ကေတအဖြစ် အခန်းကဏ္ဍကို ပြသပါ
+        name: အမည်
+        permissions_as_keys: ခွင့်ပြုချက်များ
+        position: ဦးစားပေး
+      webhook:
+        events: အကြောင်းအရာများကဏ္ဍကို ဖွင့်ထားသည်
+        url: URL ဆုံးမှတ်
+    'no': မလုပ်ပါ
+    not_recommended: ထောက်ခံထားမှုမရှိ
+    recommended: ထောက်ခံထားပြီး
+    required:
+      mark: "*"
+      text: လိုအပ်သော
+    title:
+      sessions:
+        webauthn: အကောင့်ဝင်ရန်အတွက် သင့်လုံခြုံရေးကီးများထဲမှ တစ်ခုကို အသုံးပြုပါ
+    'yes': ဟုတ်ကဲ့
diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml
index 090b398be..b23ede506 100644
--- a/config/locales/simple_form.nl.yml
+++ b/config/locales/simple_form.nl.yml
@@ -18,8 +18,8 @@ nl:
           disable: Voorkom dat de gebruiker diens account gebruikt, maar verwijder of verberg de inhoud niet.
           none: Gebruik dit om een waarschuwing naar de gebruiker te sturen, zonder dat nog een andere maatregel wordt genomen.
           sensitive: Forceer dat alle mediabijlagen van deze gebruiker als gevoelig worden gemarkeerd.
-          silence: Voorkom dat de gebruiker openbare berichten kan versturen, verberg diens berichten en meldingen voor mensen die diegene niet volgen.
-          suspend: Alle interacties van en met dit account voorkomen, en de accountgegevens verwijderen. Dit kan binnen 30 dagen worden teruggedraaid.
+          silence: Voorkom dat de gebruiker berichten kan plaatsen met openbare zichtbaarheid, verberg diens berichten en meldingen van mensen die de gebruiker niet volgen. Sluit alle rapportages tegen dit account af.
+          suspend: Voorkom interactie van of naar dit account en verwijder de inhoud. Dit is omkeerbaar binnen 30 dagen. Dit sluit alle rapporten tegen dit account af.
         warning_preset_id: Optioneel. Je kunt nog steeds handmatig tekst toevoegen aan het eind van de voorinstelling
       announcement:
         all_day: Wanneer dit is aangevinkt worden alleen de datums binnen het tijdvak getoond
@@ -74,6 +74,7 @@ nl:
           hide: Verberg de gefilterde inhoud volledig, alsof het niet bestaat
           warn: Verberg de gefilterde inhoud achter een waarschuwing, met de titel van het filter als waarschuwingstekst
       form_admin_settings:
+        activity_api_enabled: Aantallen lokaal gepubliceerde berichten, actieve gebruikers en nieuwe registraties per week
         backups_retention_period: De aangemaakte gebruikersarchieven voor het opgegeven aantal dagen behouden.
         bootstrap_timeline_accounts: Deze accounts worden bovenaan de aanbevelingen aan nieuwe gebruikers getoond. Meerdere gebruikersnamen met komma's scheiden.
         closed_registrations_message: Weergegeven wanneer registratie van nieuwe accounts is uitgeschakeld
@@ -81,6 +82,7 @@ nl:
         custom_css: Je kunt aangepaste CSS toepassen op de webversie van deze Mastodon-server.
         mascot: Overschrijft de illustratie in de geavanceerde webomgeving.
         media_cache_retention_period: Mediabestanden die van andere servers zijn gedownload worden na het opgegeven aantal dagen verwijderd en worden op verzoek opnieuw gedownload.
+        peers_api_enabled: Een lijst met domeinnamen die deze server heeft aangetroffen in de fediverse. Er zijn hier geen gegevens inbegrepen over de vraag of je verbonden bent met een bepaalde server, alleen dat je server er van weet. Dit wordt gebruikt door diensten die statistieken over de federatie in algemene zin verzamelen.
         profile_directory: De gebruikersgids bevat een lijst van alle gebruikers die ervoor gekozen hebben om ontdekt te kunnen worden.
         require_invite_text: Maak het invullen van "Waarom wil je je hier registreren?" verplicht in plaats van optioneel, wanneer registraties handmatig moeten worden goedgekeurd
         site_contact_email: Hoe mensen je kunnen bereiken voor juridische vragen of support.
@@ -89,11 +91,13 @@ nl:
         site_short_description: Een korte beschrijving om het unieke karakter van je server te tonen. Wie beheert de server, wat is de doelgroep?
         site_terms: Gebruik je eigen privacybeleid of laat leeg om de standaardwaarde te gebruiken. Kan worden opgemaakt met Markdown.
         site_title: Hoe mensen buiten de domeinnaam naar je server kunnen verwijzen.
+        status_page_url: URL van een pagina waar mensen de status van deze server kunnen zien tijdens een storing
         theme: Thema die (niet ingelogde) bezoekers en nieuwe gebruikers zien.
         thumbnail: Een afbeelding van ongeveer een verhouding van 2:1 die naast jouw serverinformatie wordt getoond.
         timeline_preview: Bezoekers (die niet zijn ingelogd) kunnen de meest recente, op de server aanwezige openbare berichten bekijken.
         trendable_by_default: Handmatige beoordeling van trends overslaan. Individuele items kunnen later alsnog worden afgekeurd.
         trends: Trends laten zien welke berichten, hashtags en nieuwsberichten op jouw server aan populariteit winnen.
+        trends_as_landing_page: Toon trending inhoud aan uitgelogde gebruikers en bezoekers in plaats van een beschrijving van deze server. Vereist dat trends zijn ingeschakeld.
       form_challenge:
         current_password: Je betreedt een veilige omgeving
       imports:
@@ -229,6 +233,7 @@ nl:
           hide: Volledig verbergen
           warn: Met een waarschuwing verbergen
       form_admin_settings:
+        activity_api_enabled: Statistieken over gebruikersactiviteit via de API publiceren
         backups_retention_period: Bewaartermijn gebruikersarchief
         bootstrap_timeline_accounts: Accounts die altijd aan nieuwe gebruikers worden aanbevolen
         closed_registrations_message: Aangepast bericht wanneer registratie is uitgeschakeld
@@ -236,6 +241,7 @@ nl:
         custom_css: Aangepaste CSS
         mascot: Aangepaste mascotte (legacy)
         media_cache_retention_period: Bewaartermijn mediacache
+        peers_api_enabled: Lijst van bekende servers via de API publiceren
         profile_directory: Gebruikersgids inschakelen
         registrations_mode: Wie kan zich registreren
         require_invite_text: Opgeven van een reden is verplicht
@@ -247,15 +253,17 @@ nl:
         site_short_description: Serveromschrijving
         site_terms: Privacybeleid
         site_title: Servernaam
+        status_page_url: URL van statuspagina
         theme: Standaardthema
         thumbnail: Serverthumbnail
         timeline_preview: Toegang tot de openbare tijdlijnen zonder in te loggen toestaan
         trendable_by_default: Trends goedkeuren zonder voorafgaande beoordeling
         trends: Trends inschakelen
+        trends_as_landing_page: Laat trends op de startpagina zien
       interactions:
         must_be_follower: Meldingen van mensen die jou niet volgen blokkeren
         must_be_following: Meldingen van mensen die jij niet volgt blokkeren
-        must_be_following_dm: Directe berichten van mensen die jij niet volgt blokkeren
+        must_be_following_dm: Privéberichten van mensen die jij niet volgt blokkeren
       invite:
         comment: Opmerking
       invite_request:
diff --git a/config/locales/simple_form.nn.yml b/config/locales/simple_form.nn.yml
index c918f3a94..2e279b93f 100644
--- a/config/locales/simple_form.nn.yml
+++ b/config/locales/simple_form.nn.yml
@@ -18,8 +18,6 @@ nn:
           disable: Hindre eigaren frå å bruke kontoen, men fjern eller skjul ikkje innhaldet deira.
           none: Bruk dette for å senda ei åtvaring til brukaren utan å utløyse andre handlingar.
           sensitive: Tving markering for sensitivt innhald på alle mediavedlegga til denne brukaren.
-          silence: Hindre brukaren frå å publisere offentlege innlegg og i å skjule eigne innlegg og varsel frå folk som ikkje fylgjer dei.
-          suspend: Hindre samhandling med– og slett innhaldet til denne kontoen. Kan omgjerast innan 30 dagar.
         warning_preset_id: Valfritt. Du kan leggja inn eigen tekst på enden av føreoppsettet
       announcement:
         all_day: Når merka, vil berre datoane til tidsramma synast
diff --git a/config/locales/simple_form.no.yml b/config/locales/simple_form.no.yml
index 7f1d28230..933bc264f 100644
--- a/config/locales/simple_form.no.yml
+++ b/config/locales/simple_form.no.yml
@@ -18,8 +18,6 @@
           disable: Forhindre brukeren fra å bruke kontoen sin, men ikke slett eller skjul innholdet deres.
           none: Bruk dette for å sende en advarsel til brukeren uten å utløse noen andre handlinger.
           sensitive: Tving alle denne brukerens medievedlegg til å bli merket som følsomme.
-          silence: Hindre brukeren i å kunne skrive offentlig synlighet, skjule sine innlegg og varsler for personer som ikke kan følge dem.
-          suspend: Forhindre interaksjon fra eller til denne kontoen og slett innholdet der. Reversibel innen 30 dager.
         warning_preset_id: Valgfritt. Du kan fortsatt legge til tilpasset tekst til slutten av forhåndsinnstillingen
       announcement:
         all_day: Hvis noen av dem er valgt, vil kun datoene av tidsrammen bli vist
diff --git a/config/locales/simple_form.oc.yml b/config/locales/simple_form.oc.yml
index d608e49be..f898f3f6b 100644
--- a/config/locales/simple_form.oc.yml
+++ b/config/locales/simple_form.oc.yml
@@ -180,9 +180,12 @@ oc:
       form_admin_settings:
         custom_css: CSS personalizada
         media_cache_retention_period: Durada de conservacion dels mèdias en cache
+        peers_api_enabled: Publicar la lista dels servidors coneguts dins l’API
         profile_directory: Activar l’annuari de perfils
         registrations_mode: Qual se pòt marcar
         require_invite_text: Requerir una rason per s’inscriure
+        show_domain_blocks: Mostrar los blocatges de domeni
+        show_domain_blocks_rationale: Afichar perque los domenis foguèron blocats
         site_contact_email: Adreça de contacte
         site_contact_username: Nom d’utilizaire de contacte
         site_extended_description: Descripcion espandida
@@ -191,6 +194,9 @@ oc:
         site_title: Nom del servidor
         theme: Tèma per defaut
         thumbnail: Miniatura del servidor
+        timeline_preview: Permtre l’accès a l’apercebut del flux public sens autentificacion
+        trendable_by_default: Activar las tendéncias sens revision prealabla
+        trends: Activar las tendéncias
       interactions:
         must_be_follower: Blocar las notificacions del mond que vos sègon pas
         must_be_following: Blocar las notificacions del mond que seguètz pas
@@ -204,9 +210,11 @@ oc:
         ip: IP
         severities:
           no_access: Blocar l’accès
+          sign_up_block: Blocar las inscripcions
           sign_up_requires_approval: Limitar las inscripcions
         severity: Règla
       notification_emails:
+        appeal: Qualqu’un a fach apèl de la decision de moderacion
         digest: Enviar un corrièl recapitulatiu
         favourite: Enviar un corrièl quand qualqu’un plaça vòstre estatut en favorit
         follow: Enviar un corrièl quand qualqu’un vos sèc
@@ -214,6 +222,8 @@ oc:
         mention: Enviar un corrièl quand qualqu’un vos menciona
         pending_account: Enviar un corrièl quand cal validar un compte novèl
         reblog: Enviar un corrièl quand qualqu’un tòrna partejar vòstre estatut
+        report: Senhalament novèl somés
+        trending_tag: Nòva tendéncia requerís una supervision
       rule:
         text: Règla
       tag:
@@ -225,9 +235,13 @@ oc:
         role: Ròtle
       user_role:
         color: Color del badge
+        highlighted: Afichar lo ròtle coma badge pels perfils utilizaire
         name: Nom
         permissions_as_keys: Autorizacions
         position: Prioritat
+      webhook:
+        events: Eveniments activats
+        url: URL de ponch de terminason
     'no': Non
     required:
       mark: "*"
diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml
index 8478282a7..712dc834e 100644
--- a/config/locales/simple_form.pl.yml
+++ b/config/locales/simple_form.pl.yml
@@ -8,24 +8,24 @@ pl:
         acct: Określ nazwę@domenę konta na które chcesz się przenieść
       account_warning_preset:
         text: Możesz korzystać ze składni której używasz we wpisach, takiej jak adresy URL, hashtagi i wspomnienia
-        title: Nieobowiązkowe. Niewidoczne dla odbiorcy
+        title: Opcjonalnie. Niewidoczne dla odbiorcy
       admin_account_action:
         include_statuses: Użytkownik zobaczy, których wpisów dotyczyło działanie moderacji lub ostrzeżenie
         send_email_notification: Użytkownik otrzyma informację, co stało się z jego kontem
-        text_html: Możesz używać składni której używasz we wpisach. Możesz <a href="%{path}">dodać szablon ostrzeżenia</a> aby zaoszczędzić czas
+        text_html: Opcjonalnie. Możesz używać składni której używasz we wpisach. Możesz <a href="%{path}">dodać szablon ostrzeżenia</a> aby zaoszczędzić czas
         type_html: Wybierz co chcesz zrobić z <strong>%{acct}</strong>
         types:
           disable: Nie pozwalaj użytkownikowi na korzystanie ze swojego konta, ale nie usuwaj ani nie ukrywaj jego zawartości.
           none: Użyj tego, aby wysłać użytkownikowi ostrzeżenie, nie wywołując żadnego innego działania.
           sensitive: Wymuś oznaczanie wszystkich załączników multimedialnych tego użytkownika jako wrażliwe.
-          silence: Zablokuj użytkownikowi możliwość publikowania z widocznością publiczną, ukrywaj jego wpisy i powiadomienia przed osobami, które go nie obserwują.
-          suspend: Zapobiegaj wszelkim interakcjom z tym kontem i usuń jego zawartość. Odwracalne w ciągu 30 dni.
-        warning_preset_id: Nieobowiązkowe. Możesz dodać niestandardowy tekst do końcowki szablonu
+          silence: Uniemożliwia użytkownikowi publikowanie postów z publiczną widocznością, ukrywanie swoich postów i powiadomień od osób nieśledzących ich. Zamyka wszystkie zgłoszenia dotyczące tego konta.
+          suspend: Uniemożliwienie wszelkich interakcji z lub na tym koncie oraz usunięcie jego zawartości. Możliwość odwrócenia w ciągu 30 dni. Zamyka wszelkie zgłoszenia dotyczące tego konta.
+        warning_preset_id: Opcjonalnie. Możesz dodać niestandardowy tekst do końcówki szablonu
       announcement:
         all_day: Jeżeli zaznaczone, tylko daty z przedziału czasu będą wyświetlane
-        ends_at: Nieobowiązkowe. Ogłoszenie zostanie automatycznie wycofane w tym czasie
+        ends_at: Opcjonalnie. Ogłoszenie zostanie automatycznie wycofane w tym czasie
         scheduled_at: Pozostaw puste, aby opublikować ogłoszenie natychmiastowo
-        starts_at: Nieobowiązkowe. Jeżeli ogłoszenie jest związane z danym przedziałem czasu
+        starts_at: Opcjonalnie. Jeżeli ogłoszenie jest związane z danym przedziałem czasu
         text: Możesz używać składni wpisu. Pamiętaj o tym, ile miejsca zajmie ogłoszenie na ekranie użytkownika
       appeal:
         text: Możesz wysłać odwołanie do ostrzeżenia tylko raz
@@ -74,6 +74,7 @@ pl:
           hide: Całkowicie ukryj przefiltrowaną zawartość, jakby nie istniała
           warn: Ukryj filtrowaną zawartość za ostrzeżeniem wskazującym tytuł filtra
       form_admin_settings:
+        activity_api_enabled: Liczby opublikowanych lokalnych postów, aktywnych użytkowników i nowych rejestracji w tygodniowych przedziałach
         backups_retention_period: Zachowaj wygenerowane archiwa użytkownika przez określoną liczbę dni.
         bootstrap_timeline_accounts: Te konta zostaną przypięte na górze rekomendacji obserwacji nowych użytkowników.
         closed_registrations_message: Wyświetlane po zamknięciu rejestracji
@@ -81,6 +82,7 @@ pl:
         custom_css: Możesz zastosować niestandardowe style w internetowej wersji Mastodon.
         mascot: Nadpisuje ilustrację w zaawansowanym interfejsie internetowym.
         media_cache_retention_period: Pobrane pliki multimedialne zostaną usunięte po określonej liczbie dni po ustawieniu na wartość dodatnią i ponownie pobrane na żądanie.
+        peers_api_enabled: Lista nazw domen, z którymi ten serwer spotkał się w fediverse. Nie są tu zawarte żadne dane o tym, czy użytkownik dokonuje federacji z danym serwerem, a jedynie, że jego serwer o tym wie. Jest to wykorzystywane przez serwisy, które zbierają statystyki dotyczące federacji w ogólnym sensie.
         profile_directory: Katalog profili zawiera listę wszystkich użytkowników, którzy zgodzili się na bycie znalezionymi.
         require_invite_text: Kiedy rejestracje wymagają ręcznego zatwierdzenia, ustaw pole "Dlaczego chcesz dołączyć?" jako obowiązkowe, a nie opcjonalne
         site_contact_email: Jak ludzie mogą się z Tobą skontaktować w celu uzyskania odpowiedzi na zapytania prawne lub wsparcie.
@@ -89,11 +91,13 @@ pl:
         site_short_description: Krótki opis, który pomoże w unikalnym zidentyfikowaniu Twojego serwera. Kto go obsługuje, do kogo jest skierowany?
         site_terms: Użyj własnej polityki prywatności lub zostaw puste, aby użyć domyślnej. Może być sformatowana za pomocą składni Markdown.
         site_title: Jak ludzie mogą odwoływać się do Twojego serwera inaczej niże przez nazwę jego domeny.
+        status_page_url: Adres URL strony, na której odwiedzający mogą zobaczyć status tego serwera w trakcie awarii
         theme: Motyw, który widzą wylogowani i nowi użytkownicy.
         thumbnail: Obraz o proporcjach mniej więcej 2:1 wyświetlany obok informacji o serwerze.
         timeline_preview: Wylogowani użytkownicy będą mogli przeglądać najnowsze publiczne wpisy dostępne na serwerze.
         trendable_by_default: Pomiń ręczny przegląd treści trendów. Pojedyncze elementy nadal mogą być usuwane z trendów po fakcie.
         trends: Tendencje pokazują, które posty, hasztagi i newsy zyskują popularność na Twoim serwerze.
+        trends_as_landing_page: Pokaż najpopularniejsze treści niezalogowanym użytkownikom i odwiedzającym zamiast opisu tego serwera. Wymaga włączenia trendów.
       form_challenge:
         current_password: Wchodzisz w strefę bezpieczną
       imports:
@@ -101,7 +105,7 @@ pl:
       invite_request:
         text: To pomoże nam w recenzji Twojej aplikacji
       ip_block:
-        comment: Niewymagane. Pamiętaj, dlaczego dodałeś tę regułę.
+        comment: Opcjonalnie. Pamiętaj, dlaczego dodałeś tę regułę.
         expires_in: Adresy IP to ograniczony zasób, czasami są współdzielone i często zmieniają właściciela. Z tego powodu blokady adresów IP nie są zalecane.
         ip: Wprowadź adres IPv4 lub IPv6. Możesz zablokować całe zakresy za pomocą składni CIDR. Uważaj, aby się nie zablokować!
         severities:
@@ -229,6 +233,7 @@ pl:
           hide: Ukryj całkowicie
           warn: Ukryj z ostrzeżeniem
       form_admin_settings:
+        activity_api_enabled: Publikuj zagregowane statystyki dotyczące aktywności użytkownika w API
         backups_retention_period: Okres przechowywania archiwum użytkownika
         bootstrap_timeline_accounts: Zawsze rekomenduj te konta nowym użytkownikom
         closed_registrations_message: Niestandardowa wiadomość, gdy rejestracje nie są dostępne
@@ -236,6 +241,7 @@ pl:
         custom_css: Niestandardowy CSS
         mascot: Własna ikona
         media_cache_retention_period: Okres przechowywania pamięci podręcznej
+        peers_api_enabled: Opublikuj listę odkrytych serwerów w API
         profile_directory: Włącz katalog profilów
         registrations_mode: Kto może się zarejestrować
         require_invite_text: Wymagaj powodu, aby dołączyć
@@ -247,11 +253,13 @@ pl:
         site_short_description: Opis serwera
         site_terms: Polityka prywatności
         site_title: Nazwa serwera
+        status_page_url: Adres URL strony statusu
         theme: Domyślny motyw
         thumbnail: Miniaturka serwera
         timeline_preview: Zezwalaj na nieuwierzytelniony dostęp do publicznych osi czasu
         trendable_by_default: Zezwalaj na trendy bez wcześniejszego przeglądu
         trends: Włącz trendy
+        trends_as_landing_page: Użyj trendów jako strony początkowej
       interactions:
         must_be_follower: Nie wyświetlaj powiadomień od osób, które Cię nie obserwują
         must_be_following: Nie wyświetlaj powiadomień od osób, których nie obserwujesz
diff --git a/config/locales/simple_form.pt-BR.yml b/config/locales/simple_form.pt-BR.yml
index fd26cb509..08a4b3704 100644
--- a/config/locales/simple_form.pt-BR.yml
+++ b/config/locales/simple_form.pt-BR.yml
@@ -18,8 +18,8 @@ pt-BR:
           disable: Impede o usuário de usar a conta, porém sem excluí-la ou suspendê-la.
           none: Use isto para enviar uma advertência ao usuário, sem nenhuma outra ação.
           sensitive: Marca todas as mídias do usuário como sensível.
-          silence: Impede o usuário de tootar publicamente, e oculta toots e notificações dos que não o seguem.
-          suspend: Impede interações e exclui conteúdo da conta. Reversível apenas dentro de 30 dias.
+          silence: Impede o usuário de enviar postagens visualmente públicas, além de ocultar suas publicações e notificações dos que não o seguem. Ademais, fecha todas as denúncias contra esta conta.
+          suspend: Impede qualquer interação de ou para esta conta e exclui seu conteúdo. Reversível dentro de 30 dias. Ademais, fecha todas as denúncias contra esta conta.
         warning_preset_id: Opcional. Você pode adicionar texto personalizado no final da advertência pré-definida
       announcement:
         all_day: Quando marcada, apenas as datas do período serão mostradas
@@ -74,6 +74,7 @@ pt-BR:
           hide: Esconder completamente o conteúdo filtrado, comportando-se como se ele não existisse
           warn: Ocultar o conteúdo filtrado por trás de um aviso mencionando o título do filtro
       form_admin_settings:
+        activity_api_enabled: Contagem de publicações locais, usuários ativos e novos usuários semanais
         backups_retention_period: Manter os arquivos de usuário gerados pelo número de dias especificados.
         bootstrap_timeline_accounts: Estas contas serão fixadas no topo das recomendações de novos usuários para seguir.
         closed_registrations_message: Exibido quando as inscrições estiverem fechadas
@@ -81,18 +82,22 @@ pt-BR:
         custom_css: Você pode aplicar estilos personalizados na versão da web do Mastodon.
         mascot: Substitui a ilustração na interface web avançada.
         media_cache_retention_period: Os arquivos de mídia baixados serão excluídos após o número especificado de dias, quando definido para um valor positivo, e baixados novamente na demanda.
+        peers_api_enabled: Uma lista de nomes de domínio que este servidor encontrou no "fediverse". Nenhum dado é incluído aqui sobre se você concorda com os padroes operacionais de um determinado servidor, apenas que o seu servidor sabe disso. Esta ferramenta é utilizado por serviços que recolhem estatísticas sob as normas da federação (grupo de empresas que concordam sob paramentros operacionais específicos), em termos gerais.
         profile_directory: O diretório de perfis lista todos os usuários que optaram por permitir que suas contas sejam descobertas.
+        require_invite_text: 'Quando o cadastro de novas contas exigir aprovação manual, tornar obrigatório, ao invés de opcional, o texto de solicitação de convite: "Por que você deseja ingressar nessa comunidade?"'
         site_contact_email: Como as pessoas podem entrar em contato com você para obter informações legais ou de suporte.
         site_contact_username: Como as pessoas podem chegar até você no Mastodon.
         site_extended_description: Quaisquer informações adicionais que possam ser úteis para os visitantes e seus usuários. Podem ser estruturadas com formato Markdown.
         site_short_description: Uma curta descrição para ajudar unicamente a identificar a sua instância. Quem está o administrando, e para quem é direcionado?
         site_terms: Use a sua própria política de privacidade ou deixe em branco para usar o padrão. Pode ser estruturado com o formato Markdown.
         site_title: Como as pessoas podem se referir ao seu servidor além do nome do domínio.
+        status_page_url: URL de uma página onde as pessoas podem ver o status deste servidor durante uma interrupção
         theme: Tema que visitantes e novos usuários veem.
         thumbnail: Uma imagem de aproximadamente 2:1 exibida ao lado da informação de sua instância.
         timeline_preview: Visitantes conseguirão navegar pelas postagens públicas mais recentes disponíveis na instância.
         trendable_by_default: Pular a revisão manual do conteúdo em tendência. Itens individuais ainda poderão ser removidos das tendências após a sua exibição.
         trends: Tendências mostram quais publicações, hashtags e notícias estão ganhando destaque na sua instância.
+        trends_as_landing_page: Mostrar conteúdo de tendências para usuários deslogados e visitantes em vez de uma descrição deste servidor. Requer que as tendências sejam ativadas.
       form_challenge:
         current_password: Você está entrando em uma área segura
       imports:
@@ -228,6 +233,7 @@ pt-BR:
           hide: Ocultar completamente
           warn: Ocultar com um aviso
       form_admin_settings:
+        activity_api_enabled: Publicar estatísticas agregadas sobre atividade de usuários na API
         backups_retention_period: Período de retenção do arquivo de usuário
         bootstrap_timeline_accounts: Sempre recomendar essas contas para novos usuários
         closed_registrations_message: Mensagem personalizada quando inscrições não estão disponíveis
@@ -235,6 +241,7 @@ pt-BR:
         custom_css: CSS personalizável
         mascot: Mascote personalizado (legado)
         media_cache_retention_period: Período de retenção do cachê de mídia
+        peers_api_enabled: Publicar lista de instâncias de servidor descobertas na API
         profile_directory: Ativar diretório de perfis
         registrations_mode: Quem pode se inscrever
         require_invite_text: Exigir uma razão para entrar
@@ -246,11 +253,13 @@ pt-BR:
         site_short_description: Descrição do servidor
         site_terms: Política de privacidade
         site_title: Nome do servidor
+        status_page_url: Endereço da página de status
         theme: Tema padrão
         thumbnail: Miniatura do servidor
         timeline_preview: Permitir acesso não autenticado às linhas do tempo públicas
         trendable_by_default: Permitir tendências sem revisão prévia
         trends: Habilitar tendências
+        trends_as_landing_page: Usar tendências como página inicial
       interactions:
         must_be_follower: Bloquear notificações de não-seguidores
         must_be_following: Bloquear notificações de não-seguidos
diff --git a/config/locales/simple_form.pt-PT.yml b/config/locales/simple_form.pt-PT.yml
index e86272e4a..e94eae4f9 100644
--- a/config/locales/simple_form.pt-PT.yml
+++ b/config/locales/simple_form.pt-PT.yml
@@ -8,64 +8,64 @@ pt-PT:
         acct: Especifique o utilizador@domínio da conta para onde você deseja migrar
       account_warning_preset:
         text: Pode usar sintaxe de escrita, como URL, etiquetas, e referências
-        title: Opcional. Não visível para o destinatário
+        title: Opcional. Invisível para o destinatário
       admin_account_action:
-        include_statuses: O utilizador verá quais toots causaram a ação de moderação ou aviso
+        include_statuses: O utilizador verá quais as publicações que foram a razão da moderação ou advertência
         send_email_notification: O utilizador receberá uma explicação sobre o que aconteceu com a sua conta
         text_html: Opcional. Pode utilizar sintaxe de escrita. Pode <a href="%{path}">adicionar avisos predefinidos</a> para poupar tempo
         type_html: Escolhe o que fazer com <strong>%{acct}</strong>
         types:
           disable: Impede o utilizador de usar a sua conta, mas não elimina ou oculta o seu conteúdo.
-          none: Use isto para enviar um aviso ao utilizador, sem acionar nenhuma outra ação.
-          sensitive: Força todos os anexos de media deste utilizador a serem sinalizados como sensíveis.
-          silence: Impede que o utilizador seja capaz de publicar com visibilidade pública, ocultando as suas publicações e notificações de pessoas que não o seguem.
-          suspend: Evita qualquer interação de ou para esta conta e elimina o seu conteúdo. Reversível num período de 30 dias.
+          none: Use isto para enviar um aviso ao utilizador, sem espoletar nenhuma outra ação.
+          sensitive: Força todos os anexos de media deste utilizador a serem sinalizados como problemáticos.
+          silence: Impede que o utilizador possa publicar com visibilidade pública, ocultando as suas publicações e notificações de pessoas que não o seguem. Encerra todas as denúncias contra esta conta.
+          suspend: Evita qualquer interação de ou para esta conta e elimina o seu conteúdo. Reversível num período de 30 dias. Encerra todas as denúncias contra esta conta.
         warning_preset_id: Opcional. Tu ainda podes adicionar texto personalizado no fim do predefinido
       announcement:
-        all_day: Quando marcado, apenas as datas do intervalo de tempo serão exibidas
+        all_day: Quando marcado, apenas as datas do intervalo de tempo serão apresentadas
         ends_at: Opcional. O anúncio será automaticamente retirado de exibição neste momento
         scheduled_at: Deixe em branco para publicar o anúncio imediatamente
         starts_at: Opcional. Caso o seu anúncio seja vinculado a um intervalo de tempo específico
-        text: Pode utilizar a sintaxe dos toot. Por favor, tenha em consideração o espaço que o anúncio ocupará no ecrã do utilizador
+        text: Pode utilizar a sintaxe de publicações. Por favor, tenha em consideração o espaço que o comunicado ocupará no ecrã do utilizador
       appeal:
-        text: Só pode recorrer de uma punição uma vez
+        text: Só pode recorrer uma única vez de uma reprimenda
       defaults:
-        autofollow: As pessoas que aderem através do convite seguir-te-ão automaticamente
-        avatar: PNG, GIF or JPG. Arquivos até %{size}. Vão ser reduzidos para %{dimensions}px
+        autofollow: As pessoas que aderirem através do convite segui-lo-ão automaticamente
+        avatar: PNG, GIF ou JPG. Ficheiros no máximo de %{size}. Serão reduzidos para %{dimensions}px
         bot: Esta conta executa essencialmente ações automatizadas e pode não ser monitorizada
-        context: Um ou múltiplos contextos nos quais o filtro deve ser aplicado
-        current_password: Para fins de segurança, por favor, introduza a palavra-passe da conta atual
-        current_username: Para confirmar, por favor, introduza o nome de utilizador da conta atual
+        context: Um ou diversos contextos nos quais o filtro deve ser aplicado
+        current_password: Para fins de segurança, queira inserir a palavra-passe da conta atual
+        current_username: Para confirmar, queira inserir o nome de utilizador da conta atual
         digest: Enviado após um longo período de inatividade e apenas se foste mencionado na tua ausência
         discoverable: Permitir que a sua conta seja descoberta por outros através de recomendações, destaques e outras funções
         email: Será enviado um e-mail de confirmação
-        fields: Pode ter até 4 itens expostos, em forma de tabela, no seu perfil
-        header: PNG, GIF or JPG. Arquivos até %{size}. Vão ser reduzidos para %{dimensions}px
-        inbox_url: Copia a URL da página inicial do repetidor que queres usar
+        fields: Pode ter até 4 elementos expostos, em forma de tabela, no seu perfil
+        header: PNG, GIF ou JPG. Ficheiros no máximo de %{size}. Serão reduzidos para %{dimensions}px
+        inbox_url: Copie o URL da página inicial do repetidor que quer usar
         irreversible: Publicações filtradas irão desaparecer irremediavelmente, mesmo que o filtro seja removido posteriormente
         locale: A língua da interface de utilizador, e-mails e notificações push
         locked: Requer aprovação manual de seguidores
-        password: Usa, pelo menos, 8 caracteres
+        password: Use pelo menos 8 caracteres
         phrase: Será correspondido independentemente da capitalização ou do aviso de conteúdo duma publicação
-        scopes: Quais as APIs a que será concedido acesso. Se escolheres uma abrangência de nível superior, não precisarás de as seleccionar individualmente.
-        setting_aggregate_reblogs: Não mostrar novas partilhas que foram partilhadas recentemente (só afecta as novas partilhas)
+        scopes: Quais as API a que será concedido acesso. Se escolher uma abrangência de nível superior, não precisará de as seleccionar individualmente.
+        setting_aggregate_reblogs: Não mostrar novos reforços de publicações recentemente reforçadas (só afecta publicações acabadas de reforçar)
         setting_always_send_emails: Normalmente as notificações por e-mail não serão enviadas quando estiver a utilizar ativamente o Mastodon
-        setting_default_sensitive: Media sensível está oculta por padrão e pode ser revelada com um clique
-        setting_display_media_default: Esconder media marcada como sensível
+        setting_default_sensitive: Media problemática oculta por padrão, pode ser revelada com um clique
+        setting_display_media_default: Esconder media marcada como problemática
         setting_display_media_hide_all: Esconder sempre toda a media
-        setting_display_media_show_all: Mostrar sempre a media marcada como sensível
+        setting_display_media_show_all: Mostrar sempre a media
         setting_hide_network: Quem tu segues e quem te segue não será mostrado no teu perfil
         setting_noindex: Afecta o teu perfil público e as páginas das tuas publicações
-        setting_show_application: A aplicação que tu usas para publicar será mostrada na vista detalhada das tuas publicações
-        setting_use_blurhash: Os gradientes são baseados nas cores das imagens escondidas, mas ofuscam quaisquer detalhes
-        setting_use_pending_items: Ocultar atualizações da cronologia atrás de um clique ao invés de rolar automaticamente o feed
+        setting_show_application: A aplicação que usa para publicar será mostrada na vista pormenorizada das suas publicações
+        setting_use_blurhash: Os gradientes são baseados nas cores das imagens escondidas, mas ofuscam quaisquer pormenores
+        setting_use_pending_items: Ocultar atualizações da cronologia por detrás dum clique, em vez de rolar automaticamente o fluxo
         username: O teu nome de utilizador será único em %{domain}
         whole_word: Quando a palavra-chave ou expressão-chave é somente alfanumérica, ela só será aplicada se corresponder à palavra completa
       domain_allow:
         domain: Este domínio será capaz de obter dados desta instância e os dados dele recebidos serão processados e armazenados
       email_domain_block:
         domain: Este pode ser o nome de domínio que aparece no endereço de e-mail ou o registo MX por ele utilizado. Eles serão verificados aquando da inscrição.
-        with_dns_records: Será feita uma tentativa de resolver os registos DNS do domínio em questão e os resultados também serão colocados na lista negra
+        with_dns_records: Será feita uma tentativa de determinar os registos DNS do domínio em questão e os resultados também serão colocados na lista negra
       featured_tag:
         name: 'Eis algumas das etiquetas que utilizou recentemente:'
       filters:
@@ -74,32 +74,36 @@ pt-PT:
           hide: Ocultar completamente o conteúdo filtrado, comportando-se como se não existisse
           warn: Ocultar o conteúdo filtrado por trás de um aviso mencionando o título do filtro
       form_admin_settings:
-        backups_retention_period: Manter os arquivos gerados pelos utilizadores por um número específico de dias.
+        activity_api_enabled: Contagem, em blocos semanais, de publicações locais, utilizadores ativos e novos registos
+        backups_retention_period: Manter os ficheiros gerados pelos utilizadores durante um número concreto de dias.
         bootstrap_timeline_accounts: Estas contas serão destacadas no topo das recomendações aos novos utilizadores.
-        closed_registrations_message: Exibido quando as inscrições estão encerradas
-        content_cache_retention_period: Publicações de outros servidores serão excluídos após o número de dias especificado, quando definido com um valor positivo. Isso pode ser irreversível.
+        closed_registrations_message: Apresentado quando as inscrições estiverem encerradas
+        content_cache_retention_period: Publicações de outros servidores serão apagadas decorrido o número de dias especificado, se estiver definido um valor positivo. Isso pode ser irreversível.
         custom_css: Pode aplicar estilos personalizados na versão web do Mastodon.
         mascot: Sobrepõe-se à ilustração na interface web avançada.
-        media_cache_retention_period: Os ficheiros de media descarregados serão excluídos após o número de dias especificado, quando definido com um valor positivo, e descarregados novamente quando solicitados.
-        profile_directory: O diretório de perfis lista todos os utilizadores que optaram por a sua conta ser sugerida a outros.
-        require_invite_text: Quando as incrições exigirem aprovação manual, faça o texto "Porque se quer juntar a nós?" da solicitação de convite, obrigatório ao invés de opcional
-        site_contact_email: Como as pessoas podem entrar em contacto consigo para obter informações legais ou de suporte.
+        media_cache_retention_period: Os ficheiros de media descarregados serão apagados decorrido o número de dias especificado, quando definido com um valor positivo, e descarregados novamente quando solicitados.
+        peers_api_enabled: Uma lista de nomes de domínio que este servidor encontrou no fediverso. Nenhum dado é incluído aqui sobre se você federa com um determinado servidor, apenas que o seu servidor o conhece. Este serviço é utilizado por serviços que recolhem estatísticas na federação, em termos gerais.
+        profile_directory: O diretório de perfis lista todos os utilizadores que optaram por ter a sua conta a ser sugerida a outros.
+        require_invite_text: Quando as incrições exigirem aprovação manual, faça o texto "Por que se quer juntar a nós?" da solicitação de convite ser obrigatório, em vez de opcional
+        site_contact_email: Como as pessoas podem entrar em contacto consigo para obter informações legais ou de apoio técnico.
         site_contact_username: Como as pessoas conseguem chegar até si no Mastodon.
         site_extended_description: Qualquer informação adicional que possa ser útil para os visitantes e os seus utilizadores. Pode ser estruturada com a sintaxe Markdown.
-        site_short_description: Uma breve descrição para ajudar a identificar de forma única o seu servidor. Quem o está a gerir, para quem é?
+        site_short_description: Uma breve descrição para ajudar a identificar de forma única o seu servidor. Quem o está a gerir, e a quem se destina?
         site_terms: Use a sua própria política de privacidade ou deixe em branco para usar a política padrão. Pode ser estruturada com a sintaxe Markdown.
         site_title: Como as pessoas podem referir-se ao seu servidor para além do seu nome de domínio.
-        theme: Tema que os visitantes e os novos utilizadores visualizam.
-        thumbnail: Uma imagem de aproximadamente 2:1, exibida ao lado da informação do seu servidor.
+        status_page_url: URL de uma página onde as pessoas podem ver o estado deste servidor durante uma interrupção
+        theme: Tema que os visitantes e os novos utilizadores veem.
+        thumbnail: Uma imagem de cerca de 2:1, apresentada ao lado da informação do seu servidor.
         timeline_preview: Os visitantes sem sessão iniciada poderão consultar as publicações públicas mais recentes disponíveis no servidor.
-        trendable_by_default: Ignorar a revisão manual do conteúdo das tendências. Itens individuais ainda poderão ser removidos das tendências após a sua exibição.
-        trends: As tendências mostram quais as publicações, etiquetas e notícias que estão a ganhar destaque no seu servidor.
+        trendable_by_default: Ignorar a revisão manual do conteúdo em alta. Elementos em avulso poderão ainda assim ser retirados das tendências mesmo após a sua apresentação.
+        trends: As publicações em alta mostram quais as publicações, etiquetas e notícias que estão a ganhar destaque no seu servidor.
+        trends_as_landing_page: Mostrar conteúdo de tendências para usuários logados e visitantes em vez de uma descrição deste servidor. Requer que as tendências sejam ativadas.
       form_challenge:
-        current_password: Está a entrar numa área restrita
+        current_password: Está a entrar numa área segura
       imports:
-        data: Arquivo CSV exportado de outra instância do Mastodon
+        data: Ficheiro CSV exportado doutra instância do Mastodon
       invite_request:
-        text: Isto vai ajudar-nos a rever o seu pedido
+        text: Isto vai ajudar-nos a analisar o seu pedido
       ip_block:
         comment: Opcional. Relembre-se por que adicionou esta regra.
         expires_in: Endereços IP são um recurso limitado, algumas vezes são compartilhados e muitas vezes mudam de mãos. Por essa razão, bloqueios de IP indefinidos não são recomendados.
@@ -122,9 +126,9 @@ pt-PT:
       user_role:
         color: Cor a ser utilizada para a função em toda a interface de utilizador, como RGB no formato hexadecimal
         highlighted: Isto torna a função visível publicamente
-        name: Nome público da função, se a função for definida para ser exibida como um distintivo
+        name: Nome público do cargo, se este estiver definido para ser apresentada com um emblema
         permissions_as_keys: Utilizadores com esta função terão acesso a...
-        position: Função mais alta decidem a resolução de conflitos em certas situações. Certas ações só podem ser executadas em funções com uma menor prioridade
+        position: Cargos mais altos decidem a resolução de conflitos em certas situações. Certas ações só podem ser executadas em cargos com uma menor prioridade
       webhook:
         events: Selecione os eventos a enviar
         url: Para onde os eventos serão enviados
@@ -147,22 +151,22 @@ pt-PT:
         type: Acção
         types:
           disable: Congelar
-          none: Não fazer algo
-          sensitive: Sensível
-          silence: Silenciar
-          suspend: Suspender e apagar irreversivelmente os dados da conta
+          none: Enviar um aviso
+          sensitive: Problemático
+          silence: Limitar
+          suspend: Suspender
         warning_preset_id: Usar um aviso pré-definido
       announcement:
         all_day: Evento de dia inteiro
         ends_at: Fim do evento
         scheduled_at: Agendar publicação
         starts_at: Início do evento
-        text: Anúncio
+        text: Comunicado
       appeal:
         text: Explique porque esta decisão deve ser revertida
       defaults:
         autofollow: Convidar para seguir a tua conta
-        avatar: Imagem de Perfil
+        avatar: Imagem de perfil
         bot: Esta é uma conta robô
         chosen_languages: Filtrar línguas
         confirm_new_password: Confirmar nova palavra-passe
@@ -170,9 +174,9 @@ pt-PT:
         context: Filtrar contextos
         current_password: Palavra-passe actual
         data: Dados
-        discoverable: Permitir sugerir esta conta a outros
-        display_name: Nome Público
-        email: Endereço de e-mail
+        discoverable: Sugerir esta conta a outros
+        display_name: Nome a apresentar
+        email: Endereço de correio electrónico
         expires_in: Expira em
         fields: Metadados de perfil
         header: Cabeçalho
@@ -180,22 +184,22 @@ pt-PT:
         inbox_url: URL da caixa de entrada do repetidor
         irreversible: Expandir em vez de esconder
         locale: Língua da interface
-        locked: Trancar conta
+        locked: Exigir pedidos de seguimento
         max_uses: Número máximo de utilizações
         new_password: Nova palavra-passe
         note: Biografia
         otp_attempt: Código de autenticação em duas etapas
         password: Palavra-passe
-        phrase: Palavra ou expressão-chave
+        phrase: Palavra-chave ou frase
         setting_advanced_layout: Ativar interface web avançada
-        setting_aggregate_reblogs: Agrupar partilhas em cronologias
-        setting_always_send_emails: Enviar notificações de email sempre
-        setting_auto_play_gif: Reproduzir GIFs automaticamente
+        setting_aggregate_reblogs: Agrupar reforços em cronologias
+        setting_always_send_emails: Enviar sempre notificações de email
+        setting_auto_play_gif: Reproduzir GIF automaticamente
         setting_boost_modal: Solicitar confirmação antes de partilhar uma publicação
-        setting_crop_images: Cortar imagens em toots não expandidos para o formato 16x9
+        setting_crop_images: Recortar imagens para o formato 16x9 nas publicações não expandidas
         setting_default_language: Língua de publicação
         setting_default_privacy: Privacidade da publicação
-        setting_default_sensitive: Sempre marcar media como sensível
+        setting_default_sensitive: Marcar sempre os media como problemáticos
         setting_delete_modal: Solicitar confirmação antes de eliminar uma publicação
         setting_disable_swiping: Desativar os movimentos de deslize
         setting_display_media: Visualização de media
@@ -207,9 +211,9 @@ pt-PT:
         setting_noindex: Não quero ser indexado por motores de pesquisa
         setting_reduce_motion: Reduz movimento em animações
         setting_show_application: Revelar sempre qual a aplicação usada para enviar as publicações
-        setting_system_font_ui: Usar a fonte padrão do teu sistema
-        setting_theme: Tema do site
-        setting_trends: Mostrar as tendências de hoje
+        setting_system_font_ui: Usar o tipo de letra padrão do sistema
+        setting_theme: Tema do sítio
+        setting_trends: Mostrar o que está hoje em alta
         setting_unfollow_modal: Solicitar confirmação antes de deixar de seguir alguém
         setting_use_blurhash: Mostrar gradientes coloridos para medias ocultas
         setting_use_pending_items: Modo lento
@@ -221,7 +225,7 @@ pt-PT:
         username_or_email: Nome de utilizador ou e-mail
         whole_word: Palavra completa
       email_domain_block:
-        with_dns_records: Incluir registos MX e IPs do domínio
+        with_dns_records: Incluir registos MX e IP do domínio
       featured_tag:
         name: Etiqueta
       filters:
@@ -229,15 +233,17 @@ pt-PT:
           hide: Ocultar por completo
           warn: Ocultar com um aviso
       form_admin_settings:
+        activity_api_enabled: Publicar estatísticas agregadas sobre a atividade dos utilizadores na API
         backups_retention_period: Período de retenção de arquivos de utilizador
-        bootstrap_timeline_accounts: Sempre recomendar essas contas para novos utilizadores
-        closed_registrations_message: Mensagem personalizada quando as inscrições não estão disponíveis
+        bootstrap_timeline_accounts: Recomendar sempre estas contas para novos utilizadores
+        closed_registrations_message: Mensagem personalizada quando as inscrições não estiverem disponíveis
         content_cache_retention_period: Período de retenção de conteúdo em cache
-        custom_css: CSS Personalizado
+        custom_css: CSS personalizado
         mascot: Mascote personalizada (legado)
         media_cache_retention_period: Período de retenção de ficheiros de media em cache
+        peers_api_enabled: Publicar lista de servidores descobertos na API
         profile_directory: Habilitar diretório de perfis
-        registrations_mode: Quem pode inscrever-se
+        registrations_mode: Quem se pode inscrever
         require_invite_text: Requerer uma razão para entrar
         show_domain_blocks: Mostrar domínios bloqueados
         show_domain_blocks_rationale: Mostrar porque os domínios foram bloqueados
@@ -245,13 +251,15 @@ pt-PT:
         site_contact_username: Nome de utilizador do contacto
         site_extended_description: Descrição estendida
         site_short_description: Descrição do servidor
-        site_terms: Política de Privacidade
+        site_terms: Política de privacidade
         site_title: Nome do servidor
+        status_page_url: URL da página de estado
         theme: Tema predefinido
         thumbnail: Miniatura do servidor
         timeline_preview: Permitir acesso não autenticado às cronologias públicas
-        trendable_by_default: Permitir tendências sem revisão prévia
-        trends: Habilitar tendências
+        trendable_by_default: Permitir publicações em alta sem revisão prévia
+        trends: Activar publicações em alta
+        trends_as_landing_page: Usar tendências como página inicial
       interactions:
         must_be_follower: Bloquear notificações de não-seguidores
         must_be_following: Bloquear notificações de pessoas que não segues
@@ -271,26 +279,26 @@ pt-PT:
       notification_emails:
         appeal: Alguém recorreu de uma decisão de moderação
         digest: Enviar e-mails de resumo
-        favourite: Quando alguém adiciona uma publicação sua aos favoritos
-        follow: Quando alguém começar a segui-lo
-        follow_request: Quando alguém solicitar ser seu seguidor
-        mention: Quando alguém o mencionar
-        pending_account: Quando uma nova conta aguarda aprovação
-        reblog: Quando alguém partilhar uma publicação sua
-        report: Nova denúncia submetida
-        trending_tag: Nova tendência requer revisão
+        favourite: Alguém adicionou uma publicação sua aos marcadores
+        follow: Alguém começou a segui-lo
+        follow_request: Alguém pediu para ser seu seguidor
+        mention: Alguém o mencionou
+        pending_account: Uma nova conta aguarda aprovação
+        reblog: Alguém reforçou uma publicação sua
+        report: Uma nova denúncia foi submetida
+        trending_tag: Uma nova publicação em alta requer avaliação
       rule:
         text: Regra
       tag:
         listable: Permitir que esta etiqueta apareça em pesquisas e no diretório de perfis
         name: Etiqueta
-        trendable: Permitir que esta etiqueta apareça em destaque
-        usable: Permitir as publicações usem esta etiqueta
+        trendable: Permitir que esta etiqueta apareça em alta
+        usable: Permitir que as publicações usem esta etiqueta
       user:
-        role: Função
+        role: Cargo
       user_role:
-        color: Cor do distintivo
-        highlighted: Exibir a função como distintivo nos perfis de utilizador
+        color: Cor do emblema
+        highlighted: Apresentar o cargo como emblema nos perfis de utilizador
         name: Nome
         permissions_as_keys: Permissões
         position: Prioridade
diff --git a/config/locales/simple_form.ru.yml b/config/locales/simple_form.ru.yml
index f644dccab..49dcb1d5f 100644
--- a/config/locales/simple_form.ru.yml
+++ b/config/locales/simple_form.ru.yml
@@ -18,8 +18,8 @@ ru:
           disable: Запретить пользователю использование своей учётной записи, без удаления или скрытия контента.
           none: Отправить пользователю предупреждение, не принимая иных действий.
           sensitive: Принудительно отметить опубликованное пользователем содержимое как «деликатного характера».
-          silence: Запретить пользователю публиковать посты с открытой видимостью, а также скрыть все прошлые посты и уведомления от людей, не читающих этого пользователя.
-          suspend: Предотвратить любое взаимодействие с этой учётной записью, удалив всё содержимое опубликованное с неё. Это действие можно отменить в течение 30 дней.
+          silence: Запретить пользователю публиковать посты с открытой видимостью, а также скрыть все прошлые посты и уведомления от людей, не читающих этого пользователя. Закрыть все отчеты по этому счету.
+          suspend: Предотвратить любое взаимодействие с этой учётной записью, удалив всё содержимое опубликованное с неё. Это действие можно отменить в течение 30 дней. Закрывает все отчеты против этого аккаунта.
         warning_preset_id: Необязательно. Вы можете добавить собственный текст в конце шаблона
       announcement:
         all_day: Если выбрано, часы начала и завершения будут скрыты
@@ -74,6 +74,7 @@ ru:
           hide: Полностью скрыть отфильтрованный контент так, как будто его не существует
           warn: Скрыть отфильтрованный контент за предупреждением с указанием названия фильтра
       form_admin_settings:
+        activity_api_enabled: Подсчёт количества локальных постов, активных пользователей и новых регистраций на еженедельной основе
         backups_retention_period: Сохранять сгенерированные пользовательские архивы для указанного количества дней.
         bootstrap_timeline_accounts: Эти аккаунты будут рекомендованы для подписки новым пользователям.
         closed_registrations_message: Отображается, когда регистрация закрыта
@@ -81,6 +82,7 @@ ru:
         custom_css: Вы можете применять пользовательские стили в веб-версии Mastodon.
         mascot: Заменяет иллюстрацию в расширенном веб-интерфейсе.
         media_cache_retention_period: Скачанные медиа-файлы будут удалены после указанного количества дней, когда установлено положительное значение и повторно загружены по требованию.
+        peers_api_enabled: Список доменных имен, с которыми сервер столкнулся в fediverse. Здесь нет данных о том, федерировались ли вы с данным сервером, только что ваш сервер знает об этом. Это используется службами, которые собирают статистику по федерации в общем смысле.
         profile_directory: В каталоге профилей перечислены все пользователи, которые согласились быть доступными для обнаружения.
         require_invite_text: Когда регистрация требует ручного одобрения, сделайте текстовый ввод "Почему вы хотите присоединиться?" обязательным, а не опциональным
         site_contact_email: Как люди могут связаться с вами по юридическим вопросам или вопросам поддержки.
@@ -89,11 +91,13 @@ ru:
         site_short_description: Краткое описание, помогающее однозначно идентифицировать ваш сервер. Кто им управляет, для кого он предназначен?
         site_terms: Используйте свою собственную политику конфиденциальности или оставьте пустым, чтобы использовать политику по умолчанию. Можно использовать синтаксис Markdown.
         site_title: Как люди могут ссылаться на ваш сервер, помимо его доменного имени.
+        status_page_url: URL страницы, на которой люди могут видеть статус этого сервера во время отключения
         theme: Тема, которую видят вышедшие из системы посетители и новые пользователи.
         thumbnail: Изображение примерно 2:1, отображаемое рядом с информацией о вашем сервере.
         timeline_preview: Посетители, вышедшие из системы, смогут просматривать последние публичные сообщения, имеющиеся на сервере.
         trendable_by_default: Пропустить ручной просмотр трендового контента. Отдельные элементы могут быть удалены из трендов уже постфактум.
         trends: Тренды показывают, какие посты, хэштеги и новостные истории набирают обороты на вашем сервере.
+        trends_as_landing_page: Показывать популярный контент для выходов пользователей и посетителей, а не для описания этого сервера. Требует включения тенденций.
       form_challenge:
         current_password: Вы переходите к настройкам безопасности
       imports:
@@ -229,6 +233,7 @@ ru:
           hide: Скрыть полностью
           warn: Скрыть с предупреждением
       form_admin_settings:
+        activity_api_enabled: Публикация агрегированной статистики активности пользователей в API
         backups_retention_period: Период хранения архива пользователя
         bootstrap_timeline_accounts: Всегда рекомендовать эти учетные записи новым пользователям
         closed_registrations_message: Сообщение, когда регистрация недоступна
@@ -236,6 +241,7 @@ ru:
         custom_css: Пользовательский CSS
         mascot: Пользовательский маскот (устаревшее)
         media_cache_retention_period: Период хранения кэша медиафайлов
+        peers_api_enabled: Публикация списка обнаруженных узлов в API
         profile_directory: Включить каталог профилей
         registrations_mode: Кто может зарегистрироваться
         require_invite_text: Требуется причина для присоединения
@@ -247,11 +253,13 @@ ru:
         site_short_description: Описание сервера
         site_terms: Политика конфиденциальности
         site_title: Имя сервера
+        status_page_url: Страница уведомлений
         theme: Тема по умолчанию
         thumbnail: Изображение сервера
         timeline_preview: Разрешить доступ к публичным лентам без авторизации
         trendable_by_default: Разрешить треды без предварительной проверки
         trends: Включить тренды
+        trends_as_landing_page: Использовать тенденции в качестве целевой страницы
       interactions:
         must_be_follower: Присылать уведомления только от подписчиков
         must_be_following: Присылать уведомления только от людей на которых вы подписаны
diff --git a/config/locales/simple_form.sc.yml b/config/locales/simple_form.sc.yml
index 96c31c374..56638352a 100644
--- a/config/locales/simple_form.sc.yml
+++ b/config/locales/simple_form.sc.yml
@@ -18,8 +18,6 @@ sc:
           disable: Impedi a s'utente de impreare su contu suo, ma non de cantzellare o cuare is cuntenutos.
           none: Imprea custu pro imbiare un'avisu a s'utente, sena pedire peruna àtera atzione.
           sensitive: Custringhe totu is alligongiados multimediales de custu utente a èssere marcados comente sensìbiles.
-          silence: Impedi a s'utente de publicare messàgios cun visibilidade pùblica, e de cuare messàgios e notìficas a persones chi non ddos sighint.
-          suspend: Impedi cale si siat cuntatu dae o cun custu contu e cantzella is cuntenutos. Si podet annullare intro de 30 dies.
         warning_preset_id: Optzionale. Podes ancora agiùnghere testu personalizadu a s'acabu de cussu predefinidu
       announcement:
         all_day: Si est marcadu, ant a èssere ammustradas isceti is datas de s'intervallu de tempus
diff --git a/config/locales/simple_form.sco.yml b/config/locales/simple_form.sco.yml
index 4127a8e7e..0dc4fdd79 100644
--- a/config/locales/simple_form.sco.yml
+++ b/config/locales/simple_form.sco.yml
@@ -18,8 +18,6 @@ sco:
           disable: Stap the uiser fae uisin their accoont, but dinnae delete or hide their content.
           none: Uise this fir tae sen a warnin tae the uiser, athoot stertin onie ither actions.
           sensitive: Mak aw this uiser's media attachments hae a flag sayin it's sensitive.
-          silence: Stap the uiser fae bein able tae post wi public visibility, plank their posts an notes fae fowk no follaein them.
-          suspend: Stap onie interactions fae this accoont an delete its contents. Revertable athin 30 days.
         warning_preset_id: Optional. Ye kin stull eik custom text tae the en o the preset
       announcement:
         all_day: Whan ticked, ainly the dates o the time range'll get shawn
diff --git a/config/locales/simple_form.si.yml b/config/locales/simple_form.si.yml
index 829d42e4c..e107d82ac 100644
--- a/config/locales/simple_form.si.yml
+++ b/config/locales/simple_form.si.yml
@@ -18,8 +18,6 @@ si:
           disable: පරිශීලකයාගේ ගිණුම භාවිතා කිරීමෙන් වළක්වන්න, නමුත් ඔවුන්ගේ අන්තර්ගතය මකා දැමීම හෝ සඟවන්න එපා.
           none: වෙනත් ක්‍රියාවක් අවුලුවාලීමකින් තොරව, පරිශීලකයාට අනතුරු ඇඟවීමක් යැවීමට මෙය භාවිතා කරන්න.
           sensitive: මෙම පරිශීලකයාගේ සියලුම මාධ්‍ය ඇමුණුම් සංවේදී ලෙස සලකුණු කිරීමට බල කරන්න.
-          silence: පරිශීලකයාට පොදු දෘශ්‍යතාව සමඟ පළ කිරීමට හැකි වීම වළක්වන්න, ඔවුන් අනුගමනය නොකරන පුද්ගලයින්ගෙන් ඔවුන්ගේ පළ කිරීම් සහ දැනුම්දීම් සඟවන්න.
-          suspend: මෙම ගිණුමෙන් හෝ මෙම ගිණුමට යම් අන්තර්ක්‍රියා වළක්වා එහි අන්තර්ගතය මකා දමන්න. දින 30 ක් ඇතුළත ආපසු හැරවිය හැකිය.
         warning_preset_id: විකල්ප. ඔබට තවමත් පෙරසිටුවීමේ අවසානයට අභිරුචි පෙළ එක් කළ හැක
       announcement:
         all_day: පරීක්ෂා කළ විට, කාල පරාසයේ දින පමණක් දර්ශනය වනු ඇත
diff --git a/config/locales/simple_form.sk.yml b/config/locales/simple_form.sk.yml
index b621ced66..cf5086be5 100644
--- a/config/locales/simple_form.sk.yml
+++ b/config/locales/simple_form.sk.yml
@@ -147,6 +147,9 @@ sk:
         whole_word: Celé slovo
       featured_tag:
         name: Haštag
+      form_admin_settings:
+        peers_api_enabled: Zverejni zoznam objavených serverov v API
+        status_page_url: URL adresa stránky stavu
       interactions:
         must_be_follower: Blokuj oboznámenia od užívateľov, ktorí ma nenasledujú
         must_be_following: Blokuj oboznámenia od ľudí, ktorých nesledujem
@@ -161,13 +164,13 @@ sk:
       notification_emails:
         digest: Zasielať súhrnné emaily
         favourite: Zaslať email, ak si niekto obľúbi tvoj príspevok
-        follow: Zaslať email, ak ťa niekto začne následovať
+        follow: Niekto ťa začal nasledovať
         follow_request: Zaslať email, ak ti niekto pošle žiadosť o sledovanie
         mention: Zaslať email, ak ťa niekto spomenie vo svojom príspevku
         pending_account: Zaslať email, ak treba prehodnotiť nový účet
         reblog: Zaslať email, ak niekto re-tootne tvoj príspevok
       tag:
-        listable: Povoľ zobrazovanie tohto haštagu v zozname profilov
+        listable: Povoľ zobrazovanie tohto haštagu v návrhoch vyhľadávaní
         name: Haštag
         trendable: Povoľ zobrazovanie tohto haštagu medzi trendujúcimi
         usable: Povoľ používanie tohto haštagu v príspevkoch
diff --git a/config/locales/simple_form.sl.yml b/config/locales/simple_form.sl.yml
index bdc7592a4..d852d9f9d 100644
--- a/config/locales/simple_form.sl.yml
+++ b/config/locales/simple_form.sl.yml
@@ -18,8 +18,8 @@ sl:
           disable: Preprečite uporabniku, da uporablja svoj račun, vendar ne izbrišite ali skrijte njegove vsebine.
           none: Uporabite to, da pošljete opozorilo uporabnik, ne da bi sprožili kakšno drugo dejanje.
           sensitive: Vsilite, da so vse medijske priponke tega uporabnika označene kot občutljive.
-          silence: Prepreči uporabniku, da lahko objavlja javno, skrije njihove objave in obvestila pred osebami, ki mu ne sledijo.
-          suspend: Prepreči vsakršno interakcijo od ali do tega računa in izbriše njegovo vsebino. Povratno v roku 30 dni.
+          silence: Prepreči uporabniku, da lahko objavlja javno, skrije njihove objave in obvestila pred osebami, ki mu ne sledijo. Zaključi vse prijave zoper ta račun.
+          suspend: Prepreči vsakršno interakcijo od ali do tega računa in izbriše njegovo vsebino. Povratno v roku 30 dni. Zaključi vse prijave zoper ta račun.
         warning_preset_id: Neobvezno. Še vedno lahko dodate besedilo po meri na konec prednastavitve
       announcement:
         all_day: Če je potrjeno, bodo prikazani le datumi časovnega obsega
@@ -74,6 +74,7 @@ sl:
           hide: Povsem skrij filtrirano vsebino, kot da ne bi obstajala
           warn: Skrij filtrirano vsebino za opozorilom, ki pomenja naslov filtra
       form_admin_settings:
+        activity_api_enabled: Številke krajevno objavljenih objav, dejavnih uporabnikov in novih registracij na tedenskih seznamih
         backups_retention_period: Hani tvorjene arhive uporabnikov navedeno število dni.
         bootstrap_timeline_accounts: Ti računi bodo pripeti na vrh priporočenih sledenj za nove uporabnike.
         closed_registrations_message: Prikazano, ko so registracije zaprte
@@ -81,6 +82,7 @@ sl:
         custom_css: Spletni različici Mastodona lahko uveljavite sloge po meri.
         mascot: Preglasi ilustracijo v naprednem spletnem vmesniku.
         media_cache_retention_period: Prenesene predstavnostne datoteke bodo izbrisane po navedenem številu dni, če je vrednost pozitivna, in ponovno prenesene na zahtevo.
+        peers_api_enabled: Seznam imen domen, na katere je ta strežnik naletel v fediverzumu. Sem niso vključeni podatki o tem, ali ste v federaciji z danim strežnikom, zgolj to, ali vaš strežnik ve zanj. To uporabljajo storitve, ki zbirajo statistične podatke o federaciji v splošnem smislu.
         profile_directory: Imenik profilov izpiše vse uporabnike, ki so dovolili, da so v njem navedeni.
         require_invite_text: Če registracije zahtevajo ročno potrditev, nastavite vnos besedila pod »Zakaj se želite pridružiti?« za obveznega.
         site_contact_email: Kako vas lahko uporabniki dosežejo glede pravnih ali podpornih vprašanj.
@@ -89,11 +91,13 @@ sl:
         site_short_description: Kratek opis v pomoč za identifikacijo vašega strežnika. Kdo ga vzdržuje, komu je namenjen?
         site_terms: Uporabite svoj lasten pravilnik o zasebnosti ali pustite prazno za privzetega. Lahko ga strukturirate s skladnjo Markdown.
         site_title: Kako naj imenujejo vaš strežnik poleg njegovega domenskega imena.
+        status_page_url: URL strani, kjer je moč videti stanje tega strežnika med prekinjenim delovanjem
         theme: Tema, ki jo vidijo odjavljeni obiskovalci in novi uporabniki.
         thumbnail: Slika v razmerju stranic približno 2:1, prikazana vzdolž podatkov o vašem strežniku.
         timeline_preview: Odjavljeni obiskovalci bodo lahko brskali po najnovejših javnih objavah, ki so na voljo na strežniku.
         trendable_by_default: Preskočite ročni pregled vsebine v trendu. Posamezne elemente še vedno lahko odstranite iz trenda post festum.
         trends: Trendi prikažejo, katere objave, ključniki in novice privlačijo zanimanje na vašem strežniku.
+        trends_as_landing_page: Odjavljenim uporabnikom in obiskovalcem namesto opisa tega strežnika pokažite vsebine v trendu. Trendi morajo biti omogočeni.
       form_challenge:
         current_password: Vstopate v varovano območje
       imports:
@@ -229,6 +233,7 @@ sl:
           hide: Povsem skrij
           warn: Skrij z opozorilom
       form_admin_settings:
+        activity_api_enabled: Objavi združeno statistiko o dejavnosti uporabnikov v API-ju
         backups_retention_period: Obdobje hrambe arhivov uporabnikov
         bootstrap_timeline_accounts: Vedno priporočaj te račune novim uporabnikom
         closed_registrations_message: Sporočilo po meri, ko registracije niso na voljo
@@ -236,6 +241,7 @@ sl:
         custom_css: CSS po meri
         mascot: Maskota po meri (opuščeno)
         media_cache_retention_period: Obdobje hrambe predpomnilnika predstavnosti
+        peers_api_enabled: Objavi seznam odkritih strežnikov v API-ju
         profile_directory: Omogoči imenik profilov
         registrations_mode: Kdo se lahko registrira
         require_invite_text: Zahtevaj razlog za pridružitev
@@ -247,11 +253,13 @@ sl:
         site_short_description: Opis strežnika
         site_terms: Pravilnik o zasebnosti
         site_title: Ime strežnika
+        status_page_url: URL strani stanja
         theme: Privzeta tema
         thumbnail: Sličica strežnika
         timeline_preview: Omogoči neoverjen dostop do javnih časovnic
         trendable_by_default: Dovoli trende brez predhodnega pregleda
         trends: Omogoči trende
+        trends_as_landing_page: Uporabi trende za pristopno stran
       interactions:
         must_be_follower: Blokiraj obvestila nesledilcev
         must_be_following: Blokiraj obvestila oseb, ki jim ne sledite
diff --git a/config/locales/simple_form.sq.yml b/config/locales/simple_form.sq.yml
index 161521a57..6fe61ca23 100644
--- a/config/locales/simple_form.sq.yml
+++ b/config/locales/simple_form.sq.yml
@@ -18,8 +18,8 @@ sq:
           disable: Pengoji përdoruesit përdorimin e llogarisë së tij, por mos fshi apo fshih lëndën e tij.
           none: Përdoreni këtë për t’i dërguar përdoruesit një sinjalizim, pa u kryer ndonjë veprim tjetër.
           sensitive: Vëru krejt bashkëngjitjeve media të këtij përdoruesi shenjë si rezervat.
-          silence: Pengoji përdoruesit të qenët i aftë të postojë publikisht, fshihja postimet dhe njoftimet e tij personave që nuk i ndjekin ato.
-          suspend: Pengo çfarëdo ndërveprimi nga ose për te kjo llogari dhe fshi lëndën e saj. E prapësueshme brenda 30 ditësh.
+          silence: Pengoji përdoruesit të jetë i aftë të postojë me dukshmëri publike, fshih postimet dhe njoftimet prej tyre për njerëz që nuk i ndjekin. Mbyll krejt raportimet kundër kësaj llogarie.
+          suspend: Pengo çfarëdo ndërveprimi prej ose për këtë llogari dhe fshi lëndën e saj. E prapakthyeshme brenda 30 ditësh. Mbyll krejt raportet kundër kësaj llogarie.
         warning_preset_id: Opsionale. Mundeni sërish të shtoni tekst vetjak në fund të paracaktimit
       announcement:
         all_day: Nëse i vihet shenjë, do të shfaqen vetëm datat e intervalit kohor
@@ -57,7 +57,7 @@ sq:
         setting_hide_network: Cilët ndiqni dhe cilët ju ndjekin nuk do të shfaqen në profilin tuaj
         setting_noindex: Prek faqet e profilit tuaj publik dhe gjendjeve
         setting_show_application: Aplikacioni që përdorni për mesazhe do të shfaqet te pamja e hollësishme për mesazhet tuaj
-        setting_use_blurhash: Gradientët bazohen në ngjyrat e elementëve pamorë të fshehur, por eerësojnë çfarëdo hollësie
+        setting_use_blurhash: Gradientët bazohen në ngjyrat e elementëve pamorë të fshehur, por errësojnë çfarëdo hollësie
         setting_use_pending_items: Fshihi përditësimet e rrjedhës kohore pas një klikimi, në vend të rrëshqitjes automatike nëpër prurje
         username: Emri juaj i përdoruesit do të jetë unik në %{domain}
         whole_word: Kur fjalëkyçi ose fraza është vetëm numerike, do të aplikohet vetëm nëse përputhet me krejt fjalën
@@ -74,6 +74,7 @@ sq:
           hide: Fshihe plotësisht lëndën e filtruar, duke u sjellë sikur të mos ekzistonte
           warn: Fshihe lëndën e filtruar pas një sinjalizimi që përmend titullin e filtrit
       form_admin_settings:
+        activity_api_enabled: Numër postimesh të botuar lokalisht, përdoruesish aktiv dhe regjistrimesh të reja sipas matjesh javore
         backups_retention_period: Mbaji arkivat e prodhuara të përdoruesve për aq ditë sa numri i dhënë.
         bootstrap_timeline_accounts: Këto llogari do të fiksohen në krye të rekomandimeve për ndjekje nga përdorues të rinj.
         closed_registrations_message: Shfaqur kur mbyllen dritare regjistrimesh
@@ -81,6 +82,7 @@ sq:
         custom_css: Stile vetjakë mund të aplikoni në versionin web të Mastodon-it.
         mascot: Anashkalon ilustrimin te ndërfaqja web e thelluar.
         media_cache_retention_period: Kartelat media të shkarkuara do të fshihen pas numrit të dhënë të ditëve, kur këtij i jepet një vlerë pozitive dhe rishkarkohen po u kërkua.
+        peers_api_enabled: Një listë emrash përkatësish që ky shërbyes ka hasur në fedivers. Këtu s’jepen të dhëna nëse jeni i federuar me shërbyesin e dhënë, thjesht tregohet se shërbyesi juaj e njeh. Kjo përdoret nga shërbime që mbledhin statistika mbi federimin në kuptimin e përgjithshëm.
         profile_directory: Drejtoria e profileve paraqet krejt përdoruesit që kanë zgjedhur të jenë të zbulueshëm.
         require_invite_text: Kur regjistrimet lypin miratim dorazi, bëje tekstin “Përse doni të bëheni pjesë?” të detyrueshëm, në vend se opsional
         site_contact_email: Si mund të lidhen me ju njerëzit, për çështje ligjore, ose për asistencë.
@@ -89,11 +91,13 @@ sq:
         site_short_description: Një përshkrim i shkurtër për të ndihmuar identifikimin unik të shërbyesit tuaj. Kush e mban në punë, për kë është?
         site_terms: Përdorni rregullat tuaja të privatësisë, ose lëreni të zbrazët që të përdoren ato parazgjedhje. Mund të hartohet me sintaksë Markdown.
         site_title: Si mund t’i referohen njerëzit shërbyesit tuaj, përveç emrit të tij të përkatësisë.
+        status_page_url: URL e faqe ku njerëzit mund të shohin gjendjen e këtij shërbyesi, gjatë një ndërprerje të funksionimit
         theme: Temë që shohin vizitorët që kanë bërë daljen dhe përdorues të rinj.
         thumbnail: Një figurë afërsisht 2:1 e shfaqur tok me hollësi mbi shërbyesin tuaj.
-        timeline_preview: Vizitorët që kanë bërë daljen do të jenë në gjendje të shfletojnë psotimet më të freskëta publike të passhme në shërbyes.
+        timeline_preview: Vizitorët që kanë bërë daljen do të jenë në gjendje të shfletojnë postimet më të freskëta publike të passhme në shërbyes.
         trendable_by_default: Anashkalo shqyrtim dorazi lënde në modë. Gjëra individuale prapë mund të hiqen nga lëndë në modë pas publikimi.
         trends: Gjërat në modë shfaqin cilat postime, hashtagë dhe histori të reja po tërheqin vëmendjen në shërbyesin tuaj.
+        trends_as_landing_page: Shfaq lëndë në modë për përdorues jo të futur në llogari dhe për vizitorë, në vend se të një përshkrimi të këtij shërbyesi. Lyp që të jenë të aktivizuara gjërat në modë.
       form_challenge:
         current_password: Po hyni në një zonë të sigurt
       imports:
@@ -123,7 +127,7 @@ sq:
         color: Ngjyrë për t’u përdorur për rolin nëpër UI, si RGB në format gjashtëmbëdhjetësh
         highlighted: Kjo e bën rolin të dukshëm publikisht
         name: Emër publik për rolin, nëse roli është ujdisur të shfaqet si një stemë
-        permissions_as_keys: Përdoruest me këtë rol do të mund të…
+        permissions_as_keys: Përdoruesit me këtë rol do të mund të…
         position: Role më të lartë vendosin zgjidhje përplasje në disa raste. Disa veprime mund të kryhen vetëm mbi role të një shkalle më të ulët
       webhook:
         events: Përzgjidhni akte për dërgim
@@ -229,6 +233,7 @@ sq:
           hide: Fshihe plotësisht
           warn: Fshihe me një sinjalizim
       form_admin_settings:
+        activity_api_enabled: Publikoni te API statistika përmbledhëse rreth veprimtarisë së përdoruesve
         backups_retention_period: Periudhë mbajtjeje arkivash përdoruesish
         bootstrap_timeline_accounts: Rekomandoju përherë këto llogari përdoruesve të rinj
         closed_registrations_message: Mesazh vetjak për pamundësi regjistrimesh të reja
@@ -236,6 +241,7 @@ sq:
         custom_css: CSS Vetjake
         mascot: Simbol vetjak (e dikurshme)
         media_cache_retention_period: Periudhë mbajtjeje lënde media
+        peers_api_enabled: Publiko te API listë shërbyesish të zbuluar
         profile_directory: Aktivizo drejtori profilesh
         registrations_mode: Kush mund të regjistrohet
         require_invite_text: Kërko një arsye për pjesëmarrje
@@ -247,11 +253,13 @@ sq:
         site_short_description: Përshkrim shërbyesi
         site_terms: Rregulla Privatësie
         site_title: Emër shërbyesi
+        status_page_url: URL faqeje gjendjesh
         theme: Temë parazgjedhje
         thumbnail: Miniaturë shërbyesi
         timeline_preview: Lejo hyrje pa mirëfilltësim te rrjedha kohore publike
         trendable_by_default: Lejoni gjëra në modë pa shqyrtim paraprak
         trends: Aktivizo gjëra në modë
+        trends_as_landing_page: Përdor gjërat në modë si faqe hyrëse
       interactions:
         must_be_follower: Blloko njoftime nga jo-ndjekës
         must_be_following: Blloko njoftime nga persona që s’i ndiqni
diff --git a/config/locales/simple_form.sr-Latn.yml b/config/locales/simple_form.sr-Latn.yml
index 069d30b6d..e987145c1 100644
--- a/config/locales/simple_form.sr-Latn.yml
+++ b/config/locales/simple_form.sr-Latn.yml
@@ -3,124 +3,128 @@ sr-Latn:
   simple_form:
     hints:
       account_alias:
-        acct: Navedi korisničko_ime@domen naloga sa kojeg želiš da pređeš
+        acct: Navedite korisničko_ime@domen naloga sa kojeg želite da se preselite
       account_migration:
-        acct: Navedi korisničko_ime@domen naloga na koji želiš da pređeš
+        acct: Navedite korisničko_ime@domen naloga na koji želite da se preselite
       account_warning_preset:
-        text: Možete koristiti sintaksu truba, kao što su npr. URL-ova, tarabe i pominjanja
+        text: Možete koristiti sintaksu objava, kao što su URL adrese, heš oznake i pominjanja
         title: Opciono. Nije vidljivo primaocu
       admin_account_action:
-        include_statuses: Korisnik će videti koje su objave prouzrokovale moderiranje ili upozorenje
-        send_email_notification: Korisnik će dobiti objašnjenje toga šta mu se desilo sa naloga
-        text_html: Opcionalno. Možete koristiti sintaksu truba. Možete <a href="%{path}">dodati upozoravajuća prepodešavanje</a> da sačuvate vreme
+        include_statuses: Korisnik će videti koje su objave prouzrokovale moderacijsku radnju ili upozorenje
+        send_email_notification: Korisnik će dobiti objašnjenje toga šta mu se desilo sa nalogom
+        text_html: Opcionalno. Možete koristiti sintaksu objava. Možete <a href="%{path}">dodati unapred određene postavke upozorenja</a> za uštedu vremena
         type_html: Izaberite šta da radite sa <strong>%{acct}</strong>
         types:
-          disable: Spreči korisnika da koristi svoj nalog, ali nemoj brisati ili sakrivati njegov sadržaj.
-          none: Koristi ovo da pošalješ upozorenje korisniku, bez pokretanja bilo koje druge akcije.
+          disable: Sprečava korisnika da koristi svoj nalog, ali ne briše niti sakriva njegove sadržaje.
+          none: Koristite ovo da pošaljete upozorenje korisniku bez pokretanja bilo koje druge radnje.
           sensitive: Učini da svi medijski prilozi ovog korisnika prisilno budu označeni kao osetljivi.
-          silence: Onemogući korisnika da objavljuje javno, sakrij sve njegove svoje objave i obaveštenja od korisnika koji ga ne prate.
-          suspend: Spreči bilo kakvu interakciju sa ovog naloga ili sa njim i izbriši njegov sadržaj. Može da se opozove u roku od 30 dana.
+          silence: Sprečava korisnika da pravi javne objave, sakriva njegove objave i obaveštenja od ljudi koji ga ne prate. Zatvara sve prijave podnete protiv ovog naloga.
+          suspend: Sprečava svu interakciju od ovog naloga i ka ovom nalogu i briše njegov sadržaj. Opozivo u roku od 30 dana. Zatvara sve prijave podnete protiv ovog naloga.
         warning_preset_id: Opcionalno. Možete i dalje dodati prilagođeni tekst na kraj preseta
       announcement:
-        all_day: Biće prikazani samo datumi vremenskog opsega koji su označeni
+        all_day: Kada je ova opcija označena, samo datumi iz vremenskog opsega će biti prikazani
         ends_at: Opciono. Objava će biti automatski opozvana u ovom trenutku
-        scheduled_at: Ostavi prazno da bi najava bila odmah objavljena
+        scheduled_at: Ostavite prazno da biste odmah objavili obaveštenje
         starts_at: Opciono. U slučaju da je najava vezana za određeni vremenski raspon
-        text: Možeš koristiti post sintaksu. Vodi računa o prostoru koji će objava zauzimati na ekranu korisnika
+        text: Možete koristiti sintaksu objava. Molimo Vas vodite računa o prostoru koji će objava zauzimati na ekranu korisnika
       appeal:
-        text: Na brisanje se možeš žaliti samo jednom
+        text: Možete podneti samo jednu žalbu na upisan prestup
       defaults:
         autofollow: Osobe koje se prijave kroz pozivnice će vas automatski zapratiti
-        avatar: PNG, GIF ili JPG. Najviše %{size}. Biće smanjena na %{dimensions}px
-        bot: Ovaj nalog uglavnom vrši automatizovane radnje i možda se ne nadgleda
+        avatar: PNG, GIF ili JPG. Najviše %{size}. Biće smanjeno na %{dimensions}px
+        bot: Daje drugima do znanja da ovaj nalog uglavnom vrši automatizovane radnje i možda se ne nadgleda
         context: Jedan ili više konteksta u kojima treba da se primeni filter
-        current_password: Unesi lozinku tekućeg naloga iz bezbednosnih razloga
-        current_username: Unesi korisničko ime tekućeg naloga za potvrdu
-        digest: Poslato posle dužeg perioda neaktivnosti sa pregledom svih bitnih stvari koje ste dobili dok ste bili odsutni
-        discoverable: Dozvoli nepoznatim korisnicima da otkriju tvoj nalog putem preporuka, trendova i drugih funkcija
-        email: Biće vam poslata e-pošta sa potvrdom
-        fields: Možete imati do 4 stavke prikazane kao tabela na vašem nalogu
-        header: PNG, GIF ili JPG. Najviše %{size}. Biće smanjena na %{dimensions}px
+        current_password: Iz bezbednosnih razloga molimo Vas unesite lozinku trenutnog naloga
+        current_username: Da biste potvrdili, Molimo Vas unesite korisničko ime trenutno aktivnog naloga
+        digest: Šalje se samo posle dužeg perioda neaktivnosti i samo u slučaju da ste primili jednu ili više ličnih poruka tokom Vašeg odsustva
+        discoverable: Dozvolite nepoznatim korisnicima da otkriju Vaš nalog putem preporuka, trendova i drugih funkcija
+        email: Biće Vam poslat mejl sa potvrdom
+        fields: Možete imati do 4 stavke prikazane kao tabela na svom profilu
+        header: PNG, GIF ili JPG. Najviše %{size}. Biće smanjeno na %{dimensions}px
         inbox_url: Kopirajte URL sa naslovne strane releja koji želite koristiti
-        irreversible: Filtrirane trube će nestati nepovratno, čak i ako je filter kasnije uklonjen
-        locale: Jezik korisničkog interfejsa, e-pošte i mobilnih obaveštenja
-        locked: Zahteva da pojedinačno odobrite pratioce
+        irreversible: Filtrirane obajve će nestati nepovratno, čak i ako je filter kasnije uklonjen
+        locale: Jezik korisničkog okruženja, e-pošte i mobilnih obaveštenja
+        locked: Kontrolišite ko može da Vas prati tako što ćete pojedinačno odobravati zahteve za praćenje
         password: Koristite najmanje 8 znakova
-        phrase: Biće uparena bez obzira na veliko ili malo slovo u tekstu ili upozorenja o sadržaju trube
-        scopes: Kojim API-jima će aplikacija dozvoliti pristup. Ako izaberete opseg najvišeg nivoa, ne morate odabrati pojedinačne.
-        setting_aggregate_reblogs: Ne pokazuj nova deljenja za trube koje su nedavno podeljene (utiče samo na nedavno primljena deljenja)
-        setting_always_send_emails: Obaveštenja e-poštom se po pravilu neće slati kada aktivno koristiš Mastodon
+        phrase: Biće uparena bez obzira na veliko ili malo slovo u tekstu ili upozorenja o sadržaju objave
+        scopes: Kojim API-jima će aplikacija imati pristup. Ako izaberete opseg najvišeg nivoa, ne morate odabrati pojedinačne.
+        setting_aggregate_reblogs: Ne prikazuj nova podržavanja za objave koje su nedavno podržane (utiče samo na nedavno primljena podržavanja)
+        setting_always_send_emails: Obaveštenja e-poštom se po pravilu neće slati kada aktivno koristite Mastodon
         setting_default_sensitive: Osetljivi mediji su podrazumevano skriveni i mogu se otkriti klikom
         setting_display_media_default: Sakrij medije označene kao osetljive
         setting_display_media_hide_all: Uvek sakrij sve medije
         setting_display_media_show_all: Uvek prikaži medije označene kao osetljive
-        setting_hide_network: Koga pratite i ko vas prati neće biti prikazano na vašem nalogu
-        setting_noindex: Utiče na Vaš javni nalog i statusne strane
-        setting_show_application: Aplikacija koju koristiš za objavljivanje biće prikazana u detaljnom prikazu tvojih objava
-        setting_use_blurhash: Gradijent se zasniva na bojama skrivenih vizuelnih prikaza, ali prikriva sve detalje
-        setting_use_pending_items: Sakrij ažuriranja vremenske ose iza klika umesto automatskog pomeranja izvora objava
+        setting_hide_network: Koga pratite i ko Vas prati neće biti prikazano na Vašem profilu
+        setting_noindex: Utiče na Vaš javni profil i stranice sa objavama
+        setting_show_application: Aplikacija koju koristite za objavljivanje će biti prikazana u detaljnom prikazu vaših objava
+        setting_use_blurhash: Gradijenti se formiraju na osnovu bojâ skrivenih slika i zamućuju prikaz, prikrivajući detalje
+        setting_use_pending_items: Sakriva ažuriranja vremenske linije iza klika umesto automatskog ažuriranja i pomeranja vremenske linije
         username: Vaš nadimak će biti jedinstven na %{domain}
-        whole_word: Kada je ključna reč ili fraza isključivo alfanumerička, biće primenjena samo ako se podudara sa celom reči
+        whole_word: Kada je ključna reč ili fraza isključivo alfanumerička, biće primenjena samo ako se podudara sa celom rečju
       domain_allow:
         domain: Ovaj domen će moći da preuzima podatke sa ovog servera i dolazni podaci sa njega će se obrađivati i čuvati
       email_domain_block:
         domain: Ovo može biti ime domena koje se pojavljuje u adresi e-pošte ili MX zapisa koji koristi. Oni će biti provereni prilikom registracije.
         with_dns_records: Biće učinjen pokušaj da se razreše DNS zapisi datog domena i rezultati će takođe biti blokirani
       featured_tag:
-        name: 'Evo nekih od heš oznaka koje ste nedavno koristili:'
+        name: 'Evo nekih od heš oznaka koje ste u prethodnom periodu često koristili:'
       filters:
-        action: Izaberi koju radnju treba izvršiti kada objava odgovara filteru
+        action: Izaberite koju radnju izvršiti kada objava odgovara filteru
         actions:
           hide: Potpuno sakrij filtrirani sadržaj, ponašajući se kao da ne postoji
           warn: Sakrij filtrirani sadržaj iza upozorenja u kome se navodi naziv filtera
       form_admin_settings:
+        activity_api_enabled: Brojevi lokalno postavljenih objava, aktivnih korisnika i novih registracija na nedeljnoj bazi
         backups_retention_period: Čuvaj generisane korisničke arhive navedeni broj dana.
         bootstrap_timeline_accounts: Ovi nalozi će biti zakačeni na vrh preporuka za praćenje novih korisnika.
-        closed_registrations_message: Prikazuje se kada su prijave zatvorene
+        closed_registrations_message: Prikazuje se kada su registracije zatvorene
         content_cache_retention_period: Kada se postavi na pozitivnu vrednost, objave sa drugih servera će biti izbrisane nakon navedenog broja dana. Ovo može biti nepovratno.
-        custom_css: Možeš da primeniš prilagođene stilove na veb verziji Mastodona.
-        mascot: Zamenjuje ilustraciju u naprednom veb interfejsu.
+        custom_css: Možete da primenite prilagođene stilove na veb verziji Mastodona.
+        mascot: Zamenjuje ilustraciju u naprednom veb okruženju.
         media_cache_retention_period: Kada se postavi na pozitivnu vrednost, preuzete medijske datoteke će biti izbrisane nakon navedenog broja dana, i ponovo preuzete na zahtev.
+        peers_api_enabled: Lista domena sa kojima se ovaj server susreo u fediverzumu. Ovde nisu sadržani podaci o tome da li se Vaš server federiše sa drugim serverima, već samo da Vaš server zna za njih. Ove informacije koriste servisi koji prikupljaju podatke i vode statistiku o federaciji u širem smislu.
         profile_directory: Direktorijum profila navodi sve korisnike koji su se opredelili da budu vidljivi.
-        require_invite_text: Kada registracije zahtevaju ručno odobrenje, postavi da unos teksta „Zašto želiš da se pridružiš?“ bude obavezan, a ne opcioni
-        site_contact_email: Kako korisnici mogu da te kontaktiraju za pravna pitanja ili pitanja u vezi podrške.
-        site_contact_username: Kako korisnici mogu da te kontaktiraju na Mastodonu.
-        site_extended_description: Bilo koja dodatna informacija koja može biti korisne posetiocima i tvojim korisnicima. Može se strukturirati pomoću Markdown sintakse.
-        site_short_description: Kratak opis pomoću koga se na jedinstven način identifikuje tvoj server. Ko ga održava, kome je namenjen?
-        site_terms: Koristi sopstvenu politiku privatnosti ili ostavi prazno da bi se koristila podrazumevana. Može se strukturirati pomoću Markdown sintakse.
-        site_title: Način na moji može da se pozove na tvoj server osim naziva njegovog domena.
+        require_invite_text: Kada registracije zahtevaju ručno odobrenje, postavite da odgovor na „Zašto želite da se pridružite?“ bude obavezan, a ne opcionalan
+        site_contact_email: Kako korisnici mogu da kontaktiraju sa Vama za pravna pitanja ili pitanja u vezi podrške.
+        site_contact_username: Kako korisnici mogu da kontaktiraju sa Vama na Mastodonu.
+        site_extended_description: Bilo kakve dodatne informacije koje mogu biti korisne posetiocima i Vašim korisnicima. Mogu se strukturirati pomoću Markdown sintakse.
+        site_short_description: Kratak opis pomoću koga se na jedinstven način identifikuje Vaš server. Ko ga održava, kome je namenjen?
+        site_terms: Koristite sopstvenu politiku privatnosti ili ostavite prazno da bi se koristila podrazumevana. Može se strukturirati pomoću Markdown sintakse.
+        site_title: Način na koji ljudi mogu da referišu na Vaš server osim naziva njegovog domena.
+        status_page_url: URL stranice gde ljudi mogu da vide status servera dok je server oboren
         theme: Tema koju vide posetioci koji nisu prijavljeni i novi korisnici.
-        thumbnail: Slika koja se približno 2:1 prikazuje pored informacija o tvom serveru.
+        thumbnail: Slika u razmeri od približno 2:1 koja se prikazuje pored informacija o Vašem serveru.
         timeline_preview: Posetioci koji nisu prijavljeni će moći da pregledaju najnovije javne objave dostupne na serveru.
         trendable_by_default: Preskoči ručni pregled sadržaja koji je u trendu. Pojedinačne stavke se nakon toga i dalje mogu ukloniti iz trendova.
-        trends: Trendovi pokazuju koje objave, heš oznake i vesti postaju sve popularniji na tvom serveru.
+        trends: Trendovi pokazuju koje objave, heš oznake i vesti postaju sve popularnije na Vašem serveru.
+        trends_as_landing_page: Prikaži sadržaj u trendu odjavljenim korisnicima i posetiocima umesto opisa ovog servera. Zahteva da trendovi budu omogućeni.
       form_challenge:
-        current_password: Ulaziš u bezbedno područje
+        current_password: Ulazite u bezbedno područje
       imports:
         data: CSV fajl izvezen sa druge Mastodont instance
       invite_request:
-        text: Ovo će nam pomoći da pregledamo tvoju prijavu
+        text: Ovo će nam pomoći da pregledamo Vašu prijavu
       ip_block:
-        comment: Opciono. Zapamti zašto si dodao ovo pravilo.
+        comment: Opciono. Zapamtite zašto ste dodali ovo pravilo.
         expires_in: IP adrese su ograničeni resurs, ponekad se dele i često menjaju korisnika. Zbog toga se IP blokovi na neograničeno vreme ne preporučuju.
-        ip: Unesi IPv4 ili IPv6 adresu. Možeš blokirati čitave opsege koristeći CIDR sintaksu. Vodi računa da sebe ne zaključaš!
+        ip: Unesite IPv4 ili IPv6 adresu. Možete blokirati čitave opsege koristeći CIDR sintaksu. Vodite računa da se ne zaključate!
         severities:
           no_access: Blokiraj pristup svim resursima
-          sign_up_block: Nove prijave neće biti moguće
-          sign_up_requires_approval: Nove prijave će zahtevati tvoje odobrenje
-        severity: Izaberi šta će se desiti sa zahtevima sa ove IP adrese
+          sign_up_block: Nove registracije neće biti moguće
+          sign_up_requires_approval: Nove registracije će zahtevati Vaše odobrenje
+        severity: Izaberite šta će se desiti sa zahtevima sa ove IP adrese
       rule:
-        text: Opiši pravilo ili zahtev za korisnike na ovom serveru. Potrudi se da opis bude kratak i jednostavan
+        text: Opišite pravilo ili uslov za korisnike na ovom serveru. Potrudite se da opis bude kratak i jednostavan
       sessions:
         otp: 'Unesite dvofaktorski kod sa Vašeg telefona ili koristite jedan od kodova za oporavak:'
-        webauthn: Ako je to USB ključ, obavezno ga ubaci i, ako je potrebno, pritisni ga.
+        webauthn: Ako je u pitanju USB ključ, obavezno ga ubacite i, ako je potrebno, pritisnite ga.
       tag:
-        name: Mogu se samo promeniti mala slova u velika, na primer, da bi bilo čitljivije
+        name: Mogu se samo promeniti mala slova u velika ili obrnuto, na primer, da bi bilo čitljivije
       user:
-        chosen_languages: Kada označite, trube u izabranim jezicima će se prikazati na javnoj vremenskoj liniji
+        chosen_languages: Kada je označeno, objave u izabranim jezicima će biti prikazane na javnoj vremenskoj liniji
         role: Uloga kontroliše koje dozvole korisnik ima
       user_role:
-        color: Boja koja će se koristiti za ulogu u celom korisničkom interfejsu, kao RGB, u heksadecimalnom formatu
+        color: Boja koja će se koristiti za ulogu u celom korisničkom okruženju, kao RGB u heksadecimalnom formatu
         highlighted: Ovo čini ulogu javno vidljivom
         name: Javni naziv uloge, ako je uloga podešena da se prikazuje kao značka
         permissions_as_keys: Korisnici sa ovom ulogom će imati pristup...
@@ -187,24 +191,24 @@ sr-Latn:
         otp_attempt: Dvofaktorski kod
         password: Lozinka
         phrase: Ključna reč ili fraza
-        setting_advanced_layout: Omogući napredni veb interfejs
+        setting_advanced_layout: Omogući napredno veb okruženje
         setting_aggregate_reblogs: Grupiši deljenja u vremenskim linijama
         setting_always_send_emails: Uvek šalji obaveštenja e-poštom
-        setting_auto_play_gif: Automatski puštaj animirane GIF-ove
+        setting_auto_play_gif: Automatski reprodukuj animirane GIF-ove
         setting_boost_modal: Prikaži dijalog za potvrdu pre davanja podrške
         setting_crop_images: Izreži slike u neproširenim objavama na 16x9
         setting_default_language: Jezik objavljivanja
         setting_default_privacy: Privatnost objava
         setting_default_sensitive: Uvek označi multimediju kao osetljivu
-        setting_delete_modal: Prikaži dijalog za potvrdu pre brisanja tuta
+        setting_delete_modal: Prikaži dijalog za potvrdu pre brisanja objave
         setting_disable_swiping: Onemogući pokrete prevlačenja
         setting_display_media: Prikaz medija
         setting_display_media_default: Podrazumevano
         setting_display_media_hide_all: Sakrij sve
         setting_display_media_show_all: Prikaži sve
-        setting_expand_spoilers: Uvek prošiti trube koje su označene upozorenjem sadržaja
+        setting_expand_spoilers: Uvek proširi objave koje su označene upozorenjem sadržaja
         setting_hide_network: Sakrij svoju mrežu
-        setting_noindex: Odjavi se od indeksiranja search engine-a
+        setting_noindex: Onemogući indeksiranje pretraživača
         setting_reduce_motion: Smanji pokrete u animacijama
         setting_show_application: Otkrij aplikaciju koja se koristi za slanje postova
         setting_system_font_ui: Koristi sistemski font
@@ -229,6 +233,7 @@ sr-Latn:
           hide: Sakrij u potpunosti
           warn: Sakrij uz upozorenje
       form_admin_settings:
+        activity_api_enabled: Objavi prikupljenu statistiku o korisničkoj aktivnosti u API
         backups_retention_period: Period čuvanja korisničke arhive
         bootstrap_timeline_accounts: Uvek preporuči ove naloge novim korisnicima
         closed_registrations_message: Prilagođena poruka kada prijave nisu moguće
@@ -236,6 +241,7 @@ sr-Latn:
         custom_css: Prilagođeni CSS
         mascot: Prilagođena maskota (nasleđe)
         media_cache_retention_period: Period čuvanja keša medija
+        peers_api_enabled: Objavite listu otkrivenih servera u API
         profile_directory: Omogući direktorijum profila
         registrations_mode: Ko može da se prijavi
         require_invite_text: Zatraži razlog za pristupanje
@@ -247,11 +253,13 @@ sr-Latn:
         site_short_description: Opis servera
         site_terms: Politika privatnosti
         site_title: Ime servera
+        status_page_url: URL statusne stranice
         theme: Podrazumevana tema
         thumbnail: Sličica servera
         timeline_preview: Dozvoli neautorizovan pristup javnim vremenskim osama
         trendable_by_default: Dozvoli trendove bez prethodnog pregleda
         trends: Omogući trendove
+        trends_as_landing_page: Koristite trendove kao stranicu dočeka
       interactions:
         must_be_follower: Blokiraj obaveštenja od korisnika koji me ne prate
         must_be_following: Blokiraj obaveštenja od ljudi koje ne pratim
@@ -262,7 +270,7 @@ sr-Latn:
         text: Zašto želiš da se pridružiš?
       ip_block:
         comment: Komentar
-        ip: Avatar
+        ip: IP
         severities:
           no_access: Blokiraj pristup
           sign_up_block: Blokiraj prijave
diff --git a/config/locales/simple_form.sr.yml b/config/locales/simple_form.sr.yml
index cf590b5f4..8ee586377 100644
--- a/config/locales/simple_form.sr.yml
+++ b/config/locales/simple_form.sr.yml
@@ -3,124 +3,128 @@ sr:
   simple_form:
     hints:
       account_alias:
-        acct: Наведи корисничко_име@домен налога са којег желиш да пређеш
+        acct: Наведите корисничко_име@домен налога са којег желите да се преселите
       account_migration:
-        acct: Наведи корисничко_име@домен налога на који желиш да пређеш
+        acct: Наведите корисничко_име@домен налога на који желите да се преселите
       account_warning_preset:
-        text: Можете користити синтаксу труба, као што су нпр. УРЛ-ова, тарабе и помињања
+        text: Можете користити синтаксу објава, као што су URL адресе, хеш ознаке и помињања
         title: Опционо. Није видљиво примаоцу
       admin_account_action:
-        include_statuses: Корисник ће видети које су објаве проузроковале модерирање или упозорење
-        send_email_notification: Корисник ће добити објашњење тога шта му се десило са налога
-        text_html: Опционално. Можете користити синтаксу труба. Можете <a href="%{path}">додати упозоравајућа преподешавање</a> да сачувате време
+        include_statuses: Корисник ће видети које су објаве проузроковале модерацијску радњу или упозорење
+        send_email_notification: Корисник ће добити објашњење тога шта му се десило са налогом
+        text_html: Опционално. Можете користити синтаксу објава. Можете <a href="%{path}">додати унапред одређене поставке упозорења</a> за уштеду времена
         type_html: Изаберите шта да радите са <strong>%{acct}</strong>
         types:
-          disable: Спречи корисника да користи свој налог, али немој брисати или сакривати његов садржај.
-          none: Користи ово да пошаљеш упозорење кориснику, без покретања било које друге акције.
+          disable: Спречава корисника да користи свој налог, али не брише нити сакрива његове садржаје.
+          none: Користите ово да пошаљете упозорење кориснику без покретања било које друге радње.
           sensitive: Учини да сви медијски прилози овог корисника присилно буду означени као осетљиви.
-          silence: Онемогући корисника да објављује јавно, сакриј све његове своје објаве и обавештења од корисника који га не прате.
-          suspend: Спречи било какву интеракцију са овог налога или са њим и избриши његов садржај. Може да се опозове у року од 30 дана.
+          silence: Спречава корисника да прави јавне објаве, сакрива његове објаве и обавештења од људи који га не прате. Затвара све пријаве поднете против овог налога.
+          suspend: Спречава сву интеракцију од овог налога и ка овом налогу и брише његов садржај. Опозиво у року од 30 дана. Затвара све пријаве поднете против овог налога.
         warning_preset_id: Опционално. Можете и даље додати прилагођени текст на крај пресета
       announcement:
-        all_day: Биће приказани само датуми временског опсега који су означени
+        all_day: Када је ова опција означена, само датуми из временског опсега ће бити приказани
         ends_at: Опционо. Објава ће бити аутоматски опозвана у овом тренутку
-        scheduled_at: Остави празно да би најава била одмах објављена
+        scheduled_at: Оставите празно да бисте одмах објавили обавештење
         starts_at: Опционо. У случају да је најава везана за одређени временски распон
-        text: Можеш користити пост синтаксу. Води рачуна о простору који ће објава заузимати на екрану корисника
+        text: Можете користити синтаксу објава. Молимо Вас водите рачуна о простору који ће објава заузимати на екрану корисника
       appeal:
-        text: На брисање се можеш жалити само једном
+        text: Можете поднети само једну жалбу на уписан преступ
       defaults:
         autofollow: Особе које се пријаве кроз позивнице ће вас аутоматски запратити
-        avatar: PNG, GIF или JPG. Највише %{size}. Биће смањена на %{dimensions}px
-        bot: Овај налог углавном врши аутоматизоване радње и можда се не надгледа
+        avatar: PNG, GIF или JPG. Највише %{size}. Биће смањено на %{dimensions}px
+        bot: Даје другима до знања да овај налог углавном врши аутоматизоване радње и можда се не надгледа
         context: Један или више контекста у којима треба да се примени филтер
-        current_password: Унеси лозинку текућег налога из безбедносних разлога
-        current_username: Унеси корисничко име текућег налога за потврду
-        digest: Послато после дужег периода неактивности са прегледом свих битних ствари које сте добили док сте били одсутни
-        discoverable: Дозволи непознатим корисницима да открију твој налог путем препорука, трендова и других функција
-        email: Биће вам послата е-пошта са потврдом
-        fields: Можете имати до 4 ставке приказане као табела на вашем налогу
-        header: PNG, GIF или JPG. Највише %{size}. Биће смањена на %{dimensions}px
+        current_password: Из безбедносних разлога молимо Вас унесите лозинку тренутног налога
+        current_username: Да бисте потврдили, Молимо Вас унесите корисничко име тренутно активног налога
+        digest: Шаље се само после дужег периода неактивности и само у случају да сте примили једну или више личних порука током Вашег одсуства
+        discoverable: Дозволите непознатим корисницима да открију Ваш налог путем препорука, трендова и других функција
+        email: Биће Вам послат мејл са потврдом
+        fields: Можете имати до 4 ставке приказане као табела на свом профилу
+        header: PNG, GIF или JPG. Највише %{size}. Биће смањено на %{dimensions}px
         inbox_url: Копирајте URL са насловне стране релеја који желите користити
-        irreversible: Филтриранe трубе ће нестати неповратно, чак и ако је филтер касније уклоњен
-        locale: Језик корисничког интерфејса, е-поште и мобилних обавештења
-        locked: Захтева да појединачно одобрите пратиоце
+        irreversible: Филтриранe обајве ће нестати неповратно, чак и ако је филтер касније уклоњен
+        locale: Језик корисничког окружења, е-поште и мобилних обавештења
+        locked: Контролишите ко може да Вас прати тако што ћете појединачно одобравати захтеве за праћење
         password: Користите најмање 8 знакова
-        phrase: Биће упарена без обзира на велико или мало слово у тексту или упозорења о садржају трубе
-        scopes: Којим API-јима ће апликација дозволити приступ. Ако изаберете опсег највишег нивоа, не морате одабрати појединачне.
-        setting_aggregate_reblogs: Не показуј нова дељења за трубе које су недавно подељене (утиче само на недавно примљена дељења)
-        setting_always_send_emails: Обавештења е-поштом се по правилу неће слати када активно користиш Мастодон
+        phrase: Биће упарена без обзира на велико или мало слово у тексту или упозорења о садржају објаве
+        scopes: Којим API-јима ће апликација имати приступ. Ако изаберете опсег највишег нивоа, не морате одабрати појединачне.
+        setting_aggregate_reblogs: Не приказуј нова подржавања за објаве које су недавно подржане (утиче само на недавно примљена подржавања)
+        setting_always_send_emails: Обавештења е-поштом се по правилу неће слати када активно користите Мастодон
         setting_default_sensitive: Осетљиви медији су подразумевано скривени и могу се открити кликом
         setting_display_media_default: Сакриј медије означене као осетљиве
         setting_display_media_hide_all: Увек сакриј све медије
         setting_display_media_show_all: Увек прикажи медије означене као осетљиве
-        setting_hide_network: Кога пратите и ко вас прати неће бити приказано на вашем налогу
-        setting_noindex: Утиче на Ваш јавни налог и статусне стране
-        setting_show_application: Апликација коју користиш за објављивање биће приказана у детаљном приказу твојих објава
-        setting_use_blurhash: Градијент се заснива на бојама скривених визуелних приказа, али прикрива све детаље
-        setting_use_pending_items: Сакриј ажурирања временске осе иза клика уместо аутоматског померања извора објава
+        setting_hide_network: Кога пратите и ко Вас прати неће бити приказано на Вашем профилу
+        setting_noindex: Утиче на Ваш јавни профил и странице са објавама
+        setting_show_application: Апликација коју користите за објављивање ће бити приказана у детаљном приказу ваших објава
+        setting_use_blurhash: Градијенти се формирају на основу бојâ скривених слика и замућују приказ, прикривајући детаље
+        setting_use_pending_items: Сакрива ажурирања временске линије иза клика уместо аутоматског ажурирања и померања временске линије
         username: Ваш надимак ће бити јединствен на %{domain}
-        whole_word: Када је кључна реч или фраза искључиво алфанумеричка, биће примењена само ако се подудара са целом речи
+        whole_word: Када је кључна реч или фраза искључиво алфанумеричка, биће примењена само ако се подудара са целом речjу
       domain_allow:
         domain: Овај домен ће моћи да преузима податке са овог сервера и долазни подаци са њега ће се обрађивати и чувати
       email_domain_block:
         domain: Ово може бити име домена које се појављује у адреси е-поште или MX записа који користи. Они ће бити проверени приликом регистрације.
         with_dns_records: Биће учињен покушај да се разреше DNS записи датог домена и резултати ће такође бити блокирани
       featured_tag:
-        name: 'Ево неких од хеш ознака које сте недавно користили:'
+        name: 'Ево неких од хеш ознака које сте у претходном периоду често користили:'
       filters:
-        action: Изабери коју радњу треба извршити када објава одговара филтеру
+        action: Изаберите коју радњу извршити када објава одговара филтеру
         actions:
           hide: Потпуно сакриј филтрирани садржај, понашајући се као да не постоји
           warn: Сакриј филтрирани садржај иза упозорења у коме се наводи назив филтера
       form_admin_settings:
+        activity_api_enabled: Бројеви локално постављених објава, активних корисника и нових регистрација на недељној бази
         backups_retention_period: Чувај генерисане корисничке архиве наведени број дана.
         bootstrap_timeline_accounts: Ови налози ће бити закачени на врх препорука за праћење нових корисника.
-        closed_registrations_message: Приказује се када су пријаве затворене
+        closed_registrations_message: Приказује се када су регистрације затворене
         content_cache_retention_period: Када се постави на позитивну вредност, објаве са других сервера ће бити избрисане након наведеног броја дана. Ово може бити неповратно.
-        custom_css: Можеш да примениш прилагођене стилове на веб верзији Мастодона.
-        mascot: Замењује илустрацију у напредном веб интерфејсу.
+        custom_css: Можете да примените прилагођене стилове на веб верзији Мастодона.
+        mascot: Замењује илустрацију у напредном веб окружењу.
         media_cache_retention_period: Када се постави на позитивну вредност, преузете медијске датотеке ће бити избрисане након наведеног броја дана, и поново преузете на захтев.
+        peers_api_enabled: Листа домена са којима се овај сервер сусрео у федиверзуму. Овде нису садржани подаци о томе да ли се Ваш сервер федерише са другим серверима, већ само да Ваш сервер зна за њих. Ове информације користе сервиси који прикупљају податке и воде статистику о федерацији у ширем смислу.
         profile_directory: Директоријум профила наводи све кориснике који су се определили да буду видљиви.
-        require_invite_text: Када регистрације захтевају ручно одобрење, постави да унос текста „Зашто желиш да се придружиш?“ буде обавезан, а не опциони
-        site_contact_email: Како корисници могу да те контактирају за правна питања или питања у вези подршке.
-        site_contact_username: Како корисници могу да те контактирају на Мастодону.
-        site_extended_description: Било која додатна информација која може бити корисне посетиоцима и твојим корисницима. Може се структурирати помоћу Markdown синтаксе.
-        site_short_description: Кратак опис помоћу кога се на јединствен начин идентификује твој сервер. Ко га одржава, коме је намењен?
-        site_terms: Користи сопствену политику приватности или остави празно да би се користила подразумевана. Може се структурирати помоћу Markdown синтаксе.
-        site_title: Начин на моји може да се позове на твој сервер осим назива његовог домена.
+        require_invite_text: Када регистрације захтевају ручно одобрење, поставите да одговор на „Зашто желите да се придружите?“ буде обавезан, а не опционалан
+        site_contact_email: Како корисници могу да контактирају са Вама за правна питања или питања у вези подршке.
+        site_contact_username: Како корисници могу да контактирају са Вама на Мастодону.
+        site_extended_description: Било какве додатне информације које могу бити корисне посетиоцима и Вашим корисницима. Могу се структурирати помоћу Markdown синтаксе.
+        site_short_description: Кратак опис помоћу кога се на јединствен начин идентификује Ваш сервер. Ко га одржава, коме је намењен?
+        site_terms: Користите сопствену политику приватности или оставите празно да би се користила подразумевана. Може се структурирати помоћу Markdown синтаксе.
+        site_title: Начин на који људи могу да реферишу на Ваш сервер осим назива његовог домена.
+        status_page_url: URL странице где људи могу да виде статус сервера док је сервер оборен
         theme: Тема коју виде посетиоци који нису пријављени и нови корисници.
-        thumbnail: Слика која се приближно 2:1 приказује поред информација о твом серверу.
+        thumbnail: Слика у размери од приближно 2:1 која се приказује поред информација о Вашем серверу.
         timeline_preview: Посетиоци који нису пријављени ће моћи да прегледају најновије јавне објаве доступне на серверу.
         trendable_by_default: Прескочи ручни преглед садржаја који је у тренду. Појединачне ставке се након тога и даље могу уклонити из трендова.
-        trends: Трендови показују које објаве, хеш ознаке и вести постају све популарнији на твом серверу.
+        trends: Трендови показују које објаве, хеш ознаке и вести постају све популарније на Вашем серверу.
+        trends_as_landing_page: Прикажи садржај у тренду одјављеним корисницима и посетиоцима уместо описа овог сервера. Захтева да трендови буду омогућени.
       form_challenge:
-        current_password: Улазиш у безбедно подручје
+        current_password: Улазите у безбедно подручје
       imports:
         data: CSV фајл извезен са друге Мастодонт инстанце
       invite_request:
-        text: Ово ће нам помоћи да прегледамо твоју пријаву
+        text: Ово ће нам помоћи да прегледамо Вашу пријаву
       ip_block:
-        comment: Опционо. Запамти зашто си додао ово правило.
+        comment: Опционо. Запамтите зашто сте додали ово правило.
         expires_in: IP адресе су ограничени ресурс, понекад се деле и често мењају корисника. Због тога се IP блокови на неограничено време не препоручују.
-        ip: Унеси IPv4 или IPv6 адресу. Можеш блокирати читаве опсеге користећи CIDR синтаксу. Води рачуна да себе не закључаш!
+        ip: Унесите IPv4 или IPv6 адресу. Можете блокирати читаве опсеге користећи CIDR синтаксу. Водите рачуна да се не закључате!
         severities:
           no_access: Блокирај приступ свим ресурсима
-          sign_up_block: Нове пријаве неће бити могуће
-          sign_up_requires_approval: Нове пријаве ће захтевати твоје одобрење
-        severity: Изабери шта ће се десити са захтевима са ове IP адресе
+          sign_up_block: Нове регистрације неће бити могуће
+          sign_up_requires_approval: Нове регистрације ће захтевати Ваше одобрење
+        severity: Изаберите шта ће се десити са захтевима са ове IP адресе
       rule:
-        text: Опиши правило или захтев за кориснике на овом серверу. Потруди се да опис буде кратак и једноставан
+        text: Опишите правило или услов за кориснике на овом серверу. Потрудите се да опис буде кратак и једноставан
       sessions:
         otp: 'Унесите двофакторски код са Вашег телефона или користите један од кодова за опоравак:'
-        webauthn: Ако је то USB кључ, обавезно га убаци и, ако је потребно, притисни га.
+        webauthn: Ако је у питању USB кључ, обавезно га убаците и, ако је потребно, притисните га.
       tag:
-        name: Могу се само променити мала слова у велика, на пример, да би било читљивије
+        name: Могу се само променити мала слова у велика или обрнуто, на пример, да би било читљивије
       user:
-        chosen_languages: Када означите, трубе у изабраним језицима ће се приказати на јавној временској линији
+        chosen_languages: Када је означено, објаве у изабраним језицима ће бити приказане на јавној временској линији
         role: Улога контролише које дозволе корисник има
       user_role:
-        color: Боја која ће се користити за улогу у целом корисничком интерфејсу, као RGB, у хексадецималном формату
+        color: Боја која ће се користити за улогу у целом корисничком окружењу, као RGB у хексадецималном формату
         highlighted: Ово чини улогу јавно видљивом
         name: Јавни назив улоге, ако је улога подешена да се приказује као значка
         permissions_as_keys: Корисници са овом улогом ће имати приступ...
@@ -187,24 +191,24 @@ sr:
         otp_attempt: Двофакторски код
         password: Лозинка
         phrase: Кључна реч или фраза
-        setting_advanced_layout: Омогући напредни веб интерфејс
+        setting_advanced_layout: Омогући напредно веб окружење
         setting_aggregate_reblogs: Групиши дељења у временским линијама
         setting_always_send_emails: Увек шаљи обавештења е-поштом
-        setting_auto_play_gif: Аутоматски пуштај анимиране GIF-ове
+        setting_auto_play_gif: Аутоматски репродукуј анимиране GIF-ове
         setting_boost_modal: Прикажи дијалог за потврду пре давања подршке
         setting_crop_images: Изрежи слике у непроширеним објавама на 16x9
         setting_default_language: Језик објављивања
         setting_default_privacy: Приватност објава
         setting_default_sensitive: Увек означи мултимедију као осетљиву
-        setting_delete_modal: Прикажи дијалог за потврду пре брисања тута
+        setting_delete_modal: Прикажи дијалог за потврду пре брисања објаве
         setting_disable_swiping: Онемогући покрете превлачења
         setting_display_media: Приказ медија
         setting_display_media_default: Подразумевано
         setting_display_media_hide_all: Сакриј све
         setting_display_media_show_all: Прикажи све
-        setting_expand_spoilers: Увек прошити трубе које су означене упозорењем садржаја
+        setting_expand_spoilers: Увек прошири објаве које су означене упозорењем садржаја
         setting_hide_network: Сакриј своју мрежу
-        setting_noindex: Одјави се од индексирања search engine-а
+        setting_noindex: Онемогући индексирање претраживача
         setting_reduce_motion: Смањи покрете у анимацијама
         setting_show_application: Откриј апликацију која се користи за слање постова
         setting_system_font_ui: Користи системски фонт
@@ -229,6 +233,7 @@ sr:
           hide: Сакриј у потпуности
           warn: Сакриј уз упозорење
       form_admin_settings:
+        activity_api_enabled: Објави прикупљену статистику о корисничкој активности у API
         backups_retention_period: Период чувања корисничке архиве
         bootstrap_timeline_accounts: Увек препоручи ове налоге новим корисницима
         closed_registrations_message: Прилагођена порука када пријаве нису могуће
@@ -236,6 +241,7 @@ sr:
         custom_css: Прилагођени CSS
         mascot: Прилагођена маскота (наслеђе)
         media_cache_retention_period: Период чувања кеша медија
+        peers_api_enabled: Објавите листу откривених сервера у API
         profile_directory: Омогући директоријум профила
         registrations_mode: Ко може да се пријави
         require_invite_text: Затражи разлог за приступање
@@ -247,11 +253,13 @@ sr:
         site_short_description: Опис сервера
         site_terms: Политика приватности
         site_title: Име сервера
+        status_page_url: URL статусне странице
         theme: Подразумевана тема
         thumbnail: Сличица сервера
         timeline_preview: Дозволи неауторизован приступ јавним временским осама
         trendable_by_default: Дозволи трендове без претходног прегледа
         trends: Омогући трендове
+        trends_as_landing_page: Користите трендове као страницу дочека
       interactions:
         must_be_follower: Блокирај обавештења од корисника који ме не прате
         must_be_following: Блокирај обавештења од људи које не пратим
diff --git a/config/locales/simple_form.sv.yml b/config/locales/simple_form.sv.yml
index 8dd5245f3..0a57fa4e3 100644
--- a/config/locales/simple_form.sv.yml
+++ b/config/locales/simple_form.sv.yml
@@ -18,8 +18,8 @@ sv:
           disable: Förhindra användaren från att använda sitt konto, men radera eller dölj inte innehållet.
           none: Använd det här för att skicka en varning till användaren, utan att vidta någon ytterligare åtgärd.
           sensitive: Tvinga alla denna användares mediebilagor till att flaggas som känsliga.
-          silence: Hindra användaren från att kunna göra offentliga inlägg, göm deras inlägg och notiser från folk som inte följer dem.
-          suspend: Hindra all interaktion från eller till detta konto och radera allt dess innehåll. Går att ångra inom 30 dagar.
+          silence: Hindra användaren från att kunna göra offentliga inlägg, göm deras inlägg och notiser från folk som inte följer dem. Stänger alla anmälningar mot detta kontot.
+          suspend: Förhindrar all interaktion från eller till detta konto och ta bort dess innehåll. Går att ångra inom 30 dagar. Stänger alla anmälningar mot detta konto.
         warning_preset_id: Valfri. Du kan lägga till anpassad text i slutet av förinställningen
       announcement:
         all_day: När det är markerat visas endast datum för tidsintervallet
@@ -74,6 +74,7 @@ sv:
           hide: Dölj det filtrerade innehållet helt (beter sig som om det inte fanns)
           warn: Dölj det filtrerade innehållet bakom en varning som visar filtrets rubrik
       form_admin_settings:
+        activity_api_enabled: Antalet lokalt publicerade inlägg, aktiva användare och nya registrerade konton per vecka
         backups_retention_period: Behåll genererade användararkiv i det angivna antalet dagar.
         bootstrap_timeline_accounts: Dessa konton kommer fästas högst upp i nya användares följrekommendationer.
         closed_registrations_message: Visas när nyregistreringar är avstängda
@@ -81,6 +82,7 @@ sv:
         custom_css: Du kan använda anpassade stilar på webbversionen av Mastodon.
         mascot: Åsidosätter illustrationen i det avancerade webbgränssnittet.
         media_cache_retention_period: Nedladdade mediefiler kommer raderas efter det angivna antalet dagar, om inställt till ett positivt värde, och laddas ned på nytt vid behov.
+        peers_api_enabled: En lista över domänen den här servern har stött på i fediversum. Ingen data inkluderas om du har federerat med servern, bara att din server känner till den. Detta används av tjänster som samlar statistik om federering i allmänhet.
         profile_directory: Profilkatalogen visar alla användare som har samtyckt till att bli upptäckbara.
         require_invite_text: Gör fältet "Varför vill du gå med?" obligatoriskt när nyregistreringar kräver manuellt godkännande
         site_contact_email: Hur människor kan nå dig för juridiska spörsmål eller supportfrågor.
@@ -89,11 +91,13 @@ sv:
         site_short_description: En kort beskrivning för att unikt identifiera din server. Vem är det som driver den, vilka är den till för?
         site_terms: Använd din egen sekretesspolicy eller lämna tomt för att använda standardinställningen. Kan struktureras med Markdown-syntax.
         site_title: Hur folk kan hänvisa till din server förutom med dess domännamn.
+        status_page_url: URL till en sida där personer kan se serverns status under ett driftavbrott
         theme: Tema som utloggade besökare och nya användare ser.
         thumbnail: En bild i cirka 2:1-proportioner som visas tillsammans med din serverinformation.
         timeline_preview: Utloggade besökare kommer kunna bläddra bland de senaste offentliga inläggen som finns på servern.
         trendable_by_default: Hoppa över manuell granskning av trendande innehåll. Enskilda objekt kan ändå raderas från trender retroaktivt.
         trends: Trender visar vilka inlägg, hashtaggar och nyheter det pratas om på din server.
+        trends_as_landing_page: Visa trendande innehåll för utloggade användare och besökare istället för en beskrivning om servern. Kräver att trender är aktiverat.
       form_challenge:
         current_password: Du går in i ett säkert område
       imports:
@@ -229,6 +233,7 @@ sv:
           hide: Dölj helt
           warn: Dölj med en varning
       form_admin_settings:
+        activity_api_enabled: Publicera aggregerad statistik om användaraktivitet i API:et
         backups_retention_period: Lagringsperiod för användararkivet
         bootstrap_timeline_accounts: Rekommendera alltid dessa konton till nya användare
         closed_registrations_message: Anpassat meddelande när nyregistreringar inte är tillgängliga
@@ -236,6 +241,7 @@ sv:
         custom_css: Anpassad CSS
         mascot: Anpassad maskot (tekniskt arv)
         media_cache_retention_period: Tid för bibehållande av mediecache
+        peers_api_enabled: Publicera lista över upptäckta servrar i API:et
         profile_directory: Aktivera profilkatalog
         registrations_mode: Vem kan registrera sig
         require_invite_text: Kräv anledning för att gå med
@@ -247,11 +253,13 @@ sv:
         site_short_description: Serverbeskrivning
         site_terms: Integritetspolicy
         site_title: Servernamn
+        status_page_url: URL för statussida
         theme: Standardtema
         thumbnail: Serverns tumnagelbild
         timeline_preview: Tillåt oautentiserad åtkomst till offentliga tidslinjer
         trendable_by_default: Tillåt trender utan föregående granskning
         trends: Aktivera trender
+        trends_as_landing_page: Använd trender som landningssida
       interactions:
         must_be_follower: Blockera notiser från icke-följare
         must_be_following: Blockera notiser från personer du inte följer
diff --git a/config/locales/simple_form.th.yml b/config/locales/simple_form.th.yml
index 3d61f78b8..052f10836 100644
--- a/config/locales/simple_form.th.yml
+++ b/config/locales/simple_form.th.yml
@@ -18,8 +18,8 @@ th:
           disable: ป้องกันไม่ให้ผู้ใช้ใช้บัญชีของเขา แต่ไม่ลบหรือซ่อนเนื้อหาของเขา
           none: ใช้สิ่งนี้เพื่อส่งคำเตือนไปยังผู้ใช้ โดยไม่ทริกเกอร์การกระทำอื่นใด
           sensitive: บังคับให้ทำเครื่องหมายไฟล์แนบสื่อของผู้ใช้นี้ทั้งหมดว่าละเอียดอ่อน
-          silence: ป้องกันไม่ให้ผู้ใช้สามารถโพสต์โดยมีการมองเห็นเป็นสาธารณะ ซ่อนโพสต์และการแจ้งเตือนของเขาจากผู้คนที่ไม่ได้กำลังติดตามผู้ใช้
-          suspend: ป้องกันไม่ให้มีการโต้ตอบใด ๆ จากหรือไปยังบัญชีนี้และลบเนื้อหาของบัญชี แปลงกลับได้ภายใน 30 วัน
+          silence: ป้องกันไม่ให้ผู้ใช้สามารถโพสต์โดยมีการมองเห็นเป็นสาธารณะ ซ่อนโพสต์และการแจ้งเตือนของเขาจากผู้คนที่ไม่ได้กำลังติดตามผู้ใช้ ปิดรายงานต่อบัญชีนี้ทั้งหมด
+          suspend: ป้องกันไม่ให้มีการโต้ตอบใด ๆ จากหรือไปยังบัญชีนี้และลบเนื้อหาของบัญชี แปลงกลับได้ภายใน 30 วัน ปิดรายงานต่อบัญชีนี้ทั้งหมด
         warning_preset_id: ไม่จำเป็น คุณยังคงสามารถเพิ่มข้อความที่กำหนดเองที่จุดสิ้นสุดของค่าที่ตั้งไว้ล่วงหน้า
       announcement:
         all_day: เมื่อกาเครื่องหมาย จะแสดงเฉพาะวันที่ของช่วงเวลาเท่านั้น
@@ -74,6 +74,7 @@ th:
           hide: ซ่อนเนื้อหาที่กรองอยู่อย่างสมบูรณ์ ทำเสมือนว่าไม่มีเนื้อหาอยู่
           warn: ซ่อนเนื้อหาที่กรองอยู่หลังคำเตือนที่กล่าวถึงชื่อเรื่องของตัวกรอง
       form_admin_settings:
+        activity_api_enabled: จำนวนโพสต์ที่เผยแพร่ในเซิร์ฟเวอร์, ผู้ใช้ที่ใช้งานอยู่ และการลงทะเบียนใหม่ในบักเก็ตรายสัปดาห์
         backups_retention_period: เก็บการเก็บถาวรผู้ใช้ที่สร้างขึ้นตามจำนวนวันที่ระบุ
         bootstrap_timeline_accounts: จะปักหมุดบัญชีเหล่านี้ไว้ด้านบนสุดของคำแนะนำการติดตามของผู้ใช้ใหม่
         closed_registrations_message: แสดงเมื่อมีการปิดการลงทะเบียน
@@ -81,6 +82,7 @@ th:
         custom_css: คุณสามารถนำไปใช้ลักษณะที่กำหนดเองใน Mastodon รุ่นเว็บ
         mascot: เขียนทับภาพประกอบในส่วนติดต่อเว็บขั้นสูง
         media_cache_retention_period: จะลบไฟล์สื่อที่ดาวน์โหลดหลังจากจำนวนวันที่ระบุเมื่อตั้งเป็นค่าบวก และดาวน์โหลดใหม่ตามความต้องการ
+        peers_api_enabled: รายการชื่อโดเมนที่เซิร์ฟเวอร์นี้พบในจักรวาลสหพันธ์ ไม่มีข้อมูลรวมอยู่ที่นี่เกี่ยวกับว่าคุณติดต่อกับเซิร์ฟเวอร์ที่กำหนดหรือไม่ เพียงแค่ว่าเซิร์ฟเวอร์ของคุณทราบเกี่ยวกับเซิร์ฟเวอร์ที่กำหนด มีการใช้สิ่งนี้โดยบริการที่เก็บรวบรวมสถิติในการติดต่อกับภายนอกในความหมายทั่วไป
         profile_directory: ไดเรกทอรีโปรไฟล์แสดงรายการผู้ใช้ทั้งหมดที่ได้เลือกรับให้สามารถค้นพบได้
         require_invite_text: เมื่อการลงทะเบียนต้องมีการอนุมัติด้วยตนเอง ทำให้การป้อนข้อความ “ทำไมคุณจึงต้องการเข้าร่วม?” บังคับแทนที่จะไม่จำเป็น
         site_contact_email: วิธีที่ผู้คนสามารถเข้าถึงคุณสำหรับการสอบถามด้านกฎหมายหรือการสนับสนุน
@@ -89,11 +91,13 @@ th:
         site_short_description: คำอธิบายแบบสั้นเพื่อช่วยระบุเซิร์ฟเวอร์ของคุณอย่างไม่ซ้ำกัน ผู้ดำเนินการเซิร์ฟเวอร์ เซิร์ฟเวอร์สำหรับใคร?
         site_terms: ใช้นโยบายความเป็นส่วนตัวของคุณเองหรือเว้นว่างไว้เพื่อใช้ค่าเริ่มต้น สามารถจัดโครงสร้างด้วยไวยากรณ์ Markdown
         site_title: วิธีที่ผู้คนอาจอ้างอิงถึงเซิร์ฟเวอร์ของคุณนอกเหนือจากชื่อโดเมนของเซิร์ฟเวอร์
+        status_page_url: URL ของหน้าที่ผู้คนสามารถเห็นสถานะของเซิร์ฟเวอร์นี้ในระหว่างการหยุดทำงาน
         theme: ชุดรูปแบบที่ผู้เยี่ยมชมที่ออกจากระบบและผู้ใช้ใหม่เห็น
         thumbnail: แสดงภาพ 2:1 โดยประมาณควบคู่ไปกับข้อมูลเซิร์ฟเวอร์ของคุณ
         timeline_preview: ผู้เยี่ยมชมที่ออกจากระบบจะสามารถเรียกดูโพสต์สาธารณะล่าสุดที่มีในเซิร์ฟเวอร์
-        trendable_by_default: ข้ามการตรวจทานเนื้อหาที่กำลังนิยมด้วยตนเอง ยังคงสามารถเอารายการแต่ละรายการออกจากแนวโน้มได้หลังเกิดเหตุ
+        trendable_by_default: ข้ามการตรวจทานเนื้อหาที่กำลังนิยมด้วยตนเอง ยังคงสามารถเอารายการแต่ละรายการออกจากแนวโน้มได้หลังจากเกิดเหตุ
         trends: แนวโน้มแสดงว่าโพสต์, แฮชแท็ก และเรื่องข่าวใดกำลังได้รับความสนใจในเซิร์ฟเวอร์ของคุณ
+        trends_as_landing_page: แสดงเนื้อหาที่กำลังนิยมแก่ผู้ใช้และผู้เยี่ยมชมที่ออกจากระบบแทนที่จะเป็นคำอธิบายของเซิร์ฟเวอร์นี้ ต้องมีการเปิดใช้งานแนวโน้ม
       form_challenge:
         current_password: คุณกำลังเข้าสู่พื้นที่ปลอดภัย
       imports:
@@ -134,9 +138,9 @@ th:
           name: ป้ายชื่อ
           value: เนื้อหา
       account_alias:
-        acct: การจัดการบัญชีเก่า
+        acct: นามของบัญชีเก่า
       account_migration:
-        acct: การจัดการบัญชีใหม่
+        acct: นามของบัญชีใหม่
       account_warning_preset:
         text: ข้อความที่ตั้งไว้ล่วงหน้า
         title: ชื่อเรื่อง
@@ -170,14 +174,14 @@ th:
         context: บริบทตัวกรอง
         current_password: รหัสผ่านปัจจุบัน
         data: ข้อมูล
-        discoverable: แนะนำบัญชีให้ผู้อื่น
+        discoverable: เสนอแนะบัญชีให้ผู้อื่น
         display_name: ชื่อที่แสดง
         email: ที่อยู่อีเมล
         expires_in: หมดอายุหลังจาก
         fields: ข้อมูลอภิพันธุ์โปรไฟล์
         header: ส่วนหัว
         honeypot: "%{label} (ไม่ต้องกรอก)"
-        inbox_url: URL กล่องขาเข้าแบบรีเลย์
+        inbox_url: URL ของกล่องขาเข้าของรีเลย์
         irreversible: ลบแทนที่จะซ่อน
         locale: ภาษาส่วนติดต่อ
         locked: ต้องมีคำขอติดตาม
@@ -229,6 +233,7 @@ th:
           hide: ซ่อนอย่างสมบูรณ์
           warn: ซ่อนด้วยคำเตือน
       form_admin_settings:
+        activity_api_enabled: เผยแพร่สถิติรวมเกี่ยวกับกิจกรรมผู้ใช้ใน API
         backups_retention_period: ระยะเวลาการเก็บรักษาการเก็บถาวรผู้ใช้
         bootstrap_timeline_accounts: แนะนำบัญชีเหล่านี้ให้กับผู้ใช้ใหม่เสมอ
         closed_registrations_message: ข้อความที่กำหนดเองเมื่อการลงทะเบียนไม่พร้อมใช้งาน
@@ -236,6 +241,7 @@ th:
         custom_css: CSS ที่กำหนดเอง
         mascot: มาสคอตที่กำหนดเอง (ดั้งเดิม)
         media_cache_retention_period: ระยะเวลาการเก็บรักษาแคชสื่อ
+        peers_api_enabled: เผยแพร่รายการเซิร์ฟเวอร์ที่ค้นพบใน API
         profile_directory: เปิดใช้งานไดเรกทอรีโปรไฟล์
         registrations_mode: ผู้ที่สามารถลงทะเบียน
         require_invite_text: ต้องมีเหตุผลที่จะเข้าร่วม
@@ -247,15 +253,17 @@ th:
         site_short_description: คำอธิบายเซิร์ฟเวอร์
         site_terms: นโยบายความเป็นส่วนตัว
         site_title: ชื่อเซิร์ฟเวอร์
+        status_page_url: URL ของหน้าสถานะ
         theme: ชุดรูปแบบเริ่มต้น
         thumbnail: ภาพขนาดย่อเซิร์ฟเวอร์
         timeline_preview: อนุญาตการเข้าถึงเส้นเวลาสาธารณะที่ไม่ได้รับรองความถูกต้อง
         trendable_by_default: อนุญาตแนวโน้มโดยไม่มีการตรวจทานล่วงหน้า
         trends: เปิดใช้งานแนวโน้ม
+        trends_as_landing_page: ใช้แนวโน้มเป็นหน้าเริ่มต้น
       interactions:
         must_be_follower: ปิดกั้นการแจ้งเตือนจากผู้ที่ไม่ใช่ผู้ติดตาม
         must_be_following: ปิดกั้นการแจ้งเตือนจากผู้คนที่คุณไม่ได้ติดตาม
-        must_be_following_dm: ปิดกั้นข้อความโดยตรงจากผู้คนที่คุณไม่ได้ติดตาม
+        must_be_following_dm: ปิดกั้นข้อความโดยตรงจากผู้คนที่คุณไม่ติดตาม
       invite:
         comment: ความคิดเห็น
       invite_request:
diff --git a/config/locales/simple_form.tr.yml b/config/locales/simple_form.tr.yml
index ede97ad3e..7fffb81da 100644
--- a/config/locales/simple_form.tr.yml
+++ b/config/locales/simple_form.tr.yml
@@ -5,9 +5,9 @@ tr:
       account_alias:
         acct: Taşımak istediğiniz hesabı kullanıcıadı@alanadı şeklinde belirtin
       account_migration:
-        acct: Yeni hesabınızı kullanıcıadı@alanadını şeklinde belirtin
+        acct: Taşımak istediğiniz hesabın kullanıcıadı@alanadını belirtin
       account_warning_preset:
-        text: URL'ler, etiketler ve bahsedenler gibi gönderi sözdizimini kullanabilirsiniz
+        text: Bğlantılar, etiketler ve bahsedenler gibi gönderi sözdizimini kullanabilirsiniz
         title: İsteğe bağlı. Alıcıya görünmez
       admin_account_action:
         include_statuses: Kullanıcı hangi gönderilerin denetleme eylemi veya uyarısına neden olduğunu görecektir
@@ -18,8 +18,8 @@ tr:
           disable: Kullanıcının hesabını kullanmasını engelle ama içeriklerini silme veya gizleme.
           none: Bunu, başka bir eylem tetiklemeden kullanıcıya bir uyarı göndermek için kullan.
           sensitive: Bu kullanıcının tüm medya eklerini hassas olarak işaretlemeye zorla.
-          silence: Kullanıcının herkese açık şekilde gönderimde bulunmasını engelle, gönderilerini ve bildirimlerini onları takip etmeyen kişilerden gizle.
-          suspend: Bu hesaptan herhangi bir etkileşimi engelle ve içeriğini sil. 30 gün içerisinde geri alınabilir.
+          silence: Kullanıcının herkese açık şekilde gönderimde bulunmasını engelle, gönderilerini ve bildirimlerini onları takip etmeyen kişilerden gizle. Bu hesaba yönelik tüm bildirimleri kapatır.
+          suspend: Bu hesaptan herhangi bir etkileşimi engelle ve içeriğini sil. 30 gün içerisinde geri alınabilir. Bu hesaba yönelik tüm bildirimleri kapatır.
         warning_preset_id: İsteğe bağlı. Hazır ayarın sonuna hala özel metin ekleyebilirsiniz
       announcement:
         all_day: İşaretlendiğinde, yalnızca zaman aralığındaki tarihler görüntülenir
@@ -74,6 +74,7 @@ tr:
           hide: Filtrelenmiş içeriği tamamen gizle, sanki varolmamış gibi
           warn: Filtrelenmiş içeriği, filtrenin başlığından söz eden bir uyarının arkasında gizle
       form_admin_settings:
+        activity_api_enabled: Yerel olarak yayınlanan gönderi, etkin kullanıcı ve yeni kayıtların haftalık sayıları
         backups_retention_period: Üretilen kullanıcı arşivlerini belirli gün sayısı kadar sakla.
         bootstrap_timeline_accounts: Bu hesaplar, yeni kullanıcıların takip önerilerinin tepesinde sabitlenecektir.
         closed_registrations_message: Kayıt olma kapalıyken görüntülenir
@@ -81,6 +82,7 @@ tr:
         custom_css: Mastodon'un web sürümüne özel biçimler uygulayabilirsiniz.
         mascot: Gelişmiş web arayüzündeki illüstrasyonu geçersiz kılar.
         media_cache_retention_period: Pozitif bir sayı girildiğinde, diğer sunuculardan indirilen medya dosyaları belirli bir gün sonra silinecektir, isteğe bağlı olarak tekrar indirilebilir.
+        peers_api_enabled: Bu sunucunun fediverse'te karşılaştığı alan adlarının bir listesi. İlgili sunucuyla birleştirme mi yapıyorsunuz yoksa sunucunuz sadece onu biliyor mu hakkında bir bilgi burada yok. Bu blgi genel olarak federasyın hakkında istatistik toplamak isteyen hizmetler tarafından kullanılıyor.
         profile_directory: Profil dizini keşfedilebilir olmayı kabul eden tüm kullanıcıları listeler.
         require_invite_text: Kayıt olmak elle doğrulama gerektiriyorsa, "Neden katılmak istiyorsunuz?" metin girdisini isteğe bağlı yerine zorunlu yapın
         site_contact_email: İnsanlar yasal konular veya destek hakkında bilgi edinmek için size nasıl ulaşabilir.
@@ -89,11 +91,13 @@ tr:
         site_short_description: Sunucunuzu tekil olarak tanımlamaya yardımcı olacak kısa tanım. Kim işletiyor, kimin için?
         site_terms: Kendi gizlilik politikanızı kullanın veya varsayılanı kullanmak için boş bırakın. Markdown sözdizimiyle biçimlendirilebilir.
         site_title: İnsanlar sunucunuzu alan adı dışında nasıl isimlendirmeli.
+        status_page_url: İnsanların bir kesinti halinde sunucunun durumunu görebilecekleri bir sayfanın URL'si
         theme: Giriş yapmamış ziyaretçilerin ve yeni kullanıcıların gördüğü tema.
         thumbnail: Sunucu bilginizin yanında gösterilen yaklaşık 2:1'lik görüntü.
         timeline_preview: Giriş yapmamış ziyaretçiler, sunucuda mevcut olan en son genel gönderileri tarayabilecekler.
         trendable_by_default: Öne çıkan içeriğin elle incelenmesini atla. Tekil öğeler sonrada öne çıkanlardan kaldırılabilir.
         trends: Öne çıkanlar, sunucunuzda ilgi toplayan gönderileri, etiketleri ve haber yazılarını gösterir.
+        trends_as_landing_page: Giriş yapmış kullanıcılar ve ziyaretçilere sunucunun açıklması yerine öne çıkan içeriği göster. Öne çıkanların etkin olması gerekir.
       form_challenge:
         current_password: Güvenli bir bölgeye giriyorsunuz
       imports:
@@ -229,6 +233,7 @@ tr:
           hide: Tamamen gizle
           warn: Uyarıyla gizle
       form_admin_settings:
+        activity_api_enabled: API'deki kullanıcı etkinliği hakkında toplu istatistikler yayınlayın
         backups_retention_period: Kullanıcı arşivi saklama süresi
         bootstrap_timeline_accounts: Bu hesapları yeni kullanıcılara her zaman öner
         closed_registrations_message: Kayıt olma mevcut değilken gösterilen özel ileti
@@ -236,6 +241,7 @@ tr:
         custom_css: Özel CSS
         mascot: Özel maskot (eski)
         media_cache_retention_period: Medya önbelleği saklama süresi
+        peers_api_enabled: API'de keşfedilen sunucuların listesini yayınla
         profile_directory: Profil dizinini etkinleştir
         registrations_mode: Kim kaydolabilir
         require_invite_text: Katılmak için bir gerekçe iste
@@ -247,11 +253,13 @@ tr:
         site_short_description: Sunucu açıklaması
         site_terms: Gizlilik Politikası
         site_title: Sunucu adı
+        status_page_url: Durum sayfası URL'si
         theme: Öntanımlı tema
         thumbnail: Sunucu küçük resmi
         timeline_preview: Genel zaman çizelgelerine yetkisiz erişime izin ver
         trendable_by_default: Ön incelemesiz öne çıkanlara izin ver
         trends: Öne çıkanları etkinleştir
+        trends_as_landing_page: Giriş sayfası olarak öne çıkanları kullan
       interactions:
         must_be_follower: Takipçim olmayan kişilerden gelen bildirimleri engelle
         must_be_following: Takip etmediğim kişilerden gelen bildirimleri engelle
diff --git a/config/locales/simple_form.tt.yml b/config/locales/simple_form.tt.yml
index d4e44c0db..db2bd025c 100644
--- a/config/locales/simple_form.tt.yml
+++ b/config/locales/simple_form.tt.yml
@@ -5,7 +5,7 @@ tt:
       account_warning_preset:
         title: Исем
       admin_account_action:
-        type: Ğämäl
+        type: Гамәл
         types:
           sensitive: Sizmäle
           suspend: Искә алмау
@@ -14,7 +14,7 @@ tt:
         data: Мәгълүмат
         email: Почта адресы
         header: Башлам
-        password: Парол
+        password: Серсүз
         setting_display_media_default: Töpcay
         username: Кулланучы исеме
       featured_tag:
diff --git a/config/locales/simple_form.uk.yml b/config/locales/simple_form.uk.yml
index 6f46dadda..677c04aa7 100644
--- a/config/locales/simple_form.uk.yml
+++ b/config/locales/simple_form.uk.yml
@@ -18,8 +18,8 @@ uk:
           disable: Не давати користувачеві можливість використовувати свій обліковий запис, але не видаляти і не приховувати його вміст.
           none: Використовуйте це, щоб надіслати попередження користувачеві без подальших дій.
           sensitive: Примусово позначати всі медіа файли цього користувача делікатними.
-          silence: Заборонити користувачеві розміщувати загальнодоступні повідомлення, приховувати їхні повідомлення та повідомлення від людей, які не слідкують за ними.
-          suspend: Заборонити взаємодію з цим обліковим записом та видалити його вміст. Можна скасувати впродовж 30 днів.
+          silence: Запобігає розміщенню загальнодоступних дописів користувачем, приховувати їх дописи та сповіщення від людей, які не слідкують за ними. Закриває всі скарги на цей обліковий запис.
+          suspend: Запобігає будь-якій взаємодії з цим і з цього облікового запису та видаляє його матеріали. Це можна скасувати протягом 30 днів. Закриває всі скарги на цей обліковий запис.
         warning_preset_id: Необов'язково. Ви можете ще додати будь-який текст до кінця шаблону
       announcement:
         all_day: Якщо вибрано, відображаються лише дати діапазону часу
@@ -74,6 +74,7 @@ uk:
           hide: Повністю сховати фільтрований вміст, ніби його не існує
           warn: Сховати відфільтрований вміст за попередженням, у якому вказано заголовок фільтра
       form_admin_settings:
+        activity_api_enabled: Кількість локальних опублікованих дописів, активних і нових користувачів у тижневих розрізах
         backups_retention_period: Зберігати створені архіви користувача вказану кількість днів.
         bootstrap_timeline_accounts: Ці облікові записи будуть закріплені в топі пропозицій для нових користувачів.
         closed_registrations_message: Показується, коли реєстрація закрита
@@ -81,6 +82,7 @@ uk:
         custom_css: Ви можете застосувати користувацькі стилі у вебверсії Mastodon.
         mascot: Змінює ілюстрацію в розширеному вебінтерфейсі.
         media_cache_retention_period: Завантажені медіафайли будуть видалені після вказаної кількості днів після встановлення додатного значення та повторного завантаження за запитом.
+        peers_api_enabled: Список доменів імен цього сервера з'явився у федівсесвіті. Сюди не входять дані чи ви пов'язані федерацією з цим сервером, а лише відомості, що вашому серверу відомо про нього. Його використовують служби, які збирають загальну статистику про федерації.
         profile_directory: У каталозі профілів перераховані всі користувачі, які погодились бути видимими.
         require_invite_text: Якщо реєстрація вимагає власноручного затвердження, зробіть текстове поле «Чому ви хочете приєднатися?» обов'язковим, а не додатковим
         site_contact_email: Як люди можуть зв'язатися з вами для отримання правової допомоги або підтримки.
@@ -89,11 +91,13 @@ uk:
         site_short_description: Короткий опис, щоб допомогти однозначно ідентифікувати ваш сервер. Хто ним керує, для кого він потрібен?
         site_terms: Використовуйте власну політику приватності або залиште поле порожнім, щоб використовувати усталене значення. Може бути структуровано за допомогою синтаксису Markdown.
         site_title: Як люди можуть посилатися на ваш сервер, окрім його доменного імені.
+        status_page_url: URL сторінки, на якій люди можуть бачити статус цього сервера під час його збою в роботі
         theme: Тема, яку бачать відвідувачі, що вийшли з системи, та нові користувачі.
         thumbnail: Зображення приблизно 2:1, що показується поряд з відомостями про ваш сервер.
         timeline_preview: Зареєстровані відвідувачі зможуть переглядати останні публічні дописи, доступні на сервері.
         trendable_by_default: Пропустити ручний огляд популярних матеріалів. Індивідуальні елементи все ще можна вилучити з популярних постфактум.
         trends: Популярні показують, які дописи, хештеґи та новини набувають популярності на вашому сервері.
+        trends_as_landing_page: Показувати популярні матеріали для зареєстрованих користувачів і відвідувачів замість опису цього сервера. Для активації потрібні тренди.
       form_challenge:
         current_password: Ви входите до безпечної зони
       imports:
@@ -229,6 +233,7 @@ uk:
           hide: Сховати повністю
           warn: Сховати за попередженням
       form_admin_settings:
+        activity_api_enabled: Публікація агрегованої статистики про активність користувачів
         backups_retention_period: Період утримання архіву користувача
         bootstrap_timeline_accounts: Завжди рекомендувати новим користувачам ці облікові записи
         closed_registrations_message: Показуване повідомлення, якщо реєстрація недоступна
@@ -236,6 +241,7 @@ uk:
         custom_css: Користувацький CSS
         mascot: Користувацький символ (застарілий)
         media_cache_retention_period: Період збереження кешу медіа
+        peers_api_enabled: Опублікувати список знайдених серверів у API
         profile_directory: Увімкнути каталог профілів
         registrations_mode: Хто може зареєструватися
         require_invite_text: Для того, щоб приєднатися потрібна причина
@@ -247,11 +253,13 @@ uk:
         site_short_description: Опис сервера
         site_terms: Політика приватності
         site_title: Назва сервера
+        status_page_url: URL сторінки статусу
         theme: Стандартна тема
         thumbnail: Мініатюра сервера
         timeline_preview: Дозволити неавтентифікований доступ до публічних стрічок
         trendable_by_default: Дозволити популярне без попереднього огляду
         trends: Увімкнути популярні
+        trends_as_landing_page: Використовуйте тенденції як цільову сторінку
       interactions:
         must_be_follower: Блокувати сповіщення від непідписаних людей
         must_be_following: Блокувати сповіщення від людей, на яких ви не підписані
diff --git a/config/locales/simple_form.uz.yml b/config/locales/simple_form.uz.yml
new file mode 100644
index 000000000..3ed042df3
--- /dev/null
+++ b/config/locales/simple_form.uz.yml
@@ -0,0 +1 @@
+uz:
diff --git a/config/locales/simple_form.vi.yml b/config/locales/simple_form.vi.yml
index d395112f8..39447f838 100644
--- a/config/locales/simple_form.vi.yml
+++ b/config/locales/simple_form.vi.yml
@@ -18,7 +18,7 @@ vi:
           disable: Tạm khóa đăng nhập tài khoản, nhưng không xóa hoặc ẩn tút.
           none: Cảnh cáo tài khoản này, không áp đặt trừng phạt.
           sensitive: Mọi tập tin của tài khoản này tải lên đều sẽ bị gắn nhãn nhạy cảm.
-          silence: Cấm tài khoản này đăng tút công khai, ẩn tút của họ hiện ra với những người chưa theo dõi họ.
+          silence: Cấm người này đăng tút công khai, ẩn tút của họ hiện ra với những người chưa theo dõi họ.
           suspend: Vô hiệu hóa và xóa sạch dữ liệu của tài khoản này. Có thể khôi phục trước 30 ngày.
         warning_preset_id: Tùy chọn. Bạn vẫn có thể thêm chú thích riêng
       announcement:
@@ -74,6 +74,7 @@ vi:
           hide: Ẩn hoàn toàn nội dung đã lọc, hoạt động như thể nó không tồn tại
           warn: Ẩn nội dung đã lọc đằng sau một cảnh báo đề cập đến tiêu đề của bộ lọc
       form_admin_settings:
+        activity_api_enabled: Số lượng tút được đăng trong máy chủ, người dùng đang hoạt động và đăng ký mới hàng tuần
         backups_retention_period: Lưu trữ dữ liệu người dùng đã tạo trong số ngày được chỉ định.
         bootstrap_timeline_accounts: Những người này sẽ được ghim vào đầu các gợi ý theo dõi của người mới.
         closed_registrations_message: Được hiển thị khi đóng đăng ký
@@ -81,6 +82,7 @@ vi:
         custom_css: Bạn có thể tùy chỉnh phong cách trên bản web của Mastodon.
         mascot: Ghi đè hình minh họa trong giao diện web nâng cao.
         media_cache_retention_period: Media đã tải xuống sẽ bị xóa sau số ngày được chỉ định và sẽ tải xuống lại theo yêu cầu.
+        peers_api_enabled: Danh sách các máy chủ khác mà máy chủ này đã liên hợp. Không có dữ liệu nào được đưa vào đây về việc bạn có liên kết với một máy chủ nhất định hay không, chỉ là máy chủ của bạn biết về nó. Điều này được sử dụng bởi các dịch vụ thu thập số liệu thống kê về liên kết theo nghĩa chung.
         profile_directory: Liệt kê tất cả người đã chọn tham gia để có thể khám phá.
         require_invite_text: Khi đăng ký yêu cầu phê duyệt thủ công, hãy đặt câu hỏi "Tại sao bạn muốn tham gia?" nhập văn bản bắt buộc thay vì tùy chọn
         site_contact_email: Cách mọi người có thể liên hệ với bạn khi có thắc mắc về pháp lý hoặc hỗ trợ.
@@ -89,11 +91,13 @@ vi:
         site_short_description: Mô tả ngắn gọn để giúp nhận định máy chủ của bạn. Ai đang điều hành nó, nó là cho ai?
         site_terms: Sử dụng chính sách bảo mật của riêng bạn hoặc để trống để sử dụng mặc định. Có thể soạn bằng cú pháp Markdown.
         site_title: Cách mọi người có thể tham chiếu đến máy chủ của bạn ngoài tên miền của nó.
+        status_page_url: URL của trang nơi mọi người có thể xem trạng thái của máy chủ này khi ngừng hoạt động
         theme: Chủ đề mà khách truy cập đăng xuất và người mới nhìn thấy.
         thumbnail: 'Một hình ảnh tỉ lệ 2: 1 được hiển thị cùng với thông tin máy chủ của bạn.'
         timeline_preview: Khách truy cập đã đăng xuất sẽ có thể xem các tút công khai gần đây nhất trên máy chủ.
         trendable_by_default: Bỏ qua việc duyệt thủ công nội dung thịnh hành. Các mục riêng lẻ vẫn có thể bị xóa khỏi xu hướng sau này.
         trends: Hiển thị những tút, hashtag và tin tức đang được thảo luận nhiều trên máy chủ của bạn.
+        trends_as_landing_page: Hiển thị nội dung thịnh hành cho người dùng đã đăng xuất và khách truy cập thay vì mô tả về máy chủ này. Yêu cầu xu hướng được kích hoạt.
       form_challenge:
         current_password: Biểu mẫu này an toàn
       imports:
@@ -229,6 +233,7 @@ vi:
           hide: Ẩn toàn bộ
           warn: Ẩn kèm theo cảnh báo
       form_admin_settings:
+        activity_api_enabled: Công khai số liệu thống kê tổng hợp về hoạt động của người dùng trong API
         backups_retention_period: Thời hạn lưu trữ nội dung người dùng sao lưu
         bootstrap_timeline_accounts: Luôn đề xuất những người này đến người mới
         closed_registrations_message: Thông báo tùy chỉnh khi tắt đăng ký
@@ -236,6 +241,7 @@ vi:
         custom_css: Tùy chỉnh CSS
         mascot: Tùy chỉnh linh vật (kế thừa)
         media_cache_retention_period: Thời hạn lưu trữ cache media
+        peers_api_enabled: Công khai danh sách các máy chủ được phát hiện trong API
         profile_directory: Cho phép hiện danh sách thành viên
         registrations_mode: Ai có thể đăng ký
         require_invite_text: Yêu cầu lí do đăng ký
@@ -247,11 +253,13 @@ vi:
         site_short_description: Mô tả máy chủ
         site_terms: Chính sách bảo mật
         site_title: Tên máy chủ
+        status_page_url: URL trang trạng thái
         theme: Chủ đề mặc định
         thumbnail: Hình thu nhỏ của máy chủ
         timeline_preview: Cho phép truy cập vào dòng thời gian công khai
         trendable_by_default: Cho phép thịnh hành mà không cần duyệt trước
         trends: Bật thịnh hành
+        trends_as_landing_page: Dùng trang thịnh hành làm trang chào mừng
       interactions:
         must_be_follower: Chặn thông báo từ những người không theo dõi bạn
         must_be_following: Chặn thông báo từ những người bạn không theo dõi
diff --git a/config/locales/simple_form.zh-CN.yml b/config/locales/simple_form.zh-CN.yml
index 9868d96f4..57b25af8b 100644
--- a/config/locales/simple_form.zh-CN.yml
+++ b/config/locales/simple_form.zh-CN.yml
@@ -18,8 +18,8 @@ zh-CN:
           disable: 禁止用户使用账户,但不会删除或隐藏账户内容。
           none: 用它来向用户发送警告,不会触发其他操作。
           sensitive: 强制将此用户的所有媒体文件标记为敏感内容。
-          silence: 阻止用户发送公开嘟文,除了关注者以外,其他人都无法看到他的嘟文和通知。
-          suspend: 阻止此账户的任何交互并删除其内容。30天内可以撤销操作。
+          silence: 阻止用户发送公开嘟文,除了关注者以外,其他人都无法看到他的嘟文和通知。关闭针对此账户的所有举报。
+          suspend: 阻止此账户的任何交互并删除其内容。30天内可以撤销操作。关闭针对此账户的所有举报。
         warning_preset_id: 可选。你可以在预置文本末尾添加自定义文本
       announcement:
         all_day: 如果选中,只有该时间段内的日期会显示。
@@ -33,7 +33,7 @@ zh-CN:
         autofollow: 通过邀请链接注册的用户将会自动关注你
         avatar: 文件大小限制 %{size},只支持 PNG、GIF 或 JPG 格式。图片分辨率将会压缩至 %{dimensions}px
         bot: 来自这个账户的绝大多数操作都是自动进行的,并且可能无人监控
-        context: 过滤器的应用环境
+        context: 过滤器的应用场景
         current_password: 为了安全起见,请输入当前账号的密码
         current_username: 请输入当前账号的用户名以确认
         digest: 仅在你长时间未登录,且收到了私信时发送
@@ -74,6 +74,7 @@ zh-CN:
           hide: 彻底屏蔽过滤内容,犹如它不曾存在过一般
           warn: 在警告中提及过滤器标题后,隐藏过滤内容
       form_admin_settings:
+        activity_api_enabled: 本地发布的帖子、 活跃用户和每周的注册数
         backups_retention_period: 将在指定天数内保留生成的用户存档。
         bootstrap_timeline_accounts: 这些账号将在新用户关注推荐中置顶。
         closed_registrations_message: 在关闭注册时显示
@@ -81,6 +82,7 @@ zh-CN:
         custom_css: 你可以为网页版 Mastodon 应用自定义样式。
         mascot: 覆盖高级网页界面中的绘图形象。
         media_cache_retention_period: 设为正数值时,来自其他服务器的媒体文件将在指定天数后被删除,并在需要时再次下载。
+        peers_api_enabled: 此服务器在联邦宇宙中遇到的域名列表。 这里不包含关于您是否与给定服务器联合的数据,只是您的服务器知道它。 这由收集一般意义上的联邦统计信息的服务使用。
         profile_directory: 个人资料目录会列出所有选择可被发现的用户。
         require_invite_text: 当注册需要手动批准时,将“你为什么想要加入?”设为必填项
         site_contact_email: 他人需要询恰法务或支持信息时的联络方式
@@ -89,11 +91,13 @@ zh-CN:
         site_short_description: 有助于区分你的服务器独特性的简短描述。谁在管理?供谁使用?
         site_terms: 使用你自己的隐私政策或留空以使用默认版。可以使用 Markdown 语法。
         site_title: 除了域名,人们还可以如何指代你的服务器。
+        status_page_url: 配置一个网址,当服务中断时,人们可以通过该网址查看服务器的状态。
         theme: 给未登录访客和新用户使用的主题。
         thumbnail: 与服务器信息一并展示的约 2:1 比例的图像。
         timeline_preview: 未登录访客将能够浏览服务器上最新的公共嘟文。
         trendable_by_default: 跳过对热门内容的手工审核。个别项目仍可在之后从趋势中删除。
         trends: 趋势中会显示正在你服务器上受到关注的嘟文、标签和新闻故事。
+        trends_as_landing_page: 向注销的用户和访问者显示趋势内容,而不是对该服务器的描述,需要启用趋势。
       form_challenge:
         current_password: 你正在进入安全区域
       imports:
@@ -167,7 +171,7 @@ zh-CN:
         chosen_languages: 语言过滤
         confirm_new_password: 确认新密码
         confirm_password: 确认密码
-        context: 过滤器环境
+        context: 过滤场景
         current_password: 当前密码
         data: 数据文件
         discoverable: 在本站用户目录中收录此账号
@@ -229,6 +233,7 @@ zh-CN:
           hide: 完全隐藏
           warn: 隐藏时显示警告信息
       form_admin_settings:
+        activity_api_enabled: 在 API 中发布有关用户活动的汇总统计数据
         backups_retention_period: 用户存档保留期
         bootstrap_timeline_accounts: 推荐新用户关注以下账号
         closed_registrations_message: 在关闭注册时显示的自定义消息
@@ -236,6 +241,7 @@ zh-CN:
         custom_css: 自定义 CSS
         mascot: 自定义吉祥物(旧)
         media_cache_retention_period: 媒体缓存保留期
+        peers_api_enabled: 在API中公开的已知实例的服务器的列表
         profile_directory: 启用用户目录
         registrations_mode: 谁可以注册
         require_invite_text: 注册前需要提供理由
@@ -247,11 +253,13 @@ zh-CN:
         site_short_description: 本站简介
         site_terms: 隐私政策
         site_title: 本站名称
+        status_page_url: 静态页面地址
         theme: 默认主题
         thumbnail: 本站缩略图
         timeline_preview: 时间轴预览
         trendable_by_default: 允许在未审核的情况下将话题置为热门
         trends: 启用趋势
+        trends_as_landing_page: 使用趋势作为登陆页面
       interactions:
         must_be_follower: 屏蔽来自未关注我的用户的通知
         must_be_following: 屏蔽来自我未关注的用户的通知
diff --git a/config/locales/simple_form.zh-HK.yml b/config/locales/simple_form.zh-HK.yml
index 0dee0783f..50bbd0e71 100644
--- a/config/locales/simple_form.zh-HK.yml
+++ b/config/locales/simple_form.zh-HK.yml
@@ -18,8 +18,8 @@ zh-HK:
           disable: 禁止該使用者使用他們的帳號,但是不刪除或隱藏他們的內容。
           none: 用這個來警告該使用者,而不進行其他操作。
           sensitive: 強制標記此用戶的所有媒體附件為敏感內容。
-          silence: 禁止該使用者發表公開嘟文,沒有跟隨他們的帳號不會看到來自該用戶的嘟文和通知。
-          suspend: 禁止該帳號的所有互動並刪除其內容。此操作在三十日內可以被復原。
+          silence: 禁止該使用者發佈公開帖文,沒有追蹤他們的人不會看到其帖文和通知。關閉所有對該帳號的檢舉報告。
+          suspend: 禁止與該帳號的所有互動,並移除其內容。可於 30 天內撤銷此動作。關閉所有對此帳號的檢舉報告。
         warning_preset_id: 選用。你仍可在預設訊息的結尾加入自訂文字
       announcement:
         all_day: 勾選後,只會顯示出時間範圍中的日期部分
@@ -74,6 +74,7 @@ zh-HK:
           hide: 完全隱藏被篩選的內容,猶如它不存在般。
           warn: 將已篩選的內容隱藏在篩選器標題的警告後面。
       form_admin_settings:
+        activity_api_enabled: 每週本站發佈的帖文、活躍使用者及新註冊的數量
         backups_retention_period: 繼續封存生成的使用者到指定的天數。
         bootstrap_timeline_accounts: 這些帳號會被置頂在新使用者的追蹤建議上。
         closed_registrations_message: 關閉註冊時顯示
@@ -81,6 +82,7 @@ zh-HK:
         custom_css: 你可以在 Mastodon 網頁版套用自訂樣式。
         mascot: 覆蓋進階網頁介面中的插圖。
         media_cache_retention_period: 當設定為正數時,已下載的媒體檔案將在指定天數後被刪除,並視乎需要重新下載。
+        peers_api_enabled: 本伺服器於聯邦宇宙相遇的網域名單。這裏不包括你與某伺服器有否聯網的數據,僅表示你的伺服器已知的網域。這是供收集一般跨站數據的服務使用。
         profile_directory: 個人檔案目錄羅列了所有選擇被發現的使用者。
         require_invite_text: 如果需要手動審核註冊,請將「為何你想加入?」文字欄設定為必填,而非選填。
         site_contact_email: 大家如何聯絡你有關法律或支援的查詢。
@@ -89,11 +91,13 @@ zh-HK:
         site_short_description: 用作辨別你的伺服器的一段簡短描述。誰營運它,它為誰服務的?
         site_terms: 使用你的私隱政策,或者留空使用預設的,可以用 Markdown 語法編寫。
         site_title: 除了域名以外,其他人會如何指稱你的伺服器。
+        status_page_url: 可在服務中斷期間,查看此伺服器狀態的網頁網址
         theme: 未登入訪客和新使用者看到的主題。
         thumbnail: 一幅約 2:1 的圖片顯示在你的伺服器資訊的旁邊。
         timeline_preview: 未登入的訪客能夠瀏覽伺服器上最新的公開帖文。
         trendable_by_default: 跳過對趨勢內容的手動審查,事後仍可從趨勢中刪除個別項目。
         trends: 趨勢顯示哪些帖文、標籤和新聞故事在你的伺服器上較有吸引力。
+        trends_as_landing_page: 向未登入的使用者及訪客展示趨勢內容,而非只有此伺服器的描述。需要啟用趨勢。
       form_challenge:
         current_password: 你正要進入安全區域
       imports:
@@ -229,6 +233,7 @@ zh-HK:
           hide: 完全隱藏
           warn: 警告並隱藏
       form_admin_settings:
+        activity_api_enabled: 在 API 中發佈使用者活動的匯總統計數據
         backups_retention_period: 封存使用者保留期
         bootstrap_timeline_accounts: 總是向新使用者推薦這些帳號
         closed_registrations_message: 無法註冊時的自訂訊息
@@ -236,6 +241,7 @@ zh-HK:
         custom_css: 自訂 CSS
         mascot: 自訂吉祥物 (舊版)
         media_cache_retention_period: 媒體快取保留期
+        peers_api_enabled: 在 API 中發佈已知的伺服器名單
         profile_directory: 啟用個人檔案目錄
         registrations_mode: 誰能註冊
         require_invite_text: 提供一個加入的理由
@@ -247,11 +253,13 @@ zh-HK:
         site_short_description: 伺服器描述
         site_terms: 私隱政策
         site_title: 伺服器名稱
+        status_page_url: 狀態頁網址
         theme: 預設主題
         thumbnail: 伺服器縮圖
         timeline_preview: 允許未經認證的人存取公共時間軸
         trendable_by_default: 允許未經審核的趨勢
         trends: 啟用趨勢
+        trends_as_landing_page: 使用趨勢作為登陸頁面
       interactions:
         must_be_follower: 隱藏你關注者以外的人的通知
         must_be_following: 隱藏你不關注的人的通知
diff --git a/config/locales/simple_form.zh-TW.yml b/config/locales/simple_form.zh-TW.yml
index 37844c7af..fbb4d94a7 100644
--- a/config/locales/simple_form.zh-TW.yml
+++ b/config/locales/simple_form.zh-TW.yml
@@ -18,8 +18,8 @@ zh-TW:
           disable: 禁止該使用者使用他們的帳號,但是不刪除或隱藏他們的內容。
           none: 使用這個寄送警告給該使用者,而不進行其他動作。
           sensitive: 強制標記此使用者所有媒體為敏感內容。
-          silence: 禁止該使用者發公開嘟文,從無跟隨他們的帳號中隱藏嘟文和通知。
-          suspend: 禁止所有對該帳號任何互動,並且刪除其內容。三十日內可以撤回。
+          silence: 禁止該使用者發公開嘟文,從無跟隨他們的帳號中隱藏嘟文和通知。關閉所有對此帳號之檢舉報告。
+          suspend: 禁止所有對該帳號任何互動,並且刪除其內容。三十天內可以撤銷此動作。關閉所有對此帳號之檢舉報告。
         warning_preset_id: 選用。您仍可在預設的結尾新增自訂文字
       announcement:
         all_day: 核取後,只會顯示出時間範圍中的日期部分
@@ -33,7 +33,7 @@ zh-TW:
         autofollow: 通過邀請網址註冊的使用者將自動跟隨您
         avatar: 支援 PNG、GIF 或 JPG 圖片格式,檔案最大為 %{size},會等比例縮減至 %{dimensions} 像素
         bot: 此帳號主要執行自動化操作且可能未受人為監控
-        context: 應該套用過濾器的一項或多項內容
+        context: 此過濾器應套用於以下一項或多項情境
         current_password: 因安全因素,請輸入目前帳號的密碼
         current_username: 請輸入目前帳號的使用者名稱以確認
         digest: 僅在您長時間未登入且在未登入期間收到私訊時傳送
@@ -49,23 +49,23 @@ zh-TW:
         phrase: 無論是嘟文的本文或是內容警告都會被過濾
         scopes: 允許讓應用程式存取的 API。 若您選擇最高階範圍,則無須選擇個別項目。
         setting_aggregate_reblogs: 請勿顯示最近已被轉嘟之嘟文的最新轉嘟(只影響最新收到的嘟文)
-        setting_always_send_emails: 一般情況下若您活躍使用 Mastodon ,我們不會寄送 e-mail 通知
+        setting_always_send_emails: 一般情況下若您活躍使用 Mastodon ,我們不會寄送電子郵件通知
         setting_default_sensitive: 敏感內容媒體預設隱藏,且按一下即可重新顯示
         setting_display_media_default: 隱藏標為敏感內容的媒體
         setting_display_media_hide_all: 總是隱藏所有媒體
-        setting_display_media_show_all: 總是顯示標為敏感的媒體
+        setting_display_media_show_all: 總是顯示標為敏感內容的媒體
         setting_hide_network: 您跟隨的人與跟隨您的人將不會在您的個人檔案頁面上顯示
         setting_noindex: 會影響您的公開個人檔案與嘟文頁面
         setting_show_application: 您用來發嘟文的應用程式將會在您嘟文的詳細檢視顯示
         setting_use_blurhash: 彩色漸層圖樣是基於隱藏媒體內容顏色產生,所有細節會變得模糊
         setting_use_pending_items: 關閉自動捲動更新,時間軸只會在點擊後更新
-        username: 您的使用者名稱將在 %{domain} 是獨一無二的
+        username: 您的使用者名稱將於 %{domain} 是獨一無二的
         whole_word: 如果關鍵字或詞組僅有字母與數字,則其將只在符合整個單字的時候才會套用
       domain_allow:
-        domain: 此域名將能夠攫取本站資料,而自域名該發出的資料也會於本站處理和留存。
+        domain: 此網域將能夠攫取本站資料,而自該網域發出的資料也會於本站處理和留存。
       email_domain_block:
-        domain: 這可以是顯示在電子郵件中的網域名稱,或是其使用的 MX 紀錄。其將在註冊時檢查。
-        with_dns_records: Mastodon 會嘗試解析所給域名的 DNS 記錄,解析結果一致者將一併封鎖
+        domain: 這可以是顯示在電子郵件中的網域名稱,或是其使用的 MX 紀錄。其將於註冊時檢查。
+        with_dns_records: Mastodon 會嘗試解析所給網域的 DNS 記錄,解析結果一致者將一併封鎖
       featured_tag:
         name: 這些是您最近使用的一些主題標籤:
       filters:
@@ -74,6 +74,7 @@ zh-TW:
           hide: 完全隱藏過濾內容,當作它似乎不曾存在過
           warn: 隱藏過濾內容於過濾器標題之警告後
       form_admin_settings:
+        activity_api_enabled: 本站使用者的嘟文數量,以及本站的活躍使用者與一週內新使用者數量
         backups_retention_period: 將已產生的使用者封存資料保存特定天數。
         bootstrap_timeline_accounts: 這些帳號將被釘選於新帳號跟隨推薦之上。
         closed_registrations_message: 於註冊關閉時顯示
@@ -81,19 +82,22 @@ zh-TW:
         custom_css: 您於 Mastodon 網頁版本中能套用客製化風格。
         mascot: 覆寫進階網頁介面中的圖例。
         media_cache_retention_period: 當設定成正值時,已下載的多媒體檔案會於指定天數後被刪除,並且視需要重新下載。
+        peers_api_enabled: 浩瀚聯邦宇宙中與此伺服器曾經擦肩而過的網域列表。不包含關於您是否與此伺服器是否有與之串連,僅僅表示您的伺服器已知此網域。這是供收集聯邦宇宙中一般性統計資料服務使用。
         profile_directory: 個人檔案目錄將會列出那些有選擇被發現的使用者。
-        require_invite_text: 如果已設定為手動審核註冊,請將「加入原因」設定為必填項目。
+        require_invite_text: 如果已設定為手動審核註冊,請將「為什麼想要加入呢?」設定為必填項目。
         site_contact_email: 其他人如何聯繫您關於法律或支援之諮詢。
         site_contact_username: 其他人如何於 Mastodon 上聯繫您。
         site_extended_description: 任何其他可能對訪客或使用者有用的額外資訊。可由 Markdown 語法撰寫。
         site_short_description: 一段有助於辨別您伺服器的簡短說明。例如:誰運行該伺服器、該伺服器是提供給哪些人群?
         site_terms: 使用您自己的隱私權政策,或者保留空白以使用預設值。可由 Markdown 語法撰寫。
         site_title: 除了網域外,其他人該如何指稱您的伺服器。
+        status_page_url: 當服務中斷時,可以提供使用者了解伺服器資訊頁面之 URL
         theme: 未登入之訪客或新使用者所見之佈景主題。
         thumbnail: 大約 2:1 圖片會顯示於您伺服器資訊之旁。
         timeline_preview: 未登入之訪客能夠瀏覽此伺服器上最新的公開嘟文。
         trendable_by_default: 跳過手動審核熱門內容。仍能在登上熱門趨勢後移除個別內容。
         trends: 熱門趨勢將顯示於您伺服器上正在吸引大量注意力的嘟文、主題標籤、或者新聞。
+        trends_as_landing_page: 顯示熱門趨勢內容給未登入使用者及訪客而不是關於此伺服器之描述。需要啟用熱門趨勢。
       form_challenge:
         current_password: 您正要進入安全區域
       imports:
@@ -103,7 +107,7 @@ zh-TW:
       ip_block:
         comment: 可選的,但請記得您為何添加這項規則。
         expires_in: IP 位址是經常共用或轉手的有限資源,不建議無限期地封鎖特定 IP 位址。
-        ip: 請輸入 IPv4 或 IPv6 位址,亦可以用 CIDR 語法以封鎖整個 IP 區段。小心不要把自己給一併封鎖掉囉!
+        ip: 請輸入 IPv4 或 IPv6 位址,亦可以用 CIDR 語法以封鎖整個 IP 區段。小心不要將自己給一併封鎖掉囉!
         severities:
           no_access: 封鎖對所有資源存取
           sign_up_block: 無法註冊新帳號
@@ -155,7 +159,7 @@ zh-TW:
       announcement:
         all_day: 全天活動
         ends_at: 活動結束時間
-        scheduled_at: 排程發佈
+        scheduled_at: 排程發布
         starts_at: 活動開始時間
         text: 公告
       appeal:
@@ -177,7 +181,7 @@ zh-TW:
         fields: 個人檔案詮釋資料
         header: 封面圖片
         honeypot: "%{label} (請勿填寫)"
-        inbox_url: 中繼收件匣的 URL
+        inbox_url: 中繼收件匣 URL
         irreversible: 放棄而非隱藏
         locale: 介面語言
         locked: 鎖定帳號
@@ -189,7 +193,7 @@ zh-TW:
         phrase: 關鍵字或片語
         setting_advanced_layout: 啟用進階網頁介面
         setting_aggregate_reblogs: 時間軸中的群組轉嘟
-        setting_always_send_emails: 總是發送 e-mail 通知
+        setting_always_send_emails: 總是發送電子郵件通知
         setting_auto_play_gif: 自動播放 GIF 動畫
         setting_boost_modal: 轉嘟前先詢問我
         setting_crop_images: 將未展開嘟文中的圖片裁剪至 16x9
@@ -229,6 +233,7 @@ zh-TW:
           hide: 完全隱藏
           warn: 隱藏於警告之後
       form_admin_settings:
+        activity_api_enabled: 於 API 中公開使用者活躍度的統計數據
         backups_retention_period: 使用者封存資料保留期間
         bootstrap_timeline_accounts: 永遠推薦這些帳號給新使用者
         closed_registrations_message: 當註冊關閉時的客製化訊息
@@ -236,6 +241,7 @@ zh-TW:
         custom_css: 自訂 CSS
         mascot: 自訂吉祥物 (legacy)
         media_cache_retention_period: 多媒體快取資料保留期間
+        peers_api_enabled: 於 API 中公開已知伺服器的列表
         profile_directory: 啟用個人檔案目錄
         registrations_mode: 誰能註冊
         require_invite_text: 要求「加入原因」
@@ -247,11 +253,13 @@ zh-TW:
         site_short_description: 伺服器描述
         site_terms: 隱私權政策
         site_title: 伺服器名稱
+        status_page_url: 狀態頁面 URL
         theme: 預設佈景主題
         thumbnail: 伺服器縮圖
         timeline_preview: 允許未登入使用者瀏覽公開時間軸
         trendable_by_default: 允許熱門趨勢直接顯示,不需經過審核
         trends: 啟用熱門趨勢
+        trends_as_landing_page: 以熱門趨勢作為登陸頁面
       interactions:
         must_be_follower: 封鎖非跟隨者的通知
         must_be_following: 封鎖您未跟隨之使用者的通知
@@ -259,7 +267,7 @@ zh-TW:
       invite:
         comment: 備註
       invite_request:
-        text: 加入的原因
+        text: 為什麼想要加入呢?
       ip_block:
         comment: 備註
         ip: IP 位址
@@ -275,7 +283,7 @@ zh-TW:
         follow: 當有使用者跟隨您時,傳送電子郵件通知
         follow_request: 當有使用者請求跟隨您時,傳送電子郵件通知
         mention: 當有使用者在嘟文提及您時,傳送電子郵件通知
-        pending_account: 需要審核的新帳號
+        pending_account: 有新的帳號需要審核
         reblog: 當有使用者轉嘟您的嘟文時,傳送電子郵件通知
         report: 新回報已遞交
         trending_tag: 新熱門趨勢需要審核
diff --git a/config/locales/sk.yml b/config/locales/sk.yml
index e1e2afd4d..494b15307 100644
--- a/config/locales/sk.yml
+++ b/config/locales/sk.yml
@@ -21,8 +21,8 @@ sk:
     pin_errors:
       following: Musíš už následovať toho človeka, ktorého si praješ zviditeľniť
     posts:
-      few: Príspevkov
-      many: Príspevky
+      few: Príspevky
+      many: Príspevkov
       one: Príspevok
       other: Príspevkov
     posts_tab_heading: Príspevky
@@ -95,6 +95,7 @@ sk:
       moderation:
         active: Aktívny/a
         all: Všetko
+        disabled: Blokovaný
         pending: Čakajúci
         silenced: Obmedzený
         suspended: Vylúčený/á
@@ -117,6 +118,7 @@ sk:
       redownloaded_msg: Úspešne obnovený profil %{username} z pôvodného
       reject: Zamietni
       rejected_msg: Úspešne zamietnutá prihláška %{username}
+      remote_suspension_irreversible: Údaje tohto účtu boli nenávratne zmazané.
       remove_avatar: Vymaž avatar
       remove_header: Vymaž záhlavie
       removed_avatar_msg: Úspešne odstránený obrázok avatara %{username}
@@ -132,6 +134,7 @@ sk:
       search: Hľadaj
       search_same_email_domain: Iní užívatelia s tou istou emailovou doménou
       search_same_ip: Ostatní užívatelia s rovnakou IP adresou
+      security: Zabezpečenie
       security_measures:
         only_password: Iba heslo
         password_and_2fa: Heslo a dvoj-faktorové overovanie
@@ -166,6 +169,7 @@ sk:
       whitelisted: Na bielej listine
     action_logs:
       action_types:
+        approve_appeal: Schváľ námietku
         approve_user: Odobri užívateľa
         assigned_to_self_report: Priraď hlásenie
         change_email_user: Zmeň email pre užívateľa
@@ -180,6 +184,7 @@ sk:
         create_user_role: Vytvoriť rolu
         demote_user: Zniž užívateľskú rolu
         destroy_announcement: Vymaž oboznámenie
+        destroy_canonical_email_block: Zruš blokovanie emailu
         destroy_custom_emoji: Vymaž vlastné emotikony
         destroy_domain_allow: Zmaž povolenie pre doménu
         destroy_domain_block: Zruš blokovanie domény
@@ -194,9 +199,11 @@ sk:
         enable_custom_emoji: Povoľ vlastné emotikony
         enable_user: Povoľ užívateľa
         promote_user: Povýš užívateľskú rolu
+        reject_appeal: Zamietni námietku
         reject_user: Zamietni užívateľa
         remove_avatar_user: Vymaž avatar
         reopen_report: Znovu otvor hlásenie
+        resend_user: Preposlať overovací email
         reset_password_user: Obnov heslo
         resolve_report: Vyrieš nahlásený problém
         sensitive_account: Vynúť všetky médiá na účte ako chúlostivé
@@ -216,7 +223,24 @@ sk:
         change_email_user_html: "%{name} zmenil/a emailovú adresu užívateľa %{target}"
         confirm_user_html: "%{name} potvrdil/a emailovú adresu používateľa %{target}"
         create_account_warning_html: "%{name} poslal/a upozornenie užívateľovi %{target}"
+        demote_user_html: "%{name} degradoval/a užívateľa %{target}"
+        destroy_canonical_email_block_html: "%{name} odblokoval/i email z hašom %{target}"
+        destroy_domain_allow_html: "%{name} zakázal/a federáciu s doménou %{target}"
+        destroy_domain_block_html: "%{name} odblokoval/i doménu %{target}"
+        destroy_ip_block_html: "%{name} vymazal/a pravidlo pre IP %{target}"
+        destroy_status_html: "%{name} zmazal/a príspevok od %{target}"
+        memorialize_account_html: "%{name} zmenil/a účet %{target} na pamätnú stránku"
+        reject_appeal_html: "%{name} zamietol/la námietku moderovacieho rozhodnutia od %{target}"
+        reopen_report_html: "%{name} znovu otvoril/a nahlásenie %{target}"
+        resend_user_html: "%{name} znovu odoslal/a potvrdzovací email pre %{target}"
+        reset_password_user_html: "%{name} resetoval/a heslo používateľa %{target}"
+        resolve_report_html: "%{name} vyriešil/a nahlásenie %{target}"
+        sensitive_account_html: "%{name} označil médium od %{target} za chúlostivé"
+        silence_account_html: "%{name} obmedzil/a účet %{target}"
+        suspend_account_html: "%{name} zablokoval/a účet používateľa %{target}"
+        unassigned_report_html: "%{name} odobral/a report od %{target}"
       deleted_account: zmazaný účet
+      empty: Žiadne záznamy nenájdené.
       filter_by_action: Filtruj podľa úkonu
       filter_by_user: Trieď podľa užívateľa
       title: Kontrólny záznam
@@ -252,10 +276,12 @@ sk:
       enable: Povoľ
       enabled: Povolené
       enabled_msg: Emoji bolo úspešne povolené
+      image_hint: PNG, alebo GIF do %{size}
       list: Zoznam
       listed: V zozname
       new:
         title: Pridaj nové, vlastné emoji
+      not_permitted: Nemáš povolenie na vykonanie tohto kroku
       overwrite: Prepíš
       shortcode: Skratka
       shortcode_hint: Aspoň 2 znaky, povolené sú alfanumerické, alebo podčiarkovník
@@ -274,15 +300,21 @@ sk:
       opened_reports: otvorené hlásenia
       resolved_reports: vyriešené hlásenia
       software: Softvér
+      sources: Zdroje registrácií
       space: Využitie miesta
       title: Spravovacie rozhranie
       top_languages: Hlavne aktívne jazyky
       top_servers: Hlavne aktívne servery
       website: Webová stránka
+    disputes:
+      appeals:
+        empty: Žiadne námietky nenájdené.
+        title: Námietky
     domain_allows:
       add_new: Povolená doména
       created_msg: Doména bola úspešne povolená
       destroyed_msg: Doména bola odstránená zo zoznamu povolených
+      export: Exportuj
       import: Nahraj
       undo: Odober zo zoznamu povolených
     domain_blocks:
@@ -299,6 +331,7 @@ sk:
         hint: Blokovanie domény stále dovolí vytvárať nové účty v databázi, ale tieto budú spätne automaticky moderované.
         severity:
           noop: Nič
+          silence: Obmedz
           suspend: Vylúč
         title: Nové blokovanie domény
       obfuscate: Zatemniť názov domény
@@ -315,13 +348,28 @@ sk:
       add_new: Pridaj nový
       created_msg: Emailová doména bola úspešne pridaná do zoznamu zakázaných
       delete: Vymaž
+      dns:
+        types:
+          mx: MX záznam
       domain: Doména
       new:
         create: Pridaj doménu
         resolve: Preveď doménu
         title: Nový email na zablokovanie
+      not_permitted: Nepovolená
       resolved_through_html: Prevedená cez %{domain}
       title: Blokované emailové adresy
+    export_domain_allows:
+      new:
+        title: Nahraj povolené domény
+      no_file: Nevybraný žiaden súbor
+    export_domain_blocks:
+      import:
+        existing_relationships_warning: Existujúce vzťahy nasledovania
+        title: Nahraj zákazy domén
+      new:
+        title: Nahraj zákazy domén
+      no_file: Nevybraný žiaden súbor
     follow_recommendations:
       description_html: "<strong>Odporúčania na sledovanie pomáhaju novým užívateľom rýchlo nájsť zaujímavý obsah</strong>. Ak užívateľ zatiaľ nedostatočne interagoval s ostatnými aby si vyformoval personalizované odporúčania na sledovanie, tak mu budú odporúčané tieto účty. Sú prepočítavané na dennej báze z mixu účtov s nedávnym najvyšším záujmom a najvyšším počtom lokálnych sledujúcich pre daný jazyk."
       language: Pre jazyk
@@ -332,6 +380,7 @@ sk:
       unsuppress: Obnoviť odporúčanie na sledovanie
     instances:
       availability:
+        no_failures_recorded: Žiadne zlyhania nezaznamenané.
         title: Dostupnosť
       back_to_all: Všetko
       back_to_limited: Obmedzené
@@ -342,12 +391,16 @@ sk:
         policies:
           reject_media: Zamietni médiá
           reject_reports: Zamietni hlásenia
+          silence: Obmedzená
           suspend: Vylúč
         policy: Zásady
         reason: Verejné odôvodnenie
         title: Zásady o obsahu
       dashboard:
         instance_accounts_dimension: Najsledovanejšie účty
+        instance_accounts_measure: uložené účty
+        instance_follows_measure: ich sledovatelia tu
+        instance_statuses_measure: uložené príspevky
       delivery:
         all: Všetko
         failing: Zlyhávajúce
@@ -411,13 +464,18 @@ sk:
       destroyed_msg: Poznámka o nahlásení úspešne vymazaná!
     reports:
       action_taken_by: Zákrok vykonal/a
+      actions:
+        suspend_description_html: Tento účet a všetok jeho obsah bude nedostupný a nakoniec zmazaný, interaktovať s ním bude nemožné. Zvrátiteľné v rámci 30 dní. Uzatvára všetky hlásenia voči tomuto účtu.
+      add_to_report: Pridaj viac do hlásenia
       are_you_sure: Si si istý/á?
       assign_to_self: Priraď sebe
       assigned: Priradený moderátor
       by_target_domain: Doména nahláseného účtu
+      cancel: Zruš
       category: Kategória
       comment:
         none: Žiadne
+      confirm: Potvrď
       created_at: Nahlásené
       delete_and_resolve: Vymaž príspevky
       forwarded: Preposlané
@@ -433,6 +491,7 @@ sk:
         delete: Vymaž
         placeholder: Opíš aké opatrenia boli urobené, alebo akékoľvek iné súvisiace aktualizácie…
         title: Poznámky
+      remote_user_placeholder: vzdialený užívateľ z %{instance}
       reopen: Znovu otvor report
       report: 'Nahlásiť #%{id}'
       reported_account: Nahlásený účet
@@ -441,6 +500,11 @@ sk:
       resolved_msg: Hlásenie úspešne vyriešené!
       status: Stav
       statuses: Nahlásený obsah
+      summary:
+        actions:
+          delete_html: Vymaž pohoršujúce príspevky
+          mark_as_sensitive_html: Označ médiá pohoršujúcich príspevkov za chúlostivé
+        close_report: 'Označ hlásenie #%{id} za vyriešené'
       title: Hlásenia
       unassign: Odober
       unresolved: Nevyriešené
@@ -456,40 +520,85 @@ sk:
       categories:
         administration: Spravovanie
         invites: Pozvánky
+        moderation: Moderácia
+      delete: Vymaž
       edit: Uprav postavenie %{name}
+      everyone: Východzie oprávnenia
       privileges:
         administrator: Správca
+        delete_user_data: Vymaž užívateľské dáta
         invite_users: Pozvi užívateľov
+        manage_announcements: Spravuj oboznámenia
+        manage_appeals: Spravuj námietky
+        manage_blocks: Spravuj blokovania
+        manage_federation: Spravuj federáciu
+        manage_reports: Spravuj hlásenia
         manage_roles: Spravuj postavenia
+        manage_rules: Spravuj pravidlá
+        manage_settings: Spravuj nastavenia
+        manage_users: Spravuj užívateľov
       title: Postavenia
     rules:
+      add_new: Pridaj pravidlo
+      delete: Vymaž
+      edit: Uprav pravidlo
       empty: Žiadne pravidlá servera ešte neboli určené.
       title: Serverové pravidlá
     settings:
+      appearance:
+        title: Vzhľad
+      discovery:
+        public_timelines: Verejné časové osi
+        publish_discovered_servers: Zverejni objavené servery
+        publish_statistics: Zverejni štatistiky
+        title: Objavovanie
+        trends: Trendy
       domain_blocks:
         all: Všetkým
         disabled: Nikomu
         users: Prihláseným, miestnym užívateľom
+      registrations:
+        title: Registrácie
       registrations_mode:
         modes:
           approved: Pre registráciu je nutné povolenie
           none: Nikto sa nemôže registrovať
           open: Ktokoľvek sa môže zaregistrovať
+      title: Nastavenia servera
     site_uploads:
       delete: Vymaž nahratý súbor
       destroyed_msg: Nahratie bolo zo stránky úspešne vymazané!
     statuses:
+      account: Autor
+      application: Aplikácia
       back_to_account: Späť na účet
+      batch:
+        report: Hlásenie
       deleted: Vymazané
+      favourites: Obľúbené
+      history: História verzií
+      in_reply_to: Odpoveď na
+      language: Jazyk
       media:
         title: Médiá
       no_status_selected: Žiadne príspevky neboli zmenené, keďže si žiadne nemal/a zvolené
+      open: Otvor príspevok
+      original_status: Pôvodný príspevok
+      status_changed: Príspevok bol zmenený
       title: Príspevky na účte
+      trending: Populárne
+      visibility: Viditeľnosť
       with_media: S médiami
+    strikes:
+      appeal_approved: Namietnuté
     system_checks:
       rules_check:
         action: Spravuj serverové pravidlá
         message_html: Neurčil/a si žiadne serverové pravidlá.
+      upload_check_privacy_error:
+        action: Pozri tu pre viac informácií
+      upload_check_privacy_error_object_storage:
+        action: Pozri tu pre viac informácií
     tags:
       review: Prehodnoť stav
       updated_msg: Nastavenia haštagov boli úspešne aktualizované
@@ -504,19 +613,31 @@ sk:
         disallow: Zakáž odkaz
         disallow_provider: Zákaž zverejňovateľa
         title: Populárne odkazy
+      only_allowed: Iba povolené
+      pending_review: Čaká na posúdenie
       preview_card_providers:
         title: Zverejňovatelia
       rejected: Odmietnuté
+      statuses:
+        disallow: Zakáž príspevok
+        disallow_account: Zakáž autora
+        title: Populárne príspevky
       tags:
         title: Populárne štítky
         trending_rank: 'Populárne #%{rank}'
         usable: Môže byť použitý
       title: Trendy
+      trending: Populárne
     warning_presets:
       add_new: Pridaj nové
       delete: Vymaž
       edit_preset: Uprav varovnú predlohu
       title: Spravuj varovné predlohy
+    webhooks:
+      delete: Zmazať
+      disable: Vypni
+      disabled: Vypnuté
+      enable: Povoľ
   admin_mailer:
     new_pending_account:
       body: Podrobnosti o novom účte sú uvedené nižšie. Môžeš túto registračnú požiadavku buď prijať, alebo zamietnúť.
@@ -525,6 +646,11 @@ sk:
       body: "%{reporter} nahlásil/a %{target}"
       body_remote: Niekto z %{domain} nahlásil/a %{target}
       subject: Nové hlásenie pre %{instance} (#%{id})
+    new_trends:
+      new_trending_links:
+        title: Populárne odkazy
+      new_trending_statuses:
+        title: Populárne príspevky
   aliases:
     add_new: Vytvor alias
     created_msg: Nový alias úspešne vytvorený. Teraz môžeš začať presun zo starého účtu.
@@ -550,6 +676,7 @@ sk:
   applications:
     created: Aplikácia bola vytvorená úspešne
     destroyed: Aplikáciu sa podarilo odstrániť
+    logout: Odhlás sa
     regenerate_token: Znovu vygeneruj prístupový token
     token_regenerated: Prístupový token bol úspešne vygenerovaný znova
     warning: Na tieto údaje dávaj ohromný pozor. Nikdy ich s nikým nezďieľaj!
@@ -561,7 +688,7 @@ sk:
     description:
       prefix_invited_by_user: "@%{name} ťa pozýva na tento Mastodon server!"
       prefix_sign_up: Zaregistruj sa na Mastodone už dnes!
-      suffix: S pomocou účtu budeš môcť následovať ľudí, posielať príspevky, a vymienať si správy s užívateľmi na hociakom Mastodon serveri, ale aj na iných serveroch!
+      suffix: S pomocou účtu budeš môcť nasledovať ľudí, posielať príspevky, a vymieňať si správy s užívateľmi na hocijakom Mastodon serveri, ale aj na iných serveroch!
     didnt_get_confirmation: Neobdržal/a si kroky na potvrdenie?
     forgot_password: Zabudnuté heslo?
     invalid_reset_password_token: Token na obnovu hesla vypršal. Prosím vypítaj si nový.
@@ -575,6 +702,8 @@ sk:
     registration_closed: "%{instance} neprijíma nových členov"
     resend_confirmation: Zašli potvrdzujúce pokyny znovu
     reset_password: Obnov heslo
+    rules:
+      back: Späť
     security: Zabezpečenie
     set_new_password: Nastav nové heslo
     setup:
@@ -591,7 +720,7 @@ sk:
     already_following: Tento účet už nasleduješ
     error: Naneštastie nastala chyba pri hľadaní vzdialeného účtu
     follow: Nasleduj
-    follow_request: 'Poslal/a si žiadosť následovať užívateľa:'
+    follow_request: 'Poslal/a si žiadosť nasledovať užívateľa:'
     following: 'Podarilo sa! Teraz nasleduješ užívateľa:'
     post_follow:
       close: Alebo môžeš iba zatvoriť toto okno.
@@ -634,6 +763,15 @@ sk:
       more_details_html: Pre viac podrobností, pozri <a href="%{terms_path}">zásady súkromia</a>.
       username_available: Tvoje užívateľské meno bude znova dostupné
       username_unavailable: Tvoja prezývka ostane neprístupná
+  disputes:
+    strikes:
+      appeal: Namietni
+      appeals:
+        submit: Pošli námietku
+      approve_appeal: Schváľ námietku
+      title_actions:
+        none: Varovanie
+        silence: Obmedzenie účtu
   domain_validator:
     invalid_domain: nieje správny tvar domény
   errors:
@@ -671,7 +809,7 @@ sk:
   featured_tags:
     add_new: Pridaj nový
     errors:
-      limit: Už si si predvolil/a najvyšší možný počet obľúbených haštagov
+      limit: Už si si predvolil/a najvyšší možný počet haštagov
   filters:
     contexts:
       account: Profily
@@ -689,8 +827,6 @@ sk:
       title: Triedenia
     new:
       title: Pridaj nové triedenie
-  footer:
-    trending_now: Teraz populárne
   generic:
     all: Všetko
     changes_saved_msg: Zmeny boli úspešne uložené!
@@ -703,8 +839,6 @@ sk:
       many: Niečo ešte nieje celkom v poriadku! Prosím skontroluj %{count} chýb uvedených nižšie
       one: Niečo ešte nieje celkom v poriadku! Prosím skontroluj chybu uvedenú nižšie
       other: Niečo ešte nieje celkom v poriadku! Prosím skontroluj %{count} chyby uvedené nižšie
-  html_validator:
-    invalid_markup: 'obsahuje neplatný HTML kód: %{error}'
   imports:
     errors:
       over_rows_processing_limit: obsahuje viac než %{count} riadkov
@@ -748,7 +882,7 @@ sk:
     title: Pozvi ľudí
   lists:
     errors:
-      limit: Dosiahli ste maximálny možný počet zoznamov
+      limit: Dosiahli ste maximálny počet zoznamov
   login_activities:
     authentication_methods:
       password: heslom
@@ -822,10 +956,14 @@ sk:
       title: Novo vyzdvyhnuté
     status:
       subject: "%{name} práve prispel/a"
+    update:
+      subject: "%{name} upravil/a príspevok"
   notifications:
     email_events: Udalosti oznamované emailom
     email_events_hint: 'Vyber si udalosti, pre ktoré chceš dostávať oboznámenia:'
     other_settings: Ostatné oboznamovacie nastavenia
+  otp_authentication:
+    enable: Povoľ
   pagination:
     newer: Novšie
     next: Ďalšie
@@ -852,6 +990,9 @@ sk:
       unrecognized_emoji: je neznámy smajlík
   relationships:
     activity: Aktivita účtu
+    confirm_follow_selected_followers: Si si istý/á, že chceš nasledovať vybraných sledujúcich?
+    confirm_remove_selected_followers: Si si istý/á, že chceš odstrániť vybraných sledovateľov?
+    confirm_remove_selected_follows: Si si istý/á, že chceš odstrániť vybraných sledovaných?
     dormant: Spiace
     follow_selected_followers: Následuj označených sledovatelov
     followers: Následovatelia
@@ -884,6 +1025,7 @@ sk:
       otter: Prehliadač Otter
       qq: QQ Prehliadač
       safari: Apple Safari
+      unknown_browser: Neznámy prehliadač
       weibo: Sina/Tencent Weibo
     current_session: Aktuálna sezóna
     description: "%{browser} na %{platform}"
@@ -893,7 +1035,7 @@ sk:
       ios: Apple iOS
       linux: GNU/Linux
       mac: MacOSX
-      other: neznáma platforma
+      unknown_platform: Neznáma platforma
       windows: Microsoft Windows
     revoke: Zamietni
     revoke_success: Sezóna úspešne zamietnutá
@@ -934,7 +1076,7 @@ sk:
       one: 'obsahoval nepovolený haštag: %{tags}'
       other: 'obsahoval nepovolené haštagy: %{tags}'
     errors:
-      in_reply_not_found: Príspevok, na ktorý sa snažíš odpovedať, už pravdepodobne neexistuje.
+      in_reply_not_found: Príspevok, na ktorý sa snažíš odpovedať, pravdepodobne neexistuje.
     open_in_web: Otvor v okne na webe
     over_character_limit: limit %{max} znakov bol presiahnutý
     pin_errors:
@@ -991,6 +1133,8 @@ sk:
     recovery_codes_regenerated: Zálohové kódy boli úspešne zvova vygenerované
     recovery_instructions_html: Keď hocikedy stratíš prístup k svojmu telefónu, môžeš použiť jeden z prístupových kódov nižšie pre obnovenie prístupu k svojmu účtu. <strong>Skladuj tieto prístupové kódy na bezpečnom mieste</strong>. Napríklad ich môžeš vytlačiť a uložiť ich spolu s inými dôležitými dokumentami.
   user_mailer:
+    appeal_approved:
+      action: Choď na svoj účet
     backup_ready:
       explanation: Vyžiadal/a si si úplnú zálohu svojho Mastodon účtu. Táto záloha je teraz pripravená na stiahnutie!
       subject: Tvoj archív je pripravený na stiahnutie
@@ -1021,5 +1165,4 @@ sk:
     seamless_external_login: Si prihlásená/ý cez externú službu, takže nastavenia hesla a emailu ti niesú prístupné.
     signed_in_as: 'Prihlásená/ý ako:'
   verification:
-    explanation_html: 'Môžeš sa <strong>overiť ako majiteľ odkazov v metadátach tvojho profilu</strong>. Na to ale musí odkazovaná stránka obsahovať odkaz späť na tvoj Mastodon profil. Tento spätný odkaz <strong>musí</strong> mať prívlastok <code>rel="me"</code>. Na texte odkazu nezáleží. Tu je príklad:'
     verification: Overenie
diff --git a/config/locales/sl.yml b/config/locales/sl.yml
index 299f6107b..00bdd10a9 100644
--- a/config/locales/sl.yml
+++ b/config/locales/sl.yml
@@ -95,6 +95,7 @@ sl:
       moderation:
         active: Dejaven
         all: Vse
+        disabled: Onemogočeno
         pending: Na čakanju
         silenced: Omejeno
         suspended: Suspendiran
@@ -139,6 +140,7 @@ sl:
       search: Iskanje
       search_same_email_domain: Drugi uporabniki z isto e-poštno domeno
       search_same_ip: Drugi uporabniki z istim IP
+      security: Varnost
       security_measures:
         only_password: Samo geslo
         password_and_2fa: Geslo in 2FA
@@ -443,6 +445,7 @@ sl:
         resolve: Razreši domeno
         title: Nov vnos e-pošte na črni seznam
       no_email_domain_block_selected: Nobena domena e-računa ni bila spremenjena, ker nobena ni bila izbrana
+      not_permitted: Ni dovoljeno
       resolved_dns_records_hint_html: Ime domene se razreši na naslednje domene MX, ki so končno odgovorne za sprejemanje e-pošte. Blokiranje domene MX bo blokiralo prijave s poljubnega e-poštnega naslova, ki uporablja isto domeno MX, tudi če je vidno ime domene drugačno. <strong>Pazite, da ne blokirate večjih ponudnikov e-pošte.</strong>
       resolved_through_html: Razrešeno prek %{domain}
       title: Črni seznam e-pošt
@@ -457,6 +460,7 @@ sl:
         private_comment_description_html: 'Kot pomoč pri sledenju izvora uvoženih blokad bodo le-te ustvarjene z naslednjim zasebnim komentarjem: <q>%{comment}</q>'
         private_comment_template: Uvoženo iz %{source} %{date}
         title: Uvozi blokade domen
+      invalid_domain_block: 'Ena ali več blokad domen je bila preskočena zaradi naslednjih napak: %{error}'
       new:
         title: Uvozi blokade domen
       no_file: Nobena datoteka ni izbrana
@@ -492,6 +496,7 @@ sl:
       content_policies:
         comment: Interna opomba
         description_html: Določite lahko pravila (t.i. politike), ki bodo veljale za vse račune te domene ter vseh njenih poddomen.
+        limited_federation_mode_description_html: Izberete lahko, ali dovolite federacijo s to domeno.
         policies:
           reject_media: Zavrni večpredstavnost
           reject_reports: Zavrni prijave
@@ -599,19 +604,23 @@ sl:
         mark_as_sensitive_description_html: Mediji v prijavljenih objavah bodo označeni kot občutljivi in ukrep bo zabeležen, da vam pomaga stopnjevati ukrepe ob naslednjih kršitvah z istega računa.
         other_description_html: Oglejte si več možnosti za nadzor vedenja računa in prilagodite komunikacijo s prijavljenim računom.
         resolve_description_html: Proti prijavljenemu računu ne bo izvedeno nobeno dejanje, noben ukrep ne bo zabeležen in prijava bo zaprta.
-        silence_description_html: Profil bo viden samo tistim, ki mu že sledijo ali ga ročno poiščejo, s čimer bo resno omejen njegov doseg. To je vedno možno povrniti.
-        suspend_description_html: Profil in vsa njegova vsebina bodo postali nedosegljivi, dokler niso dokončno izbrisani. Interakcija z računom ne bo možna. Povrnitev je možna v času 30 dni.
+        silence_description_html: Račun bo viden samo tistim, ki mu že sled ijo ali ga ročno poiščejo, s čimer bo resno omejen njegov doseg. To je vedno možno povrniti. Zaključi vse prijave zoper ta račun.
+        suspend_description_html: Račun in vsa njegova vsebina ne bo dostopna in bo postopoma izbrisana, interakcija z njim pa ne bo več možna. Dejanje je moč povrniti v roku 30 dni. Zaključi vse prijave zoper ta račun.
       actions_description_html: Odločite se, katere ukrepe boste sprejeli za rešitev te prijave. Če sprejmete kazenski ukrep proti prijavljenemu računu, mu bo poslano e-poštno obvestilo, razen če je izbrana kategorija <strong>Neželena pošta</strong>.
+      actions_description_remote_html: Odločite se za dejanje, ki bo odločilo o tej prijavi. To bo vplivalo le na to, kako <strong>vaš</strong> strežnik komunicira s tem oddaljenim računom in obravnava njegovo vsebino.
       add_to_report: Dodaj več v prijavo
       are_you_sure: Ali ste prepričani?
       assign_to_self: Dodeli meni
       assigned: Dodeljen moderator
       by_target_domain: Domena prijavljenega računa
+      cancel: Prekliči
       category: Kategorija
       category_description_html: Razlog, zakaj je ta račun in/ali vsebina prijavljena, bo naveden v komunikaciji z računom iz prijave
       comment:
         none: Brez
       comment_description_html: 'V pojasnilo je %{name} zapisal/a:'
+      confirm: Potrdi
+      confirm_action: Potrdite dejanje moderiranja proti @%{acct}
       created_at: Prijavljeno
       delete_and_resolve: Izbriši objave
       forwarded: Posredovano
@@ -628,6 +637,7 @@ sl:
         placeholder: Opišite dejanja, ki ste jih izvedli, ali katere koli druge posodobitve ...
         title: Zapiski
       notes_description_html: Pokaži in pusti opombe drugim moderatorjem in sebi v prihodnosti
+      processed_msg: 'Prijava #%{id} uspešno obdelana'
       quick_actions_description_html: 'Opravite hitro dejanje ali podrsajte navzdol, da si ogledate prijavljeno vsebino:'
       remote_user_placeholder: oddaljeni uporabnik iz %{instance}
       reopen: Ponovno odpri prijavo
@@ -640,9 +650,28 @@ sl:
       status: Stanje
       statuses: Prijavljena vsebina
       statuses_description_html: Žaljiva vsebina bo citirana v komunikaciji z računom iz prijave
+      summary:
+        action_preambles:
+          delete_html: 'Nekatere od objav uporabnika/ce <strong>@%{acct}</strong> boste <strong>odstranili</strong>. S tem boste:'
+          mark_as_sensitive_html: 'Nekatere od objav uporabnika/ce <strong>@%{acct}</strong> boste <strong>označili</strong> kot <strong>občutljive</strong>. S tem boste:'
+          silence_html: 'Račun uporabnika/ce <strong>@%{acct}</strong> boste <strong>omejili</strong>. S tem boste:'
+          suspend_html: "<strong>Suspendirali</strong> boste račun uporabnika/ce <strong>@%{acct}</strong>. S tem boste:"
+        actions:
+          delete_html: Odstrani žaljive objave
+          mark_as_sensitive_html: Označi večpredstavnost žaljivih objav kot občutljivo
+          silence_html: Močno omejite doseg osebe <strong>@%{acct}</strong>, tako da naredite njihov profil in vsebino vidno samo osebam, ki jih že spremljajo ali ročno poiščejo profil
+          suspend_html: Suspendirajte <strong>@%{acct}</strong>, s čimer postaneta njihov profil in vsebina nedostopna in z njim ni mogoče komunicirati
+        close_report: 'Označi prijavo #%{id} kot rešeno'
+        close_reports_html: Označi <strong>vse</strong> prijave zoper <strong>@%{acct}</strong> kot rešene
+        delete_data_html: Izbriši profil in vsebine <strong>@%{acct}</strong> čez 30 dni, razen če suspenz v tem času ni preklican
+        preview_preamble_html: 'Oseba <strong>@%{acct}</strong> bo prejela opozorilo z naslednjo vsebino:'
+        record_strike_html: Izdajte opomin računu <strong>@%{acct}</strong>, da boste lažje stopnjevali svoj odziv ob prihodnjih kršitvah s tega računa
+        send_email_html: Pošlji <strong>@%{acct}</strong> opozorilno e-sporočilo
+        warning_placeholder: Neobvezna dodatna utemeljitev dejanja moderiranja.
       target_origin: Izvor prijavljenega računa
       title: Prijave
       unassign: Odstopljeni
+      unknown_action_msg: 'Neznano dejanje: %{action}'
       unresolved: Nerešeni
       updated_at: Posodobljeni
       view_profile: Pokaži profil
@@ -741,6 +770,8 @@ sl:
         preamble: Izpostavljanje zanimivih vsebin je ključno za pridobivanje novih uporabnikov, ki morda ne poznajo nikogar na Mastodonu. Nadzirajte, kako različne funkcionalnosti razkritja delujejo na vašem strežniku.
         profile_directory: Imenik profilov
         public_timelines: Javne časovnice
+        publish_discovered_servers: Objavi odkrite strežnike
+        publish_statistics: Objavi statistiko
         title: Razkrivanje
         trends: Trendi
       domain_blocks:
@@ -795,6 +826,7 @@ sl:
         suspend: "%{name} je suspendiral/a račun uporabnika %{target}"
       appeal_approved: Pritoženo
       appeal_pending: Čakajoč na ugovor
+      appeal_rejected: Pritožba zavrnjena
     system_checks:
       database_schema_check:
         message_html: Na čakanju so migracije zbirke podatkov. Prosimo, izvedite jih, da zagotovite, da se program vede pričakovano
@@ -808,6 +840,12 @@ sl:
         message_html: Nobenih pravil strežnika niste določili.
       sidekiq_process_check:
         message_html: Noben proces Sidekiq ne poteka za %{value} vrst. Preglejte svojo prilagoditev Sidekiq
+      upload_check_privacy_error:
+        action: Preverite tukaj za več informacij
+        message_html: "<strong>Vaš spletni strežnik je napačno nastavljen. Zasebnost vaših uporabnikov je izpostavljena tveganjem.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Preverite tukaj za več informacij
+        message_html: "<strong>Vaša predmetna shramba je napačno nastavljena. Zasebnost vaših uporabnikov je izpostavljena tveganjem.</strong>"
     tags:
       review: Stanje pregleda
       updated_msg: Nastavitve ključnikov uspešno posodobljene
@@ -832,6 +870,7 @@ sl:
           two: Delili %{count} osebi v zadnjem tednu
         title: Povezave v trendu
         usage_comparison: Danes deljeno %{today}-krat, včeraj pa %{yesterday}-krat
+      not_allowed_to_trend: Ni dovoljeno, da bi bilo v trendu
       only_allowed: Samo dovoljeni
       pending_review: Čakajoče na pregled
       preview_card_providers:
@@ -969,6 +1008,7 @@ sl:
   applications:
     created: Aplikacija je bila uspešno ustvarjena
     destroyed: Aplikacija je bila uspešno izbrisana
+    logout: Odjava
     regenerate_token: Obnovite dostopni žeton
     token_regenerated: Dostopni žeton je bil uspešno regeneriran
     warning: Bodite zelo previdni s temi podatki. Nikoli jih ne delite z nikomer!
@@ -976,6 +1016,8 @@ sl:
   auth:
     apply_for_account: Zaprosite za račun
     change_password: Geslo
+    confirmations:
+      wrong_email_hint: Če ta e-poštni naslov ni pravilen, ga lahko spremenite v nastavitvah računa.
     delete_account: Izbriši račun
     delete_account_html: Če želite izbrisati svoj račun, lahko nadaljujete <a href="%{path}">tukaj</a>. Prosili vas bomo za potrditev.
     description:
@@ -1003,6 +1045,8 @@ sl:
     resend_confirmation: Ponovno pošlji navodila za potrditev
     reset_password: Ponastavi geslo
     rules:
+      accept: Sprejmi
+      back: Nazaj
       preamble: Slednje določajo in njihovo spoštovanje zagotavljajo moderatorji %{domain}.
       title: Nekaj osnovnih pravil.
     security: Varnost
@@ -1200,8 +1244,6 @@ sl:
       index:
         hint: Ta filter se nanaša na posamezne objave ne glede na druge pogoje. Filtru lahko dodate več objav prek spletnega vmesnika.
         title: Filtrirane objave
-  footer:
-    trending_now: Zdaj v trendu
   generic:
     all: Vse
     all_items_on_page_selected_html:
@@ -1232,8 +1274,6 @@ sl:
       one: Nekaj še ni čisto v redu! Spodaj si oglejte napako
       other: Nekaj še ni čisto v redu! Spodaj si oglejte %{count} napak
       two: Nekaj še ni čisto v redu! Spodaj si oglejte %{count} napaki
-  html_validator:
-    invalid_markup: 'vsebuje neveljavno oznako HTML: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Neveljavna datoteka CSV. Napaka: %{error}'
@@ -1419,7 +1459,11 @@ sl:
       unrecognized_emoji: ni prepoznan emotikon
   relationships:
     activity: Dejavnost računa
+    confirm_follow_selected_followers: Ali ste prepričani, da želite slediti izbranim sledilcem?
+    confirm_remove_selected_followers: Ali ste prepričani, da želite odstraniti izbrane sledilce?
+    confirm_remove_selected_follows: Ali ste prepričani, da želite odstraniti izbrana sledenja?
     dormant: Skrit
+    follow_failure: Nekaterim od izbranih računov ni bilo mogoče slediti.
     follow_selected_followers: Sledi izbranim sledilcem
     followers: Sledilci
     following: Sledi
@@ -1459,6 +1503,7 @@ sl:
       electron: Electron
       firefox: Firefox
       generic: Neznan brskalnik
+      huawei_browser: Brskalnik Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Brskalnik Nokia S40 Ovi
@@ -1468,6 +1513,7 @@ sl:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Neznan brskalnik
       weibo: Weibo
     current_session: Trenutna seja
     description: "%{browser} na %{platform}"
@@ -1480,9 +1526,10 @@ sl:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: neznana platforma
+      unknown_platform: Neznana platforma
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1607,7 +1654,7 @@ sl:
       '7889238': 3 mesece
     min_age_label: Starostna meja
     min_favs: Obrži objave priljubljene vsaj
-    min_favs_hint: Ne izbriše nobene od vaših objav, ki je prejela vsaj takšno količino priljubljenih. Pustite prazno, če želite izbrisati objave ne glede na število všečkov
+    min_favs_hint: Ne izbriše nobene od vaših objav, ki je prejela vsaj takšno število priljubljenih. Pustite prazno, če želite izbrisati objave ne glede na število všečkov
     min_reblogs: Obdrži objave izpostavljene vsaj
     min_reblogs_hint: Ne izbriše nobene od vaših objav, ki je bila vsaj tolikokrat podprta. Pustite prazno, če želite izbrisati objave ne glede na število izpostavitev
   stream_entries:
@@ -1707,12 +1754,13 @@ sl:
       title: Dobrodošli, %{name}!
   users:
     follow_limit_reached: Ne morete spremljati več kot %{limit} ljudi
+    go_to_sso_account_settings: Pojdite na nastavitve svojega računa ponudnika identitete
     invalid_otp_token: Neveljavna dvofaktorska koda
     otp_lost_help_html: Če ste izgubili dostop do obeh, stopite v stik z %{email}
     seamless_external_login: Prijavljeni ste prek zunanje storitve, tako da nastavitve gesla in e-pošte niso na voljo.
     signed_in_as: 'Vpisani kot:'
   verification:
-    explanation_html: '<strong>V metapodatkih svojega profila se lahko potrdite kot lastnik povezav</strong>. Za to mora povezano spletno mesto vsebovati povezavo do vašega Mastodon profila. Povezava <strong>mora</strong> imeti atribut <code>el="me"</code>. Vsebina besedila povezave ni pomembna. Tukaj je primer:'
+    explanation_html: '<strong>V metapodatkih svojega profila se lahko potrdite kot lastnik povezav</strong>. Za to mora povezano spletno mesto vsebovati povezavo do vašega profila Mastodon. Po dodajanju povezave se boste morda morali vrniti sem in ponovno shraniti svoj profil, da bo overjanje učinkovalo. Povezava <strong>mora</strong> imeti atribut <code>el="me"</code>. Vsebina besedila povezave ni pomembna. Tukaj je primer:'
     verification: Potrditev
   webauthn_credentials:
     add: Dodaj nov varnostni ključ
diff --git a/config/locales/sq.yml b/config/locales/sq.yml
index 8b46063a7..281ef7cb1 100644
--- a/config/locales/sq.yml
+++ b/config/locales/sq.yml
@@ -91,6 +91,7 @@ sq:
       moderation:
         active: Aktiv
         all: Krejt
+        disabled: I çaktivizuar
         pending: Pezull
         silenced: I kufizuar
         suspended: Të pezulluara
@@ -117,7 +118,7 @@ sq:
       reject: Hidhe tej
       rejected_msg: Aplikimi për regjistrim i %{username} u hodh poshtë me sukses
       remote_suspension_irreversible: Të dhënat e kësaj llogarie janë fshirë në mënyrë të pakthyeshme.
-      remote_suspension_reversible_hint_html: Llogaria është pezulluar në shërbyesit e tyre dhe të dhënat do të hiqen plotësisht më %{date}. Deri atëherë, shërbyesi i largët mund të rikthejë këtë llogari pa u cënuar. Nëse dëshironi të hiqen menjëherë të dhënat e llogarisë, mund ta bëni më poshtë.
+      remote_suspension_reversible_hint_html: Llogaria është pezulluar në shërbyesit e tyre dhe të dhënat do të hiqen plotësisht më %{date}. Deri atëherë, shërbyesi i largët mund të rikthejë këtë llogari pa u cenuar. Nëse dëshironi të hiqen menjëherë të dhënat e llogarisë, mund ta bëni më poshtë.
       remove_avatar: Hiqe avatarin
       remove_header: Hiqe kryen
       removed_avatar_msg: U hoq me sukses figura e avatarit të %{username}
@@ -133,6 +134,7 @@ sq:
       search: Kërkoni
       search_same_email_domain: Të tjerë përdorues me të njëjtën përkatësi email-i
       search_same_ip: Të tjerë përdorues me të njëjtën IP
+      security: Siguri
       security_measures:
         only_password: Vetëm fjalëkalim
         password_and_2fa: Fjalëkalim dhe 2FA
@@ -289,7 +291,7 @@ sq:
       empty: S’u gjetën regjistra.
       filter_by_action: Filtroji sipas veprimit
       filter_by_user: Filtroji sipas përdoruesit
-      title: Auditim regjistri
+      title: Regjistër auditimesh
     announcements:
       destroyed_msg: Lajmërimi u fshi me sukses!
       edit:
@@ -427,12 +429,13 @@ sq:
         resolve: Ftilloje përkatësinë
         title: Zë i ri email në listë bllokimesh
       no_email_domain_block_selected: S’u ndryshuan blloqe përkatësish email, ngaqë s’qe përzgjedhur ndonjë
+      not_permitted: Jo i lejuar
       resolved_dns_records_hint_html: Emri i përkatësisë jep u përket përkatësive vijuese MX, që janë përgjegjëset për pranim email-esh. Bllokimi i një përkatësie MX do të bllokojë regjistrime nga çfarëdo adrese email që përdor të njëjtën përkatësi MX, edhe nëse emri i dukshëm i përkatësisë është i ndryshëm. <strong>Jini i kujdesshëm të mos bllokoni shërbime të njohur email-esh.</strong>
       resolved_through_html: Zgjidhur përmes %{domain}
       title: Listë bllokimesh email-esh
     export_domain_allows:
       new:
-        title: Impotoni lejime përkatësish
+        title: Importoni lejime përkatësish
       no_file: S’u përzgjodh kartelë
     export_domain_blocks:
       import:
@@ -441,6 +444,7 @@ sq:
         private_comment_description_html: 'Për t’ju ndihmuar të ndiqni se nga vijnë bllokimet e importuara, këto do të krijohen me komentin vijues privat: <q>%{comment}</q>'
         private_comment_template: Importuar nga %{source} më %{date}
         title: Importoni bllokime përkatësish
+      invalid_domain_block: 'U anashkalua një ose më tepër bllokime përkatësish, për shkak të gabimit(eve) vijues: %{error}'
       new:
         title: Importoni bllokime përkatësish
       no_file: S’u përzgjodh kartelë
@@ -471,6 +475,7 @@ sq:
       content_policies:
         comment: Shënim i brendshëm
         description_html: Mund të përkufizoni rregulla lënde që do të zbatohen mbi krejt llogaritë prej kësaj përkatësie dhe cilësdo nënpërkatësi të saj.
+        limited_federation_mode_description_html: Mund të zgjidhni të lejohet ose jo federimi me këtë përkatësi.
         policies:
           reject_media: Mos prano media
           reject_reports: Hidh tej raportimet
@@ -574,19 +579,23 @@ sq:
         mark_as_sensitive_description_html: Medias te postimet e raportuara do t’i vihet shenjë si me spec dhe për të do të regjistrohet një paralajmërim, për t’ju ndihmuar të përshkallëzoni masat tuaja mbi shkelje të ardhshme nga e njëjta llogari.
         other_description_html: Shihni më tepër mundësi për kontroll të sjelljes së një llogari dhe përshtatni komunikimin me llogarinë e raportuar.
         resolve_description_html: Ndaj llogarisë së raportuar nuk do të ndërmerret ndonjë veprim, s’do të regjistrohet ndonjë paralajmërim dhe raporti do të mbyllet.
-        silence_description_html: Profili do të jetë i dukshëm vetëm për ata që e ndjekin tashmë, ose që e kërkojnë dorazi, duke reduktuar rëndë përhapjen e tij. Mundet përherë të prapakthehet.
-        suspend_description_html: Profili dhe krej lënda e tij do të bëhen të papërdorshëm, deri sa më në fund të fshihet. Ndërveprimi me llogarinë do të jetë i pamundur. E prapakthyeshme brenda 30 ditësh.
+        silence_description_html: Llogaria do të jetë e dukshme vetëm për ata që e ndjekin tashmë, ose e kërkojnë atë dorazi, duke kufizuar rëndë shtrirjen e saj. Kjo mundet përherë të prapakthehet. Mbyll krejt raportimet kundër kësaj llogarie.
+        suspend_description_html: Llogaria dhe krejt lënda e saj s’do të jenë të përdorshme dhe, së fundi, do të fshihen dhe ndërveprimi me te do të jetë i pamundur. E prapakthyeshme brenda 30 ditësh. Mbyll krejt raportimet kundër kësaj llogarie.
       actions_description_html: Vendosni cili veprim të kryhet për të zgjidhur këtë raportim. Nëse ndërmerrni një veprim ndëshkues kundër llogarisë së raportuar, atyre do t’u dërgohet një njoftim me email, hiq rastin kur përzgjidhet kategoria <strong>I padëshiruar</strong>.
+      actions_description_remote_html: Vendosni cili veprim të ndërmerret për zgjidhjen e këtij raportimi. Kjo do të prekë vetëm mënyrën se si shërbyesi <strong>juaj</strong> komunikon me këtë llogari të largët dhe se si e trajtojnë lëndën e saj.
       add_to_report: Shtoni më tepër te raportimi
       are_you_sure: A jeni i sigurt?
       assign_to_self: Caktojani vetes
       assigned: Iu caktua moderator
       by_target_domain: Përkatësi e llogarisë së raportuar
+      cancel: Anuloje
       category: Kategori
       category_description_html: Arsyeja pse kjo llogari dhe/ose lëndë raportohet do të citohet te komunikimi me llogarinë e raportuar
       comment:
         none: Asnjë
       comment_description_html: 'Për të dhënë më tepër informacion, %{name} shkroi:'
+      confirm: Ripohojeni
+      confirm_action: Ripohoni veprim moderimi kundër @%{acct}
       created_at: Raportuar më
       delete_and_resolve: Fshiji postimet
       forwarded: U përcoll
@@ -603,6 +612,7 @@ sq:
         placeholder: Përshkruani ç’veprime janë ndërmarrë, ose çfarëdo përditësimi tjetër që lidhet me të…
         title: Shënime
       notes_description_html: Shihni dhe lini shënime për moderatorët e tjerë dhe për veten në të ardhmen
+      processed_msg: 'Raportimi #%{id} u përpunua me sukses'
       quick_actions_description_html: 'Kryeni një veprim të shpejtë, ose rrëshqitni poshtë për të parë lëndën e raportuar:'
       remote_user_placeholder: përdoruesi i largët prej %{instance}
       reopen: Rihape raportimin
@@ -615,9 +625,28 @@ sq:
       status: Gjendje
       statuses: Lëndë e raportuar
       statuses_description_html: Lënda problematike do të citohet në komunikimin me llogarinë e raportuar
+      summary:
+        action_preambles:
+          delete_html: 'Ju ndan një hap nga <strong>heqja</strong> e disa postimeve të <strong>@%{acct}</strong>. Kjo do të sjellë:'
+          mark_as_sensitive_html: 'Ju ndan një hap nga <strong>vënia shenjë</strong> disa postimeve të <strong>@%{acct}</strong> si <strong>me spec</strong>. Kjo do të sjellë:'
+          silence_html: 'Ju ndan një hap nga <strong>kufizimi</strong> i llogarisë së <strong>@%{acct}</strong>. Kjo do të sjellë:'
+          suspend_html: 'Ju ndan një hap nga <strong>pezullimi</strong> i llogarisë së <strong>@%{acct}</strong>. Kjo do të sjellë:'
+        actions:
+          delete_html: Hiqi postimet fyese
+          mark_as_sensitive_html: Vëru shenjë si me spec mediave të postimeve fyese
+          silence_html: Kufizoje fort shtrirjen e <strong>@%{acct}</strong>, duke e bërë profilin dhe lëndën e tij të dukshme vetëm për persona që e ndjekin tashmë, ose që kërkojnë dorazi për profilin e tij
+          suspend_html: Pezulloje <strong>@%{acct}</strong>, duke e bërë profilin dhe lëndën e tij të pahapshme dhe të pamundur ndërveprimin me të
+        close_report: 'Vëri shenjë raportimit #%{id} si të zgjidhur'
+        close_reports_html: Vëru shenjë <strong>krejt</strong> raportimeve kundër <strong>@%{acct}</strong> si të zgjidhur
+        delete_data_html: Fshije profilin e <strong>@%{acct}</strong> dhe lëndën e 30 ditëve nga sot, veç në u pezulloftë ndërkohë
+        preview_preamble_html: "<strong>@%{acct}</strong> do të marrë një sinjalizim me lëndën vijuese:"
+        record_strike_html: Regjistroni një vërejtje kundër <strong>@%{acct}</strong> për t’ju ndihmuar të përshkallëzoni qëndrim në ras cenimesh të ardhshme nga kjo llogari
+        send_email_html: Dërgojini <strong>@%{acct}</strong> një vërejtje me email
+        warning_placeholder: Arsye shtesë, në daçi, për veprimin e moderimit.
       target_origin: Origjinë e llogarisë së raportuar
       title: Raportime
       unassign: Hiqja
+      unknown_action_msg: 'Veprim i panjohur: %{action}'
       unresolved: Të pazgjidhur
       updated_at: U përditësua më
       view_profile: Shihni profilin
@@ -645,7 +674,7 @@ sq:
         delete_user_data: Të Fshijë të Dhëna Përdoruesi
         delete_user_data_description: U lejon përdoruesve të fshijnë pa humbur kohë të dhëna përdoruesish të tjerë
         invite_users: Të Ftojë Përdorues
-        invite_users_description: U lejon përdruesve të ftojë te shërbyesi persona të rinj
+        invite_users_description: U lejon përdoruesve të ftojë te shërbyesi persona të rinj
         manage_announcements: Të Administrojë Njoftime
         manage_announcements_description: U lejon përdoruesve të administrojë njoftime te shërbyesi
         manage_appeals: Të Administrojë Apelime
@@ -654,7 +683,7 @@ sq:
         manage_blocks_description: U lejon përdoruesve të bllokojnë shërbime email dhe adresa IP
         manage_custom_emojis: Të Administrojë Emoxhi Vetjake
         manage_custom_emojis_description: U lejon përdoruesve të administrojnë te shërbyesi emoxhi vetjake
-        manage_federation: Të Administrjë Federim
+        manage_federation: Të Administrojë Federim
         manage_federation_description: U lejon përdoruesve të bllokojnë ose lejojnë federim me përkatësi të tjera dhe të kontrollojnë shpërndarjen
         manage_invites: Të Administrojë Ftesa
         manage_invites_description: U lejon përdoruesve të shfletojnë dhe çaktivizojnë lidhje ftesash
@@ -683,7 +712,7 @@ sq:
     rules:
       add_new: Shtoni rregull
       delete: Fshije
-      description_html: Edhe pse shumica pretendon se kanë lexuar dhe pajtohen me kushtet e shërbimit, zakonisht njerëzit nuk e lexojnë nga fillimi në fund, deri kur del një problem. <strong>Bëjeni më të lehtë parjen e rregullave të shërbyesit tuaj me një vështim, duke i dhënë në një listë të thjeshtë me pika.</strong> Përpiquni që rregullat të jenë secili të shkurtër dhe të thjeshtë, por as mos u përpiqni t’i ndani në shumë zëra të veçantë.
+      description_html: Edhe pse shumica pretendon se kanë lexuar dhe pajtohen me kushtet e shërbimit, zakonisht njerëzit nuk e lexojnë nga fillimi në fund, deri kur del një problem. <strong>Bëjeni më të lehtë parjen e rregullave të shërbyesit tuaj me një vështrim, duke i dhënë në një listë të thjeshtë me pika.</strong> Përpiquni që rregullat të jenë secili të shkurtër dhe të thjeshtë, por as mos u përpiqni t’i ndani në shumë zëra të veçantë.
       edit: Përpunoni rregull
       empty: S’janë përcaktuar ende rregulla shërbyesi.
       title: Rregulla shërbyesi
@@ -710,6 +739,8 @@ sq:
         preamble: Shpërfaqja e lëndës interesante është me rëndësi kyçe për mirëseardhjen e përdoruesve të rinj që mund të mos njohin njeri në Mastodon. Kontrolloni se si funksionojnë në shërbyesin tuaj veçori të ndryshme zbulimi.
         profile_directory: Drejtori profilesh
         public_timelines: Rrjedha kohore publike
+        publish_discovered_servers: Publiko shërbyes të njohur
+        publish_statistics: Publiko statistika
         title: Zbulim
         trends: Në modë
       domain_blocks:
@@ -764,6 +795,7 @@ sq:
         suspend: "%{name} e pezulloi llogarinë e %{target}"
       appeal_approved: Apeluar
       appeal_pending: Apelim pezull
+      appeal_rejected: Apelimi u hodh poshtë
     system_checks:
       database_schema_check:
         message_html: Ka migrime bazash të dhënash pezull. Ju lutemi, kryejini, për të qenë të sigurt se aplikacioni sillet siç priteet
@@ -777,6 +809,12 @@ sq:
         message_html: S’keni përcaktuar ndonjë rregull shërbyesi.
       sidekiq_process_check:
         message_html: S’ka proces Sidekiq në punë për %{value} radhë. Ju lutemi, shqyrtoni formësimin tuaj për Sidekiq-un
+      upload_check_privacy_error:
+        action: Për më tepër hollësi, shihni këtu
+        message_html: "<strong>Shërbyesi juaj është formësuar keq. Privatësia e përdoruesve tuaj është në rrezik.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Për më tepër hollësi, shihni këtu
+        message_html: "<strong>Depozita juaj e objekteve është e formësuar keq. Privatësia e përdoruesve tuaj është në rrezik.</strong>"
     tags:
       review: Gjendje rishikimi
       updated_msg: Rregullimet për hashtag-ët u përditësuan me sukses
@@ -908,7 +946,7 @@ sq:
     remove: Hiqe aliasin
   appearance:
     advanced_web_interface: Ndërfaqe web e thelluar
-    advanced_web_interface_hint: 'Nëse doni të shfrytëzoni krejt gjerësinë e ekranit tuaj, ndërfaqja e thelluar web ju lejon të formësoni shumë shtylla për të parë aq më tepër të dhëna në të njëjtën kohë sa doni: Kreu, njoftime, rrjedhë kohore të federuarash, çfarëdo numri listash dhe hashtag-ësh.'
+    advanced_web_interface_hint: 'Nëse doni të shfrytëzoni krejt gjerësinë e ekranit tuaj, ndërfaqja e thelluar web ju lejon të formësoni shumë shtylla për të parë në të njëjtën kohë aq hollësi sa doni: Kreu, njoftime, rrjedhë kohore të federuarash, çfarëdo numri listash dhe hashtag-ësh.'
     animations_and_accessibility: Animacione dhe përdorim nga persona me aftësi të kufizuara
     confirmation_dialogs: Dialogë ripohimesh
     discovery: Zbulim
@@ -928,6 +966,7 @@ sq:
   applications:
     created: Aplikimi u krijua me sukses
     destroyed: Aplikimi u fshi me sukses
+    logout: Dalje
     regenerate_token: Riprodho token hyrjesh
     token_regenerated: Token-i i hyrjeve u riprodhua me sukses
     warning: Bëni shumë kujdes me ato të dhëna. Mos ia jepni kurrë njeriu!
@@ -935,6 +974,8 @@ sq:
   auth:
     apply_for_account: Kërkoni një llogari
     change_password: Fjalëkalim
+    confirmations:
+      wrong_email_hint: Nëse ajo adresë email s’është e saktë, mund ta ndryshoni te rregullimet e llogarisë.
     delete_account: Fshije llogarinë
     delete_account_html: Nëse dëshironi të fshihni llogarinë tuaj, mund <a href="%{path}">ta bëni që këtu</a>. Do t’ju kërkohet ta ripohoni.
     description:
@@ -962,6 +1003,8 @@ sq:
     resend_confirmation: Ridërgo udhëzime ripohimi
     reset_password: Ricaktoni fjalëkalimin
     rules:
+      accept: Pranoje
+      back: Mbrapsht
       preamble: Këto vendosen dhe zbatimi i tyre është nën kujdesin e moderatorëve të %{domain}.
       title: Disa rregulla bazë.
     security: Siguri
@@ -1025,7 +1068,7 @@ sq:
       x_months: "%{count}mj"
       x_seconds: "%{count}s"
   deletes:
-    challenge_not_passed: Të dhënat që dhatë s’qenë të sakta
+    challenge_not_passed: Hollësitë që dhatë s’qenë të sakta
     confirm_password: Që të verifikohet identiteti juaj, jepni fjalëkalimin tuaj të tanishëm
     confirm_username: Që të ripohohet procedura, jepni emrin tuaj të përdoruesit
     proceed: Fshini llogarinë
@@ -1054,7 +1097,7 @@ sq:
       approve_appeal: Miratoni apelimin
       associated_report: Raportimi i përshoqëruar
       created_at: Datuar
-      description_html: Këto janë veprime të ndërmara kundër llogarisë tuaj dhe sinjalizime që ju janë dërguar nga stafi i %{instance}.
+      description_html: Këto janë veprime të ndërmarra kundër llogarisë tuaj dhe sinjalizime që ju janë dërguar nga stafi i %{instance}.
       recipient: Drejtuar
       reject_appeal: Hidheni poshtë apelimin
       status: "#%{id} postimi"
@@ -1109,7 +1152,7 @@ sq:
   featured_tags:
     add_new: Shtoni të re
     errors:
-      limit: Keni përdorur tashmë si të zgjedhur sasinë maksimum të hashtag-ëve
+      limit: Keni përdorur tashmë numrin maksimum të hashtag-ëve
     hint_html: "<strong>Çfarë janë hashtag-ët e zgjedhur?</strong> Këta shfaqen dukshëm te profili juaj publik dhe u lejojnë të tjerëve të shfletojnë postime tuajt publikë posaçërisht nën këta hashtag-ë. Janë një mjet i goditur për të ndjekur punë krijuese ose projekte afatgjata."
   filters:
     contexts:
@@ -1153,8 +1196,6 @@ sq:
       index:
         hint: Ky filtër aplikohet për të përzgjedhur postime individuale, pavarësisht kriteresh të tjera. Që nga ndërfaqja web mund të shtoni më tepër postime te ky filtër.
         title: Postime të filtruar
-  footer:
-    trending_now: Prirjet e tashme
   generic:
     all: Krejt
     all_items_on_page_selected_html:
@@ -1177,8 +1218,6 @@ sq:
     validation_errors:
       one: Diçka s’është ende si duhet! Ju lutemi, shqyrtoni gabimin më poshtë
       other: Diçka s’është ende si duhet! Ju lutemi, shqyrtoni %{count} gabimet më poshtë
-  html_validator:
-    invalid_markup: 'përmban elementë HTML të pavlefshëm: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Kartelë CSV e pavlefshme. Gabim: %{error}'
@@ -1221,7 +1260,7 @@ sq:
     title: Ftoni njerëz
   lists:
     errors:
-      limit: Keni mbërritur në numrin maksimum të sasisë së listave
+      limit: Keni mbërritur në numrin maksimum të listave
   login_activities:
     authentication_methods:
       otp: aplikacion mirëfilltësimi dyfaktorësh
@@ -1362,7 +1401,11 @@ sq:
       unrecognized_emoji: s’është emotikon i pranuar
   relationships:
     activity: Veprimtari llogarie
+    confirm_follow_selected_followers: Jeni i sigurt se doni të ndiqet ndjekësit e përzgjedhur?
+    confirm_remove_selected_followers: Jeni i sigurt se doni të hiqen ndjekësit e përzgjedhur?
+    confirm_remove_selected_follows: Jeni i sigurt se doni të hiqen ndjekjet e përzgjedhura?
     dormant: Në gjumë
+    follow_failure: S’u ndoqën dot disa nga llogaritë e përzgjedhura.
     follow_selected_followers: Ndiq ndjekësit e përzgjedhur
     followers: Ndjekës
     following: Ndjek
@@ -1402,6 +1445,7 @@ sq:
       electron: Electron
       firefox: Firefox
       generic: Shfletues i panjohur
+      huawei_browser: Shfletues Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Shfletues Nokia S40 Ovi
@@ -1411,6 +1455,7 @@ sq:
       qq: QQ Browser
       safari: Safari
       uc_browser: Shfletues UC
+      unknown_browser: Shfletues i Panjohur
       weibo: Weibo
     current_session: Sesioni i tanishëm
     description: "%{browser} në %{platform}"
@@ -1423,9 +1468,10 @@ sq:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: platformë e panjohur
+      unknown_platform: Platformë e Panjohur
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1538,7 +1584,7 @@ sq:
       '7889238': 3 muaj
     min_age_label: Prag moshe
     min_favs: Mbaji postimet e parapëlqyera më shumë se
-    min_favs_hint: Nuk fshihet ndonjë nga postimet tuaja që kanë marrë më shumë se sa ky numër parapëlqimesh. Lëreni të zbrazët për të fshirë postimet, pavarësisht të numrit të parapëlqimeve për to
+    min_favs_hint: Mos fshini ndonjë nga postimet tuaja që kanë marrë të paktën këtë numër si të parapëlqyer. Lëreni të zbrazët, që të fshihen postime pavarësisht nga numri se sa herë janë të parapëlqyer
     min_reblogs: Mbaji postimet e përforcuara më shumë se
     min_reblogs_hint: Nuk fshihet ndonjë nga postimet tuaja që kanë marrë më shumë se sa ky numër përforcimesh. Lëreni të zbrazët për të fshirë postimet, pavarësisht të numrit të përforcimeve për to
   stream_entries:
@@ -1572,7 +1618,7 @@ sq:
     otp: Aplikacion mirëfilltësimesh
     recovery_codes: Kopjeruani kode rikthimesh
     recovery_codes_regenerated: Kodet e rikthimeve u riprodhuan me sukses
-    recovery_instructions_html: Në ndodhtë që të humbni hyrje te telefoni juaj, mund të përdorni një nga kodet e rikthimit më poshtë, që të rifitoni hyrje te llogaria juaj. <strong>Mbajini të parrezikuar kodet e rikthimeve</strong>. Për shembull, mund t’i shtypni dhe t’i ruani tok me dokumente të tjerë të rëndësishëm.
+    recovery_instructions_html: Në ndodhtë që të humbni hyrje te telefoni juaj, mund të përdorni një nga kodet e rikthimit më poshtë, që të rifitoni hyrje te llogaria juaj. <strong>Mbajini të parrezik kodet e rikthimeve</strong>. Për shembull, mund t’i shtypni dhe t’i ruani tok me dokumente të tjerë të rëndësishëm.
     webauthn: Kyçe sigurie
   user_mailer:
     appeal_approved:
@@ -1623,7 +1669,7 @@ sq:
         disable: Llogari e ngrirë
         mark_statuses_as_sensitive: Postimeve iu vu shenjë si me spec
         none: Sinjalizim
-        sensitive: Llogarice iu vu shenjë si me spec
+        sensitive: Llogarisë iu vu shenjë si me spec
         silence: Llogari e kufizuar
         suspend: Llogari e pezulluar
     welcome:
@@ -1638,12 +1684,13 @@ sq:
       title: Mirë se vini, %{name}!
   users:
     follow_limit_reached: S’mund të ndiqni më tepër se %{limit} persona
+    go_to_sso_account_settings: Kaloni te rregullime llogarie te shërbimi juaj i identitetit
     invalid_otp_token: Kod dyfaktorësh i pavlefshëm
     otp_lost_help_html: Nëse humbët hyrjen te të dy, mund të lidheni me %{email}
     seamless_external_login: Jeni futur përmes një shërbimi të jashtëm, ndaj s’ka rregullime fjalëkalimi dhe email.
     signed_in_as: 'I futur si:'
   verification:
-    explanation_html: 'Mundeni <strong>të verifikoni veten si i zoti i lidhjeve te tejtëdhënat e profilit tuaj</strong>. Për këtë, sajti i lidhur duhet të përmbajë një lidhje për te profili juaj Mastodon. Lidhje për te ajo <strong>duhet</strong> të ketë një atribut <code>rel="me"</code>. Lënda tekst e lidhjes nuk ngre peshë. Ja një shembull:'
+    explanation_html: 'Mundeni të <strong>verifikoni veten si i zoti i lidhjeve që nga tejtëdhëna të profilit tuaj</strong>. Për këtë, sajti i lidhur duhet të përmbajë një lidhje që shpie te profili juaj Mastodon. Pas shtimit të lidhjes mund t’ju duhet të ktheheni këtu dhe të riruani profilin tuaj, që verifikimi të ketë efekt. Lidhja e kthimit <strong>duhet</strong> të përmbajë një atribut <code>rel="me"</code>. Teksi i lidhjes s’ka rëndësi. Ja një shembull:'
     verification: Verifikim
   webauthn_credentials:
     add: Shtoni kyç të ri sigurie
diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml
index dc62f2220..a2e860ef6 100644
--- a/config/locales/sr-Latn.yml
+++ b/config/locales/sr-Latn.yml
@@ -1,237 +1,1269 @@
 ---
 sr-Latn:
   about:
-    about_mastodon_html: Mastodont je društvena mreža bazirana na otvorenim protokolima i slobodnom softveru otvorenog koda. Decentralizovana je kao što je decentralizovana e-pošta.
+    about_mastodon_html: 'Društvena mreža budućnosti: bez reklama, bez korporativnog praćenja, etički dizajn, i decentralizacija! Posedujte svoje podatke sa Mastodonom!'
     contact_missing: Nije postavljeno
-    hosted_on: Mastodont hostovan na %{domain}
+    contact_unavailable: Nije dostupno
+    hosted_on: Mastodon hostovan na %{domain}
+    title: O instanci
   accounts:
-    nothing_here: Ovde nema ništa!
+    follow: Zaprati
+    followers:
+      few: Pratioca
+      one: Pratilac
+      other: Pratilaca
+    following: Pratim
+    instance_actor_flash: Ovaj nalog je virtuelni akter koji ne predstavlja nijednog korisnika lično, već sâm server. Koristi se u svrhu federacije i ne treba ga suspendovati.
+    last_active: najskorija aktivnost
+    link_verified_on: Vlasništvo nad ovom vezom je provereno %{date}
+    nothing_here: Ovde nema ničega!
+    pin_errors:
+      following: Morate pratiti osobu koju želite da preporučite
+    posts:
+      few: Objave
+      one: Objava
+      other: Objava
+    posts_tab_heading: Objave
   admin:
+    account_actions:
+      action: Izvršite radnju
+      title: Izvršite moderatorske radnje na %{acct}
     account_moderation_notes:
-      create: Napravi
+      create: Ostavite belešku
       created_msg: Moderatorska beleška uspešno napravljena!
       destroyed_msg: Moderatorska beleška uspešno obrisana!
     accounts:
+      add_email_domain_block: Blokiraj domen e-pošte
+      approve: Odobri
+      approved_msg: Zahtev za registraciju korisnika %{username} je uspešno odobren
       are_you_sure: Da li ste sigurni?
+      avatar: Avatar
       by_domain: Domen
+      change_email:
+        changed_msg: E-pošta naloga uspešno promenjena!
+        current_email: Trenutna adresa e-pošte
+        label: Promenite adresu e-pošte
+        new_email: Nova adresa e-pošte
+        submit: Promenite adresu e-pošte
+        title: Promenite adresu e-pošte za %{username}
+      change_role:
+        changed_msg: Uloga uspešno promenjena!
+        label: Promeni ulogu
+        no_role: Nema ulogu
+        title: Promeni ulogu za %{username}
       confirm: Potvrdi
       confirmed: Potvrđeno
-      confirming: Potvrđujući
+      confirming: Potvrđivanje
+      custom: Proizvoljno
+      delete: Obriši podatke
+      deleted: Izbrisano
       demote: Ražaluj
-      disable: Isključi
+      destroyed_msg: Podaci korisnika %{username} su nepovratno stavljeni u red za brisanje
+      disable: Zamrzni
+      disable_sign_in_token_auth: Onemogući imejl autentifikaciju
       disable_two_factor_authentication: Isključi 2FA
-      disabled: Isključena
-      display_name: Prikazano ime
+      disabled: Zamrznut
+      display_name: Ime za prikaz
       domain: Domen
       edit: Izmeni
       email: E-pošta
       email_status: Status e-pošte
-      enable: Uključi
-      enabled: Uključeno
+      enable: Omogući
+      enable_sign_in_token_auth: Omogući imejl autentifikaciju
+      enabled: Omogućen
+      enabled_msg: Uspešno odleđen nalog korisnika %{username}
       followers: Pratioci
       follows: Praćeni
+      header: Zaglavlje
       inbox_url: Adresa sandučeta
+      invite_request_text: Razlozi za pridruživanje
+      invited_by: Pozvan od strane
+      ip: IP
+      joined: Pridružio/-la se
       location:
         all: Sve
-        local: Lokalne
-        remote: Udaljene
+        local: Lokalni
+        remote: Udaljeni
         title: Lokacija
       login_status: Status prijave
       media_attachments: Multimedijalni prilozi
       memorialize: Prebaci u in memoriam
+      memorialized: Podignut spomenik
+      memorialized_msg: "%{username} je uspešno pretvoren u memorijalni nalog"
       moderation:
+        active: Aktivan
         all: Svi
+        disabled: Isključen
+        pending: Na čekanju
+        silenced: Ograničeno
         suspended: Suspendovani
         title: Moderacija
       moderation_notes: Moderatorske beleške
       most_recent_activity: Najskorija aktivnost
       most_recent_ip: Najskorija IP adresa
+      no_account_selected: Nijedan nalog nije promenjen jer nijedan nije izabran
+      no_limits_imposed: Nema ograničenja
+      no_role_assigned: Nijedna uloga nije dodeljena
       not_subscribed: Nije pretplaćen
-      perform_full_suspension: Izvrši kompletno isključenje
+      pending: Čeka na pregled
+      perform_full_suspension: Suspenduj
+      previous_strikes: Prethodni prekršaji
+      previous_strikes_description_html:
+        few: Ovaj nalog ima <strong>%{count}</strong> prekršaja.
+        one: Ovaj nalog ima <strong>jedan</strong> prekršaj.
+        other: Ovaj nalog ima <strong>%{count}</strong> prekršaja.
       promote: Unapredi
       protocol: Protokol
       public: Javno
-      redownload: Osveži avatar
-      remote_suspension_reversible_hint_html: Налог је суспендован на њиховом серверу, а подаци ће бити у потпуности уклоњени %{date}. До тада, удаљени сервер може вратити овај налог без икаквих негативних ефеката. Ако желите одмах да уклоните све податке налога, то можете учинити у наставку.
+      push_subscription_expires: PuSH pretplata ističe
+      redownload: Osveži profil
+      redownloaded_msg: Uspešno osvežen profil korisnika %{username} iz izvora
+      reject: Odbij
+      rejected_msg: Zahtev za registraciju korisnika %{username} je uspešno odbijen
+      remote_suspension_irreversible: Podaci ovog naloga su nepovratno izbrisani.
+      remote_suspension_reversible_hint_html: Nalog je suspendovan sa svog servera i njegovi podaci će biti izbrisani datuma %{date}. Do tada, udaljeni server može da vrati ovaj nalog bez ikakvih promena. Ukoliko želite da odmah obrišete sve podatke naloga, možete to učiniti ispod.
+      remove_avatar: Ukloni avatar
+      remove_header: Odstrani zaglavlje
+      removed_avatar_msg: Slika avatara korisnika %{username} je uspešno uklonjena
+      removed_header_msg: Uspešno obrisana slika zaglavlja korisnika %{username}
       resend_confirmation:
         already_confirmed: Ovaj korisnik je već potvrđen
-        send: Ponovo pošaljite e-poruku za potvrdu
-        success: E-mail potvrde je uspešno poslat!
+        send: Ponovo pošalji imejl potvrdu
+        success: Imejl za potvrdu je uspešno poslat!
       reset: Resetuj
       reset_password: Resetuj lozinku
       resubscribe: Ponovo se pretplati
+      role: Uloga
       search: Pretraga
+      search_same_email_domain: Ostali korisnici sa istim domenom e-pošte
+      search_same_ip: Ostali korisnici sa istom IP adresom
+      security: Bezbednost
+      security_measures:
+        only_password: Samo lozinka
+        password_and_2fa: Lozinka i dvofaktorska autentifikacija
+      sensitive: Označi kao osetljiv
+      sensitized: Označeno kao osetljivo
       shared_inbox_url: Adresa deljenog sandučeta
       show:
-        created_reports: Prijave koje je napravio ovaj nalog
-        targeted_reports: Prijave napravljene o ovom nalogu
+        created_reports: Podnete prijave
+        targeted_reports: Prijave od strane drugih
       silence: Ućutkaj
-      statuses: Statusi
+      silenced: Ućutkan
+      statuses: Objave
+      strikes: Prethodni prestupi
       subscribe: Pretplati se
+      suspend: Suspenduj
+      suspended: Suspendovani
+      suspension_irreversible: Podaci ovog naloga su nepovratno izbrisani. Možete odsuspendovati ovaj nalog čime će on postati upotrebljiv, ali se podaci prethodno sadržani na nalogu neće vratiti.
+      suspension_reversible_hint_html: Nalog je suspendovan i njegovi podaci će biti izbrisani datuma %{date}. Do tada, ovaj nalog može biti vraćen bez ikakvih promena. Ukoliko želite da odmah obrišete sve podatke naloga, možete to učiniti ispod.
       title: Nalozi
+      unblock_email: Odblokiraj adresu e-pošte
+      unblocked_email_msg: Uspešno odblokirana imejl adresa korisnika %{username}
+      unconfirmed_email: Nepotvrđena adresa e-pošte
+      undo_sensitized: Ukloni oznaku „osetljiv”
       undo_silenced: Ukini ćutanje
       undo_suspension: Ukini suspenziju
+      unsilenced_msg: Uspešno poništeno ograničenje naloga %{username}
       unsubscribe: Ukini pretplatu
+      unsuspended_msg: Uspešno poništena suspenzija naloga %{username}
       username: Korisničko ime
+      view_domain: Pročitaj opis domena
+      warn: Upozori
       web: Veb
+      whitelisted: Dozvoljena federacija
     action_logs:
+      action_types:
+        approve_appeal: Uvaži žalbu
+        approve_user: Odobri korisnika
+        assigned_to_self_report: Dodeli prijavu
+        change_email_user: Promeni e-adresu korisnika
+        change_role_user: Promeni ulogu korisnika
+        confirm_user: Potvrdi korisnika
+        create_account_warning: Kreiraj upozorenje
+        create_announcement: Kreiraj najavu
+        create_canonical_email_block: Kreiraj blok e-pošte
+        create_custom_emoji: Napravi prilagođeni emodži
+        create_domain_allow: Dodaj dozvoljeni domen
+        create_domain_block: Dodaj blokirani domen
+        create_email_domain_block: Blokiraj imejl domen
+        create_ip_block: Napravi IP uslov
+        create_unavailable_domain: Dodaj domen kao nedostupan
+        create_user_role: Kreiraj ulogu
+        demote_user: Smanji ovlašćenja korisnika
+        destroy_announcement: Izbriši najavu
+        destroy_canonical_email_block: Izbriši blok e-pošte
+        destroy_custom_emoji: Obriši prilagođeni emodži
+        destroy_domain_allow: Obriši dozvoljeni domen
+        destroy_domain_block: Obriši blokirani domen
+        destroy_email_domain_block: Izbriši blok e-pošte
+        destroy_instance: Očisti domen
+        destroy_ip_block: Obriši IP uslov
+        destroy_status: Izbriši post
+        destroy_unavailable_domain: Obriši nedostupan domen
+        destroy_user_role: Uništi poziciju
+        disable_2fa_user: Onemogući dvofaktorsku autentifikaciju
+        disable_custom_emoji: Onemogući prilagođene emodžije
+        disable_sign_in_token_auth_user: Onemogući imejl autentifikaciju za korisnika
+        disable_user: Onemogući korisnika
+        enable_custom_emoji: Omogući prilagođene emodžije
+        enable_sign_in_token_auth_user: Omogući imejl autentifikaciju za korisnika
+        enable_user: Omogući korisnika
+        memorialize_account: Pretvori u memorijalni nalog
+        promote_user: Unapredi korisnika
+        reject_appeal: Odbij žalbu
+        reject_user: Odbaci korisnika
+        remove_avatar_user: Ukloni avatar
+        reopen_report: Ponovo otvori prijavu
+        resend_user: Ponovo pošalji mejl za potvrdu
+        reset_password_user: Resetuj lozinku
+        resolve_report: Zatvori prijavu
+        sensitive_account: Označi nalog kao osetljiv
+        silence_account: Ograniči nalog
+        suspend_account: Obustavi nalog
+        unassigned_report: Povuci prijavu
+        unblock_email_account: Odblokiraj imejl adresu
+        unsensitive_account: Ukloni dodeljenu oznaku „osetljiv”
+        unsilence_account: Povuci ograničenje naloga
+        unsuspend_account: Povuci suspenziju naloga
+        update_announcement: Saopštenje o ažuriranju
+        update_custom_emoji: Ažuriraj prilagođene emodžije
+        update_domain_block: Ažuriraj domen blok
+        update_ip_block: Ažuriraj IP uslov
+        update_status: Uredi objavu
+        update_user_role: Uredi ulogu
+      actions:
+        approve_appeal_html: "%{name} je uvažio žalbu korisnika %{target} na odluku moderatora"
+        approve_user_html: "%{name} je odobrio/-la registraciju korisnika %{target}"
+        assigned_to_self_report_html: "%{name} je sebi dodelio/-la prijavu %{target}"
+        change_email_user_html: "%{name} je promenio/-la imejl adresu korisnika %{target}"
+        change_role_user_html: "%{name} je promenio/-la ovlašćenja korisnika %{target}"
+        confirm_user_html: "%{name} je potvrdio/-la imejl adresu korisnika %{target}"
+        create_account_warning_html: "%{name} je poslao/-la upozorenje korisniku %{target}"
+        create_announcement_html: "%{name} je napravio/-la novo saopštenje %{target}"
+        create_canonical_email_block_html: "%{name} je blokirao/-la imejl adresu sa hešom %{target}"
+        create_custom_emoji_html: "%{name} je otpremio/-la nove emodžije %{target}"
+        create_domain_allow_html: "%{name} je dozvolio/-la federaciju sa domenom %{target}"
+        create_domain_block_html: "%{name} je blokirao/-la domen %{target}"
+        create_email_domain_block_html: "%{name} je blokirao/-la imejl domen %{target}"
+        create_ip_block_html: "%{name} je napravio/-la uslov za IP adrese %{target}"
+        create_unavailable_domain_html: "%{name} je obustavio/-la isporučivanje domenu %{target}"
+        create_user_role_html: "%{name} je napravio/-la %{target} poziciju"
+        demote_user_html: "%{name} je smanjio ovlašćenja korisnika %{target}"
+        destroy_announcement_html: "%{name} je obrisao/-la saopštenje %{target}"
+        destroy_canonical_email_block_html: "%{name} je odblokirao/-la imejl adresu sa hešom %{target}"
+        destroy_custom_emoji_html: "%{name} je obrisao/-la emodži %{target}"
+        destroy_domain_allow_html: "%{name} je zabranio/-la federaciju sa domenom %{target}"
+        destroy_domain_block_html: "%{name} je odblokirao/-la domen %{target}"
+        destroy_email_domain_block_html: "%{name} je odblokirao/-la imejl domen %{target}"
+        destroy_instance_html: "%{name} je očistio/-la domen %{target}"
+        destroy_ip_block_html: "%{name} je obrisao/-la uslov za IP adrese %{target}"
+        destroy_status_html: "%{name} je izbrisao/-la objavu korisnika %{target}"
+        destroy_unavailable_domain_html: "%{name} je ponovo uspostavio/-la isporučivanje domenu %{target}"
+        destroy_user_role_html: "%{name} je izbrisao/-la %{target} poziciju"
+        disable_2fa_user_html: "%{name} je onemogućio/-la dvofaktorsku autentifikaciju za korisnika %{target}"
+        disable_custom_emoji_html: "%{name} je onemogućio/-la emodži %{target}"
+        disable_sign_in_token_auth_user_html: "%{name} je onemogućio/-la imejl autentifikaciju za korisnika %{target}"
+        disable_user_html: "%{name} je onemogućio/-la prijavljivanje za korisnika %{target}"
+        enable_custom_emoji_html: "%{name} je omogućio/-la emodži %{target}"
+        enable_sign_in_token_auth_user_html: "%{name} je omogućio/-la imejl autentifikaciju za %{target}"
+        enable_user_html: "%{name} je omogućio/-la prijavljivanje korisniku %{target}"
+        memorialize_account_html: "%{name} je pretvorio/-la nalog korisnika %{target} u memorijalnu stranicu"
+        promote_user_html: "%{name} je unapredio/-la korisnika %{target}"
+        reject_appeal_html: "%{name} je odbio/-la žalbu na moderacijsku odluku koju je priložio korisnik %{target}"
+        reject_user_html: "%{name} je odbio/-la registraciju korisnika %{target}"
+        remove_avatar_user_html: "%{name} je uklonio avatar korisnika %{target}"
+        reopen_report_html: "%{name} je ponovo otvorio/-la prijavu %{target}"
+        resend_user_html: "%{name} je ponovo poslao/-la imejl za potvrdu korisniku %{target}"
+        reset_password_user_html: "%{name} je resetovao/-la lozinku korisnika %{target}"
+        resolve_report_html: "%{name} je rešio/-la prijavu %{target}"
+        sensitive_account_html: "%{name} je označio/-la medije naloga %{target} kao osetljive"
+        silence_account_html: "%{name} je ograničio/-la nalog %{target}"
+        suspend_account_html: "%{name} je suspendovao/-la nalog %{target}"
+        unassigned_report_html: "%{name} je povukao/-la dodelu prijave %{target}"
+        unblock_email_account_html: "%{name} je odblokirao/-la imejl adresu korisnika %{target}"
+        unsensitive_account_html: "%{name} je uklonio/-la oznaku „osetljivo” sa medija naloga %{target}"
+        unsilence_account_html: "%{name} je povukao/-la ograničenje naloga %{target}"
+        unsuspend_account_html: "%{name} je povukao/-la suspenziju naloga %{target}"
+        update_announcement_html: "%{name} je ažurirao/-la saopštenje %{target}"
+        update_custom_emoji_html: "%{name} je ažurirao/-la emodži %{target}"
+        update_domain_block_html: "%{name} je ažurirao/-la blok domena %{target}"
+        update_ip_block_html: "%{name} je promenio/-la IP uslov za %{target}"
+        update_status_html: "%{name} je ažurirao/-la objavu korisnika %{target}"
+        update_user_role_html: "%{name} je promenio/-la poziciju %{target}"
+      deleted_account: obrisan nalog
+      empty: Nije pronađen nijedan log.
+      filter_by_action: Filtriraj po aktivnosti
+      filter_by_user: Filtriraj po korisniku
       title: Zapisnik
+    announcements:
+      destroyed_msg: Saopštenje je uspešno obrisano!
+      edit:
+        title: Uredi najavu
+      empty: Nijedna najava nije pronađena.
+      live: Uživo
+      new:
+        create: Kreiraj najavu
+        title: Nova najava
+      publish: Objavi
+      published_msg: Najava uspešno objavljena!
+      scheduled_for: Zakazano za %{time}
+      scheduled_msg: Saopštenje je zakazano za objavljivanje!
+      title: Najave
+      unpublish: Povuci objavu
+      unpublished_msg: Objava saopštenja je uspešno povučena!
+      updated_msg: Saopštenje je uspešno ažurirano!
     custom_emojis:
+      assign_category: Dodeli kategoriju
       by_domain: Domen
-      copied_msg: Uspešno napravljena lokalna kopija emotikona
+      copied_msg: Uspešno napravljena lokalna kopija emodžija
       copy: Kopiraj
-      copy_failed_msg: Ne mogu da napravim lokalnu kopiju tog emotikona
-      created_msg: Emotikon uspešno napravljen!
+      copy_failed_msg: Ne mogu da napravim lokalnu kopiju tog emotidžija
+      create_new_category: Kreiraj novu kategoriju
+      created_msg: Emodži uspešno napravljen!
       delete: Obriši
-      destroyed_msg: Emotikon uspešno obrisan!
+      destroyed_msg: Emodži uspešno obrisan!
       disable: Onemogući
-      disabled_msg: Emotikon uspešno onemogućen
-      emoji: Emotikon
+      disabled: Onemogućeno
+      disabled_msg: Emodži uspešno onemogućen
+      emoji: Emodži
       enable: Omogući
-      enabled_msg: Emotikon uspešno omogućen
+      enabled: Omogućeno
+      enabled_msg: Emodži uspešno omogućen
+      image_hint: PNG ili GIF fajl veličine do %{size}
+      list: Lista
       listed: Izlistan
       new:
-        title: Dodaj novi proizvoljni emotikon
+        title: Dodaj novi proizvoljni emodži
+      no_emoji_selected: Nijedan emodži nije promenjen jer nijedan nije izabran
+      not_permitted: Niste ovlašćeni da obavljate ovu radnju
       overwrite: Prepiši
       shortcode: Prečica
       shortcode_hint: Najmanje 2 karaktera, dozvoljeni su samo slova, brojevi i donje crte
-      title: Proizvoljni emotikoni
+      title: Proizvoljni emotidžiji
+      uncategorized: Nekategorizovano
+      unlist: Neizlistan
       unlisted: Neizlistan
-      update_failed_msg: Ne mogu da ažuriram ovaj emotikon
-      updated_msg: emotikon uspešno ažuriran!
+      update_failed_msg: Ne mogu da ažuriram ovaj emodži
+      updated_msg: Emodži uspešno ažuriran!
       upload: Otpremi
+    dashboard:
+      active_users: aktivni korisnici
+      interactions: interakcije
+      media_storage: Multimedijalno skladište
+      new_users: novi korisnici
+      opened_reports: otvorene prijave
+      pending_appeals_html:
+        few: "<strong>%{count}</strong> žalbe na čekanju"
+        one: "<strong>%{count}</strong> žalba na čekanju"
+        other: "<strong>%{count}</strong> žalbi na čekanju"
+      pending_reports_html:
+        few: "<strong>%{count}</strong> prijave na čekanju"
+        one: "<strong>%{count}</strong> prijava na čekanju"
+        other: "<strong>%{count}</strong> prijava na čekanju"
+      pending_tags_html:
+        few: "<strong>%{count}</strong> heš oznake na čekanju"
+        one: "<strong>%{count}</strong> heš oznaka na čekanju"
+        other: "<strong>%{count}</strong> heš oznaka na čekanju"
+      pending_users_html:
+        few: "<strong>%{count}</strong> korisnika na čekanju"
+        one: "<strong>%{count}</strong> korisnik na čekanju"
+        other: "<strong>%{count}</strong> korisnika na čekanju"
+      resolved_reports: rešene prijave
+      software: Softver
+      sources: Izvori registracija
+      space: Korišćenje prostora
+      title: Komandna tabla
+      top_languages: Najzastupljeniji jezici
+      top_servers: Najaktivniji serveri
+      website: Vebsajt
+    disputes:
+      appeals:
+        empty: Nijedna žalba nije pronađena.
+        title: Žalbe
+    domain_allows:
+      add_new: Dozvoli federaciju sa domenom
+      created_msg: Domen je uspešno odobren za federaciju
+      destroyed_msg: Domen je odbijen za federaciju
+      export: Izvoz
+      import: Uvoz
+      undo: Zabrani federaciju sa domenom
     domain_blocks:
-      add_new: Dodaj novi
+      add_new: Dodaj novi blok domena
       created_msg: Blokiranje domena se obrađuje
       destroyed_msg: Blokiranje domena je opozvano
       domain: Domen
+      edit: Izmeni blok domena
+      existing_domain_block: Već ste uspostavili stroža ograničenja prema korisniku %{name}.
+      existing_domain_block_html: Već ste uspostavili stroža ograničenja prema %{name}, potrebno je da ga prvo <a href="%{unblock_url}">odblokirate</a>.
+      export: Izvoz
+      import: Uvoz
       new:
         create: Napravi blokadu
         hint: Blokiranje domena neće sprečiti pravljenje naloga u bazi, ali će retroaktivno i automatski primeniti određene moderatorske metode nad tim nalozima.
         severity:
+          desc_html: "<strong>Ograničenje</strong> će sakriti objave naloga sa ovog domena od svakog ko ih ne prati. <strong>Suspenzija</strong> će obrisati sav sadržaj, medije i podatke profila sa naloga ovog domena sa Vašeg servera. Koristite <strong>Ništa</strong> ukoliko samo želite da odbijete medijske fajlove."
           noop: Ništa
+          silence: Ograniči
           suspend: Suspenzija
         title: Novo blokiranje domena
+      no_domain_block_selected: Nijedan blok domena nije promenjen jer nijedan nije izabran
+      not_permitted: Niste ovlašćeni da obavljate ovu radnju
+      obfuscate: Sakrij ime domena
+      obfuscate_hint: Delimično sakrij ime domena na listi ako je reklamiranje liste ograničenih domena omogućeno
+      private_comment: Privatni komentar
+      private_comment_hint: Komentar o ograničenju ovog domena za internu upotrebu od strane moderatora.
+      public_comment: Javni komentar
+      public_comment_hint: Komentar o ograničenju ovog domena za javnost, ukoliko je reklamiranje liste ograničenih domena omogućeno.
       reject_media: Odbaci multimediju
-      reject_media_hint: Uklanja lokalno uskladištene multimedijske fajlove i odbija da ih skida na dalje. Nebitno je za suspenziju
-      undo: Poništi
+      reject_media_hint: Uklanja lokalno uskladištene multimedijske fajlove i odbija da ih skida ubuduće. Nebitno je za suspenziju
+      reject_reports: Odbaci izveštaj
+      reject_reports_hint: Ignoriši sve izveštaje koji dolaze sa ovog domena. Nebitno je za suspenzije
+      undo: Poništi blok domena
+      view: Pročitaj blok domena
     email_domain_blocks:
-      add_new: Dodaj novuAdd new
-      created_msg: Uspešno dodao domen e-pošte na crnu listu
-      delete: Ukloni
+      add_new: Dodaj novi
+      attempts_over_week:
+        few: "%{count} pokušaja tokom prethodne nedelje"
+        one: "%{count} pokušaj tokom prethodne nedelje"
+        other: "%{count} pokušaja registracije tokom prethodne nedelje"
+      created_msg: Uspešno dodao domen E-pošte na crnu listu
+      delete: Obriši
+      dns:
+        types:
+          mx: MX izveštaj
       domain: Domen
       new:
         create: Dodaj domen
-        title: Nova stavka u crnoj listi e-pošti
-      title: Crna lista adresa e-pošte
+        resolve: Pretvori domen
+        title: Nova stavka e-pošte u crnoj listi
+      no_email_domain_block_selected: Nijedan blok imejl domena nije promenjen jer nijedan nije izabran
+      not_permitted: Nije dozvoljeno
+      resolved_dns_records_hint_html: Ime domena se pretvara u sledeće MX domene, koji su naposletku odgovorni za prihvatanje elektronske pošte. Blokiranje MX domena će blokirati registracije sa svake imejl adrese koja koristi taj MX domen, čak i u slučaju kada se vidljivo ime domena razlikuje. <strong>Vodite računa o tome da ne blokirate velike imejl provajdere.</strong>
+      resolved_through_html: Preusmereno kroz %{domain}
+      title: Crna lista E-pošte
+    export_domain_allows:
+      new:
+        title: Uvezi dozvoljene domene
+      no_file: Nijedan fajl nije odabran
+    export_domain_blocks:
+      import:
+        description_html: Upravo ćete uvesti listu blokiranih domena. Molimo Vas, vrlo pažljivo pregledajte ovu listu, posebno ukoliko je niste sami napravili.
+        existing_relationships_warning: Postojeći odnosi u obliku praćenja
+        private_comment_description_html: 'Da bi Vam pomogli da pratite odakle su blokovi uvezeni, uvezeni blokovi će biti napravljeni sa sledećim privatnim komentarom: <q>%{comment}</q>'
+        private_comment_template: Uvezeno sa izvora %{source} na datum %{date}
+        title: Uvezi blokirane domene
+      invalid_domain_block: 'Jedan ili više blokova domena je preskočen zbog sledeće greške tj. grešaka: %{error}'
+      new:
+        title: Uvezi blokirane domene
+      no_file: Nijedan fajl nije odabran
+    follow_recommendations:
+      description_html: "<strong>Predlozi za praćenje pomažu novim korisnicima da brzo pronađu zanimljiv sadržaj</strong>. Kada korisnik nije dovoljno interagovao sa ostalima da bi se za njega formirali personalizovani predlozi za praćenje, ovi nalozi će biti preporučeni umesto toga. Oni se generišu na dnevnoj bazi iz skupa naloga sa najviše nedavnih angažovanja i najviše lokalnih pratilaca za jedan jezik."
+      language: Za jezik
+      status: Status
+      suppress: Potisni preporuke za praćenje
+      suppressed: Potisnuto
+      title: Preporuke za praćenje
+      unsuppress: Vrati preporuku za praćenje
     instances:
-      title: Poznate instance
+      availability:
+        description_html:
+          few: Ukoliko isporuka domenu ne uspe nijednom tokom <strong>%{count} različitih dana</strong>, dalji pokušaji isporuke neće biti inicirani osim ukoliko se ne primi isporuka <em>sa</em> domena.
+          one: Ako isporuka domenu ne uspe nijednom u vremenskom periodu od <strong>%{count} dana</strong>, dalji pokušaji isporuke se neće inicirati osim ukoliko se ne primi isporuka <em>sa</em> domena.
+          other: Ukoliko isporuka domenu ne uspe nijednom tokom <strong>%{count} različitih dana</strong>, dalji pokušaji isporuke se neće inicirati osim ukoliko se ne primi isporuka <em>sa</em> domena.
+        failure_threshold_reached: Prag neuspeha dostignut datuma %{date}.
+        failures_recorded:
+          few: Neuspeli pokušaji tokom %{count} različita dana.
+          one: Neuspeli pokušaj tokom %{count} dana.
+          other: Neuspeli pokušaji tokom %{count} različitih dana.
+        no_failures_recorded: Bez zabeleženih neuspeha.
+        title: Dostupnost
+        warning: Poslednji pokušaj povezivanja sa ovim serverom je bio neuspešan
+      back_to_all: Sve
+      back_to_limited: Ograničeno
+      back_to_warning: Upozorenje
+      by_domain: Domen
+      confirm_purge: Da li ste sigurni da želite da trajno uklonite podatke sa ovog domena?
+      content_policies:
+        comment: Interna beleška
+        description_html: Možete da definišete politiku sadržaja koja će važiti za sve naloge na ovom domenu i svakom od njegovih poddomena.
+        limited_federation_mode_description_html: Možete da odlučite da li da dopustite federaciju sa ovim domenom.
+        policies:
+          reject_media: Odbij multimediju
+          reject_reports: Odbij prijave
+          silence: Ograniči
+          suspend: Suspenduj
+        policy: Politika
+        reason: Javni razlog
+        title: Politika sadržaja
+      dashboard:
+        instance_accounts_dimension: Najpraćeniji nalozi
+        instance_accounts_measure: uskladišteni nalozi
+        instance_followers_measure: naši pratioci ovde
+        instance_follows_measure: njihovi pratioci ovde
+        instance_languages_dimension: Najzastupljeniji jezici
+        instance_media_attachments_measure: uskladišteni multimedijalni prilozi
+        instance_reports_measure: prijave protiv njih
+        instance_statuses_measure: uskladištene objave
+      delivery:
+        all: Sve
+        clear: Očisti greške prilikom isporuke
+        failing: Bez uspeha
+        restart: Započni isporuku ponovo
+        stop: Obustavi isporuku
+        unavailable: Nedostupno
+      delivery_available: Dostava je dostupna
+      delivery_error_days: Dani neuspešnih isporuka
+      delivery_error_hint: Ukoliko isporuka nije moguća %{count} dana, automatski će biti obeležena kao neisporučiva.
+      destroyed_msg: Podaci sa %{domain} su sada u redu za čekanje za izvesno brisanje.
+      empty: Nijedan domen nije pronađen.
+      known_accounts:
+        few: "%{count} poznata naloga"
+        one: "%{count} poznat nalog"
+        other: "%{count} poznatih naloga"
+      moderation:
+        all: Sve
+        limited: Ograničeno
+        title: Moderacija
+      private_comment: Privatni komentar
+      public_comment: Javni komentar
+      purge: Čistka
+      purge_description_html: Ukoliko verujete da je ovaj domen trajno ugašen, možete da obrišete sve zapise naloga i srodne podatke ovog domena sa svog skladišta. Ovo može da potraje.
+      title: Federacija
+      total_blocked_by_us: Blokirano od strane nas
+      total_followed_by_them: Praćeni od strane njih
+      total_followed_by_us: Praćeni od strane nas
+      total_reported: Prijave vezane za njih
+      total_storage: Multimedijalni prilozi
+      totals_time_period_hint_html: Ukupne vrednosti prikazane ispod uključuju podatke za sva vremena.
     invites:
+      deactivate_all: Deaktiviraj sve
       filter:
         all: Sve
-        available: Aktivne
-        expired: Istekle
+        available: Dostupni
+        expired: Istekli
+        title: Filter
       title: Pozivnice
+    ip_blocks:
+      add_new: Napravi pravilo
+      created_msg: Uspešno je dodato novo IP pravilo
+      delete: Izbriši
+      expires_in:
+        '1209600': 2 nedelje
+        '15778476': 6 meseci
+        '2629746': 1 mesec
+        '31556952': 1 godina
+        '86400': 1 dan
+        '94670856': 3 godine
+      new:
+        title: Kreiraj novo IP pravilo
+      no_ip_block_selected: Nijedno IP pravilo nije promenjeno jer nijedno nije izabrano
+      title: IP pravila
+    relationships:
+      title: Odnosi korisnika %{acct}
+    relays:
+      add_new: Dodaj novi relej
+      delete: Obriši
+      description_html: "<strong>Federalni relej</strong> je posrednički server koji razmenjuje velike količine javnih truba između servera na koji je pretplaćen i na koji objavljuje.<strong>Može pomoći malim i srednjim serverima da otkriju sadržaj iz fediversa</strong>, koji inače zahteva od lokalnih korisnika da ručno pratiti ostale ljude na udaljenim serverima."
+      disable: Isključi
+      disabled: Isključen
+      enable: Uključi
+      enable_hint: Kada se omogući, vaš server će biti pretplaćen na sve javne objave sa ovog releja, i počeće da šalje javne objave ovog servera na njega.
+      enabled: Uključen
+      inbox_url: URL Releja
+      pending: Čeka se odobrenje releja
+      save_and_enable: Sačuvaj i omogući
+      setup: Podesi vezu releja
+      signatures_not_enabled: Prenosi možda neće raditi ispravno dok je uključen bezbedni režim ili režim ograničene federacije
+      status: Status
+      title: Releji
+    report_notes:
+      created_msg: Beleška prijave uspešno napravljena!
+      destroyed_msg: Beleška prijave uspešno izbrisana!
     reports:
+      account:
+        notes:
+          few: "%{count} beleške"
+          one: "%{count} beleška"
+          other: "%{count} beležaka"
+      action_log: Zapisnik
       action_taken_by: Akciju izveo
+      actions:
+        delete_description_html: Prijavljene objave će biti obrisane i prekršaj će biti upisan da bi Vam olakšali intervenciju prilikom budućih prestupa sa istog naloga.
+        mark_as_sensitive_description_html: Multimedijalni sadržaj sa prijavljenih objava će biti označen kao osetljiv i prekršaj će biti upisan radi lakše intervencije u slučaju daljih prekršaja sa istog naloga.
+        other_description_html: Pogledajte više opcija za kontrolisanje ponašanja naloga i prilagodite komunikaciju sa prijavljenim nalogom.
+        resolve_description_html: Nijedna radnja neće biti preduzeta protiv prijavljenog naloga, nijedan prestup nije upisan i prijava će biti zatvorena.
+        silence_description_html: Nalog će biti vidljiv samo onima koji ga već prate ili koji ga ručno potraže, što će značajno ograničiti njegov domet. Ograničenje se može povući u svakom trenutku. Zatvara sve prijave podnete protiv ovog naloga.
+        suspend_description_html: Nalog i svi njegovi sadržaji će postati nedostupni i u jednom trenutku izbrisani, a interakcija sa nalogom više neće biti moguća. Suspenzija se može povući u roku od 30 dana. Zatvara sve prijave podnete protiv ovog naloga.
+      actions_description_html: Odlučite koju radnju da sprovedete radi rešavanja ove prijave. Ukoliko sprovedete kaznenu radnju protiv prijavljenog naloga, vlasnik naloga će biti obavešten putem i-mejla, osim ukoliko oznaka <strong>„Nepoželjne poruke”</strong> nije odabrana.
+      actions_description_remote_html: Odlučite koju radnju da preduzmete radi rešavanja ove prijave. Ovo će uticati samo na to kako <strong>Vaš</strong> server komunicira sa ovim udaljenim nalogom i obrađuje njegov sadržaj.
+      add_to_report: Dodaj još u prijavu
       are_you_sure: Da li ste sigurni?
+      assign_to_self: Dodeli meni
+      assigned: Dodeljeni moderator
+      by_target_domain: Domen prijavljenog naloga
+      cancel: Otkaži
+      category: Kategorija
+      category_description_html: Razlog zbog kog je ovaj nalog i/ili sadržaj prijavljen će biti obrazložen u komunikaciji sa prijavljenim nalogom
       comment:
         none: Ništa
-      mark_as_resolved: Označi kao rešen
+      comment_description_html: 'Radi pružanja više informacija, %{name} je napisao/-la:'
+      confirm: Potvrdi
+      confirm_action: Potvrdi moderacijsku radnju prema @%{acct}
+      created_at: Prijavljena
+      delete_and_resolve: Obriši objave
+      forwarded: Prosleđeno
+      forwarded_to: Prosleđeno ka %{domain}
+      mark_as_resolved: Označi kao rešenu
+      mark_as_sensitive: Obeleži kao osetljivo
+      mark_as_unresolved: Označi kao nerešenu
+      no_one_assigned: Niko
+      notes:
+        create: Dodaj belešku
+        create_and_resolve: Reši sa beleškom
+        create_and_unresolve: Otvori ponovo sa beleškom
+        delete: Obriši
+        placeholder: Opišite kakve su radnje preduzete, ili bilo kakve povezane novosti...
+        title: Beleške
+      notes_description_html: Pročitajte i ostavite napomene drugim moderatorima i sebi u budućnosti
+      processed_msg: 'Prijava #%{id} uspešno obrađena'
+      quick_actions_description_html: 'Preduzmite brzu radnju ili se spustite niže da biste videli prijavljeni sadržaj:'
+      remote_user_placeholder: udaljeni korisnik sa %{instance}
+      reopen: Otvori prijavu ponovo
       report: 'Prijava #%{id}'
       reported_account: Prijavljeni nalog
       reported_by: Prijavio
-      resolved: Rešeni
+      resolved: Rešena
+      resolved_msg: Prijava uspešno razrešena!
+      skip_to_actions: Preskoči do radnji
+      status: Status
+      statuses: Prijavljeni sadržaj
+      statuses_description_html: Sporni sadržaj će biti naveden u komunikaciji sa prijavljenim nalogom
+      summary:
+        action_preambles:
+          delete_html: 'Upravo ćete <strong>obrisati</strong> neke od objava korisnika <strong>@%{acct}</strong>. Ovo će:'
+          mark_as_sensitive_html: 'Upravo ćete <strong>označiti</strong> neke objave korisnika <strong>@%{acct}</strong> kao <strong>osetljive</strong>. Ovo će:'
+          silence_html: 'Upravo ćete <strong>ograničiti</strong> nalog korisnika <strong>@%{acct}</strong>. Ovo će:'
+          suspend_html: 'Upravo ćete <strong>suspendovati</strong> nalog korisnika <strong>@%{acct}</strong>. Ovo će:'
+        actions:
+          delete_html: Obrišite sporne objave
+          mark_as_sensitive_html: Obeležite medije spornih objava kao osetljive
+          silence_html: Žestoko ograničite domet korisnika <strong>@%{acct}</strong> tako što ćete učiniti njegov profil i sadržaje vidljive samo ljudima koji ih već prate i ljudima koji ručno potraže profil
+          suspend_html: Suspendujte korisnika <strong>@%{acct}</strong>, što će učiniti njegov profil i sadržaje nedostupnim za pristup i interakciju
+        close_report: 'Označite prijavu #%{id} kao rešenu'
+        close_reports_html: Označi <strong>sve</strong> prijave protiv <strong>@%{acct}</strong> kao rešene
+        delete_data_html: Obriši profil i sadržaje korisnika <strong>@%{acct}</strong> za 30 dana osim ukoliko suspenzija bude povučena u međuvremenu
+        preview_preamble_html: "<strong>@%{acct}</strong> će primiti upozorenje sledeće sadržine:"
+        record_strike_html: Upišite prekršaj na ime <strong>@%{acct}</strong> da bi Vam bilo lakše da u budućnosti intervenišete prilikom daljih prestupa sa ovog naloga
+        send_email_html: Pošalji imejl upozorenja korisniku <strong>@%{acct}</strong>
+        warning_placeholder: Opciono dodatno obrazloženje za moderacijsku radnju.
+      target_origin: Poreklo prijavljenog naloga
       title: Prijave
-      unresolved: Nerešeni
+      unassign: Ukloni dodelu
+      unknown_action_msg: 'Nepoznata radnja: %{action}'
+      unresolved: Nerešene
+      updated_at: Ažurirana
+      view_profile: Pogledaj profil
+    roles:
+      add_new: Dodaj ulogu
+      assigned_users:
+        few: "%{count} korisnika"
+        one: "%{count} korisnik"
+        other: "%{count} korisnika"
+      categories:
+        administration: Administracija
+        devops: DevOps
+        invites: Pozivnice
+        moderation: Moderacija
+        special: Posebno
+      delete: Izbriši
+      description_html: Pomoću <strong>korisničkih uloga</strong> možete da podesite kojim funkcijama i delovima Mastodona Vaši korisnici mogu da pristupe.
+      edit: Izmeni ulogu '%{name}'
+      everyone: Podrazumevana ovlašćenja
+      everyone_full_description_html: Ovo je <strong>osnovna uloga</strong> koja se odnosi na <strong>sve korisnike</strong>, čak i one kojima nije dodeljena uloga. Sve druge uloge nasleđuju ovlašćenja od osnovne uloge.
+      permissions_count:
+        few: "%{count} dozvole"
+        one: "%{count} dozvola"
+        other: "%{count} dozvola"
+      privileges:
+        administrator: Administrator
+        administrator_description: Korisnici sa ovom privilegijom mogu da zaobiđu sva druga ograničenja
+        delete_user_data: Izbriši podatke korisnika
+        delete_user_data_description: Dopušta korisnicima da izbrišu podatke drugih korisnika bez odlaganja
+        invite_users: Pozovi korisnike
+        invite_users_description: Dopušta korisnicima da pozove nove ljude na server
+        manage_announcements: Upravljaj obaveštenjima
+        manage_announcements_description: Dopušta korisnicima da rukovode saopštenjima na serveru
+        manage_appeals: Nadgledanje žalbi
+        manage_appeals_description: Dopušta korisnicima da pregledaju žalbe na moderacijske radnje
+        manage_blocks: Nadgledanje blokova
+        manage_blocks_description: Dopušta korisnicima da blokiraju imejl provajdere i IP adrese
+        manage_custom_emojis: Nadležnost nad prilagođenim emodžijima
+        manage_custom_emojis_description: Daje korisnicima kontrolu nad prilagođenim emodžijima na serveru
+        manage_federation: Nadgledanje federacije
+        manage_federation_description: Dopušta korisnicima da blokiraju ili dozvole federaciju sa drugim domenima i kontrolišu isporučivanje podataka drugim serverima
+        manage_invites: Nadgledanje pozivnica
+        manage_invites_description: Dopušta korisnicima da pretražuju i deaktiviraju pozivnice
+        manage_reports: Nadgledanje prijava
+        manage_reports_description: Dopušta korisnicima da pregledaju prijave i izvršavaju moderacijske radnje nad njima
+        manage_roles: Upravljaj ulogama
+        manage_roles_description: Dopušta korisnicima da nadgledaju i dodeljuju uloge sa nižim ovlašćenjima od njihove
+        manage_rules: Upravljaj pravilima
+        manage_rules_description: Dozvoli korisnicima da menjaju pravila servera
+        manage_settings: Upravljaj postavkama
+        manage_settings_description: Dozvoli korisnicima da menjaju postavke sajta
+        manage_taxonomies: Nadgledanje taksonomija
+        manage_taxonomies_description: Dopušta korisnicima da pregledaju sadržaj u trendu i ažuriraju postavke heš oznaka
+        manage_user_access: Nadležnost nad korisničkim pristupom
+        manage_user_access_description: Dopušta korisnicima da onemoguće dvofaktorsku autentifikaciju drugih korisnika, menjaju im imejl adrese i resetuju im lozinke
+        manage_users: Nadležnost nad korisnicima
+        manage_users_description: Dopušta korisnicima da pročitaju detalje drugih korisnika i izvršavaju moderacijske radnje nad njima
+        manage_webhooks: Nadležnost nad webhook elementima
+        manage_webhooks_description: Dopušta korisnicima da uspostave webhook elemente za administrativne radnje
+        view_audit_log: Pročitaj zapisnik revizija
+        view_audit_log_description: Dopušta korisnicima da vide istoriju administrativnih radnji na serveru
+        view_dashboard: Pogledaj kontrolni panel
+        view_dashboard_description: Dopušta korisnicima da pristupe kontrolnom panelu i raznim metrikama
+        view_devops: DevOps
+        view_devops_description: Dopušta korisnicima da pristupe Sidekiq i pgHero kontrolnim panelima
+      title: Uloge
+    rules:
+      add_new: Dodaj pravilo
+      delete: Izbriši
+      description_html: Dok većina tvrdi da je pročitala i slaže se sa uslovima korišćenja, ljudi ih obično ne čitaju sve dok se ne javi problem. <strong>Učinite pravila Vašeg servera čitljivijim i pristupačnijim tako što ćete ih izložiti u formi liste.</strong> Potrudite se da pojedinačna pravila budu kratka i jednostavna, ali pokušajte i da ih ne iscepkate u previše odvojenih stavki.
+      edit: Uredi pravilo
+      empty: Nijedno pravilo servera još nije definisano.
+      title: Pravila servera
+    settings:
+      about:
+        manage_rules: Upravljanje pravilima servera
+        preamble: Pružite detaljne informacije o tome kako se server vodi, moderira i finansira.
+        rules_hint: Postoji predviđeno mesto za pravila koja se od korisnika očekuje da poštuju.
+        title: Naziv
+      appearance:
+        preamble: Prilagodite veb interfejs Mastodona.
+        title: Izgled
+      branding:
+        preamble: Brendiranje Vašeg servera ga izdvaja od drugih servera na mreži. Ove informacije mogu biti prikazane u raznim okruženjima, poput Mastodonovog veb interfejsa, nativnih aplikacija, u pregledima linkova na drugim serverima i u aplikacijama za razmenu poruka, itd. Iz ovog razloga, najbolje je da ove informacije budu kratke, jasne i koncizne.
+        title: Brendiranje
+      content_retention:
+        preamble: Kontrolišite kako se sadržaj generisan od strane korisnika skladišti na Mastodonu.
+        title: Zadržavanje sadržaja
+      default_noindex:
+        desc_html: Utiče na sve korisnike koji nisu sami promenili ovu postavku
+        title: Podrazumevano isključi korisnike iz indeksiranja pretraživača
+      discovery:
+        follow_recommendations: Preporuke za praćenje
+        preamble: Održavanje zanimljivih sadržaja na površini je ključno u privlačenju novih korisnika koji možda ne znaju nikoga na Mastodonu. Kontrolišite kako različiti načini istraživanja funkcionišu na Vašem serveru.
+        profile_directory: Direktorijum profilâ
+        public_timelines: Javne vremenske linije
+        publish_discovered_servers: Objavi otkrivene servere
+        publish_statistics: Objavi statistiku
+        title: Otkrivanje
+        trends: Trendovi
+      domain_blocks:
+        all: Svima
+        disabled: Nikome
+        users: Prijavljenim lokalnim korisnicima
+      registrations:
+        preamble: Kontrolišite ko sme da napravi nalog na Vašem serveru.
+        title: Registracije
+      registrations_mode:
+        modes:
+          approved: Odobrenje neophodno za registraciju
+          none: Niko ne može da se registruje
+          open: Bilo ko može da se registruje
+      title: Podešavanja servera
+    site_uploads:
+      delete: Obriši otpremljeni fajl
+      destroyed_msg: Otpremanje uspešno obrisano!
     statuses:
+      account: Autor
+      application: Aplikacija
       back_to_account: Nazad na stranu naloga
+      back_to_report: Nazad na stranicu sa prijavama
+      batch:
+        remove_from_report: Uklonite iz prijave
+        report: Prijavi
+      deleted: Obrisano
+      favourites: Omiljeno
+      history: Istorija verzijâ
+      in_reply_to: Odgovor na
+      language: Jezik
       media:
         title: Multimedija
+      metadata: Meta podaci
+      no_status_selected: Nijedan status nije promenjen jer nijedan nije izabran
+      open: Otvori objavu
+      original_status: Originalna objava
+      reblogs: Deljenja
+      status_changed: Objava promenjena
       title: Statusi naloga
+      trending: U trendu
+      visibility: Vidljivost
       with_media: Sa multimedijom
+    strikes:
+      actions:
+        delete_statuses: "%{name} je obrisao/-la objave %{target}"
+        disable: "%{name} je zamrznuo nalog korisnika %{target}"
+        mark_statuses_as_sensitive: "%{name} je označio/-la objave korisnika %{target} kao osetljive"
+        none: "%{name} je poslao/-la upozorenje korisniku %{target}"
+        sensitive: "%{name} je obeležio/-la nalog %{target} kao osetljiv"
+        silence: "%{name} je ograničio/-la nalog %{target}"
+        suspend: "%{name} je suspendovao/-la nalog %{target}"
+      appeal_approved: Žalba uvažena
+      appeal_pending: Žalba u razmatranju
+      appeal_rejected: Žalba odbijena
+    system_checks:
+      database_schema_check:
+        message_html: Selidbe bazâ podataka su na čekanju. Molimo Vas obavite ih da bi se aplikacija ponašala kako treba
+      elasticsearch_running_check:
+        message_html: Povezivanje na Elasticsearch nije bilo moguće. Molimo Vas proverite da li je pokrenut, ili onemogućite pretragu celog teksta
+      elasticsearch_version_check:
+        message_html: 'Neusklađena Elasticsearch verzija: %{value}'
+        version_comparison: Elasticsearch %{running_version} je instaliran a %{required_version} je neophodan
+      rules_check:
+        action: Upravljanje pravilima servera
+        message_html: Niste definisali nijedno pravilo servera.
+      sidekiq_process_check:
+        message_html: Nijedan Sidekiq proces nije pokrenut za red(ove) %{value}. Molimo Vas pregledajte svoju Sidekiq konfiguraciju
+      upload_check_privacy_error:
+        action: Proverite ovde za više informacija
+        message_html: "<strong>Vaš veb server je pogrešno konfigurisan. Privatnost vaših korisnika je izložena riziku.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Proverite ovde za više informacija
+        message_html: "<strong>Vaše skladište objekta je pogrešno konfigurisano. Privatnost vaših korisnika je izložena riziku.</strong>"
+    tags:
+      review: Pregledaj status
+      updated_msg: Podešavanja heš oznaka uspešno ažurirana
     title: Administracija
+    trends:
+      allow: Dozvoli
+      approved: Odobreno
+      disallow: Zabrani
+      links:
+        allow: Dozvoli link
+        allow_provider: Dozvoli izdavača
+        description_html: Ovo su linkovi koji se trenutno često dele među nalozima koje Vaš server vidi. Može pomoći Vašim korisnicima da saznaju šta se dešava u svetu. Nijedan link nije javno prikazan sve dok Vi ne odobrite izdavača. Takođe možete da dozvolite ili odbijete zasebne linkove.
+        disallow: Zabrani link
+        disallow_provider: Zabrani izdavača
+        no_link_selected: Nijedan link nije promenjen jer nijedan nije izabran
+        publishers:
+          no_publisher_selected: Nijedan izdavač nije promenjen jer nijedan nije izabran
+        shared_by_over_week:
+          few: Podeljen od strane %{count} osobe tokom prethodne nedelje
+          one: Podeljen od strane jedne osobe tokom prethodne nedelje
+          other: Podeljen od strane %{count} osoba tokom prethodne nedelje
+        title: Linkovi u trendu
+        usage_comparison: Podeljeno %{today} puta danas, u poređenju sa %{yesterday} puta juče
+      not_allowed_to_trend: Nije odobreno za trend
+      only_allowed: Samo dozvoljeno
+      pending_review: Pregled na čekanju
+      preview_card_providers:
+        allowed: Linkovi sa ovog izvora mogu da budu „u trendu”
+        description_html: Ovo su domeni čiji se linkovi često dele na Vašem serveru. Linkovi neće biti javno prikazani kao „u trendu” osim ukoliko je domen linkova odobren. Vaše odobrenje (ili odbijanje) se prenosi i na poddomene.
+        rejected: Linkovi sa ovog izvora neće biti „u trendu”
+        title: Izdavači
+      rejected: Odbijen
+      statuses:
+        allow: Dozvoli objavu
+        allow_account: Odobri autora
+        description_html: Ovo su objave za koje Vaš server zna, a koje se trenutno često dele i koje korisnici često stavljaju u „omiljene”. Mogu pomoći Vašim novim korisnicima i povratnicima da pronađu još ljudi za praćenje. Nijedna objava nije prikazana javno sve dok Vi ne odobrite autora, odnosno dok autor ne dozvoli da njegov/njen nalog bude preporučen drugima. Takođe možete da odobrite ili odbijete zasebne objave.
+        disallow: Zabrani objavu
+        disallow_account: Zabrani autora
+        no_status_selected: Nijedna objava u trendu nije promenjena jer nijedna nije izabrana
+        not_discoverable: Autor nije dao saglasnost da bude preporučen
+        shared_by:
+          few: Podeljeno i stavljeno u „omiljene” %{friendly_count} puta
+          one: Podeljeno ili stavljeno u „omiljene” jednom
+          other: Podeljeno i stavljeno u „omiljene” %{friendly_count} puta
+        title: Objave u trendu
+      tags:
+        current_score: Trenutna vrednost %{score}
+        dashboard:
+          tag_accounts_measure: jedinstvene upotrebe
+          tag_languages_dimension: Najzastupljeniji jezici
+          tag_servers_dimension: Najaktivniji serveri
+          tag_servers_measure: različiti serveri
+          tag_uses_measure: ukupno upotreba
+        description_html: Ovo su heš oznake koje se trenutno često pojavljuju u objavama koje Vaš server vidi. Mogu pomoći Vašim korisnicima da otkriju o čemu se trenutno najviše govori. Nijedna heš oznaka nije prikazana javno sve dok Vi to ne odobrite.
+        listable: Može se preporučiti
+        no_tag_selected: Nijedna oznaka nije izmenjena jer nijedna nije izabrana
+        not_listable: Neće biti preporučeno
+        not_trendable: Neće se pojavljivati u trendovima
+        not_usable: Ne može se koristiti
+        peaked_on_and_decaying: Vrhunac dostignut datuma %{date}, od tada u opadanju
+        title: Heš oznake u trendu
+        trendable: Može da se pojavi pod trendovima
+        trending_rank: 'U trendu #%{rank}'
+        usable: Može se koristiti
+        usage_comparison: Upotrebljeno %{today} puta danas, u poređenju sa %{yesterday} puta juče
+        used_by_over_week:
+          few: Korišćeno od strane %{count} osobe tokom prethodne nedelje
+          one: Korišćeno od strane jedne osobe tokom prethodne nedelje
+          other: Korišćeno od strane %{count} ljudi tokom prethodne nedelje
+      title: Trendovi
+      trending: U trendu
+    warning_presets:
+      add_new: Dodaj novi
+      delete: Izbriši
+      edit_preset: Uredi preset upozorenja
+      empty: Još uvek niste definisali nijedan šablon upozorenja.
+      title: Upravljaj presetima upozorenja
+    webhooks:
+      add_new: Dodaj krajnju tačku
+      delete: Izbriši
+      description_html: "<strong>Webhook</strong> omogućava Mastodonu da Vašoj aplikaciji isporučuje <strong>obaveštenja u realnom vremenu</strong> o odabranim događajima, tako da Vaša aplikacija može da <strong>automatski izazove reakciju</strong>."
+      disable: Onemogući
+      disabled: Onemogućeno
+      edit: Izmeni krajnju tačku
+      empty: Još uvek nemate nijednu konfigurisanu webhook krajnju tačku.
+      enable: Omogući
+      enabled: Aktivno
+      enabled_events:
+        few: "%{count} omogućena događaja"
+        one: 1 omogućen događaj
+        other: "%{count} omogućenih događaja"
+      events: Događaji
+      new: Novi webhook
+      rotate_secret: Rotacija tajni
+      secret: Tajno potpisivanje
+      status: Status
+      title: Veb-presretač
+      webhook: Veb-presretač
   admin_mailer:
+    new_appeal:
+      actions:
+        delete_statuses: obrisati objave korisnika
+        disable: zamrznuti nalog korisnika
+        mark_statuses_as_sensitive: označiti objave korisnika kao osetljive
+        none: upozorenje
+        sensitive: označiti nalog kao osetljiv
+        silence: ograničiti nalog
+        suspend: suspendovati nalog
+      body: "%{target} prilaže žalbu na moderacijsku odluku korisnika %{action_taken_by} od %{date}, koja je glasila %{type}. U žalbi piše:"
+      next_steps: Možete uvažiti žalbu da biste povukli moderacijsku odluku, ili je možete ignorisati.
+      subject: "%{username} prilaže žalbu na moderacijsku odluku sa %{instance}"
+    new_pending_account:
+      body: Detalji novog naloga su navedeni dole. Možete odobriti ili odbiti ovaj zahtev.
+      subject: Nov nalog za pregled na %{instance} (%{username})
     new_report:
       body: "%{reporter} je prijavio %{target}"
+      body_remote: Neka sa domena %{domain} je prijavio %{target}
       subject: Nova prijava za %{instance} (#%{id})
+    new_trends:
+      body: 'Sledeće stavke je potrebno pregledati pre nego što mogu javno da se prikažu:'
+      new_trending_links:
+        title: Linkovi u trendu
+      new_trending_statuses:
+        title: Objave u trendu
+      new_trending_tags:
+        no_approved_tags: Trenutno nema odobrenih heš oznaka u trendu.
+        requirements: 'Bilo koji od sledećih kandidata bi mogao prevazići #%{rank} odobrenu heš oznaku u trendu, koja je trenutno #%{lowest_tag_name} sa vrednošću %{lowest_tag_score}.'
+        title: Heš oznake u trendu
+      subject: Novi trendovi za pregled na %{instance}
+  aliases:
+    add_new: Napravi pseudonim
+    created_msg: Uspešno je napravljen novi pseudonim. Sada možete inicirati preseljenje sa starog naloga.
+    deleted_msg: Uspešno je uklonjen pseudonim. Premeštanje sa tog naloga na ovaj više neće biti moguće.
+    empty: Nemate nijedan pseudonim.
+    hint_html: Ako želite da se preselite sa drugog naloga na ovaj, ovde možete napraviti pseudonim, koji je neophodan pre nego što možete nastaviti sa prebacivanjem pratilaca sa starog naloga na ovaj. Ova radnja sama po sebi je <strong>bezopasna i reverzibilna</strong>. <strong>Preseljenje naloga se inicira sa starog naloga</strong>.
+    remove: Odveži pseudonim
+  appearance:
+    advanced_web_interface: Napredno veb okruženje
+    advanced_web_interface_hint: 'Ako želite da iskoristite celu širinu ekrana, napredno veb okruženje vam omogućuje da konfigurišete mnogo različitih kolona da biste videli onoliko informacija u isto vreme koliko želite: početnu stranicu, obaveštenja, združenu vremensku liniju, bilo koji broj lista i heš oznaka.'
+    animations_and_accessibility: Animacije i pristupačnost
+    confirmation_dialogs: Dijalozi potvrde
+    discovery: Otkrivanje
+    localization:
+      body: Mastodon prevode dobrovoljci.
+      guide_link: https://crowdin.com/project/mastodon
+      guide_link_text: Svako može doprineti.
+    sensitive_content: Osetljiv sadržaj
+    toot_layout: Raspored objava
   application_mailer:
+    notification_preferences: Promeni preference E-pošte
+    salutation: Poštovani %{name},
     settings: 'Promeni podešavanja e-pošte: %{link}'
     view: 'Pogledaj:'
+    view_profile: Pogledaj nalog
+    view_status: Pogledaj status
   applications:
     created: Aplikacija uspešno napravljena
     destroyed: Aplikacija uspešno obrisana
+    logout: Odjava
     regenerate_token: Rekreiraj pristupni token
     token_regenerated: Pristupni token uspešno rekreiran
     warning: Oprezno sa ovim podacima. Nikad je ne delite ni sa kim!
     your_token: Vaš pristupni token
   auth:
-    delete_account: Obriši nalog
-    delete_account_html: Ako želite da obrišete Vaš nalog, možete <a href="%{path}">nastaviti ovde</a>. Bićete upitani da potvrdite.
+    apply_for_account: Zatražite nalog
+    change_password: Lozinka
+    confirmations:
+      wrong_email_hint: Ako ta imejl adresa nije ispravna, možete je promeniti u podešavanjima naloga.
+    delete_account: Brisanje naloga
+    delete_account_html: Ako želite da izbrišete vaš nalog, možete <a href="%{path}">nastaviti ovde</a>. Od vas će se tražiti potvrda.
+    description:
+      prefix_invited_by_user: "@%{name} Vas poziva da se pridružite ovom serveru Mastodona!"
+      prefix_sign_up: Pridružite se Mastodonu danas!
+      suffix: Sa nalogom, moći ćete da pratite ljude, objavljujete novosti i razmenjujete poruke sa korisnicima bilo kog Mastodon servera i više!
     didnt_get_confirmation: Niste dobili poruku sa uputstvima za potvrdu naloga?
+    dont_have_your_security_key: Nemate sigurnosni ključ?
     forgot_password: Zaboravili ste lozinku?
     invalid_reset_password_token: Token za resetovanje lozinke je neispravan ili je istekao. Zatražite novi.
+    link_to_otp: Unesite dvofaktorski kod sa svog telefona ili rezervni kod
+    link_to_webauth: Koristite svoj sigurnosni ključ
+    log_in_with: Prijavite se pomoću
     login: Prijavi se
     logout: Odjava
-    migrate_account: Pomeri u drugi nalog
+    migrate_account: Premeštanje u drugi nalog
     migrate_account_html: Ako želite da preusmerite ovaj nalog na neki drugi, možete to <a href="%{path}">podesiti ovde</a>.
+    or_log_in_with: Ili se prijavite sa
+    privacy_policy_agreement_html: Pročitao/-la sam i saglasan/-a sam sa <a href="%{privacy_policy_path}" target="_blank">politikom privatnosti</a>
+    providers:
+      cas: CAS-om
+      saml: SAML-om
     register: Registruj se
+    registration_closed: "%{instance} ne prima nove članove"
     resend_confirmation: Pošalji poruku sa uputstvima o potvrdi naloga ponovo
     reset_password: Resetuj lozinku
+    rules:
+      accept: Prihvati
+      back: Nazad
+      preamble: Ovo su pravila koja su uspostavili i koja sprovode moderatori servera %{domain}.
+      title: Neka osnovna pravila.
     security: Bezbednost
     set_new_password: Postavi novu lozinku
+    setup:
+      email_below_hint_html: Ako je imejl adresa ispod neispravna, možete je promeniti ovde i dobiti novi imejl potvrde.
+      email_settings_hint_html: Imejl potvrde je poslat na %{email}. Ako ta imejl adresa nije ispravna, možete je promeniti u podešavanjima naloga.
+      title: Postavljanje
+    sign_in:
+      preamble_html: Prijavite se sa svojim podacima za <strong>%{domain}</strong>. Ako se Vaš nalog nalazi na drugom serveru, nećete moći da se prijavite ovde.
+      title: Prijavite se na %{domain}
+    sign_up:
+      preamble: Sa nalogom na ovom Mastodon serveru, moći ćete da pratite bilo koga sa mreže, bez obzira na to na kom serveru se njegov/njen nalog nalazi.
+      title: Hajde da Vam namestimo nalog na %{domain}.
+    status:
+      account_status: Status naloga
+      confirming: Čekanje na potvrdu putem imejla.
+      functional: Vaš nalog je potpuno operativan.
+      pending: Vaš zahtev je na čekanju za pregled od strane našeg osoblja. Ovo može potrajati neko vreme. Primićete imejl poruku ukoliko Vam zahtev bude odobren.
+      redirecting_to: Vaš nalog je neaktivan jer preusmerava na %{acct}.
+      view_strikes: Pogledajte prethodne prestupe upisane na Vaše ime
+    too_fast: Formular je podnet prebrzo, pokušajte ponovo.
+    use_security_key: Koristite sigurnosni ključ
   authorize_follow:
+    already_following: Već pratite ovaj nalog
+    already_requested: Već ste poslali zahtev za praćenje tom nalogu
     error: Nažalost, desila se greška pri traženju udaljenog naloga
     follow: Zaprati
     follow_request: 'Poslali ste zahtev za praćenjen za:'
     following: 'Sjajno! Sada pratite:'
     post_follow:
       close: Ili možete zatvoriti ovaj prozor.
-      return: Vrati se na profil ovog korisnika
+      return: Vrati se na nalog ovog korisnika
       web: Idi na veb
     title: Zaprati %{acct}
+  challenge:
+    confirm: Nastavi
+    hint_html: "<strong>Savet:</strong> Nećemo Vas pitati za lozinku ponovo u narednih sat vremena."
+    invalid_password: Neispravna lozinka
+    prompt: Potvrdite lozinku za nastavak
+  crypto:
+    errors:
+      invalid_key: nije validan Ed25519 ili Curve25519 ključ
+      invalid_signature: nije validan Ed25519 potpis
+  date:
+    formats:
+      default: "%d. %b. %Y."
+      with_month_name: "%d. %B %Y."
   datetime:
     distance_in_words:
+      about_x_hours: "%{count} č."
       about_x_months: "%{count}mesec"
       about_x_years: "%{count}god"
       almost_x_years: "%{count}god"
       half_a_minute: Upravo sad
+      less_than_x_minutes: "%{count} min."
       less_than_x_seconds: Upravo sad
       over_x_years: "%{count}god"
+      x_days: "%{count}d"
+      x_minutes: "%{count} min."
       x_months: "%{count}mesec"
+      x_seconds: "%{count} sek."
   deletes:
+    challenge_not_passed: Lozinka koju ste uneli nije bila ispravna
     confirm_password: Unesite trenutnu lozinku da bismo proverili Vaš identitet
+    confirm_username: Unesite svoje korisničko ime da biste potvrdili proceduru
     proceed: Obriši nalog
     success_msg: Vaš nalog je uspešno obrisan
+    warning:
+      before: 'Pre nego što nastavite, molimo Vas pažljivo pročitajte sledeće napomene:'
+      caches: Sadržaj koji je keširan na drugim serverima može da ostane netaknut
+      data_removal: Vaše objave i drugi podaci će biti trajno izbrisani
+      email_change_html: Možete <a href="%{path}">promeniti svoju imejl adresu</a> bez brisanja svog naloga
+      email_contact_html: Ako poruka i dalje ne stiže, možete poslati mejl na <a href="mailto:%{email}">%{email}</a> za pomoć
+      email_reconfirmation_html: Ukoliko Vam imejl za potvrdu ne stiže, možete ga <a href="%{path}">ponovo zatražiti</a>
+      irreversible: Nećete biti u mogućnosti da vratite ili reaktivirate svoj nalog
+      more_details_html: Za više detalja, pogledajte <a href="%{terms_path}">politiku privatnosti</a>.
+      username_available: Vaše korisničko ime će ponovo postati dostupno
+      username_unavailable: Vaše korisničko ime će ostati nedostupno
+  disputes:
+    strikes:
+      action_taken: Radnja preduzeta
+      appeal: Žalba
+      appeal_approved: Žalba na ovaj prestup je uvažena i prestup više nije validan
+      appeal_rejected: Žalba je odbijena
+      appeal_submitted_at: Žalba je priložena
+      appealed_msg: Vaša žalba je priložena. Bićete obavešteni ukoliko ona bude bila uvažena.
+      appeals:
+        submit: Priloži žalbu
+      approve_appeal: Uvaži žalbu
+      associated_report: Srodna prijava
+      created_at: Datum
+      description_html: Ovo su radnje preduzete protiv Vašeg naloga i upozorenja koja su Vam poslali članovi osoblja %{instance}.
+      recipient: Usmereno prema
+      reject_appeal: Odbij žalbu
+      status: 'Objava #%{id}'
+      status_removed: Objava je već uklonjena iz sistema
+      title: "%{action} od %{date}"
+      title_actions:
+        delete_statuses: Brisanje objava
+        disable: Zamrzavanje naloga
+        mark_statuses_as_sensitive: Označavanje objava kao osetljivih
+        none: Upozorenje
+        sensitive: Označavanje naloga kao osetljivog
+        silence: Ograničenje naloga
+        suspend: Suspenzija naloga
+      your_appeal_approved: Vaša žalba je uvažena
+      your_appeal_pending: Priložili ste žalbu
+      your_appeal_rejected: Vaša žalba je odbijena
+  domain_validator:
+    invalid_domain: nelegitimno ime domena
   errors:
-    '400': The request you submitted was invalid or malformed.
+    '400': Zahtev koji ste podneli je bio nelegitiman ili u pogrešnom formatu.
     '403': Nemate dozvola da vidite ovu stranu.
     '404': Strana koju ste tražili ne postoji.
-    '406': This page is not available in the requested format.
+    '406': Ova stranica nije dostupna u izabranom formatu.
     '410': Strana koju ste tražili više ne postoji.
-    '422': 
+    '422':
+      content: Bezbedonosna provera nije uspela. Da ne blokirate kolačiće?
+      title: Bezbedonosna provera nije uspela
     '429': Uspored
     '500':
       content: Izvinjavamo se, nešto je pošlo po zlu sa ove strane.
       title: Strana nije ispravna
-    '503': The page could not be served due to a temporary server failure.
+    '503': Stranicu nije bilo moguće dostaviti usled privremenog pada servera.
     noscript_html: Da biste koristili Mastodont veb aplikaciju, omogućite JavaScript. U suprotnom, probajte neku od <a href="%{apps_path}">originalnih aplikacija</a> za Mastodont za Vašu platformu.
+  existing_username_validator:
+    not_found: nije bilo moguće pronaći lokalnog korisnika sa tim korisničkim imenom
+    not_found_multiple: nije bilo moguće pronaći %{usernames}
   exports:
+    archive_takeout:
+      date: Datum
+      download: Preuzmite Vašu arhivu
+      hint_html: Možete zatražiti arhivu vaših <strong>objava i otpremljenih medija</strong>. Izvezeni podaci će biti u ActivityPub formatu, koji može čitati bilo koji kompatibilan softver. Arhivu možete zatražiti svakih 7 dana.
+      in_progress: Sastavljanje vaše arhive...
+      request: Zatražite svoju arhivu
+      size: Veličina
     blocks: Blokirali ste
-    mutes: Ućutkali ste
+    bookmarks: Obeleživači
+    csv: CSV
+    domain_blocks: Blokovi domena
+    lists: Liste
+    mutes: Ignorišete
     storage: Multimedijalno skladište
+  featured_tags:
+    add_new: Dodaj novu
+    errors:
+      limit: Već ste istakli maksimalan broj heš oznaka
+    hint_html: "<strong>Šta su istaknute heš onake?</strong> One se prikazuju istaknuto na vašem javnom profilu i omogućuju ljudima da pregledaju vaše javne objave konkretno pod tim heš oznakama. One su sjajan alat za praćenje kreativnih radova ili dugoročnih projekata."
+  filters:
+    contexts:
+      account: Profili
+      home: Vremenska linija početne
+      notifications: Obaveštenja
+      public: Javne vremenske linije
+      thread: Razgovori
+    edit:
+      add_keyword: Dodaj ključnu reč
+      keywords: Ključne reči
+      statuses: Zasebne objave
+      statuses_hint_html: Ovaj filter važi za odabrane zasebne objave bez obzira na to da li sadrže ključne reči navedene ispod. <a href="%{path}">Pregledajte ili uklonite objave iz filtera</a>.
+      title: Izmeni filter
+    errors:
+      deprecated_api_multiple_keywords: Ovi parametri ne mogu biti promenjeni u ovoj aplikaciji zato što se odnose na više od jedne ključne reči. Koristite ažurniju aplikaciju ili veb interfejs.
+      invalid_context: Nijedan ili nevažeći kontekst isporučen
+    index:
+      contexts: Filtrira u %{contexts}
+      delete: Izbriši
+      empty: Nemate filtere.
+      expires_in: Ističe za %{distance}
+      expires_on: Ističe datuma %{date}
+      keywords:
+        few: "%{count} ključne reči"
+        one: "%{count} ključna reč"
+        other: "%{count} ključnih reči"
+      statuses:
+        few: "%{count} objave"
+        one: "%{count} objava"
+        other: "%{count} objava"
+      statuses_long:
+        few: "%{count} zasebne objave sakrivene"
+        one: "%{count} zasebna objava sakrivena"
+        other: "%{count} zasebnih objava sakriveno"
+      title: Filteri
+    new:
+      save: Sačuvaj novi filter
+      title: Dodaj novi filter
+    statuses:
+      back_to_filter: Nazad na filter
+      batch:
+        remove: Ukloni iz filtera
+      index:
+        hint: Ovaj filter važi za odabrane zasebne objave bez obzira na druge kriterijume. Možete da dodate više objava u ovaj filter putem veb interfejsa.
+        title: Filtrirane objave
   generic:
+    all: Svi
+    all_items_on_page_selected_html:
+      few: Sve <strong>%{count}</strong> stavke sa ove stranice su izabrane.
+      one: "<strong>%{count}</strong> stavka sa ove stranice je izabrana."
+      other: Svih <strong>%{count}</strong> stavki sa ove stranice je izabrano.
+    all_matching_items_selected_html:
+      few: Sve <strong>%{count}</strong> stavke koje se poklapaju sa Vašom pretragom su izabrane.
+      one: "<strong>%{count}</strong> stavka koja se poklapa sa Vašom pretragom je izabrana."
+      other: Svih <strong>%{count}</strong> stavki koje se poklapaju sa Vašom pretragom su izabrane.
     changes_saved_msg: Izmene uspešno sačuvane!
-    save_changes: Snimi izmene
+    copy: Kopiraj
+    delete: Izbriši
+    deselect: Poništi sve izbore
+    none: Nijedna
+    order_by: Sortiraj prema
+    save_changes: Sačuvaj promene
+    select_all_matching_items:
+      few: Odaberite sve %{count} stavke koje se poklapaju sa Vašom pretragom.
+      one: Odaberite %{count} stavku koja se poklapa sa Vašom pretragom.
+      other: Odaberite svih %{count} stavki koje se poklapaju sa Vašom pretragom.
+    today: danas
     validation_errors:
       few: Nešto nije baš kako treba! Pregledajte %{count} greške ispod
       one: Nešto nije baš kako treba! Pregledajte greške ispod
       other: Nešto nije baš kako treba! Pregledajte %{count} grešaka ispod
   imports:
+    errors:
+      invalid_csv_file: 'Neispravan CSV fajl. Greška: %{error}'
+      over_rows_processing_limit: sadrži više od %{count} redova
+    modes:
+      merge: Stapanje
+      merge_long: Zadržite postojeće zapise i dodajte nove
+      overwrite: Zameni
+      overwrite_long: Zameni trenutne zapise novima
     preface: Možete uvesti podatke koje ste izvezli sa druge instance, kao što su liste ljudi koje ste pratili ili blokirali.
     success: Vaši podaci su uspešno otpremljeni i biće obrađeni uskoro
     types:
       blocking: Lista blokiranja
+      bookmarks: Obeleživači
+      domain_blocking: Lista blokiranih domena
       following: Lista pratilaca
       muting: Lista ućutkanih
     upload: Otpremi
@@ -243,10 +1275,11 @@ sr-Latn:
       '21600': 6 sati
       '3600': 1 sad
       '43200': 12 sati
-      '604800': 1 week
+      '604800': 1 nedelja
       '86400': 1 dan
     expires_in_prompt: Nikad
     generate: Generiši
+    invited_by: 'Pozvao Vas je:'
     max_uses:
       few: "%{count} korišćenja"
       one: 1 korišćenje
@@ -256,105 +1289,460 @@ sr-Latn:
     table:
       expires_at: Ističe
       uses: Korišćenja
-    title: Pozovi ljude
+    title: Pozovite ljude
   lists:
     errors:
-      limit: Dostigli ste limit broja listi
+      limit: Dostigli ste maksimalni broj listâ
+  login_activities:
+    authentication_methods:
+      otp: aplikacija za dvofaktorsku autentifikaciju
+      password: lozinka
+      sign_in_token: imejl sigurnosni kod
+      webauthn: sigurnosni ključevi
+    description_html: Ukoliko primetite aktivnost koju ne prepoznajete, razmislite o tome da promenite svoju lozinku i uključite dvofaktorsku autentifikaciju.
+    empty: Istorija autentifikacije nije dostupna
+    failed_sign_in_html: Neuspešan pokušaj prijavljivanja putem %{method} sa %{ip} (%{browser})
+    successful_sign_in_html: Uspešan pokušaj prijavljivanja putem %{method} sa %{ip} (%{browser})
+    title: Istorija autentifikacije
   media_attachments:
     validations:
       images_and_video: Ne može da se prikači video na status koji već ima slike
+      not_ready: Ne mogu se priložiti fajlovi koji još uvek nisu obrađeni. Pokušajte ponovo za koji trenutak!
       too_many: Ne može se prikačiti više od 4 fajla
   migrations:
     acct: korisnik@domen novog naloga
+    cancel: Otkaži preusmerenje
+    cancel_explanation: Otkazivanje preusmerenja će ponovo aktivirati Vaš sadašnji nalog, ali neće vratiti pratioce koji su premešteni na drugi nalog.
+    cancelled_msg: Uspešno je otkazano preusmerenje.
+    errors:
+      already_moved: već ste se preselili na isti nalog
+      missing_also_known_as: nije pseudonim ovog naloga
+      move_to_self: ne možete se preseliti na sadašnji nalog
+      not_found: nije bilo moguće pronaći nalog
+      on_cooldown: Pod ograničenjem ste
+    followers_count: Pratioci u trenutku premeštaja
+    incoming_migrations: Premeštanje iz drugog naloga
+    incoming_migrations_html: Da biste prešli sa drugog naloga na ovaj, prvo morate <a href="%{path}">kreirate pseudonim naloga</a>.
+    moved_msg: Vaš nalog sada preusmerava na %{acct} i Vaši pratioci se trenutno prebacuju.
+    not_redirecting: Vaš nalog trenutno ne preusmerava ni na jedan drugi nalog.
+    on_cooldown: Nedavno ste se preselili na novi nalog. Ova funkcija će Vam ponovo postati dostupna za %{count} dana.
+    past_migrations: Prethodne selidbe
+    proceed_with_move: Prebacite pratioce
+    redirected_msg: Vaš nalog sada preusmerava na %{acct}.
+    redirecting_to: Vaš nalog preusmerava na %{acct}.
+    set_redirect: Postavi preusmerenje
+    warning:
+      backreference_required: Novi nalog prvo mora biti konfigurisan tako da referiše na ovaj nalog
+      before: 'Pre nego što nastavite, molimo Vas pažljivo pročitajte sledeće napomene:'
+      cooldown: Nakon preseljenja potrebno je da prođe određeno vreme pre nego što ćete ponovo moći da se preselite
+      disabled_account: Vaš trenutni nalog više neće biti upotrebljiv. Međutim, imaćete pristup izvozu podataka kao i reaktivaciji.
+      followers: Ova radnja će premestiti sve pratioce sa trenutnog naloga na novi nalog
+      only_redirect_html: Umesto preseljenja, možete <a href="%{path}">samo dodati preusmeravajući link na svoj profil.</a>.
+      other_data: Ostali podaci neće biti automatski prebačeni
+      redirect: Profil Vašeg sadašnjeg naloga će biti ažuriran sa obaveštenjem o preusmerenju i biće isključen iz pretrage
   moderation:
     title: Moderacija
+  move_handler:
+    carry_blocks_over_text: Ovaj korisnik se preselio sa naloga %{acct}, koji ste blokirali.
+    carry_mutes_over_text: Ovaj korisnik se preselio sa naloga %{acct}, koji ste utišali.
+    copy_account_note_text: 'Ovaj korisnik se preselio sa naloga %{acct}, o kome ste zapisali sledeće beleške:'
+  navigation:
+    toggle_menu: Prikaži/sakrij meni
   notification_mailer:
+    admin:
+      report:
+        subject: "%{name} je podneo/-la prijavu"
+      sign_up:
+        subject: "%{name} se registrovao/-la"
     favourite:
       body: "%{name} je postavio kao omiljen Vaš status:"
       subject: "%{name} je postavio kao omiljen Vaš status"
+      title: Novi omiljeni
     follow:
       body: "%{name} Vas je zapratio!"
       subject: "%{name} Vas je zapratio"
+      title: Novi pratioc
     follow_request:
+      action: Upravljajte zahtevima za praćenje
       body: "%{name} je zatražio da Vas zaprati"
       subject: 'Pratioci na čekanju: %{name}'
+      title: Novi zahtev za praćenje
     mention:
+      action: Odgovori
       body: "%{name} Vas je pomenuo u:"
       subject: "%{name} Vas je pomenuo"
+      title: Novo spominjanje
+    poll:
+      subject: Anketa korisnika %{name} se završila
     reblog:
-      body: "%{name} Vam je podržao(la) status:"
-      subject: "%{name} je podržao(la) Vaš status"
+      body: "%{name} Vam je podržao/la status:"
+      subject: "%{name} je podržao/la Vaš status"
+      title: Nova podrška
+    status:
+      subject: "%{name} je upravo postavio/-la objavu"
+    update:
+      subject: "%{name} je izmenio/-la objavu"
+  notifications:
+    email_events: Događaji za obaveštenja e-poštom
+    email_events_hint: 'Izaberite dešavanja za koja želite da primate obaveštenja:'
+    other_settings: Ostala podešavanja obaveštenja
+  number:
+    human:
+      decimal_units:
+        format: "%n %u"
+        units:
+          billion: mlrd.
+          million: mil.
+          quadrillion: tril.
+          thousand: hilj.
+          trillion: bil.
+  otp_authentication:
+    code_hint: Ukucajte kod generisan u Vašoj aplikaciji za autentifikaciju da biste potvrdili
+    description_html: Ako uključite <strong>dvofaktorsku autentifikaciju</strong> putem aplikacije za autentifikaciju, moraćete da budete pri telefonu prilikom prijavljivanja da biste imali pristup generisanim kodovima za prijavu.
+    enable: Omogući
+    instructions_html: "<strong>Skenirajte ovaj QR kod pomoću Google autentifikatora ili slične aplikacije na svom telefonu</strong>. Od sada pa ubuduće, ta aplikacija će generisati pristupne kodove koje ćete morati da unesete prilikom prijavljivanja."
+    manual_instructions: 'Ako ne možete da skenirate QR kod i morate da ga unesete ručno, evo njegove tekstualne šifre:'
+    setup: Instalacija
+    wrong_code: Uneseni kod je bio neispravan! Da li su vreme servera i vreme uređaja ispravni?
   pagination:
-    next: Sledeći
+    newer: Novije
+    next: Sledeće
+    older: Starije
     prev: Prethodni
+    truncate: "&hellip;"
+  polls:
+    errors:
+      already_voted: Već ste glasali u ovoj anketi
+      duplicate_options: sadrži duplikate
+      duration_too_long: previše je daleko u budućnosti
+      duration_too_short: previše je skoro
+      expired: Anketa je već završena
+      invalid_choice: Izabrana opcija ne postoji
+      over_character_limit: ne može biti duže od po %{max} karaktera
+      too_few_options: mora imati više od jedne opcije
+      too_many_options: ne može da sadrži više od %{max} opcija
   preferences:
-    other: Ostali
+    other: Ostalo
+    posting_defaults: Podrazumevana podešavanja objavljivanja
+    public_timelines: Javne vremenske linije
+  privacy_policy:
+    title: Politika privatnosti
+  reactions:
+    errors:
+      limit_reached: Dostignuto je ograničenje različitih reakcija
+      unrecognized_emoji: nije prepoznat emodži
+  relationships:
+    activity: Aktivnost naloga
+    confirm_follow_selected_followers: Da li ste sigurni da želite da pratite izabrane pratioce?
+    confirm_remove_selected_followers: Da li ste sigurni da želite da uklonite izabrane pratioce?
+    confirm_remove_selected_follows: Da li ste sigurni da želite da uklonite izabrana praćenja?
+    dormant: Neaktivan
+    follow_failure: Nije moguće pratiti neke od izabranih naloga.
+    follow_selected_followers: Prati izabrane pratioce
+    followers: Pratioci
+    following: Praćenja
+    invited: Pozvan
+    last_active: Poslednji put aktivan
+    most_recent: Najnoviji
+    moved: Premešten
+    mutual: Zajednički
+    primary: Primarni
+    relationship: Odnos
+    remove_selected_domains: Ukloni sve pratioce sa izabranih domena
+    remove_selected_followers: Ukloni izabrane pratioce
+    remove_selected_follows: Otprati izabrane korisnike
+    status: Status naloga
   remote_follow:
     missing_resource: Ne mogu da nađem zahtevanu adresu preusmeravanja za Vaš nalog
+  reports:
+    errors:
+      invalid_rules: ne referiše na legitimna pravila
+  rss:
+    content_warning: 'Upozorenje o sadržaju:'
+    descriptions:
+      account: Javne objave sa @%{acct}
+      tag: 'Javne objave označene sa #%{hashtag}'
+  scheduled_statuses:
+    over_daily_limit: Prekoračili ste granicu od %{limit} planiranih objava za danas
+    over_total_limit: Prekoračili ste granicu od %{limit} planiranih objava
+    too_soon: Planirani datum mora biti u budućnosti
   sessions:
     activity: Poslednja aktivnost
     browser: Veb čitač
     browsers:
-      chrome: Hrom
+      alipay: Alipej
+      blackberry: Blekberi
+      chrome: Chrome
+      edge: Majkrosoft Edž
+      electron: Elektron
+      firefox: Fajerfoks
       generic: Nepoznati veb čitač
+      huawei_browser: Huawei pregledač
+      ie: Internet Eksplorer
+      micro_messenger: MajkroMesendžer
+      nokia: Nokija S40 Ovi Pretraživač
+      opera: Opera
+      otter: Oter
+      phantom_js: FantomDžejEs
+      qq: KjuKju Pretraživač
+      safari: Safari
+      uc_browser: UC Browser
+      unknown_browser: Nepoznati pregledač
+      weibo: Veibo
     current_session: Trenutna sesija
     description: "%{browser} sa %{platform}"
-    explanation: Ovo su trenutno prijavljeni veb čitači na Vaš Mastodont nalog.
+    explanation: Ovo su veb pretraživači koji su trenutno prijavljeni na Vaš Mastodon nalog.
+    ip: IP
     platforms:
-      adobe_air: Adobe Air-a
+      adobe_air: Adob Er-a
       android: Androida
+      blackberry: Blekberi
+      chrome_os: ChromeOS
       firefox_os: Fajerfoks OS-a
+      ios: iOS-a
+      kai_os: KaiOS
       linux: Linuksa
-      mac: Mac-a
-      other: nepoznate platforme
-      windows: Vindouza
-      windows_mobile: Vindouz mobilnog
-      windows_phone: Vindouz telefona
+      mac: Meka
+      unknown_platform: Nepoznata platforma
+      windows: Windows
+      windows_mobile: Windows Mobile
+      windows_phone: Windows Phone
     revoke: Opozovi
     revoke_success: Sesija uspešno opozvana
     title: Sesije
+    view_authentication_history: Pogledajte istoriju autentifikacije vašeg naloga
   settings:
+    account: Nalog
+    account_settings: Podešavanja naloga
+    aliases: Pseudonimi naloga
+    appearance: Izgled
     authorized_apps: Autorizovane aplikacije
-    back: Nazad na Mastodonta
+    back: Nazad na Mastodon
     delete: Brisanje naloga
     development: Razvoj
-    edit_profile: Izmena profila
+    edit_profile: Uređivanje profila
     export: Izvoz podataka
+    featured_tags: Istaknute heš oznake
     import: Uvoz
+    import_and_export: Uvoz i izvoz
     migrate: Prebacivanje naloga
     notifications: Obaveštenja
     preferences: Podešavanja
+    profile: Nalog
+    relationships: Praćenja i pratioci
+    statuses_cleanup: Automatsko brisanje objava
+    strikes: Moderacijski prestupi
     two_factor_authentication: Dvofaktorska identifikacija
+    webauthn_authentication: Sigurnosni ključevi
   statuses:
+    attached:
+      audio:
+        few: "%{count} audio zapisa"
+        one: "%{count} audio zapis"
+        other: "%{count} audio zapisa"
+      description: 'U prilogu: %{attached}'
+      image:
+        few: "%{count} slika"
+        one: "%{count} sliku"
+        other: "%{count} slika"
+      video:
+        few: "%{count} video zapisa"
+        one: "%{count} video zapis"
+        other: "%{count} video zapisa"
+    boosted_from_html: Podržano od %{acct_link}
+    content_warning: 'Upozorenje na sadržaj: %{warning}'
+    default_language: Isto kao jezik okruženja
+    disallowed_hashtags:
+      few: 'sadrži zabranjene heštegove: %{tags}'
+      one: 'sadrži zabranjeni hešteg: %{tags}'
+      other: 'sadrži zabranjene heštegove: %{tags}'
+    edited_at_html: Izmenjeno %{date}
+    errors:
+      in_reply_not_found: Objava na koju pokušavate da odgovorite naizgled ne postoji.
     open_in_web: Otvori u vebu
     over_character_limit: ograničenje od %{max} karaktera prekoračeno
     pin_errors:
-      limit: Već imate prikačen najveći broj tutova
-      ownership: Tuđi tutovi ne mogu da se prikače
+      direct: Objave koje su vidljive samo pomenutim korisnicima ne mogu biti prikačene
+      limit: Već ste zakačili maksimalan broj objava
+      ownership: Tuđa objava se ne može zakačiti
       reblog: Podrška ne može da se prikači
+    poll:
+      total_people:
+        few: "%{count} osobe"
+        one: "%{count} osoba"
+        other: "%{count} ljudi"
+      total_votes:
+        few: "%{count} glasa"
+        one: "%{count} glas"
+        other: "%{count} glasova"
+      vote: Glasajte
     show_more: Prikaži još
+    show_newer: Nikad ne prikazuj
+    show_older: Prikaži starije
+    show_thread: Prikaži niz
+    sign_in_to_participate: Prijavite se da učestvujete u razgovoru
+    title: "%{name}: „%{quote}”"
     visibilities:
+      direct: Direktno
       private: Samo pratioci
-      private_long: Samo prikaži pratiocima
+      private_long: Prikaži samo pratiocima
       public: Javno
       public_long: Svako može da vidi
       unlisted: Neizlistano
-      unlisted_long: Svako može da vidi, ali nije izlistano na javnim lajnama
+      unlisted_long: Svako može da vidi, ali nije izlistano na javnim vremenskim linijama
+  statuses_cleanup:
+    enabled: Automatski izbriši stare objave
+    enabled_hint: Automatski briše vaše objave kada dostignu određeni starosni prag, osim ako se ne podudaraju sa jednim od izuzetaka u nastavku
+    exceptions: Izuzeci
+    explanation: Pošto je brisanje objava skupa operacija, ovo se radi polako tokom vremena kada server inače nije zauzet. Iz tog razloga, vaše objave mogu biti izbrisane neko vreme nakon što dostignu starosni prag.
+    ignore_favs: Ignoriši omiljene
+    ignore_reblogs: Ignoriši podržavanja
+    interaction_exceptions: Izuzeci zasnovani na interakcijama
+    interaction_exceptions_explanation: Imajte na umu da ne postoji garancija da će objave biti izbrisane ako broj označavanja kao omiljenih ili broj podržavanja padne ispod praga nakon što ga premaše.
+    keep_direct: Zadrži direktne poruke
+    keep_direct_hint: Ne briše nijednu od vaših direktnih poruka
+    keep_media: Zadrži objave sa medijskim prilozima
+    keep_media_hint: Ne briše nijednu od vaših objava koje imaju medijske priloge
+    keep_pinned: Zadrži zakačene objave
+    keep_pinned_hint: Ne briše nijednu od vaših zakačenih objava
+    keep_polls: Zadrži ankete
+    keep_polls_hint: Ne briše nijednu od vaših anketa
+    keep_self_bookmark: Zadrži objave koje ste dodali u obeleživače
+    keep_self_bookmark_hint: Ne briše vaše sopstvene objave ako ste ih dodlai u obeleživače
+    keep_self_fav: Zadrži omiljene objave
+    keep_self_fav_hint: Ne briše vaše sopstvene objave ako ste ih označili kao omiljene
+    min_age:
+      '1209600': 2 sedmice
+      '15778476': 6 meseci
+      '2629746': 1 mesec
+      '31556952': 1 godina
+      '5259492': 2 meseca
+      '604800': 1 sedmica
+      '63113904': 2 godine
+      '7889238': 3 meseca
+    min_age_label: Starosni prag
+    min_favs: Zadrži objave označene kao omiljene najmanje
+    min_favs_hint: Ne briše nijednu vašu objavu koja je dobila najmanje ovaj broj omiljenih. Ostavite prazno za brisanje objava bez obzira na njihov broj omiljenih
+    min_reblogs: Zadrži objave podržane barem
+    min_reblogs_hint: Ne briše nijednu vašu objavu koja je bila podržana najmanje ovoliko puta. Ostavite prazno za brisanje objava bez obzira na njihov broj podržavanja
   stream_entries:
-    pinned: Prikačeni tut
+    pinned: Zakačena objava
     reblogged: podržano
     sensitive_content: Osetljiv sadržaj
+  strikes:
+    errors:
+      too_late: Istekao je rok za podnošenje žalbe na zabeležen prestup
+  tags:
+    does_not_match_previous_name: ne poklapa se sa prethodnim imenom
   themes:
-    default: Mastodont
+    contrast: Veliki kontrast
+    default: Mastodon
+    mastodon-light: Mastodon (svetlo)
+  time:
+    formats:
+      default: "%d %b %Y, %H:%M"
+      month: "%b %Y"
+      time: "%H:%M"
   two_factor_authentication:
+    add: Dodaj
     disable: Isključi
+    disabled_success: Dvofaktorska autentifikacija je uspešno onemogućena
+    edit: Izmeni
     enabled: Dvofaktorska identifikacija je uključena
     enabled_success: Dvofaktorska identifikacija je uspešno uključena
     generate_recovery_codes: Generiši kodove za oporavak
-    lost_recovery_codes: Kodovi za oporavak Vam omogućavaju da povratite pristup nalogu ako izgubite telefon. Ako izgubite kodove za oporavak, možete ih regenerisati ovde. Od tog trenutka, stari kodovi za oporavak više ne važe.
+    lost_recovery_codes: Kodovi za oporavak Vam omogućavaju da povratite pristup nalogu ako izgubite telefon. Ako izgubite kodove za oporavak, možete ih re-generisati ovde. Od tog trenutka, stari kodovi za oporavak više ne važe.
+    methods: Metode dvofaktorske autentifikacije
+    otp: Aplikacija za autentifikaciju
     recovery_codes: Napravite rezervu kodova za oporavak
-    recovery_codes_regenerated: Kodovi za oporavak uspešno regenerisani
+    recovery_codes_regenerated: Kodovi za oporavak uspešno re-generisani
     recovery_instructions_html: Ako ikada izgubite pristup telefonu, možete iskoristiti kodove za oporavak date ispod da povratite pristup nalogu. <strong>Držite kodove za oporavak na sigurnom</strong>. Na primer, odštampajte ih i čuvajte ih sa ostalim važnim dokumentima.
+    webauthn: Sigurnosni ključevi
+  user_mailer:
+    appeal_approved:
+      action: Idite na svoj nalog
+      explanation: Žalba podneta datuma %{appeal_date} na upisan prestup na Vaše ime datuma %{strike_date} je uvažena. Vaš nalog je ponovo u povoljnom položaju.
+      subject: Vaša žalba podneta %{date} je uvažena
+      title: Žalba uvažena
+    appeal_rejected:
+      explanation: Žalba podneta datuma %{appeal_date} na upisan prestup na Vaše ime datuma %{strike_date} je odbijena.
+      subject: Vaša žalba podneta %{date} je odbijena
+      title: Žalba odbijena
+    backup_ready:
+      explanation: Tražili ste potpunu rezervnu kopiju vašeg Mastodon računa. Spremna za preuzimanje!
+      subject: Vaša arhiva je spremna za preuzimanje
+      title: Izvoz arhive
+    suspicious_sign_in:
+      change_password: promenite svoju lozinku
+      details: 'Evo detalja o prijavi:'
+      explanation: Primetili smo prijavu na Vaš nalog sa nepoznate IP adrese.
+      further_actions_html: Ukoliko to niste bili Vi, preporučujemo da odmah %{action} i uključite dvofaktorsku autentifikaciju da biste održali bezbednost svog naloga.
+      subject: Vašem nalogu je pristupljeno sa nepozate IP adrese
+      title: Novo prijavljivanje
+    warning:
+      appeal: Priložite žalbu
+      appeal_description: Ukoliko verujete da je u pitanju greška, možete priložiti žalbu osoblju %{instance}.
+      categories:
+        spam: Neželjena pošta
+        violation: Sadržaj krši sledeća pravila zajednice
+      explanation:
+        delete_statuses: Za neke od Vaših objava je ustanovljeno da krše jedno ili više pravila zajednice i usled toga su uklonjene od strane moderatora %{instance}.
+        disable: Više ne možete da koristite svoj nalog, ali Vaš profil i drugi podaci ostaju netaknuti. Možete da zatražite rezervnu kopiju svojih podataka, promenite podešavanja naloga ili obrišete svoj nalog.
+        mark_statuses_as_sensitive: Neke od Vaših objava su označene kao osetljive od strane moderatora %{instance}. Ovo znači da će ljudi morati da kliknu na multimedije u objavama pre nego što mogu da ih vide. Ubuduće možete sami da označite svoju multimediju kao osetljivu prilikom sastavljanja objave.
+        sensitive: Od sada će svi Vaši otpremljeni multimedijalni fajlovi biti označeni kao osetljivi i sakriveni iza tastera upozorenja.
+        silence: I dalje možete koristiti svoj nalog ali samo ljudi koji Vas već prate će videti Vaše objave na ovom serveru, i možda ćete biti isključeni iz raznih mehanizama otkrivanja. Međutim, drugi ljudi i dalje mogu ručno da Vas zaprate.
+        suspend: Više ne možete da koristite svoj nalog, i Vaš profil i ostali podaci Vam više nisu dostupni. I dalje možete da se prijavite da biste zatražili rezervnu kopiju svojih podataka sve dok se Vaši podaci trajno ne izbrišu za oko 30 dana, s tim što ćemo zadržati neke osnovne podatke da bismo Vas sprečili u eventualnom zaobilaženju suspenzije.
+      reason: 'Obrazloženje:'
+      statuses: 'Citirane objave:'
+      subject:
+        delete_statuses: Vaše objave sa %{acct} su izbrisane
+        disable: Vaš nalog %{acct} je zamrznut
+        mark_statuses_as_sensitive: Vaše objave sa %{acct} su obeležene kao osetljive
+        none: Upozorenje za %{acct}
+        sensitive: Vaše objave sa %{acct} će ubuduće biti označene kao osetljive
+        silence: Vaš nalog %{acct} je ograničen
+        suspend: Vaš nalog %{acct} je suspendovan
+      title:
+        delete_statuses: Objave su obrisane
+        disable: Nalog zamrznut
+        mark_statuses_as_sensitive: Objave su označene kao osetljive
+        none: Upozorenje
+        sensitive: Nalog je označen kao osetljiv
+        silence: Nalog ograničen
+        suspend: Nalog suspendovan
+    welcome:
+      edit_profile_action: Podesi nalog
+      edit_profile_step: Možete prilagoditi svoj profil tako što ćete postaviti profilnu sliku, promeniti ime za prikaz i tako dalje. Možete dati saglasnost da pregledate nove pratioce pre nego što im dozvolite da Vas zaprate.
+      explanation: Evo nekoliko saveta za početak
+      final_action: Počnite objavljivati
+      final_step: 'Počnite da objavljujete! Čak i bez pratilaca, Vaše javne objave su vidljive drugim ljudima, na primer na lokalnoj vremenskoj liniji ili u heš oznakama. Možda želite da se predstavite sa heš oznakom #introductions ili #predstavljanja.'
+      full_handle: Vaš pun nadimak
+      full_handle_hint: Ovo biste rekli svojim prijateljima kako bi vam oni poslali poruku, ili zapratili sa druge instance.
+      subject: Dobrodošli na Mastodon
+      title: Dobrodošli, %{name}!
   users:
+    follow_limit_reached: Ne možete pratiti više od %{limit} ljudi
+    go_to_sso_account_settings: Idite na podešavanja naloga svog dobavljača identiteta
     invalid_otp_token: Neispravni dvofaktorski kod
-    signed_in_as: 'Prijavljen kao:'
+    otp_lost_help_html: Ako izgubite pristup za oba, možete stupiti u kontakt sa %{email}
+    seamless_external_login: Prijavljeni ste putem spoljašnje usluge, tako da lozinka i podešavanja E-pošte nisu dostupni.
+    signed_in_as: 'Prijavljen/a kao:'
+  verification:
+    verification: Provera
+  webauthn_credentials:
+    add: Dodajte novi sigurnosni ključ
+    create:
+      error: Iskrsao je problem prilikom dodavanja Vašeg sigurnosnog ključa. Molimo Vas pokušajte ponovo.
+      success: Vaš sigurnosni ključ je uspešno dodat.
+    delete: Izbriši
+    delete_confirmation: Da li ste sigurni da želite da izbrišete ovaj sigurnosni ključ?
+    description_html: Ako uključite <strong>autentifikaciju sigurnosnim ključem</strong>, moraćete da koristite jedan od svojih sigurnosnih ključeva prilikom prijavljivanja.
+    destroy:
+      error: Iskrsao je problem prilikom brisanja Vašeg sigurnosnog ključa. Molimo Vas pokušajte ponovo.
+      success: Vaš sigurnosni ključ je uspešno obrisan.
+    invalid_credential: Neispravan sigurnosni ključ
+    nickname_hint: Unesite nadimak svog novog sigurnosnog ključa
+    not_enabled: Još uvek niste omogućili WebAuthn
+    not_supported: Ovaj pretraživač ne podržava sigurnosne ključeve
+    otp_required: Da biste koristili sigurnosne ključeve, molimo Vas prvo uključite dvofaktorsku autentifikaciju.
+    registered_on: Registrovan/-a %{date}
diff --git a/config/locales/sr.yml b/config/locales/sr.yml
index 656b6f763..fb90cef5d 100644
--- a/config/locales/sr.yml
+++ b/config/locales/sr.yml
@@ -1,23 +1,24 @@
 ---
 sr:
   about:
-    about_mastodon_html: Мастодон је друштвена мрежа базирана на отвореним протоколима и слободном софтверу отвореног кода. Децентрализована је као што је децентрализована е-пошта.
+    about_mastodon_html: 'Друштвена мрежа будућности: без реклама, без корпоративног праћења, етички дизајн, и децентрализација! Поседујте своје податке са Мастодоном!'
     contact_missing: Није постављено
     contact_unavailable: Није доступно
     hosted_on: Мастодон хостован на %{domain}
-    title: О
+    title: О инстанци
   accounts:
-    follow: Прати
+    follow: Запрати
     followers:
-      few: Пратиоци
-      one: Пратиоц
-      other: Пратиоци
+      few: Пратиоца
+      one: Пратилац
+      other: Пратилаца
     following: Пратим
-    last_active: последњи пут активни
+    instance_actor_flash: Овај налог је виртуелни актер који не представља ниједног корисника лично, већ сâм сервер. Користи се у сврху федерације и не треба га суспендовати.
+    last_active: најскорија активност
     link_verified_on: Власништво над овом везом је проверено %{date}
-    nothing_here: Овде нема ништа!
+    nothing_here: Овде нема ничега!
     pin_errors:
-      following: Морате пратити ову особу ако хоћете да потврдите
+      following: Морате пратити особу коју желите да препоручите
     posts:
       few: Објаве
       one: Објава
@@ -34,16 +35,17 @@ sr:
     accounts:
       add_email_domain_block: Блокирај домен е-поште
       approve: Одобри
+      approved_msg: Захтев за регистрацију корисника %{username} је успешно одобрен
       are_you_sure: Да ли сте сигурни?
       avatar: Аватар
       by_domain: Домен
       change_email:
         changed_msg: Е-пошта налога успешно промењена!
-        current_email: Тренутна е-пошта
-        label: Промените е-пошту
-        new_email: Нова e-пошта
-        submit: Промените e-пошту
-        title: Промените e-пошту за %{username}
+        current_email: Тренутна адреса е-поште
+        label: Промените адресу е-поште
+        new_email: Нова адреса e-поште
+        submit: Промените адресу e-поште
+        title: Промените адресу e-поште за %{username}
       change_role:
         changed_msg: Улога успешно промењена!
         label: Промени улогу
@@ -51,21 +53,25 @@ sr:
         title: Промени улогу за %{username}
       confirm: Потврди
       confirmed: Потврђено
-      confirming: Потврдување
+      confirming: Потврђивање
       custom: Произвољно
       delete: Обриши податке
       deleted: Избрисано
       demote: Ражалуј
-      disable: Искључи
+      destroyed_msg: Подаци корисника %{username} су неповратно стављени у ред за брисање
+      disable: Замрзни
+      disable_sign_in_token_auth: Онемогући имејл аутентификацију
       disable_two_factor_authentication: Искључи 2FA
-      disabled: Искључена
-      display_name: Приказано име
+      disabled: Замрзнут
+      display_name: Име за приказ
       domain: Домен
       edit: Измени
       email: Е-пошта
       email_status: Статус е-поште
       enable: Омогући
-      enabled: Укључено
+      enable_sign_in_token_auth: Омогући имејл аутентификацију
+      enabled: Омогућен
+      enabled_msg: Успешно одлеђен налог корисника %{username}
       followers: Пратиоци
       follows: Праћени
       header: Заглавље
@@ -73,18 +79,21 @@ sr:
       invite_request_text: Разлози за придруживање
       invited_by: Позван од стране
       ip: IP
-      joined: Придружио се
+      joined: Придружио/-ла се
       location:
         all: Све
-        local: Локалне
-        remote: Удаљене
+        local: Локални
+        remote: Удаљени
         title: Локација
       login_status: Статус пријаве
       media_attachments: Мултимедијални прилози
       memorialize: Пребаци у in memoriam
+      memorialized: Подигнут споменик
+      memorialized_msg: "%{username} је успешно претворен у меморијални налог"
       moderation:
         active: Активан
         all: Сви
+        disabled: Искључен
         pending: На чекању
         silenced: Ограничено
         suspended: Суспендовани
@@ -92,75 +101,202 @@ sr:
       moderation_notes: Модераторске белешке
       most_recent_activity: Најскорија активност
       most_recent_ip: Најскорија IP адреса
+      no_account_selected: Ниједан налог није промењен јер ниједан није изабран
       no_limits_imposed: Нема ограничења
       no_role_assigned: Ниједна улога није додељена
       not_subscribed: Није претплаћен
       pending: Чека на преглед
-      perform_full_suspension: Искључи
+      perform_full_suspension: Суспендуј
+      previous_strikes: Претходни прекршаји
+      previous_strikes_description_html:
+        few: Овај налог има <strong>%{count}</strong> прекршаја.
+        one: Овај налог има <strong>један</strong> прекршај.
+        other: Овај налог има <strong>%{count}</strong> прекршаја.
       promote: Унапреди
       protocol: Протокол
       public: Јавно
       push_subscription_expires: PuSH претплата истиче
-      redownload: Освежи налог
-      reject: Одбаци
+      redownload: Освежи профил
+      redownloaded_msg: Успешно освежен профил корисника %{username} из извора
+      reject: Одбиј
+      rejected_msg: Захтев за регистрацију корисника %{username} је успешно одбијен
+      remote_suspension_irreversible: Подаци овог налога су неповратно избрисани.
+      remote_suspension_reversible_hint_html: Налог је суспендован са свог сервера и његови подаци ће бити избрисани датума %{date}. До тада, удаљени сервер може да врати овај налог без икаквих промена. Уколико желите да одмах обришете све податке налога, можете то учинити испод.
       remove_avatar: Уклони аватар
       remove_header: Одстрани заглавље
+      removed_avatar_msg: Слика аватара корисника %{username} је успешно уклоњена
+      removed_header_msg: Успешно обрисана слика заглавља корисника %{username}
       resend_confirmation:
-        already_confirmed: Овој корисник е веќе потврден
-        send: Препрати го е-мајлот за потврда
-        success: Е-пошта за потврда успешно испратена!
+        already_confirmed: Овај корисник је већ потврђен
+        send: Поново пошаљи имејл потврду
+        success: Имејл за потврду је успешно послат!
       reset: Ресетуј
       reset_password: Ресетуј лозинку
       resubscribe: Поново се претплати
       role: Улога
       search: Претрага
+      search_same_email_domain: Остали корисници са истим доменом е-поште
+      search_same_ip: Остали корисници са истом IP адресом
+      security: Безбедност
       security_measures:
         only_password: Само лозинка
+        password_and_2fa: Лозинка и двофакторска аутентификација
+      sensitive: Означи као осетљив
       sensitized: Означено као осетљиво
       shared_inbox_url: Адреса дељеног сандучета
       show:
-        created_reports: Направљени извештаји
+        created_reports: Поднете пријаве
         targeted_reports: Пријаве од стране других
       silence: Ућуткај
       silenced: Ућуткан
-      statuses: Статуси
+      statuses: Објаве
+      strikes: Претходни преступи
       subscribe: Претплати се
       suspend: Суспендуј
       suspended: Суспендовани
+      suspension_irreversible: Подаци овог налога су неповратно избрисани. Можете одсуспендовати овај налог чиме ће он постати употребљив, али се подаци претходно садржани на налогу неће вратити.
+      suspension_reversible_hint_html: Налог је суспендован и његови подаци ће бити избрисани датума %{date}. До тада, овај налог може бити враћен без икаквих промена. Уколико желите да одмах обришете све податке налога, можете то учинити испод.
       title: Налози
       unblock_email: Одблокирај адресу е-поште
-      unconfirmed_email: Непотврђена е-пошта
+      unblocked_email_msg: Успешно одблокирана имејл адреса корисника %{username}
+      unconfirmed_email: Непотврђена адреса е-поште
+      undo_sensitized: Уклони ознаку „осетљив”
       undo_silenced: Укини ћутање
       undo_suspension: Укини суспензију
+      unsilenced_msg: Успешно поништено ограничење налога %{username}
       unsubscribe: Укини претплату
+      unsuspended_msg: Успешно поништена суспензија налога %{username}
       username: Корисничко име
+      view_domain: Прочитај опис домена
       warn: Упозори
       web: Веб
+      whitelisted: Дозвољена федерација
     action_logs:
       action_types:
+        approve_appeal: Уважи жалбу
         approve_user: Одобри корисника
+        assigned_to_self_report: Додели пријаву
         change_email_user: Промени e-адресу корисника
         change_role_user: Промени улогу корисника
         confirm_user: Потврди корисника
         create_account_warning: Креирај упозорење
         create_announcement: Креирај најаву
         create_canonical_email_block: Креирај блок е-поште
+        create_custom_emoji: Направи прилагођени емоџи
+        create_domain_allow: Додај дозвољени домен
+        create_domain_block: Додај блокирани домен
+        create_email_domain_block: Блокирај имејл домен
+        create_ip_block: Направи IP услов
+        create_unavailable_domain: Додај домен као недоступан
         create_user_role: Креирај улогу
+        demote_user: Смањи овлашћења корисника
         destroy_announcement: Избриши најаву
         destroy_canonical_email_block: Избриши блок е-поште
+        destroy_custom_emoji: Обриши прилагођени емоџи
+        destroy_domain_allow: Обриши дозвољени домен
+        destroy_domain_block: Обриши блокирани домен
+        destroy_email_domain_block: Избриши блок е-поште
+        destroy_instance: Очисти домен
+        destroy_ip_block: Обриши IP услов
         destroy_status: Избриши пост
+        destroy_unavailable_domain: Обриши недоступан домен
+        destroy_user_role: Уништи позицију
+        disable_2fa_user: Онемогући двофакторску аутентификацију
+        disable_custom_emoji: Онемогући прилагођене емоџије
+        disable_sign_in_token_auth_user: Онемогући имејл аутентификацију за корисника
         disable_user: Онемогући корисника
+        enable_custom_emoji: Омогући прилагођене емоџије
+        enable_sign_in_token_auth_user: Омогући имејл аутентификацију за корисника
         enable_user: Омогући корисника
+        memorialize_account: Претвори у меморијални налог
+        promote_user: Унапреди корисника
+        reject_appeal: Одбиј жалбу
         reject_user: Одбаци корисника
+        remove_avatar_user: Уклони аватар
+        reopen_report: Поново отвори пријаву
+        resend_user: Поново пошаљи мејл за потврду
+        reset_password_user: Ресетуј лозинку
+        resolve_report: Затвори пријаву
+        sensitive_account: Означи налог као осетљив
         silence_account: Ограничи налог
         suspend_account: Обустави налог
+        unassigned_report: Повуци пријаву
+        unblock_email_account: Одблокирај имејл адресу
+        unsensitive_account: Уклони додељену ознаку „осетљив”
+        unsilence_account: Повуци ограничење налога
+        unsuspend_account: Повуци суспензију налога
+        update_announcement: Саопштење о ажурирању
+        update_custom_emoji: Ажурирај прилагођене емоџије
+        update_domain_block: Ажурирај домен блок
+        update_ip_block: Ажурирај IP услов
         update_status: Уреди објаву
         update_user_role: Уреди улогу
+      actions:
+        approve_appeal_html: "%{name} је уважио жалбу корисника %{target} на одлуку модератора"
+        approve_user_html: "%{name} је одобрио/-ла регистрацију корисника %{target}"
+        assigned_to_self_report_html: "%{name} је себи доделио/-ла пријаву %{target}"
+        change_email_user_html: "%{name} је променио/-ла имејл адресу корисника %{target}"
+        change_role_user_html: "%{name} је променио/-ла овлашћења корисника %{target}"
+        confirm_user_html: "%{name} је потврдио/-ла имејл адресу корисника %{target}"
+        create_account_warning_html: "%{name} је послао/-ла упозорење кориснику %{target}"
+        create_announcement_html: "%{name} је направио/-ла ново саопштење %{target}"
+        create_canonical_email_block_html: "%{name} је блокирао/-ла имејл адресу са хешом %{target}"
+        create_custom_emoji_html: "%{name} је отпремио/-ла нове емоџије %{target}"
+        create_domain_allow_html: "%{name} је дозволио/-ла федерацију са доменом %{target}"
+        create_domain_block_html: "%{name} је блокирао/-ла домен %{target}"
+        create_email_domain_block_html: "%{name} је блокирао/-ла имејл домен %{target}"
+        create_ip_block_html: "%{name} је направио/-ла услов за IP адресе %{target}"
+        create_unavailable_domain_html: "%{name} је обуставио/-ла испоручивање домену %{target}"
+        create_user_role_html: "%{name} је направио/-ла %{target} позицију"
+        demote_user_html: "%{name} је смањио овлашћења корисника %{target}"
+        destroy_announcement_html: "%{name} је обрисао/-ла саопштење %{target}"
+        destroy_canonical_email_block_html: "%{name} је одблокирао/-ла имејл адресу са хешом %{target}"
+        destroy_custom_emoji_html: "%{name} је обрисао/-ла емоџи %{target}"
+        destroy_domain_allow_html: "%{name} је забранио/-ла федерацију са доменом %{target}"
+        destroy_domain_block_html: "%{name} је одблокирао/-ла домен %{target}"
+        destroy_email_domain_block_html: "%{name} је одблокирао/-ла имејл домен %{target}"
+        destroy_instance_html: "%{name} је очистио/-ла домен %{target}"
+        destroy_ip_block_html: "%{name} је обрисао/-ла услов за IP адресе %{target}"
+        destroy_status_html: "%{name} је избрисао/-ла објаву корисника %{target}"
+        destroy_unavailable_domain_html: "%{name} је поново успоставио/-ла испоручивање домену %{target}"
+        destroy_user_role_html: "%{name} је избрисао/-ла %{target} позицију"
+        disable_2fa_user_html: "%{name} је онемогућио/-ла двофакторску аутентификацију за корисника %{target}"
+        disable_custom_emoji_html: "%{name} је онемогућио/-ла емоџи %{target}"
+        disable_sign_in_token_auth_user_html: "%{name} је онемогућио/-ла имејл аутентификацију за корисника %{target}"
+        disable_user_html: "%{name} је онемогућио/-ла пријављивање за корисника %{target}"
+        enable_custom_emoji_html: "%{name} је омогућио/-ла емоџи %{target}"
+        enable_sign_in_token_auth_user_html: "%{name} је омогућио/-ла имејл аутентификацију за %{target}"
+        enable_user_html: "%{name} је омогућио/-ла пријављивање кориснику %{target}"
+        memorialize_account_html: "%{name} је претворио/-ла налог корисника %{target} у меморијалну страницу"
+        promote_user_html: "%{name} је унапредио/-ла корисника %{target}"
+        reject_appeal_html: "%{name} је одбио/-ла жалбу на модерацијску одлуку коју је приложио корисник %{target}"
+        reject_user_html: "%{name} је одбио/-ла регистрацију корисника %{target}"
+        remove_avatar_user_html: "%{name} је уклонио аватар корисника %{target}"
+        reopen_report_html: "%{name} је поново отворио/-ла пријаву %{target}"
+        resend_user_html: "%{name} је поново послао/-ла имејл за потврду кориснику %{target}"
+        reset_password_user_html: "%{name} је ресетовао/-ла лозинку корисника %{target}"
+        resolve_report_html: "%{name} је решио/-ла пријаву %{target}"
+        sensitive_account_html: "%{name} је означио/-ла медије налога %{target} као осетљиве"
+        silence_account_html: "%{name} је ограничио/-ла налог %{target}"
+        suspend_account_html: "%{name} је суспендовао/-ла налог %{target}"
+        unassigned_report_html: "%{name} је повукао/-ла доделу пријаве %{target}"
+        unblock_email_account_html: "%{name} је одблокирао/-ла имејл адресу корисника %{target}"
+        unsensitive_account_html: "%{name} је уклонио/-ла ознаку „осетљиво” са медија налога %{target}"
+        unsilence_account_html: "%{name} је повукао/-ла ограничење налога %{target}"
+        unsuspend_account_html: "%{name} је повукао/-ла суспензију налога %{target}"
+        update_announcement_html: "%{name} је ажурирао/-ла саопштење %{target}"
+        update_custom_emoji_html: "%{name} је ажурирао/-ла емоџи %{target}"
+        update_domain_block_html: "%{name} је ажурирао/-ла блок домена %{target}"
+        update_ip_block_html: "%{name} је променио/-ла IP услов за %{target}"
+        update_status_html: "%{name} је ажурирао/-ла објаву корисника %{target}"
+        update_user_role_html: "%{name} је променио/-ла позицију %{target}"
       deleted_account: обрисан налог
+      empty: Није пронађен ниједан лог.
       filter_by_action: Филтрирај по активности
       filter_by_user: Филтрирај по кориснику
       title: Записник
     announcements:
+      destroyed_msg: Саопштење је успешно обрисано!
       edit:
         title: Уреди најаву
       empty: Ниједна најава није пронађена.
@@ -170,7 +306,12 @@ sr:
         title: Нова најава
       publish: Објави
       published_msg: Најава успешно објављена!
+      scheduled_for: Заказано за %{time}
+      scheduled_msg: Саопштење је заказано за објављивање!
       title: Најаве
+      unpublish: Повуци објаву
+      unpublished_msg: Објава саопштења је успешно повучена!
+      updated_msg: Саопштење је успешно ажурирано!
     custom_emojis:
       assign_category: Додели категорију
       by_domain: Домен
@@ -188,82 +329,212 @@ sr:
       enable: Омогући
       enabled: Омогућено
       enabled_msg: Емоџи успешно омогућен
+      image_hint: PNG или GIF фајл величине до %{size}
+      list: Листа
       listed: Излистан
       new:
         title: Додај нови произвољни емоџи
+      no_emoji_selected: Ниједан емоџи није промењен јер ниједан није изабран
+      not_permitted: Нисте овлашћени да обављате ову радњу
       overwrite: Препиши
       shortcode: Пречица
       shortcode_hint: Најмање 2 карактера, дозвољени су само слова, бројеви и доње црте
       title: Произвољни емотиџији
+      uncategorized: Некатегоризовано
+      unlist: Неизлистан
       unlisted: Неизлистан
       update_failed_msg: Не могу да ажурирам овај емоџи
       updated_msg: Емоџи успешно ажуриран!
       upload: Отпреми
     dashboard:
+      active_users: активни корисници
+      interactions: интеракције
+      media_storage: Мултимедијално складиште
+      new_users: нови корисници
+      opened_reports: отворене пријаве
+      pending_appeals_html:
+        few: "<strong>%{count}</strong> жалбе на чекању"
+        one: "<strong>%{count}</strong> жалба на чекању"
+        other: "<strong>%{count}</strong> жалби на чекању"
+      pending_reports_html:
+        few: "<strong>%{count}</strong> пријаве на чекању"
+        one: "<strong>%{count}</strong> пријава на чекању"
+        other: "<strong>%{count}</strong> пријава на чекању"
+      pending_tags_html:
+        few: "<strong>%{count}</strong> хеш ознаке на чекању"
+        one: "<strong>%{count}</strong> хеш ознака на чекању"
+        other: "<strong>%{count}</strong> хеш ознака на чекању"
+      pending_users_html:
+        few: "<strong>%{count}</strong> корисника на чекању"
+        one: "<strong>%{count}</strong> корисник на чекању"
+        other: "<strong>%{count}</strong> корисника на чекању"
+      resolved_reports: решене пријаве
       software: Софтвер
+      sources: Извори регистрација
       space: Коришћење простора
       title: Командна табла
+      top_languages: Најзаступљенији језици
+      top_servers: Најактивнији сервери
+      website: Вебсајт
+    disputes:
+      appeals:
+        empty: Ниједна жалба није пронађена.
+        title: Жалбе
     domain_allows:
+      add_new: Дозволи федерацију са доменом
+      created_msg: Домен је успешно одобрен за федерацију
+      destroyed_msg: Домен је одбијен за федерацију
       export: Извоз
       import: Увоз
+      undo: Забрани федерацију са доменом
     domain_blocks:
       add_new: Додај нови блок домена
       created_msg: Блокирање домена се обрађује
       destroyed_msg: Блокирање домена је опозвано
       domain: Домен
+      edit: Измени блок домена
+      existing_domain_block: Већ сте успоставили строжа ограничења према кориснику %{name}.
+      existing_domain_block_html: Већ сте успоставили строжа ограничења према %{name}, потребно је да га прво <a href="%{unblock_url}">одблокирате</a>.
       export: Извоз
       import: Увоз
       new:
         create: Направи блокаду
         hint: Блокирање домена неће спречити прављење налога у бази, али ће ретроактивно и аутоматски применити одређене модераторске методе над тим налозима.
         severity:
+          desc_html: "<strong>Ограничење</strong> ће сакрити објаве налога са овог домена од сваког ко их не прати. <strong>Суспензија</strong> ће обрисати сав садржај, медије и податке профила са налога овог домена са Вашег сервера. Користите <strong>Ништа</strong> уколико само желите да одбијете медијске фајлове."
           noop: Ништа
+          silence: Ограничи
           suspend: Суспензија
         title: Ново блокирање домена
+      no_domain_block_selected: Ниједан блок домена није промењен јер ниједан није изабран
+      not_permitted: Нисте овлашћени да обављате ову радњу
+      obfuscate: Сакриј име домена
+      obfuscate_hint: Делимично сакриј име домена на листи ако је рекламирање листе ограничених домена омогућено
       private_comment: Приватни коментар
+      private_comment_hint: Коментар о ограничењу овог домена за интерну употребу од стране модератора.
       public_comment: Јавни коментар
+      public_comment_hint: Коментар о ограничењу овог домена за јавност, уколико је рекламирање листе ограничених домена омогућено.
       reject_media: Одбаци мултимедију
       reject_media_hint: Уклања локално ускладиштене мултимедијске фајлове и одбија да их скида убудуће. Небитно је за суспензију
       reject_reports: Одбаци извештај
       reject_reports_hint: Игнориши све извештаје који долазе са овог домена. Небитно је за суспензије
       undo: Поништи блок домена
+      view: Прочитај блок домена
     email_domain_blocks:
       add_new: Додај нови
+      attempts_over_week:
+        few: "%{count} покушаја током претходне недеље"
+        one: "%{count} покушај током претходне недеље"
+        other: "%{count} покушаја регистрације током претходне недеље"
       created_msg: Успешно додао домен Е-поште на црну листу
       delete: Обриши
+      dns:
+        types:
+          mx: MX извештај
       domain: Домен
       new:
         create: Додај домен
+        resolve: Претвори домен
         title: Нова ставка е-поштe у црној листи
+      no_email_domain_block_selected: Ниједан блок имејл домена није промењен јер ниједан није изабран
+      not_permitted: Није дозвољено
+      resolved_dns_records_hint_html: Име домена се претвара у следеће MX домене, који су напослетку одговорни за прихватање електронске поште. Блокирање MX домена ће блокирати регистрације са сваке имејл адресе која користи тај MX домен, чак и у случају када се видљиво име домена разликује. <strong>Водите рачуна о томе да не блокирате велике имејл провајдере.</strong>
+      resolved_through_html: Преусмерено кроз %{domain}
       title: Црна листа E-поште
+    export_domain_allows:
+      new:
+        title: Увези дозвољене домене
+      no_file: Ниједан фајл није одабран
+    export_domain_blocks:
+      import:
+        description_html: Управо ћете увести листу блокираних домена. Молимо Вас, врло пажљиво прегледајте ову листу, посебно уколико је нисте сами направили.
+        existing_relationships_warning: Постојећи односи у облику праћења
+        private_comment_description_html: 'Да би Вам помогли да пратите одакле су блокови увезени, увезени блокови ће бити направљени са следећим приватним коментаром: <q>%{comment}</q>'
+        private_comment_template: Увезено са извора %{source} на датум %{date}
+        title: Увези блокиране домене
+      invalid_domain_block: 'Један или више блокова домена је прескочен због следеће грешке тј. грешака: %{error}'
+      new:
+        title: Увези блокиране домене
+      no_file: Ниједан фајл није одабран
     follow_recommendations:
+      description_html: "<strong>Предлози за праћење помажу новим корисницима да брзо пронађу занимљив садржај</strong>. Када корисник није довољно интераговао са осталима да би се за њега формирали персонализовани предлози за праћење, ови налози ће бити препоручени уместо тога. Они се генеришу на дневној бази из скупа налога са највише недавних ангажовања и највише локалних пратилаца за један језик."
+      language: За језик
       status: Статус
+      suppress: Потисни препоруке за праћење
+      suppressed: Потиснуто
+      title: Препоруке за праћење
+      unsuppress: Врати препоруку за праћење
     instances:
+      availability:
+        description_html:
+          few: Уколико испорука домену не успе ниједном током <strong>%{count} различитих дана</strong>, даљи покушаји испоруке неће бити иницирани осим уколико се не прими испорука <em>са</em> домена.
+          one: Ако испорука домену не успе ниједном у временском периоду од <strong>%{count} дана</strong>, даљи покушаји испоруке се неће иницирати осим уколико се не прими испорука <em>са</em> домена.
+          other: Уколико испорука домену не успе ниједном током <strong>%{count} различитих дана</strong>, даљи покушаји испоруке се неће иницирати осим уколико се не прими испорука <em>са</em> домена.
+        failure_threshold_reached: Праг неуспеха достигнут датума %{date}.
+        failures_recorded:
+          few: Неуспели покушаји током %{count} различита дана.
+          one: Неуспели покушај током %{count} дана.
+          other: Неуспели покушаји током %{count} различитих дана.
+        no_failures_recorded: Без забележених неуспеха.
+        title: Доступност
+        warning: Последњи покушај повезивања са овим сервером је био неуспешан
       back_to_all: Све
       back_to_limited: Ограничено
       back_to_warning: Упозорење
       by_domain: Домен
+      confirm_purge: Да ли сте сигурни да желите да трајно уклоните податке са овог домена?
       content_policies:
+        comment: Интерна белешка
+        description_html: Можете да дефинишете политику садржаја која ће важити за све налоге на овом домену и сваком од његових поддомена.
+        limited_federation_mode_description_html: Можете да одлучите да ли да допустите федерацију са овим доменом.
         policies:
+          reject_media: Одбиј мултимедију
+          reject_reports: Одбиј пријаве
           silence: Ограничи
           suspend: Суспендуј
         policy: Политика
         reason: Јавни разлог
+        title: Политика садржаја
+      dashboard:
+        instance_accounts_dimension: Најпраћенији налози
+        instance_accounts_measure: ускладиштени налози
+        instance_followers_measure: наши пратиоци овде
+        instance_follows_measure: њихови пратиоци овде
+        instance_languages_dimension: Најзаступљенији језици
+        instance_media_attachments_measure: ускладиштени мултимедијални прилози
+        instance_reports_measure: пријаве против њих
+        instance_statuses_measure: ускладиштене објаве
       delivery:
         all: Све
+        clear: Очисти грешке приликом испоруке
+        failing: Без успеха
+        restart: Започни испоруку поново
+        stop: Обустави испоруку
         unavailable: Недоступно
       delivery_available: Достава је доступна
+      delivery_error_days: Дани неуспешних испорука
+      delivery_error_hint: Уколико испорука није могућа %{count} дана, аутоматски ће бити обележена као неиспоручива.
+      destroyed_msg: Подаци са %{domain} су сада у реду за чекање за извесно брисање.
+      empty: Ниједан домен није пронађен.
+      known_accounts:
+        few: "%{count} позната налога"
+        one: "%{count} познат налог"
+        other: "%{count} познатих налога"
       moderation:
         all: Све
         limited: Ограничено
         title: Модерација
       private_comment: Приватни коментар
       public_comment: Јавни коментар
+      purge: Чистка
+      purge_description_html: Уколико верујете да је овај домен трајно угашен, можете да обришете све записе налога и сродне податке овог домена са свог складишта. Ово може да потраје.
       title: Федерација
       total_blocked_by_us: Блокирано од стране нас
       total_followed_by_them: Праћени од стране њих
       total_followed_by_us: Праћени од стране нас
       total_reported: Пријаве везане за њих
+      total_storage: Мултимедијални прилози
+      totals_time_period_hint_html: Укупне вредности приказане испод укључују податке за сва времена.
     invites:
       deactivate_all: Деактивирај све
       filter:
@@ -273,6 +544,9 @@ sr:
         title: Филтер
       title: Позивнице
     ip_blocks:
+      add_new: Направи правило
+      created_msg: Успешно је додато ново IP правило
+      delete: Избриши
       expires_in:
         '1209600': 2 недеље
         '15778476': 6 месеци
@@ -283,6 +557,9 @@ sr:
       new:
         title: Креирај ново IP правило
       no_ip_block_selected: Ниједно IP правило није промењено јер ниједно није изабрано
+      title: IP правила
+    relationships:
+      title: Односи корисника %{acct}
     relays:
       add_new: Додај нови релеј
       delete: Обриши
@@ -290,95 +567,422 @@ sr:
       disable: Искључи
       disabled: Искључен
       enable: Укључи
-      enable_hint: Када се омогући, Ваш сервер ће бити претплаћен на све јавне трубе са овог релеја, и почеће да шаље своје јавне трубу на њега.
+      enable_hint: Када се омогући, ваш сервер ће бити претплаћен на све јавне објаве са овог релеја, и почеће да шаље јавне објаве овог сервера на њега.
       enabled: Укључен
       inbox_url: URL Релеја
       pending: Чека се одобрење релеја
       save_and_enable: Сачувај и омогући
       setup: Подеси везу релеја
+      signatures_not_enabled: Преноси можда неће радити исправно док је укључен безбедни режим или режим ограничене федерације
       status: Статус
       title: Релеји
     report_notes:
       created_msg: Белешка пријаве успешно направљена!
       destroyed_msg: Белешка пријаве успешно избрисана!
     reports:
+      account:
+        notes:
+          few: "%{count} белешке"
+          one: "%{count} белешка"
+          other: "%{count} бележака"
+      action_log: Записник
       action_taken_by: Акцију извео
+      actions:
+        delete_description_html: Пријављене објаве ће бити обрисане и прекршај ће бити уписан да би Вам олакшали интервенцију приликом будућих преступа са истог налога.
+        mark_as_sensitive_description_html: Мултимедијални садржај са пријављених објава ће бити означен као осетљив и прекршај ће бити уписан ради лакше интервенције у случају даљих прекршаја са истог налога.
+        other_description_html: Погледајте више опција за контролисање понашања налога и прилагодите комуникацију са пријављеним налогом.
+        resolve_description_html: Ниједна радња неће бити предузета против пријављеног налога, ниједан преступ није уписан и пријава ће бити затворена.
+        silence_description_html: Налог ће бити видљив само онима који га већ прате или који га ручно потраже, што ће значајно ограничити његов домет. Ограничење се може повући у сваком тренутку. Затвара све пријаве поднете против овог налога.
+        suspend_description_html: Налог и сви његови садржаји ће постати недоступни и у једном тренутку избрисани, а интеракција са налогом више неће бити могућа. Суспензија се може повући у року од 30 дана. Затвара све пријаве поднете против овог налога.
+      actions_description_html: Одлучите коју радњу да спроведете ради решавања ове пријаве. Уколико спроведете казнену радњу против пријављеног налога, власник налога ће бити обавештен путем и-мејла, осим уколико ознака <strong>„Непожељне поруке”</strong> није одабрана.
+      actions_description_remote_html: Одлучите коју радњу да предузмете ради решавања ове пријаве. Ово ће утицати само на то како <strong>Ваш</strong> сервер комуницира са овим удаљеним налогом и обрађује његов садржај.
+      add_to_report: Додај још у пријаву
       are_you_sure: Да ли сте сигурни?
       assign_to_self: Додели мени
       assigned: Додељени модератор
+      by_target_domain: Домен пријављеног налога
+      cancel: Откажи
+      category: Категорија
+      category_description_html: Разлог због ког је овај налог и/или садржај пријављен ће бити образложен у комуникацији са пријављеним налогом
       comment:
         none: Ништа
+      comment_description_html: 'Ради пружања више информација, %{name} је написао/-ла:'
+      confirm: Потврди
+      confirm_action: Потврди модерацијску радњу према @%{acct}
       created_at: Пријављена
+      delete_and_resolve: Обриши објаве
+      forwarded: Прослеђено
+      forwarded_to: Прослеђено ка %{domain}
       mark_as_resolved: Означи као решену
+      mark_as_sensitive: Обележи као осетљиво
       mark_as_unresolved: Означи као нерешену
+      no_one_assigned: Нико
       notes:
         create: Додај белешку
         create_and_resolve: Реши са белешком
         create_and_unresolve: Отвори поново са белешком
         delete: Обриши
         placeholder: Опишите какве су радње предузете, или било какве повезане новости...
+        title: Белешке
+      notes_description_html: Прочитајте и оставите напомене другим модераторима и себи у будућности
+      processed_msg: 'Пријава #%{id} успешно обрађена'
+      quick_actions_description_html: 'Предузмите брзу радњу или се спустите ниже да бисте видели пријављени садржај:'
+      remote_user_placeholder: удаљени корисник са %{instance}
       reopen: Отвори пријаву поново
       report: 'Пријава #%{id}'
       reported_account: Пријављени налог
       reported_by: Пријавио
       resolved: Решена
       resolved_msg: Пријава успешно разрешена!
+      skip_to_actions: Прескочи до радњи
       status: Статус
+      statuses: Пријављени садржај
+      statuses_description_html: Спорни садржај ће бити наведен у комуникацији са пријављеним налогом
+      summary:
+        action_preambles:
+          delete_html: 'Управо ћете <strong>обрисати</strong> неке од објава корисника <strong>@%{acct}</strong>. Ово ће:'
+          mark_as_sensitive_html: 'Управо ћете <strong>означити</strong> неке објаве корисника <strong>@%{acct}</strong> као <strong>осетљиве</strong>. Ово ће:'
+          silence_html: 'Управо ћете <strong>ограничити</strong> налог корисника <strong>@%{acct}</strong>. Ово ће:'
+          suspend_html: 'Управо ћете <strong>суспендовати</strong> налог корисника <strong>@%{acct}</strong>. Ово ће:'
+        actions:
+          delete_html: Обришите спорне објаве
+          mark_as_sensitive_html: Обележите медије спорних објава као осетљиве
+          silence_html: Жестоко ограничите домет корисника <strong>@%{acct}</strong> тако што ћете учинити његов профил и садржаје видљиве само људима који их већ прате и људима који ручно потраже профил
+          suspend_html: Суспендујте корисника <strong>@%{acct}</strong>, што ће учинити његов профил и садржаје недоступним за приступ и интеракцију
+        close_report: 'Означите пријаву #%{id} као решену'
+        close_reports_html: Означи <strong>све</strong> пријаве против <strong>@%{acct}</strong> као решене
+        delete_data_html: Обриши профил и садржаје корисника <strong>@%{acct}</strong> за 30 дана осим уколико суспензија буде повучена у међувремену
+        preview_preamble_html: "<strong>@%{acct}</strong> ће примити упозорење следеће садржине:"
+        record_strike_html: Упишите прекршај на име <strong>@%{acct}</strong> да би Вам било лакше да у будућности интервенишете приликом даљих преступа са овог налога
+        send_email_html: Пошаљи имејл упозорења кориснику <strong>@%{acct}</strong>
+        warning_placeholder: Опционо додатно образложење за модерацијску радњу.
+      target_origin: Порекло пријављеног налога
       title: Пријаве
       unassign: Уклони доделу
+      unknown_action_msg: 'Непозната радња: %{action}'
       unresolved: Нерешене
       updated_at: Ажурирана
       view_profile: Погледај профил
     roles:
       add_new: Додај улогу
+      assigned_users:
+        few: "%{count} корисника"
+        one: "%{count} корисник"
+        other: "%{count} корисника"
       categories:
         administration: Администрација
+        devops: DevOps
+        invites: Позивнице
         moderation: Модерација
+        special: Посебно
       delete: Избриши
+      description_html: Помоћу <strong>корисничких улога</strong> можете да подесите којим функцијама и деловима Мастодона Ваши корисници могу да приступе.
+      edit: Измени улогу '%{name}'
+      everyone: Подразумевана овлашћења
+      everyone_full_description_html: Ово је <strong>основна улога</strong> која се односи на <strong>све кориснике</strong>, чак и оне којима није додељена улога. Све друге улоге наслеђују овлашћења од основне улоге.
+      permissions_count:
+        few: "%{count} дозволе"
+        one: "%{count} дозвола"
+        other: "%{count} дозвола"
       privileges:
         administrator: Администратор
+        administrator_description: Корисници са овом привилегијом могу да заобиђу сва друга ограничења
         delete_user_data: Избриши податке корисника
+        delete_user_data_description: Допушта корисницима да избришу податке других корисника без одлагања
         invite_users: Позови кориснике
+        invite_users_description: Допушта корисницима да позове нове људе на сервер
         manage_announcements: Управљај обавештењима
+        manage_announcements_description: Допушта корисницима да руководе саопштењима на серверу
+        manage_appeals: Надгледање жалби
+        manage_appeals_description: Допушта корисницима да прегледају жалбе на модерацијске радње
+        manage_blocks: Надгледање блокова
+        manage_blocks_description: Допушта корисницима да блокирају имејл провајдере и IP адресе
+        manage_custom_emojis: Надлежност над прилагођеним емоџијима
+        manage_custom_emojis_description: Даје корисницима контролу над прилагођеним емоџијима на серверу
+        manage_federation: Надгледање федерације
+        manage_federation_description: Допушта корисницима да блокирају или дозволе федерацију са другим доменима и контролишу испоручивање података другим серверима
+        manage_invites: Надгледање позивница
+        manage_invites_description: Допушта корисницима да претражују и деактивирају позивнице
+        manage_reports: Надгледање пријава
+        manage_reports_description: Допушта корисницима да прегледају пријаве и извршавају модерацијске радње над њима
         manage_roles: Управљај улогама
+        manage_roles_description: Допушта корисницима да надгледају и додељују улоге са нижим овлашћењима од њихове
         manage_rules: Управљај правилима
         manage_rules_description: Дозволи корисницима да мењају правила сервера
         manage_settings: Управљај поставкама
         manage_settings_description: Дозволи корисницима да мењају поставке сајта
+        manage_taxonomies: Надгледање таксономија
+        manage_taxonomies_description: Допушта корисницима да прегледају садржај у тренду и ажурирају поставке хеш ознака
+        manage_user_access: Надлежност над корисничким приступом
+        manage_user_access_description: Допушта корисницима да онемогуће двофакторску аутентификацију других корисника, мењају им имејл адресе и ресетују им лозинке
+        manage_users: Надлежност над корисницима
+        manage_users_description: Допушта корисницима да прочитају детаље других корисника и извршавају модерацијске радње над њима
+        manage_webhooks: Надлежност над webhook елементима
+        manage_webhooks_description: Допушта корисницима да успоставе webhook елементе за административне радње
+        view_audit_log: Прочитај записник ревизија
+        view_audit_log_description: Допушта корисницима да виде историју административних радњи на серверу
+        view_dashboard: Погледај контролни панел
+        view_dashboard_description: Допушта корисницима да приступе контролном панелу и разним метрикама
+        view_devops: DevOps
+        view_devops_description: Допушта корисницима да приступе Sidekiq и pgHero контролним панелима
+      title: Улоге
     rules:
       add_new: Додај правило
       delete: Избриши
+      description_html: Док већина тврди да је прочитала и слаже се са условима коришћења, људи их обично не читају све док се не јави проблем. <strong>Учините правила Вашег сервера читљивијим и приступачнијим тако што ћете их изложити у форми листе.</strong> Потрудите се да појединачна правила буду кратка и једноставна, али покушајте и да их не исцепкате у превише одвојених ставки.
       edit: Уреди правило
       empty: Ниједно правило сервера још није дефинисано.
       title: Правила сервера
     settings:
+      about:
+        manage_rules: Управљање правилима сервера
+        preamble: Пружите детаљне информације о томе како се сервер води, модерира и финансира.
+        rules_hint: Постоји предвиђено место за правила која се од корисника очекује да поштују.
+        title: Назив
       appearance:
+        preamble: Прилагодите веб интерфејс Мастодона.
         title: Изглед
+      branding:
+        preamble: Брендирање Вашег сервера га издваја од других сервера на мрежи. Ове информације могу бити приказане у разним окружењима, попут Мастодоновог веб интерфејса, нативних апликација, у прегледима линкова на другим серверима и у апликацијама за размену порука, итд. Из овог разлога, најбоље је да ове информације буду кратке, јасне и концизне.
+        title: Брендирање
+      content_retention:
+        preamble: Контролишите како се садржај генерисан од стране корисника складишти на Мастодону.
+        title: Задржавање садржаја
       default_noindex:
         desc_html: Утиче на све кориснике који нису сами променили ову поставку
         title: Подразумевано искључи кориснике из индексирања претраживача
       discovery:
+        follow_recommendations: Препоруке за праћење
+        preamble: Одржавање занимљивих садржаја на површини је кључно у привлачењу нових корисника који можда не знају никога на Мастодону. Контролишите како различити начини истраживања функционишу на Вашем серверу.
+        profile_directory: Директоријум профилâ
         public_timelines: Јавне временске линије
+        publish_discovered_servers: Објави откривене сервере
+        publish_statistics: Објави статистику
+        title: Откривање
+        trends: Трендови
+      domain_blocks:
+        all: Свима
+        disabled: Никоме
+        users: Пријављеним локалним корисницима
+      registrations:
+        preamble: Контролишите ко сме да направи налог на Вашем серверу.
+        title: Регистрације
+      registrations_mode:
+        modes:
+          approved: Одобрење неопходно за регистрацију
+          none: Нико не може да се региструје
+          open: Било ко може да се региструје
+      title: Подешавања сервера
+    site_uploads:
+      delete: Обриши отпремљени фајл
+      destroyed_msg: Отпремање успешно обрисано!
     statuses:
+      account: Аутор
+      application: Апликација
       back_to_account: Назад на страну налога
+      back_to_report: Назад на страницу са пријавама
+      batch:
+        remove_from_report: Уклоните из пријаве
+        report: Пријави
+      deleted: Обрисано
+      favourites: Омиљено
+      history: Историја верзијâ
+      in_reply_to: Одговор на
+      language: Језик
       media:
         title: Мултимедија
+      metadata: Мета подаци
       no_status_selected: Ниједан статус није промењен јер ниједан није изабран
+      open: Отвори објаву
+      original_status: Оригинална објава
+      reblogs: Дељења
+      status_changed: Објава промењена
       title: Статуси налога
+      trending: У тренду
+      visibility: Видљивост
       with_media: Са мултимедијом
+    strikes:
+      actions:
+        delete_statuses: "%{name} је обрисао/-ла објаве %{target}"
+        disable: "%{name} је замрзнуо налог корисника %{target}"
+        mark_statuses_as_sensitive: "%{name} је означио/-ла објаве корисника %{target} као осетљиве"
+        none: "%{name} је послао/-ла упозорење кориснику %{target}"
+        sensitive: "%{name} је обележио/-ла налог %{target} као осетљив"
+        silence: "%{name} је ограничио/-ла налог %{target}"
+        suspend: "%{name} је суспендовао/-ла налог %{target}"
+      appeal_approved: Жалба уважена
+      appeal_pending: Жалба у разматрању
+      appeal_rejected: Жалба одбијена
+    system_checks:
+      database_schema_check:
+        message_html: Селидбе базâ података су на чекању. Молимо Вас обавите их да би се апликација понашала како треба
+      elasticsearch_running_check:
+        message_html: Повезивање на Elasticsearch није било могуће. Молимо Вас проверите да ли је покренут, или онемогућите претрагу целог текста
+      elasticsearch_version_check:
+        message_html: 'Неусклађена Elasticsearch верзија: %{value}'
+        version_comparison: Elasticsearch %{running_version} је инсталиран а %{required_version} је неопходан
+      rules_check:
+        action: Управљање правилима сервера
+        message_html: Нисте дефинисали ниједно правило сервера.
+      sidekiq_process_check:
+        message_html: Ниједан Sidekiq процес није покренут за ред(ове) %{value}. Молимо Вас прегледајте своју Sidekiq конфигурацију
+      upload_check_privacy_error:
+        action: Проверите овде за више информација
+        message_html: "<strong>Ваш веб сервер је погрешно конфигурисан. Приватност ваших корисника је изложена ризику.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Проверите овде за више информација
+        message_html: "<strong>Ваше складиште објекта је погрешно конфигурисано. Приватност ваших корисника је изложена ризику.</strong>"
+    tags:
+      review: Прегледај статус
+      updated_msg: Подешавања хеш ознака успешно ажурирана
     title: Администрација
+    trends:
+      allow: Дозволи
+      approved: Одобрено
+      disallow: Забрани
+      links:
+        allow: Дозволи линк
+        allow_provider: Дозволи издавача
+        description_html: Ово су линкови који се тренутно често деле међу налозима које Ваш сервер види. Може помоћи Вашим корисницима да сазнају шта се дешава у свету. Ниједан линк није јавно приказан све док Ви не одобрите издавача. Такође можете да дозволите или одбијете засебне линкове.
+        disallow: Забрани линк
+        disallow_provider: Забрани издавача
+        no_link_selected: Ниједан линк није промењен јер ниједан није изабран
+        publishers:
+          no_publisher_selected: Ниједан издавач није промењен јер ниједан није изабран
+        shared_by_over_week:
+          few: Подељен од стране %{count} особе током претходне недеље
+          one: Подељен од стране једне особе током претходне недеље
+          other: Подељен од стране %{count} особа током претходне недеље
+        title: Линкови у тренду
+        usage_comparison: Подељено %{today} пута данас, у поређењу са %{yesterday} пута јуче
+      not_allowed_to_trend: Није одобрено за тренд
+      only_allowed: Само дозвољено
+      pending_review: Преглед на чекању
+      preview_card_providers:
+        allowed: Линкови са овог извора могу да буду „у тренду”
+        description_html: Ово су домени чији се линкови често деле на Вашем серверу. Линкови неће бити јавно приказани као „у тренду” осим уколико је домен линкова одобрен. Ваше одобрење (или одбијање) се преноси и на поддомене.
+        rejected: Линкови са овог извора неће бити „у тренду”
+        title: Издавачи
+      rejected: Одбијен
+      statuses:
+        allow: Дозволи објаву
+        allow_account: Одобри аутора
+        description_html: Ово су објаве за које Ваш сервер зна, а које се тренутно често деле и које корисници често стављају у „омиљене”. Могу помоћи Вашим новим корисницима и повратницима да пронађу још људи за праћење. Ниједна објава није приказана јавно све док Ви не одобрите аутора, односно док аутор не дозволи да његов/њен налог буде препоручен другима. Такође можете да одобрите или одбијете засебне објаве.
+        disallow: Забрани објаву
+        disallow_account: Забрани аутора
+        no_status_selected: Ниједна објава у тренду није промењена јер ниједна није изабрана
+        not_discoverable: Аутор није дао сагласност да буде препоручен
+        shared_by:
+          few: Подељено и стављено у „омиљене” %{friendly_count} пута
+          one: Подељено или стављено у „омиљене” једном
+          other: Подељено и стављено у „омиљене” %{friendly_count} пута
+        title: Објаве у тренду
+      tags:
+        current_score: Тренутна вредност %{score}
+        dashboard:
+          tag_accounts_measure: јединствене употребе
+          tag_languages_dimension: Најзаступљенији језици
+          tag_servers_dimension: Најактивнији сервери
+          tag_servers_measure: различити сервери
+          tag_uses_measure: укупно употреба
+        description_html: Ово су хеш ознаке које се тренутно често појављују у објавама које Ваш сервер види. Могу помоћи Вашим корисницима да открију о чему се тренутно највише говори. Ниједна хеш ознака није приказана јавно све док Ви то не одобрите.
+        listable: Може се препоручити
+        no_tag_selected: Ниједна ознака није измењена јер ниједна није изабрана
+        not_listable: Неће бити препоручено
+        not_trendable: Неће се појављивати у трендовима
+        not_usable: Не може се користити
+        peaked_on_and_decaying: Врхунац достигнут датума %{date}, од тада у опадању
+        title: Хеш ознаке у тренду
+        trendable: Може да се појави под трендовима
+        trending_rank: 'У тренду #%{rank}'
+        usable: Може се користити
+        usage_comparison: Употребљено %{today} пута данас, у поређењу са %{yesterday} пута јуче
+        used_by_over_week:
+          few: Коришћено од стране %{count} особе током претходне недеље
+          one: Коришћено од стране једне особе током претходне недеље
+          other: Коришћено од стране %{count} људи током претходне недеље
+      title: Трендови
+      trending: У тренду
     warning_presets:
       add_new: Додај нови
       delete: Избриши
       edit_preset: Уреди пресет упозорења
+      empty: Још увек нисте дефинисали ниједан шаблон упозорења.
       title: Управљај пресетима упозорења
+    webhooks:
+      add_new: Додај крајњу тачку
+      delete: Избриши
+      description_html: "<strong>Webhook</strong> омогућава Мастодону да Вашој апликацији испоручује <strong>обавештења у реалном времену</strong> о одабраним догађајима, тако да Ваша апликација може да <strong>aутоматски изазове реакцију</strong>."
+      disable: Онемогући
+      disabled: Онемогућено
+      edit: Измени крајњу тачку
+      empty: Још увек немате ниједну конфигурисану webhook крајњу тачку.
+      enable: Омогући
+      enabled: Активно
+      enabled_events:
+        few: "%{count} омогућена догађаја"
+        one: 1 омогућен догађај
+        other: "%{count} омогућених догађаја"
+      events: Догађаји
+      new: Нови webhook
+      rotate_secret: Ротација тајни
+      secret: Тајно потписивање
+      status: Статус
+      title: Веб-пресретач
+      webhook: Веб-пресретач
   admin_mailer:
+    new_appeal:
+      actions:
+        delete_statuses: обрисати објаве корисника
+        disable: замрзнути налог корисника
+        mark_statuses_as_sensitive: означити објаве корисника као осетљиве
+        none: упозорење
+        sensitive: означити налог као осетљив
+        silence: ограничити налог
+        suspend: суспендовати налог
+      body: "%{target} прилаже жалбу на модерацијску одлуку корисника %{action_taken_by} од %{date}, која је гласила %{type}. У жалби пише:"
+      next_steps: Можете уважити жалбу да бисте повукли модерацијску одлуку, или је можете игнорисати.
+      subject: "%{username} прилаже жалбу на модерацијску одлуку са %{instance}"
+    new_pending_account:
+      body: Детаљи новог налога су наведени доле. Можете одобрити или одбити овај захтев.
+      subject: Нов налог за преглед на %{instance} (%{username})
     new_report:
       body: "%{reporter} је пријавио %{target}"
       body_remote: Нека са домена %{domain} је пријавио %{target}
       subject: Нова пријава за %{instance} (#%{id})
+    new_trends:
+      body: 'Следеће ставке је потребно прегледати пре него што могу јавно да се прикажу:'
+      new_trending_links:
+        title: Линкови у тренду
+      new_trending_statuses:
+        title: Објаве у тренду
+      new_trending_tags:
+        no_approved_tags: Тренутно нема одобрених хеш ознака у тренду.
+        requirements: 'Било који од следећих кандидата би могао превазићи #%{rank} одобрену хеш ознаку у тренду, која је тренутно #%{lowest_tag_name} са вредношћу %{lowest_tag_score}.'
+        title: Хеш ознаке у тренду
+      subject: Нови трендови за преглед на %{instance}
+  aliases:
+    add_new: Направи псеудоним
+    created_msg: Успешно је направљен нови псеудоним. Сада можете иницирати пресељење са старог налога.
+    deleted_msg: Успешно је уклоњен псеудоним. Премештање са тог налога на овај више неће бити могуће.
+    empty: Немате ниједан псеудоним.
+    hint_html: Ако желите да се преселите са другог налога на овај, овде можете направити псеудоним, који је неопходан пре него што можете наставити са пребацивањем пратилаца са старог налога на овај. Ова радња сама по себи је <strong>безопасна и реверзибилна</strong>. <strong>Пресељење налога се иницира са старог налога</strong>.
+    remove: Одвежи псеудоним
+  appearance:
+    advanced_web_interface: Напредно веб окружење
+    advanced_web_interface_hint: 'Ако желите да искористите целу ширину екрана, напредно веб окружење вам омогућује да конфигуришете много различитих колона да бисте видели онолико информација у исто време колико желите: почетну страницу, обавештења, здружену временску линију, било који број листа и хеш ознака.'
+    animations_and_accessibility: Анимације и приступачност
+    confirmation_dialogs: Дијалози потврде
+    discovery: Откривање
+    localization:
+      body: Mastodon преводе добровољци.
+      guide_link: https://crowdin.com/project/mastodon
+      guide_link_text: Свако може допринети.
+    sensitive_content: Осетљив садржај
+    toot_layout: Распоред објава
   application_mailer:
     notification_preferences: Промени преференце Е-поште
+    salutation: Поштовани %{name},
     settings: 'Промени подешавања е-поште: %{link}'
     view: 'Погледај:'
     view_profile: Погледај налог
@@ -386,32 +990,71 @@ sr:
   applications:
     created: Апликација успешно направљена
     destroyed: Апликација успешно обрисана
+    logout: Одјава
     regenerate_token: Рекреирај приступни токен
     token_regenerated: Приступни токен успешно рекреиран
     warning: Опрезно са овим подацима. Никад је не делите ни са ким!
     your_token: Ваш приступни токен
   auth:
+    apply_for_account: Затражите налог
     change_password: Лозинка
-    delete_account: Обриши налог
-    delete_account_html: Ако желите да обришете Ваш налог, можете <a href="%{path}">наставити овде</a>. Бићете упитани да потврдите.
+    confirmations:
+      wrong_email_hint: Ако та имејл адреса није исправна, можете је променити у подешавањима налога.
+    delete_account: Брисање налога
+    delete_account_html: Ако желите да избришете ваш налог, можете <a href="%{path}">наставити овде</a>. Од вас ће се тражити потврда.
+    description:
+      prefix_invited_by_user: "@%{name} Вас позива да се придружите овом серверу Мастодона!"
+      prefix_sign_up: Придружите се Мастодону данас!
+      suffix: Са налогом, моћи ћете да пратите људе, објављујете новости и размењујете поруке са корисницима било ког Мастодон сервера и више!
     didnt_get_confirmation: Нисте добили поруку са упутствима за потврду налога?
+    dont_have_your_security_key: Немате сигурносни кључ?
     forgot_password: Заборавили сте лозинку?
     invalid_reset_password_token: Токен за ресетовање лозинке је неисправан или је истекао. Затражите нови.
+    link_to_otp: Унесите двофакторски код са свог телефона или резервни код
+    link_to_webauth: Користите свој сигурносни кључ
+    log_in_with: Пријавите се помоћу
     login: Пријави се
     logout: Одјава
-    migrate_account: Помери у други налог
+    migrate_account: Премештање у други налог
     migrate_account_html: Ако желите да преусмерите овај налог на неки други, можете то <a href="%{path}">подесити овде</a>.
     or_log_in_with: Или се пријавите са
+    privacy_policy_agreement_html: Прочитао/-ла сам и сагласан/-а сам са <a href="%{privacy_policy_path}" target="_blank">политиком приватности</a>
     providers:
       cas: CAS-ом
       saml: SAML-ом
     register: Региструј се
+    registration_closed: "%{instance} не прима нове чланове"
     resend_confirmation: Пошаљи поруку са упутствима о потврди налога поново
     reset_password: Ресетуј лозинку
+    rules:
+      accept: Прихвати
+      back: Назад
+      preamble: Ово су правила која су успоставили и која спроводе модератори сервера %{domain}.
+      title: Нека основна правила.
     security: Безбедност
     set_new_password: Постави нову лозинку
+    setup:
+      email_below_hint_html: Ако је имејл адреса испод неисправна, можете је променити овде и добити нови имејл потврде.
+      email_settings_hint_html: Имејл потврде је послат на %{email}. Ако та имејл адреса није исправна, можете је променити у подешавањима налога.
+      title: Постављање
+    sign_in:
+      preamble_html: Пријавите се са својим подацима за <strong>%{domain}</strong>. Ако се Ваш налог налази на другом серверу, нећете моћи да се пријавите овде.
+      title: Пријавите се на %{domain}
+    sign_up:
+      preamble: Са налогом на овом Мастодон серверу, моћи ћете да пратите било кога са мреже, без обзира на то на ком серверу се његов/њен налог налази.
+      title: Хајде да Вам наместимо налог на %{domain}.
+    status:
+      account_status: Статус налога
+      confirming: Чекање на потврду путем имејла.
+      functional: Ваш налог је потпуно оперативан.
+      pending: Ваш захтев је на чекању за преглед од стране нашег особља. Ово може потрајати неко време. Примићете имејл поруку уколико Вам захтев буде одобрен.
+      redirecting_to: Ваш налог је неактиван јер преусмерава на %{acct}.
+      view_strikes: Погледајте претходне преступе уписане на Ваше име
+    too_fast: Формулар је поднет пребрзо, покушајте поново.
+    use_security_key: Користите сигурносни кључ
   authorize_follow:
     already_following: Већ пратите овај налог
+    already_requested: Већ сте послали захтев за праћење том налогу
     error: Нажалост, десила се грешка при тражењу удаљеног налога
     follow: Запрати
     follow_request: 'Послали сте захтев за праћењен за:'
@@ -421,25 +1064,87 @@ sr:
       return: Врати се на налог овог корисника
       web: Иди на веб
     title: Запрати %{acct}
+  challenge:
+    confirm: Настави
+    hint_html: "<strong>Савет:</strong> Нећемо Вас питати за лозинку поново у наредних сат времена."
+    invalid_password: Неисправна лозинка
+    prompt: Потврдите лозинку за наставак
+  crypto:
+    errors:
+      invalid_key: није валидан Ed25519 или Curve25519 кључ
+      invalid_signature: није валидан Ed25519 потпис
+  date:
+    formats:
+      default: "%d. %b. %Y."
+      with_month_name: "%d. %B %Y."
   datetime:
     distance_in_words:
+      about_x_hours: "%{count} ч."
       about_x_months: "%{count}месец"
       about_x_years: "%{count}год"
       almost_x_years: "%{count}год"
       half_a_minute: Управо сад
+      less_than_x_minutes: "%{count} мин."
       less_than_x_seconds: Управо сад
       over_x_years: "%{count}год"
       x_days: "%{count}д"
+      x_minutes: "%{count} мин."
       x_months: "%{count}месец"
+      x_seconds: "%{count} сек."
   deletes:
+    challenge_not_passed: Лозинка коју сте унели није била исправна
     confirm_password: Унесите тренутну лозинку да бисмо проверили Ваш идентитет
+    confirm_username: Унесите своје корисничко име да бисте потврдили процедуру
     proceed: Обриши налог
     success_msg: Ваш налог је успешно обрисан
+    warning:
+      before: 'Пре него што наставите, молимо Вас пажљиво прочитајте следеће напомене:'
+      caches: Садржај који је кеширан на другим серверима може да остане нетакнут
+      data_removal: Ваше објаве и други подаци ће бити трајно избрисани
+      email_change_html: Можете <a href="%{path}">променити своју имејл адресу</a> без брисања свог налога
+      email_contact_html: Ако порука и даље не стиже, можете послати мејл на <a href="mailto:%{email}">%{email}</a> за помоћ
+      email_reconfirmation_html: Уколико Вам имејл за потврду не стиже, можете га <a href="%{path}">поново затражити</a>
+      irreversible: Нећете бити у могућности да вратите или реактивирате свој налог
+      more_details_html: За више детаља, погледајте <a href="%{terms_path}">политику приватности</a>.
+      username_available: Ваше корисничко име ће поново постати доступно
+      username_unavailable: Ваше корисничко име ће остати недоступно
+  disputes:
+    strikes:
+      action_taken: Радња предузета
+      appeal: Жалба
+      appeal_approved: Жалба на овај преступ је уважена и преступ више није валидан
+      appeal_rejected: Жалба је одбијена
+      appeal_submitted_at: Жалба је приложена
+      appealed_msg: Ваша жалба је приложена. Бићете обавештени уколико она буде била уважена.
+      appeals:
+        submit: Приложи жалбу
+      approve_appeal: Уважи жалбу
+      associated_report: Сродна пријава
+      created_at: Датум
+      description_html: Ово су радње предузете против Вашег налога и упозорења која су Вам послали чланови особља %{instance}.
+      recipient: Усмерено према
+      reject_appeal: Одбиј жалбу
+      status: 'Објава #%{id}'
+      status_removed: Објава је већ уклоњена из система
+      title: "%{action} од %{date}"
+      title_actions:
+        delete_statuses: Брисање објава
+        disable: Замрзавање налога
+        mark_statuses_as_sensitive: Означавање објава као осетљивих
+        none: Упозорење
+        sensitive: Означавање налога као осетљивог
+        silence: Ограничење налога
+        suspend: Суспензија налога
+      your_appeal_approved: Ваша жалба је уважена
+      your_appeal_pending: Приложили сте жалбу
+      your_appeal_rejected: Ваша жалба је одбијена
+  domain_validator:
+    invalid_domain: нелегитимно име домена
   errors:
-    '400': The request you submitted was invalid or malformed.
+    '400': Захтев који сте поднели је био нелегитиман или у погрешном формату.
     '403': Немате дозвола да видите ову страну.
     '404': Страна коју сте тражили не постоји.
-    '406': This page is not available in the requested format.
+    '406': Ова страница није доступна у изабраном формату.
     '410': Страна коју сте тражили више не постоји.
     '422':
       content: Безбедоносна провера није успела. Да не блокирате колачиће?
@@ -448,49 +1153,117 @@ sr:
     '500':
       content: Извињавамо се, нешто је пошло по злу са ове стране.
       title: Страна није исправна
-    '503': The page could not be served due to a temporary server failure.
+    '503': Страницу није било могуће доставити услед привременoг пада сервера.
     noscript_html: Да бисте користили Мастодонт веб апликацију, омогућите JavaScript. У супротном, пробајте неку од <a href="%{apps_path}">оригиналних апликација</a> за Мастодонт за Вашу платформу.
+  existing_username_validator:
+    not_found: није било могуће пронаћи локалног корисника са тим корисничким именом
+    not_found_multiple: није било могуће пронаћи %{usernames}
   exports:
     archive_takeout:
       date: Датум
       download: Преузмите Вашу архиву
-      hint_html: Можете затражити архиву ваших <strong>труба и отпремљених медија</strong>. Извезени подаци ће бити у АктивитиПаб формату, који можете читати са било којим усаглашеним софтвером. Архиву можете затражити сваких 7 дана.
+      hint_html: Можете затражити архиву ваших <strong>објава и отпремљених медија</strong>. Извезени подаци ће бити у ActivityPub формату, који може читати било који компатибилан софтвер. Архиву можете затражити сваких 7 дана.
       in_progress: Састављање ваше архиве...
-      request: Затражите Вашу архиву
+      request: Затражите своју архиву
       size: Величина
     blocks: Блокирали сте
+    bookmarks: Обележивачи
+    csv: CSV
     domain_blocks: Блокови домена
     lists: Листе
-    mutes: Ућуткали сте
+    mutes: Игноришете
     storage: Мултимедијално складиште
+  featured_tags:
+    add_new: Додај нову
+    errors:
+      limit: Већ сте истакли максималан број хеш ознака
+    hint_html: "<strong>Шта су истакнуте хеш онаке?</strong> Оне се приказују истакнуто на вашем јавном профилу и омогућују људима да прегледају ваше јавне објаве конкретно под тим хеш ознакама. Оне су сјајан алат за праћење креативних радова или дугорочних пројеката."
   filters:
     contexts:
+      account: Профили
       home: Временска линија почетне
       notifications: Обавештења
       public: Јавне временске линије
       thread: Разговори
     edit:
+      add_keyword: Додај кључну реч
+      keywords: Кључне речи
+      statuses: Засебне објаве
+      statuses_hint_html: Овај филтер важи за одабране засебне објаве без обзира на то да ли садрже кључне речи наведене испод. <a href="%{path}">Прегледајте или уклоните објаве из филтера</a>.
       title: Измени филтер
     errors:
+      deprecated_api_multiple_keywords: Ови параметри не могу бити промењени у овој апликацији зато што се односе на више од једне кључне речи. Користите ажурнију апликацију или веб интерфејс.
       invalid_context: Ниједан или неважећи контекст испоручен
     index:
+      contexts: Филтрира у %{contexts}
       delete: Избриши
+      empty: Немате филтере.
+      expires_in: Истиче за %{distance}
+      expires_on: Истиче датума %{date}
+      keywords:
+        few: "%{count} кључне речи"
+        one: "%{count} кључна реч"
+        other: "%{count} кључних речи"
+      statuses:
+        few: "%{count} објаве"
+        one: "%{count} објава"
+        other: "%{count} објава"
+      statuses_long:
+        few: "%{count} засебне објаве сакривене"
+        one: "%{count} засебна објава сакривена"
+        other: "%{count} засебних објава сакривено"
       title: Филтери
     new:
+      save: Сачувај нови филтер
       title: Додај нови филтер
+    statuses:
+      back_to_filter: Назад на филтер
+      batch:
+        remove: Уклони из филтера
+      index:
+        hint: Овај филтер важи за одабране засебне објаве без обзира на друге критеријуме. Можете да додате више објава у овај филтер путем веб интерфејса.
+        title: Филтриране објаве
   generic:
+    all: Сви
+    all_items_on_page_selected_html:
+      few: Све <strong>%{count}</strong> ставке са ове странице су изабране.
+      one: "<strong>%{count}</strong> ставка са ове странице је изабрана."
+      other: Свих <strong>%{count}</strong> ставки са ове странице је изабрано.
+    all_matching_items_selected_html:
+      few: Све <strong>%{count}</strong> ставке које се поклапају са Вашом претрагом су изабране.
+      one: "<strong>%{count}</strong> ставка која се поклапа са Вашом претрагом је изабрана."
+      other: Свих <strong>%{count}</strong> ставки које се поклапају са Вашом претрагом су изабране.
     changes_saved_msg: Измене успешно сачуване!
     copy: Копирај
-    save_changes: Сними измене
+    delete: Избриши
+    deselect: Поништи све изборе
+    none: Ниједна
+    order_by: Сортирај према
+    save_changes: Сачувај промене
+    select_all_matching_items:
+      few: Одаберите све %{count} ставке које се поклапају са Вашом претрагом.
+      one: Одаберите %{count} ставку која се поклапа са Вашом претрагом.
+      other: Одаберите свих %{count} ставки које се поклапају са Вашом претрагом.
+    today: данас
     validation_errors:
       few: Нешто није баш како треба! Прегледајте %{count} грешке испод
       one: Нешто није баш како треба! Прегледајте грешке испод
       other: Нешто није баш како треба! Прегледајте %{count} грешака испод
   imports:
+    errors:
+      invalid_csv_file: 'Неисправан CSV фајл. Грешка: %{error}'
+      over_rows_processing_limit: садржи више од %{count} редова
+    modes:
+      merge: Стапање
+      merge_long: Задржите постојеће записе и додајте нове
+      overwrite: Замени
+      overwrite_long: Замени тренутне записе новима
     preface: Можете увести податке које сте извезли са друге инстанце, као што су листе људи које сте пратили или блокирали.
     success: Ваши подаци су успешно отпремљени и биће обрађени ускоро
     types:
       blocking: Листа блокирања
+      bookmarks: Обележивачи
+      domain_blocking: Листа блокираних домена
       following: Листа пратилаца
       muting: Листа ућутканих
     upload: Отпреми
@@ -519,16 +1292,68 @@ sr:
     title: Позовите људе
   lists:
     errors:
-      limit: Достигли сте лимит броја листи
+      limit: Достигли сте максимални број листâ
+  login_activities:
+    authentication_methods:
+      otp: апликација за двофакторску аутентификацију
+      password: лозинка
+      sign_in_token: имејл сигурносни код
+      webauthn: сигурносни кључеви
+    description_html: Уколико приметите активност коју не препознајете, размислите о томе да промените своју лозинку и укључите двофакторску аутентификацију.
+    empty: Историја аутентификације није доступна
+    failed_sign_in_html: Неуспешан покушај пријављивања путем %{method} са %{ip} (%{browser})
+    successful_sign_in_html: Успешан покушај пријављивања путем %{method} са %{ip} (%{browser})
+    title: Историја аутентификације
   media_attachments:
     validations:
       images_and_video: Не може да се прикачи видео на статус који већ има слике
+      not_ready: Не могу се приложити фајлови који још увек нису обрађени. Покушајте поново за који тренутак!
       too_many: Не може се прикачити више од 4 фајла
   migrations:
     acct: корисник@домен новог налога
+    cancel: Откажи преусмерење
+    cancel_explanation: Отказивање преусмерења ће поново активирати Ваш садашњи налог, али неће вратити пратиоце који су премештени на други налог.
+    cancelled_msg: Успешно је отказано преусмерење.
+    errors:
+      already_moved: већ сте се преселили на исти налог
+      missing_also_known_as: није псеудоним овог налога
+      move_to_self: не можете се преселити на садашњи налог
+      not_found: није било могуће пронаћи налог
+      on_cooldown: Под ограничењем сте
+    followers_count: Пратиоци у тренутку премештаја
+    incoming_migrations: Премештање из другог налога
+    incoming_migrations_html: Да бисте прешли са другог налога на овај, прво морате <a href="%{path}">креирате псеудоним налога</a>.
+    moved_msg: Ваш налог сада преусмерава на %{acct} и Ваши пратиоци се тренутно пребацују.
+    not_redirecting: Ваш налог тренутно не преусмерава ни на један други налог.
+    on_cooldown: Недавно сте се преселили на нови налог. Ова функција ће Вам поново постати доступна за %{count} дана.
+    past_migrations: Претходне селидбе
+    proceed_with_move: Пребаците пратиоце
+    redirected_msg: Ваш налог сада преусмерава на %{acct}.
+    redirecting_to: Ваш налог преусмерава на %{acct}.
+    set_redirect: Постави преусмерење
+    warning:
+      backreference_required: Нови налог прво мора бити конфигурисан тако да реферише на овај налог
+      before: 'Пре него што наставите, молимо Вас пажљиво прочитајте следеће напомене:'
+      cooldown: Након пресељења потребно је да прође одређено време пре него што ћете поново моћи да се преселите
+      disabled_account: Ваш тренутни налог више неће бити употребљив. Међутим, имаћете приступ извозу података као и реактивацији.
+      followers: Ова радња ће преместити све пратиоце са тренутног налога на нови налог
+      only_redirect_html: Уместо пресељења, можете <a href="%{path}">само додати преусмеравајући линк на свој профил.</a>.
+      other_data: Остали подаци неће бити аутоматски пребачени
+      redirect: Профил Вашег садашњег налога ће бити ажуриран са обавештењем о преусмерењу и биће искључен из претраге
   moderation:
     title: Модерација
+  move_handler:
+    carry_blocks_over_text: Овај корисник се преселио са налога %{acct}, који сте блокирали.
+    carry_mutes_over_text: Овај корисник се преселио са налога %{acct}, који сте утишали.
+    copy_account_note_text: 'Овај корисник се преселио са налога %{acct}, о коме сте записали следеће белешке:'
+  navigation:
+    toggle_menu: Прикажи/сакриј мени
   notification_mailer:
+    admin:
+      report:
+        subject: "%{name} је поднео/-ла пријаву"
+      sign_up:
+        subject: "%{name} се регистровао/-ла"
     favourite:
       body: "%{name} је поставио као омиљен Ваш статус:"
       subject: "%{name} је поставио као омиљен Ваш статус"
@@ -547,36 +1372,112 @@ sr:
       body: "%{name} Вас је поменуо у:"
       subject: "%{name} Вас је поменуо"
       title: Ново спомињање
+    poll:
+      subject: Анкета корисника %{name} се завршила
     reblog:
       body: "%{name} Вам је подржао/ла статус:"
       subject: "%{name} је подржао/ла Ваш статус"
       title: Нова подршка
+    status:
+      subject: "%{name} jе управо поставио/-ла објаву"
+    update:
+      subject: "%{name} је изменио/-ла објаву"
   notifications:
+    email_events: Догађаји за обавештења е-поштом
     email_events_hint: 'Изаберите дешавања за која желите да примате обавештења:'
+    other_settings: Остала подешавања обавештења
+  number:
+    human:
+      decimal_units:
+        format: "%n %u"
+        units:
+          billion: млрд.
+          million: мил.
+          quadrillion: трил.
+          thousand: хиљ.
+          trillion: бил.
+  otp_authentication:
+    code_hint: Укуцајте код генерисан у Вашој апликацији за аутентификацију да бисте потврдили
+    description_html: Ако укључите <strong>двофакторску аутентификацију</strong> путем апликације за аутентификацију, мораћете да будете при телефону приликом пријављивања да бисте имали приступ генерисаним кодовима за пријаву.
+    enable: Омогући
+    instructions_html: "<strong>Скенирајте овај QR код помоћу Google аутентификатора или сличне апликације на свом телефону</strong>. Од сада па убудуће, та апликација ће генерисати приступне кодове које ћете морати да унесете приликом пријављивања."
+    manual_instructions: 'Ако не можете да скенирате QR код и морате да га унесете ручно, ево његове текстуалне шифре:'
+    setup: Инсталација
+    wrong_code: Унесени код је био неисправан! Да ли су време сервера и време уређаја исправни?
   pagination:
     newer: Новије
     next: Следеће
     older: Старије
     prev: Претходни
+    truncate: "&hellip;"
+  polls:
+    errors:
+      already_voted: Већ сте гласали у овој анкети
+      duplicate_options: садржи дупликате
+      duration_too_long: превише је далеко у будућности
+      duration_too_short: превише је скоро
+      expired: Анкета је већ завршена
+      invalid_choice: Изабрана опција не постоји
+      over_character_limit: не може бити дуже од по %{max} карактера
+      too_few_options: мора имати више од једне опције
+      too_many_options: не може да садржи више од %{max} опција
   preferences:
     other: Остало
+    posting_defaults: Подразумевана подешавања објављивања
     public_timelines: Јавне временске линије
+  privacy_policy:
+    title: Политика приватности
+  reactions:
+    errors:
+      limit_reached: Достигнуто је ограничење различитих реакција
+      unrecognized_emoji: није препознат емоџи
+  relationships:
+    activity: Активност налога
+    confirm_follow_selected_followers: Да ли сте сигурни да желите да пратите изабране пратиоце?
+    confirm_remove_selected_followers: Да ли сте сигурни да желите да уклоните изабране пратиоце?
+    confirm_remove_selected_follows: Да ли сте сигурни да желите да уклоните изабрана праћења?
+    dormant: Неактиван
+    follow_failure: Није могуће пратити неке од изабраних налога.
+    follow_selected_followers: Прати изабране пратиоце
+    followers: Пратиоци
+    following: Праћења
+    invited: Позван
+    last_active: Последњи пут активан
+    most_recent: Најновији
+    moved: Премештен
+    mutual: Заједнички
+    primary: Примарни
+    relationship: Однос
+    remove_selected_domains: Уклони све пратиоце са изабраних домена
+    remove_selected_followers: Уклони изабране пратиоце
+    remove_selected_follows: Отпрати изабране кориснике
+    status: Статус налога
   remote_follow:
     missing_resource: Не могу да нађем захтевану адресу преусмеравања за Ваш налог
+  reports:
+    errors:
+      invalid_rules: не реферише на легитимна правила
+  rss:
+    content_warning: 'Упозорење о садржају:'
+    descriptions:
+      account: Јавне објаве са @%{acct}
+      tag: 'Јавне објаве означене са #%{hashtag}'
   scheduled_statuses:
-    over_daily_limit: Прекорачили сте границу од %{limit} планираних труба за тај дан
-    over_total_limit: Прекорачили сте границу од %{limit} планираних труба
+    over_daily_limit: Прекорачили сте границу од %{limit} планираних објава за данас
+    over_total_limit: Прекорачили сте границу од %{limit} планираних објава
     too_soon: Планирани датум мора бити у будућности
   sessions:
     activity: Последња активност
     browser: Веб читач
     browsers:
       alipay: Алипеј
-      chrome: Хром
+      blackberry: Блекбери
+      chrome: Chrome
       edge: Мајкрософт Еџ
       electron: Електрон
       firefox: Фајерфокс
       generic: Непознати веб читач
+      huawei_browser: Huawei прегледач
       ie: Интернет Експлорер
       micro_messenger: МајкроМесенџер
       nokia: Нокија С40 Ови Претраживач
@@ -585,32 +1486,43 @@ sr:
       phantom_js: ФантомЏејЕс
       qq: КјуКју Претраживач
       safari: Сафари
+      uc_browser: UC Browser
+      unknown_browser: Непознати прегледач
       weibo: Веибо
     current_session: Тренутна сесија
     description: "%{browser} са %{platform}"
     explanation: Ово су веб претраживачи који су тренутно пријављени на Ваш Мастодон налог.
+    ip: IP
     platforms:
       adobe_air: Адоб Ер-а
       android: Андроида
+      blackberry: Блекбери
+      chrome_os: ChromeOS
       firefox_os: Фајерфокс ОС-а
       ios: иОС-а
+      kai_os: KaiOS
       linux: Линукса
       mac: Мека
-      other: непознате платформе
-      windows: Виндоуза
-      windows_mobile: Виндоуз мобилног
-      windows_phone: Виндоуз телефона
+      unknown_platform: Непозната платформа
+      windows: Windows
+      windows_mobile: Windows Mobile
+      windows_phone: Windows Phone
     revoke: Опозови
     revoke_success: Сесија успешно опозвана
     title: Сесије
+    view_authentication_history: Погледајте историју аутентификације вашег налога
   settings:
+    account: Налог
+    account_settings: Подешавања налога
+    aliases: Псеудоними налога
     appearance: Изглед
     authorized_apps: Ауторизоване апликације
     back: Назад на Мастодон
     delete: Брисање налога
     development: Развој
-    edit_profile: Измена налога
+    edit_profile: Уређивање профила
     export: Извоз података
+    featured_tags: Истакнуте хеш ознаке
     import: Увоз
     import_and_export: Увоз и извоз
     migrate: Пребацивање налога
@@ -618,9 +1530,16 @@ sr:
     preferences: Подешавања
     profile: Налог
     relationships: Праћења и пратиоци
+    statuses_cleanup: Аутоматско брисање објава
+    strikes: Модерацијски преступи
     two_factor_authentication: Двофакторска идентификација
+    webauthn_authentication: Сигурносни кључеви
   statuses:
     attached:
+      audio:
+        few: "%{count} аудио записа"
+        one: "%{count} аудио запис"
+        other: "%{count} аудио записа"
       description: 'У прилогу: %{attached}'
       image:
         few: "%{count} слика"
@@ -632,72 +1551,199 @@ sr:
         other: "%{count} видео записа"
     boosted_from_html: Подржано од %{acct_link}
     content_warning: 'Упозорење на садржај: %{warning}'
+    default_language: Исто као језик окружења
     disallowed_hashtags:
       few: 'садржи забрањене хештегове: %{tags}'
       one: 'садржи забрањени хештег: %{tags}'
       other: 'садржи забрањене хештегове: %{tags}'
+    edited_at_html: Измењено %{date}
+    errors:
+      in_reply_not_found: Објава на коју покушавате да одговорите наизглед не постоји.
     open_in_web: Отвори у вебу
     over_character_limit: ограничење од %{max} карактера прекорачено
     pin_errors:
-      limit: Већ имате прикачен највећи број труба
+      direct: Објаве које су видљиве само поменутим корисницима не могу бити прикачене
+      limit: Већ сте закачили максималан број објава
       ownership: Туђа објава се не може закачити
       reblog: Подршка не може да се прикачи
+    poll:
+      total_people:
+        few: "%{count} особе"
+        one: "%{count} особа"
+        other: "%{count} људи"
+      total_votes:
+        few: "%{count} гласа"
+        one: "%{count} глас"
+        other: "%{count} гласова"
+      vote: Гласајте
     show_more: Прикажи још
+    show_newer: Никад не приказуј
+    show_older: Прикажи старије
+    show_thread: Прикажи низ
     sign_in_to_participate: Пријавите се да учествујете у разговору
+    title: "%{name}: „%{quote}”"
     visibilities:
+      direct: Директно
       private: Само пратиоци
       private_long: Прикажи само пратиоцима
       public: Јавно
       public_long: Свако може да види
       unlisted: Неизлистано
       unlisted_long: Свако може да види, али није излистано на јавним временским линијама
+  statuses_cleanup:
+    enabled: Аутоматски избриши старе објаве
+    enabled_hint: Аутоматски брише ваше објаве када достигну одређени старосни праг, осим ако се не подударају са једним од изузетака у наставку
+    exceptions: Изузеци
+    explanation: Пошто је брисање објава скупа операција, ово се ради полако током времена када сервер иначе није заузет. Из тог разлога, ваше објаве могу бити избрисане неко време након што достигну старосни праг.
+    ignore_favs: Игнориши омиљене
+    ignore_reblogs: Игнориши подржавања
+    interaction_exceptions: Изузеци засновани на интеракцијама
+    interaction_exceptions_explanation: Имајте на уму да не постоји гаранција да ће објаве бити избрисане ако број означавања као омиљених или број подржавања падне испод прага након што га премаше.
+    keep_direct: Задржи директне поруке
+    keep_direct_hint: Не брише ниједну од ваших директних порука
+    keep_media: Задржи објаве са медијским прилозима
+    keep_media_hint: Не брише ниједну од ваших објава које имају медијске прилоге
+    keep_pinned: Задржи закачене објаве
+    keep_pinned_hint: Не брише ниједну од ваших закачених објава
+    keep_polls: Задржи анкете
+    keep_polls_hint: Не брише ниједну од ваших анкета
+    keep_self_bookmark: Задржи објаве које сте додали у обележиваче
+    keep_self_bookmark_hint: Не брише ваше сопствене објаве ако сте их додлаи у обележиваче
+    keep_self_fav: Задржи омиљене објаве
+    keep_self_fav_hint: Не брише ваше сопствене објаве ако сте их означили као омиљене
+    min_age:
+      '1209600': 2 седмице
+      '15778476': 6 месеци
+      '2629746': 1 месец
+      '31556952': 1 година
+      '5259492': 2 месеца
+      '604800': 1 седмица
+      '63113904': 2 годинe
+      '7889238': 3 месеца
+    min_age_label: Старосни праг
+    min_favs: Задржи објаве означене као омиљене најмање
+    min_favs_hint: Не брише ниједну вашу објаву која је добила најмање овај број омиљених. Оставите празно за брисање објава без обзира на њихов број омиљених
+    min_reblogs: Задржи објаве подржане барем
+    min_reblogs_hint: Не брише ниједну вашу објаву која је била подржана најмање оволико пута. Оставите празно за брисање објава без обзира на њихов број подржавања
   stream_entries:
     pinned: Закачена објава
     reblogged: подржано
     sensitive_content: Осетљив садржај
+  strikes:
+    errors:
+      too_late: Истекао је рок за подношење жалбе на забележен преступ
+  tags:
+    does_not_match_previous_name: не поклапа се са претходним именом
   themes:
     contrast: Велики контраст
     default: Мастодон
     mastodon-light: Мастодон (светло)
+  time:
+    formats:
+      default: "%d %b %Y, %H:%M"
+      month: "%b %Y"
+      time: "%H:%M"
   two_factor_authentication:
+    add: Додај
     disable: Искључи
+    disabled_success: Двофакторска аутентификација је успешно онемогућена
+    edit: Измени
     enabled: Двофакторска идентификација је укључена
     enabled_success: Двофакторска идентификација је успешно укључена
     generate_recovery_codes: Генериши кодове за опоравак
     lost_recovery_codes: Кодови за опоравак Вам омогућавају да повратите приступ налогу ако изгубите телефон. Ако изгубите кодове за опоравак, можете их ре-генерисати овде. Од тог тренутка, стари кодови за опоравак више не важе.
+    methods: Методе двофакторске аутентификације
+    otp: Апликација за аутентификацију
     recovery_codes: Направите резерву кодова за опоравак
     recovery_codes_regenerated: Кодови за опоравак успешно ре-генерисани
     recovery_instructions_html: Ако икада изгубите приступ телефону, можете искористити кодове за опоравак дате испод да повратите приступ налогу. <strong>Држите кодове за опоравак на сигурном</strong>. На пример, одштампајте их и чувајте их са осталим важним документима.
+    webauthn: Сигурносни кључеви
   user_mailer:
+    appeal_approved:
+      action: Идите на свој налог
+      explanation: Жалба поднета датума %{appeal_date} на уписан преступ на Ваше име датума %{strike_date} је уважена. Ваш налог је поново у повољном положају.
+      subject: Ваша жалба поднета %{date} је уважена
+      title: Жалба уважена
+    appeal_rejected:
+      explanation: Жалба поднета датума %{appeal_date} на уписан преступ на Ваше име датума %{strike_date} је одбијена.
+      subject: Ваша жалба поднета %{date} је одбијена
+      title: Жалба одбијена
     backup_ready:
       explanation: Тражили сте потпуну резервну копију вашег Мастодон рачуна. Спремна за преузимање!
       subject: Ваша архива је спремна за преузимање
       title: Извоз архиве
+    suspicious_sign_in:
+      change_password: промените своју лозинку
+      details: 'Ево детаља о пријави:'
+      explanation: Приметили смо пријаву на Ваш налог са непознате IP адресе.
+      further_actions_html: Уколико то нисте били Ви, препоручујемо да одмах %{action} и укључите двофакторску аутентификацију да бисте одржали безбедност свог налога.
+      subject: Вашем налогу је приступљено са непозате IP адресе
+      title: Ново пријављивање
     warning:
+      appeal: Приложите жалбу
+      appeal_description: Уколико верујете да је у питању грешка, можете приложити жалбу особљу %{instance}.
+      categories:
+        spam: Нежељена пошта
+        violation: Садржај крши следећа правила заједнице
+      explanation:
+        delete_statuses: За неке од Ваших објава је установљено да крше једно или више правила заједнице и услед тога су уклоњене од стране модератора %{instance}.
+        disable: Више не можете да користите свој налог, али Ваш профил и други подаци остају нетакнути. Можете да затражите резервну копију својих података, промените подешавања налога или обришете свој налог.
+        mark_statuses_as_sensitive: Неке од Ваших објава су означене као осетљиве од стране модератора %{instance}. Ово значи да ће људи морати да кликну на мултимедије у објавама пре него што могу да их виде. Убудуће можете сами да означите своју мултимедију као осетљиву приликом састављања објаве.
+        sensitive: Од сада ће сви Ваши отпремљени мултимедијални фајлови бити означени као осетљиви и сакривени иза тастера упозорења.
+        silence: И даље можете користити свој налог али само људи који Вас већ прате ће видети Ваше објаве на овом серверу, и можда ћете бити искључени из разних механизама откривања. Међутим, други људи и даље могу ручно да Вас запрате.
+        suspend: Више не можете да користите свој налог, и Ваш профил и остали подаци Вам више нису доступни. И даље можете да се пријавите да бисте затражили резервну копију својих података све док се Ваши подаци трајно не избришу за око 30 дана, с тим што ћемо задржати неке основне податке да бисмо Вас спречили у евентуалном заобилажењу суспензије.
+      reason: 'Образложење:'
+      statuses: 'Цитиране објаве:'
       subject:
+        delete_statuses: Ваше објаве са %{acct} су избрисане
         disable: Ваш налог %{acct} је замрзнут
+        mark_statuses_as_sensitive: Ваше објаве са %{acct} су обележене као осетљиве
         none: Упозорење за %{acct}
+        sensitive: Ваше објаве са %{acct} ће убудуће бити означене као осетљиве
         silence: Ваш налог %{acct} је ограничен
         suspend: Ваш налог %{acct} је суспендован
       title:
+        delete_statuses: Објаве су обрисане
         disable: Налог замрзнут
+        mark_statuses_as_sensitive: Објаве су означене као осетљиве
         none: Упозорење
+        sensitive: Налог је означен као осетљив
         silence: Налог ограничен
         suspend: Налог суспендован
     welcome:
       edit_profile_action: Подеси налог
+      edit_profile_step: Можете прилагодити свој профил тако што ћете поставити профилну слику, променити име за приказ и тако даље. Можете дати сагласност да прегледате нове пратиоце пре него што им дозволите да Вас запрате.
       explanation: Ево неколико савета за почетак
       final_action: Почните објављивати
+      final_step: 'Почните да објављујете! Чак и без пратилаца, Ваше јавне објаве су видљиве другим људима, на пример на локалној временској линији или у хеш ознакама. Можда желите да се представите са хеш ознаком #introductions или #представљања.'
       full_handle: Ваш пун надимак
       full_handle_hint: Ово бисте рекли својим пријатељима како би вам они послали поруку, или запратили са друге инстанце.
       subject: Добродошли на Мастодон
       title: Добродошли, %{name}!
   users:
     follow_limit_reached: Не можете пратити више од %{limit} људи
+    go_to_sso_account_settings: Идите на подешавања налога свог добављача идентитета
     invalid_otp_token: Неисправни двофакторски код
     otp_lost_help_html: Ако изгубите приступ за оба, можете ступити у контакт са %{email}
     seamless_external_login: Пријављени сте путем спољашње услуге, тако да лозинка и подешавања Е-поште нису доступни.
     signed_in_as: 'Пријављен/а као:'
   verification:
-    explanation_html: 'Можете <strong>извршити проверу да сте Ви власник веза на Вашем налогу</strong>. Да би то радило, повезани веб сајт мора да садржи везу назад ка Вашем Мастодон налогу. Веза назад <strong>мора</strong> да има <code>rel="me"</code> атрибут. Текстуелни садржај везе није битан. Ево примера:'
+    explanation_html: 'Можете да се <strong>верификујете као власник веза у метаподацима профила</strong>. За то је потребно да повезани веб сајт мора садржати везу до вашег Mastodon профила. Након што додате везу, можда ћете морати да се вратите овде и поново сачувате свој профил да би верификација ступила на снагу. Повратна веза <strong>мора</strong> имати атрибут <code>rel="me"</code>. Текстуални садржај везе није битан. Ево примера:'
     verification: Провера
+  webauthn_credentials:
+    add: Додајте нови сигурносни кључ
+    create:
+      error: Искрсао је проблем приликом додавања Вашег сигурносног кључа. Молимо Вас покушајте поново.
+      success: Ваш сигурносни кључ је успешно додат.
+    delete: Избриши
+    delete_confirmation: Да ли сте сигурни да желите да избришете овај сигурносни кључ?
+    description_html: Ако укључите <strong>аутентификацију сигурносним кључем</strong>, мораћете да користите један од својих сигурносних кључева приликом пријављивања.
+    destroy:
+      error: Искрсао је проблем приликом брисања Вашег сигурносног кључа. Молимо Вас покушајте поново.
+      success: Ваш сигурносни кључ је успешно обрисан.
+    invalid_credential: Неисправан сигурносни кључ
+    nickname_hint: Унесите надимак свог новог сигурносног кључа
+    not_enabled: Још увек нисте омогућили WebAuthn
+    not_supported: Овај претраживач не подржава сигурносне кључеве
+    otp_required: Да бисте користили сигурносне кључеве, молимо Вас прво укључите двофакторску аутентификацију.
+    registered_on: Регистрован/-а %{date}
diff --git a/config/locales/sv.yml b/config/locales/sv.yml
index bae4a964b..6e7deb9ee 100644
--- a/config/locales/sv.yml
+++ b/config/locales/sv.yml
@@ -91,6 +91,7 @@ sv:
       moderation:
         active: Aktiv
         all: Alla
+        disabled: Inaktiverad
         pending: Väntande
         silenced: Begränsad
         suspended: Avstängda
@@ -133,6 +134,7 @@ sv:
       search: Sök
       search_same_email_domain: Andra användare med samma e-postdomän
       search_same_ip: Annan användare med samma IP-adress
+      security: Säkerhet
       security_measures:
         only_password: Endast lösenord
         password_and_2fa: Lösenord och 2FA
@@ -427,6 +429,7 @@ sv:
         resolve: Slå upp domän
         title: Blockera ny e-postdomän
       no_email_domain_block_selected: Inga blockeringar av e-postdomäner ändrades eftersom inga valdes
+      not_permitted: Ej tillåtet
       resolved_dns_records_hint_html: Domännamnet ger uppslag till följande MX-domäner, vilka är ytterst ansvariga för att e-post tas emot. Att blockera en MX-domän blockerar även registreringar från alla e-postadresser som använder samma MX-domän, även om det synliga domännamnet är annorlunda. <strong>Var noga med att inte blockera stora e-postleverantörer.</strong>
       resolved_through_html: Uppslagen genom %{domain}
       title: Blockerade e-postdomäner
@@ -441,6 +444,7 @@ sv:
         private_comment_description_html: 'För att hjälpa dig spåra var importerade blockeringar kommer från kommer importerade blockeringar att skapas med följande privata kommentar: <q>%{comment}</q>'
         private_comment_template: Importerad från %{source} den %{date}
         title: Importera domänblockeringar
+      invalid_domain_block: 'Ett eller flera domänblock hoppades över på grund av följande fel: %{error}'
       new:
         title: Importera domänblockeringar
       no_file: Ingen fil vald
@@ -472,6 +476,7 @@ sv:
       content_policies:
         comment: Intern anteckning
         description_html: Du kan definiera innehållspolicyer som kommer tillämpas på alla konton från denna domän samt alla dess underdomäner.
+        limited_federation_mode_description_html: Du kan välja om du vill tillåta federering med den här domänen.
         policies:
           reject_media: Avvisa media
           reject_reports: Avvisa rapporter
@@ -575,19 +580,23 @@ sv:
         mark_as_sensitive_description_html: Medierna i de rapporterade inläggen kommer markeras som känsliga och en prick kommer registreras för att hjälpa dig eskalera framtida överträdelser av samma konto.
         other_description_html: Se fler alternativ för att kontrollera kontots beteende och anpassa kommunikationen till det rapporterade kontot.
         resolve_description_html: Ingen åtgärd vidtas mot det rapporterade kontot, ingen prick registreras och rapporten stängs.
-        silence_description_html: Profilen kommer endast synas för de som redan följer den eller manuellt söker på den, vilket dramatiskt minskar dess räckvidd. Kan alltid ångras.
-        suspend_description_html: Profilen och allt dess innehåll kommer att bli oåtkomligt tills det slutligen raderas. Det kommer inte vara möjligt att interagera med kontot. Går att ångra inom 30 dagar.
+        silence_description_html: Kontot kommer endast synas för de som redan följer den eller manuellt söker på den, vilket dramatiskt minskar dess räckvidd. Kan alltid ångras. Stänger alla anmälningar mot detta konto.
+        suspend_description_html: Kontot och allt dess innehåll kommer att vara oåtkomligt och så småningom tas bort, det kommer inte gå att interagera med kontot. Kan ångras inom 30 dagar. Stänger alla anmälningar mot detta konto.
       actions_description_html: Välj vilken åtgärd som skall vidtas för att lösa denna rapport. Om du vidtar en bestraffningsåtgärd mot det rapporterade kontot kommer en e-postnotis att skickas till dem, förutom om du valt kategorin <strong>Skräppost</strong>.
+      actions_description_remote_html: Bestäm vilka åtgärder som ska vidtas för att lösa denna rapport. Detta kommer bara att påverka hur <strong>din</strong> server kommunicerar med detta fjärrkonto och hanterar dess innehåll.
       add_to_report: Lägg till mer i rapporten
       are_you_sure: Är du säker?
       assign_to_self: Tilldela till mig
       assigned: Tilldelad moderator
       by_target_domain: Domän för rapporterat konto
+      cancel: Avbryt
       category: Kategori
       category_description_html: Anledningen till att kontot och/eller innehållet rapporterades kommer att visas i kommunikation med det rapporterade kontot
       comment:
         none: Ingen
       comment_description_html: 'För att ge mer information, skrev %{name}:'
+      confirm: Bekräfta
+      confirm_action: Bekräfta modereringsåtgärd mot @%{acct}
       created_at: Anmäld
       delete_and_resolve: Ta bort inlägg
       forwarded: Vidarebefordrad
@@ -604,6 +613,7 @@ sv:
         placeholder: Beskriv vilka åtgärder som vidtagits eller andra uppdateringar till den här anmälan.
         title: Anteckningar
       notes_description_html: Visa och lämna anteckningar till andra moderatorer och ditt framtida jag
+      processed_msg: 'Rapporten #%{id} har behandlats'
       quick_actions_description_html: 'Ta en snabb åtgärd eller bläddra ner för att se rapporterat innehåll:'
       remote_user_placeholder: fjärranvändaren från %{instance}
       reopen: Återuppta anmälan
@@ -616,9 +626,28 @@ sv:
       status: Status
       statuses: Rapporterat innehåll
       statuses_description_html: Stötande innehåll kommer att citeras i kommunikationen med det rapporterade kontot
+      summary:
+        action_preambles:
+          delete_html: 'Du håller på att <strong>ta bort</strong> några av <strong>@%{acct}</strong>s inlägg. Detta kommer:'
+          mark_as_sensitive_html: 'Du håller på att <strong>markera</strong> några av <strong>@%{acct}</strong>s inlägg som <strong>känsliga</strong>. Detta kommer:'
+          silence_html: 'Du håller på att <strong>begränsa</strong> <strong>@%{acct}</strong>s konto. Detta kommer:'
+          suspend_html: 'Du håller på att <strong>stänga av</strong> <strong>@%{acct}</strong>s konto. Detta kommer:'
+        actions:
+          delete_html: Ta bort kränkande inlägg
+          mark_as_sensitive_html: Markera de anmälda inläggens media som känslig
+          silence_html: Begränsa kraftigt <strong>@%{acct}</strong> räckvidd genom att göra deras profil och innehåll bara synligt till folk som redan följer dom eller som manuellt söker upp deras profil
+          suspend_html: Stäng av <strong>@%{acct}</strong>, vilket gör deras profil och innehåll oåtkomligt och omöjligt att interagera med
+        close_report: 'Markera anmälningen #%{id} som löst'
+        close_reports_html: Markera <strong>alla</strong> anmälningar mot <strong>@%{acct}</strong> som lösta
+        delete_data_html: Ta bort <strong>@%{acct}</strong>s profil och innehåll om 30 dagar ifall deras avstängning inte tagits bort under tiden
+        preview_preamble_html: "<strong>@%{acct}</strong> kommer få en varning med följande innehåll:"
+        record_strike_html: Registrera en varning mot <strong>@%{acct}</strong> för att hjälpa dig eskalera vid framtida överträdelser från detta konto
+        send_email_html: Skicka <strong>@%{acct}</strong> ett varningsmejl
+        warning_placeholder: Valfri ytterligare resonemang för modereringsåtgärd.
       target_origin: Ursprung för anmält konto
       title: Anmälningar
       unassign: Otilldela
+      unknown_action_msg: 'Okänd åtgärd: %{action}'
       unresolved: Olösta
       updated_at: Uppdaterad
       view_profile: Visa profil
@@ -713,6 +742,8 @@ sv:
         preamble: Att visa intressant innehåll är avgörande i onboarding av nya användare som kanske inte känner någon på Mastodon. Styr hur olika upptäcktsfunktioner fungerar på din server.
         profile_directory: Profilkatalog
         public_timelines: Offentliga tidslinjer
+        publish_discovered_servers: Publicera upptäckta servrar
+        publish_statistics: Publicera statistik
         title: Upptäck
         trends: Trender
       domain_blocks:
@@ -767,6 +798,7 @@ sv:
         suspend: "%{name} stängde av %{target}s konto"
       appeal_approved: Överklagad
       appeal_pending: Överklagande väntar
+      appeal_rejected: Överklagan avslagen
     system_checks:
       database_schema_check:
         message_html: Det finns väntande databasmigreringar. Vänligen kör dem för att säkerställa att programmet beter sig som förväntat
@@ -780,6 +812,12 @@ sv:
         message_html: Du har inte definierat några serverregler.
       sidekiq_process_check:
         message_html: Ingen Sidekiq-process körs för kön/köerna %{value}. Vänligen kontrollera din Sidekiq-konfiguration
+      upload_check_privacy_error:
+        action: Kolla här för mer information
+        message_html: "<strong>Din webbserver är felkonfigurerad. Sekretessen för dina användare är i fara.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Kolla här för mer information
+        message_html: "<strong>Din objektlagring är felkonfigurerad. Sekretessen för dina användare är i riskzonen.</strong>"
     tags:
       review: Granskningsstatus
       updated_msg: Hashtagg-inställningarna har uppdaterats
@@ -802,6 +840,7 @@ sv:
           other: Delad av %{count} personer under den senaste veckan
         title: Trendande länkar
         usage_comparison: Delade %{today} gånger idag, jämfört med %{yesterday} igår
+      not_allowed_to_trend: Inte tillåtet att trenda
       only_allowed: Endast tillåtna
       pending_review: Väntar på granskning
       preview_card_providers:
@@ -933,6 +972,7 @@ sv:
   applications:
     created: Ansökan är framgångsrikt skapad
     destroyed: Ansökan är framgångsrikt borttagen
+    logout: Logga ut
     regenerate_token: Regenerera access token
     token_regenerated: Access token lyckades regenereras
     warning: Var mycket försiktig med denna data. Dela aldrig den med någon!
@@ -940,6 +980,8 @@ sv:
   auth:
     apply_for_account: Ansök om konto
     change_password: Lösenord
+    confirmations:
+      wrong_email_hint: Om e-postadressen inte är rätt, kan du ändra den i kontoinställningarna.
     delete_account: Radera konto
     delete_account_html: Om du vill radera ditt konto kan du <a href="%{path}">fortsätta här</a>. Du kommer att bli ombedd att bekräfta.
     description:
@@ -967,6 +1009,8 @@ sv:
     resend_confirmation: Skicka instruktionerna om bekräftelse igen
     reset_password: Återställ lösenord
     rules:
+      accept: Godkänn
+      back: Tillbaka
       preamble: Dessa bestäms och upprätthålls av moderatorerna för %{domain}.
       title: Några grundregler.
     security: Säkerhet
@@ -1114,7 +1158,7 @@ sv:
   featured_tags:
     add_new: Lägg till ny
     errors:
-      limit: Du har redan det maximala antalet utvalda hashtaggar
+      limit: Du har redan fäst det maximala antalet hashtags
     hint_html: "<strong>Vad är utvalda hashtaggar?</strong> De visas tydligt på din offentliga profil och låter andra bläddra bland dina offentliga inlägg specifikt under dessa hashtaggar. De är ett bra verktyg för att hålla reda på kreativa arbeten eller långsiktiga projekt."
   filters:
     contexts:
@@ -1158,8 +1202,6 @@ sv:
       index:
         hint: Detta filter gäller för att välja enskilda inlägg oavsett andra kriterier. Du kan lägga till fler inlägg till detta filter från webbgränssnittet.
         title: Filtrerade inlägg
-  footer:
-    trending_now: Trendar nu
   generic:
     all: Alla
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ sv:
     validation_errors:
       one: Något är inte riktigt rätt ännu! Kontrollera felet nedan
       other: Något är inte riktigt rätt ännu! Kontrollera dom %{count} felen nedan
-  html_validator:
-    invalid_markup: 'innehåller ogiltig HTML: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Ogiltig CSV-fil. Felmeddelande: %{error}'
@@ -1367,7 +1407,11 @@ sv:
       unrecognized_emoji: är inte en igenkänd emoji
   relationships:
     activity: Kontoaktivitet
+    confirm_follow_selected_followers: Är du säker på att du vill följa valda följare?
+    confirm_remove_selected_followers: Är du säker på att du vill ta bort valda följare?
+    confirm_remove_selected_follows: Är du säker på att du vill ta bort valda följare?
     dormant: Vilande
+    follow_failure: Kan inte följa några av de valda kontona.
     follow_selected_followers: Följ valda personer
     followers: Följare
     following: Följer
@@ -1407,6 +1451,7 @@ sv:
       electron: Electron
       firefox: Firefox
       generic: Okänd browser
+      huawei_browser: Huawei Browser
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1416,6 +1461,7 @@ sv:
       qq: QQ browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Okänd browser
       weibo: Weibo
     current_session: Nuvarande session
     description: "%{browser} på %{platform}"
@@ -1428,9 +1474,10 @@ sv:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: okänd plattform
+      unknown_platform: Okänd plattform
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1543,7 +1590,7 @@ sv:
       '7889238': 3 månader
     min_age_label: Åldersgräns
     min_favs: Behåll favoritmarkerade inlägg i minst
-    min_favs_hint: Raderar inte något av dina inlägg som har blivit favoritmarkerat minst detta antal gånger. Lämna tomt för att radera inlägg oavsett antal favoritmarkeringar
+    min_favs_hint: Raderar inte några av dina inlägg som har fått minst detta antalet av favoritmarkeringar. Lämna tomt för att ta bort inlägg oavsett hur många favoritmarkeringar de har fått
     min_reblogs: Behåll boostade inlägg i minst
     min_reblogs_hint: Raderar inte något av dina inlägg som har blivit boostat minst detta antal gånger. Lämna tomt för att radera inlägg oavsett antal boostar
   stream_entries:
@@ -1643,12 +1690,13 @@ sv:
       title: Välkommen ombord, %{name}!
   users:
     follow_limit_reached: Du kan inte följa fler än %{limit} personer
+    go_to_sso_account_settings: Gå till din identitetsleverantörs kontoinställningar
     invalid_otp_token: Ogiltig tvåfaktorskod
     otp_lost_help_html: Om du förlorat åtkomst till båda kan du komma i kontakt med %{email}
     seamless_external_login: Du är inloggad via en extern tjänst, inställningar för lösenord och e-post är därför inte tillgängliga.
     signed_in_as: 'Inloggad som:'
   verification:
-    explanation_html: 'Du kan <strong>verifiera dig själv som ägare av länkar i din profilmetadata</strong>, genom att på den länkade webbplatsen även länka tillbaka till din Mastodon-profil. Länken tillbaka <strong>måste</strong> ha attributet <code>rel="me"</code>. Textinnehållet i länken spelar ingen roll. Här är ett exempel:'
+    explanation_html: 'Du kan <strong>bekräfta att du är ägare till länkarna i dina profilmetadata</strong>. För detta krävs att den länkade webbplatsen innehåller en länk tillbaka till din Mastodon-profil. När du har lagt till länken kan du behöva komma tillbaka hit och spara din profil på nytt för att verifieringen ska träda i kraft. Tillbakalänken <strong>måste</strong> ha attributet <code>rel="me"</code>. Länkens textinnehåll spelar ingen roll. Här är ett exempel:'
     verification: Bekräftelse
   webauthn_credentials:
     add: Lägg till ny säkerhetsnyckel
diff --git a/config/locales/szl.yml b/config/locales/szl.yml
index 4359f4d61..2e059c51a 100644
--- a/config/locales/szl.yml
+++ b/config/locales/szl.yml
@@ -6,7 +6,5 @@ szl:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/ta.yml b/config/locales/ta.yml
index d691c0ec8..2d2bce86a 100644
--- a/config/locales/ta.yml
+++ b/config/locales/ta.yml
@@ -203,9 +203,7 @@ ta:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
   filters:
     index:
diff --git a/config/locales/tai.yml b/config/locales/tai.yml
index 3b22e9999..f347ac620 100644
--- a/config/locales/tai.yml
+++ b/config/locales/tai.yml
@@ -6,7 +6,5 @@ tai:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/te.yml b/config/locales/te.yml
index d325d0fba..e3b3f97ab 100644
--- a/config/locales/te.yml
+++ b/config/locales/te.yml
@@ -76,7 +76,5 @@ te:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/th.yml b/config/locales/th.yml
index 9ef2b4f30..574ca91e4 100644
--- a/config/locales/th.yml
+++ b/config/locales/th.yml
@@ -71,7 +71,7 @@ th:
       followers: ผู้ติดตาม
       follows: การติดตาม
       header: ส่วนหัว
-      inbox_url: URL กล่องขาเข้า
+      inbox_url: URL ของกล่องขาเข้า
       invite_request_text: เหตุผลสำหรับการเข้าร่วม
       invited_by: เชิญโดย
       ip: IP
@@ -89,6 +89,7 @@ th:
       moderation:
         active: ใช้งานอยู่
         all: ทั้งหมด
+        disabled: ปิดใช้งานอยู่
         pending: รอดำเนินการ
         silenced: จำกัดอยู่
         suspended: ระงับอยู่
@@ -130,12 +131,13 @@ th:
       search: ค้นหา
       search_same_email_domain: ผู้ใช้อื่น ๆ ที่มีโดเมนอีเมลเดียวกัน
       search_same_ip: ผู้ใช้อื่น ๆ ที่มี IP เดียวกัน
+      security: ความปลอดภัย
       security_measures:
         only_password: รหัสผ่านเท่านั้น
         password_and_2fa: รหัสผ่านและ 2FA
       sensitive: บังคับให้ละเอียดอ่อน
       sensitized: ทำเครื่องหมายว่าละเอียดอ่อนแล้ว
-      shared_inbox_url: URL กล่องขาเข้าที่แบ่งปัน
+      shared_inbox_url: URL ของกล่องขาเข้าที่แบ่งปัน
       show:
         created_reports: รายงานที่สร้าง
         targeted_reports: รายงานโดยผู้อื่น
@@ -419,6 +421,7 @@ th:
         resolve: แปลงที่อยู่โดเมน
         title: ปิดกั้นโดเมนอีเมลใหม่
       no_email_domain_block_selected: ไม่มีการเปลี่ยนแปลงการปิดกั้นโดเมนอีเมลเนื่องจากไม่มีการเลือก
+      not_permitted: ไม่ได้รับอนุญาต
       resolved_dns_records_hint_html: ชื่อโดเมนแปลงที่อยู่เป็นโดเมน MX ดังต่อไปนี้ ซึ่งท้ายที่สุดแล้วจะรับผิดชอบสำหรับการยอมรับอีเมล การปิดกั้นโดเมน MX จะปิดกั้นการลงทะเบียนจากที่อยู่อีเมลใด ๆ ซึ่งใช้โดเมน MX เดียวกัน แม้ว่าชื่อโดเมนที่ปรากฏจะแตกต่างกันก็ตาม <strong>ระวังอย่าปิดกั้นผู้ให้บริการอีเมลรายใหญ่</strong>
       resolved_through_html: แปลงที่อยู่ผ่าน %{domain}
       title: โดเมนอีเมลที่ปิดกั้นอยู่
@@ -433,6 +436,7 @@ th:
         private_comment_description_html: 'เพื่อช่วยให้คุณติดตามว่าการปิดกั้นที่นำเข้ามาจากที่ใด จะสร้างการปิดกั้นที่นำเข้าโดยมีความคิดเห็นส่วนตัวดังต่อไปนี้: <q>%{comment}</q>'
         private_comment_template: นำเข้าจาก %{source} เมื่อ %{date}
         title: นำเข้าการปิดกั้นโดเมน
+      invalid_domain_block: 'มีการข้ามการปิดกั้นโดเมนจำนวนหนึ่งหรือมากกว่าเนื่องจากข้อผิดพลาดดังต่อไปนี้: %{error}'
       new:
         title: นำเข้าการปิดกั้นโดเมน
       no_file: ไม่ได้เลือกไฟล์
@@ -462,6 +466,7 @@ th:
       content_policies:
         comment: หมายเหตุภายใน
         description_html: คุณสามารถกำหนดนโยบายเนื้อหาที่จะนำไปใช้กับบัญชีทั้งหมดจากโดเมนนี้และโดเมนย่อยใดก็ตามของโดเมน
+        limited_federation_mode_description_html: คุณสามารถเลือกได้ว่าจะอนุญาตการติดต่อกับภายนอกกับโดเมนนี้หรือไม่
         policies:
           reject_media: ปฏิเสธสื่อ
           reject_reports: ปฏิเสธรายงาน
@@ -542,11 +547,11 @@ th:
       enable: เปิดใช้งาน
       enable_hint: เมื่อเปิดใช้งาน เซิร์ฟเวอร์ของคุณจะบอกรับโพสต์สาธารณะทั้งหมดจากรีเลย์นี้ และจะเริ่มส่งโพสต์สาธารณะของเซิร์ฟเวอร์นี้ไปยังรีเลย์
       enabled: เปิดใช้งานอยู่
-      inbox_url: URL รีเลย์
+      inbox_url: URL ของรีเลย์
       pending: กำลังรอการอนุมัติของรีเลย์
       save_and_enable: บันทึกแล้วเปิดใช้งาน
-      setup: ตั้งค่าการเชื่อมต่อแบบรีเลย์
-      signatures_not_enabled: รีเลย์จะทำงานไม่ถูกต้องขณะที่มีการเปิดใช้งานโหมดปลอดภัยหรือโหมดการติดต่อกับภายนอกแบบจำกัด
+      setup: ตั้งค่าการเชื่อมต่อรีเลย์
+      signatures_not_enabled: รีเลย์อาจทำงานไม่ถูกต้องขณะที่มีการเปิดใช้งานโหมดปลอดภัยหรือโหมดการติดต่อกับภายนอกแบบจำกัด
       status: สถานะ
       title: รีเลย์
     report_notes:
@@ -562,20 +567,24 @@ th:
         delete_description_html: จะลบโพสต์ที่รายงานและจะบันทึกการดำเนินการเพื่อช่วยให้คุณเลื่อนระดับการละเมิดในอนาคตโดยบัญชีเดียวกัน
         mark_as_sensitive_description_html: จะทำเครื่องหมายสื่อในโพสต์ที่รายงานว่าละเอียดอ่อนและจะบันทึกการดำเนินการเพื่อช่วยให้คุณเลื่อนระดับการละเมิดในอนาคตโดยบัญชีเดียวกัน
         other_description_html: ดูตัวเลือกเพิ่มเติมสำหรับการควบคุมพฤติกรรมของบัญชีและปรับแต่งการสื่อสารไปยังบัญชีที่รายงาน
-        resolve_description_html: จะไม่ใช้การกระทำกับบัญชีที่รายงาน ไม่มีการบันทึกการดำเนินการ และจะปิดรายงาน
-        silence_description_html: โปรไฟล์จะปรากฏแก่เฉพาะผู้ที่ติดตามโปรไฟล์อยู่แล้วหรือค้นหาโปรไฟล์ด้วยตนเองเท่านั้น จำกัดการเข้าถึงโปรไฟล์อย่างมาก สามารถแปลงกลับได้เสมอ
-        suspend_description_html: โปรไฟล์และเนื้อหาของโปรไฟล์ทั้งหมดจะเข้าถึงไม่ได้จนกว่าจะลบโปรไฟล์ในที่สุด การโต้ตอบกับบัญชีจะเป็นไปไม่ได้ แปลงกลับได้ภายใน 30 วัน
-      actions_description_html: ตัดสินใจว่าการกระทำใดที่จะใช้เพื่อแก้ปัญหารายงานนี้ หากคุณใช้การกระทำที่เป็นการลงโทษกับบัญชีที่รายงาน จะส่งการแจ้งเตือนอีเมลถึงเขา ยกเว้นเมื่อมีการเลือกหมวดหมู่ <strong>สแปม</strong>
+        resolve_description_html: จะไม่ใช้การกระทำต่อบัญชีที่รายงาน ไม่มีการบันทึกการดำเนินการ และจะปิดรายงาน
+        silence_description_html: บัญชีจะปรากฏแก่เฉพาะผู้ที่ติดตามโปรไฟล์อยู่แล้วหรือค้นหาโปรไฟล์ด้วยตนเองเท่านั้น จำกัดการเข้าถึงโปรไฟล์อย่างมาก สามารถแปลงกลับได้เสมอ ปิดรายงานต่อบัญชีนี้ทั้งหมด
+        suspend_description_html: บัญชีและเนื้อหาของบัญชีทั้งหมดจะเข้าถึงไม่ได้และได้รับการลบในที่สุด และการโต้ตอบกับบัญชีจะเป็นไปไม่ได้ แปลงกลับได้ภายใน 30 วัน ปิดรายงานต่อบัญชีนี้ทั้งหมด
+      actions_description_html: ตัดสินใจว่าการกระทำใดที่จะใช้เพื่อแก้ปัญหารายงานนี้ หากคุณใช้การกระทำที่เป็นการลงโทษต่อบัญชีที่รายงาน จะส่งการแจ้งเตือนอีเมลถึงเขา ยกเว้นเมื่อมีการเลือกหมวดหมู่ <strong>สแปม</strong>
+      actions_description_remote_html: ตัดสินใจว่าการกระทำใดที่จะใช้เพื่อแก้ปัญหารายงานนี้ นี่จะมีผลต่อวิธีที่เซิร์ฟเวอร์ <strong>ของคุณ</strong> สื่อสารกับบัญชีระยะไกลนี้และจัดการเนื้อหาของบัญชีเท่านั้น
       add_to_report: เพิ่มข้อมูลเพิ่มเติมไปยังรายงาน
       are_you_sure: คุณแน่ใจหรือไม่?
       assign_to_self: มอบหมายให้ฉัน
       assigned: ผู้ควบคุมที่ได้รับมอบหมาย
       by_target_domain: โดเมนของบัญชีที่ได้รับการรายงาน
+      cancel: ยกเลิก
       category: หมวดหมู่
       category_description_html: จะอ้างถึงเหตุผลที่บัญชีและ/หรือเนื้อหานี้ได้รับการรายงานในการสื่อสารกับบัญชีที่ได้รับการรายงาน
       comment:
         none: ไม่มี
       comment_description_html: 'เพื่อให้ข้อมูลเพิ่มเติม %{name} ได้เขียน:'
+      confirm: ยืนยัน
+      confirm_action: ยืนยันการกระทำการควบคุมต่อ @%{acct}
       created_at: รายงานเมื่อ
       delete_and_resolve: ลบโพสต์
       forwarded: ส่งต่อแล้ว
@@ -592,6 +601,7 @@ th:
         placeholder: อธิบายว่าการกระทำใดที่ใช้ หรือการอัปเดตที่เกี่ยวข้องอื่นใด...
         title: หมายเหตุ
       notes_description_html: ดูและฝากหมายเหตุถึงผู้ควบคุมอื่น ๆ และตัวคุณเองในอนาคต
+      processed_msg: 'ประมวลผลรายงาน #%{id} สำเร็จ'
       quick_actions_description_html: 'ดำเนินการอย่างรวดเร็วหรือเลื่อนลงเพื่อดูเนื้อหาที่รายงาน:'
       remote_user_placeholder: ผู้ใช้ระยะไกลจาก %{instance}
       reopen: เปิดรายงานใหม่
@@ -604,9 +614,28 @@ th:
       status: สถานะ
       statuses: เนื้อหาที่รายงาน
       statuses_description_html: จะอ้างถึงเนื้อหาที่ละเมิดในการสื่อสารกับบัญชีที่ได้รับการรายงาน
+      summary:
+        action_preambles:
+          delete_html: 'คุณกำลังจะ <strong>เอา</strong> โพสต์บางส่วนของ <strong>@%{acct}</strong> <strong>ออก</strong> นี่จะ:'
+          mark_as_sensitive_html: 'คุณกำลังจะ <strong>ทำเครื่องหมาย</strong> โพสต์บางส่วนของ <strong>@%{acct}</strong> ว่า <strong>ละเอียดอ่อน</strong> นี่จะ:'
+          silence_html: 'คุณกำลังจะ <strong>จำกัด</strong> บัญชีของ <strong>@%{acct}</strong> นี่จะ:'
+          suspend_html: 'คุณกำลังจะ <strong>ระงับ</strong> บัญชีของ <strong>@%{acct}</strong> นี่จะ:'
+        actions:
+          delete_html: เอาโพสต์ที่ละเมิดออก
+          mark_as_sensitive_html: ทำเครื่องหมายสื่อของโพสต์ที่ละเมิดว่าละเอียดอ่อน
+          silence_html: จำกัดการเข้าถึงของ <strong>@%{acct}</strong> อย่างมากโดยทำให้โปรไฟล์และเนื้อหาของเขาปรากฏแก่เฉพาะผู้คนที่กำลังติดตามเขาอยู่แล้วหรือค้นหาโปรไฟล์ของบัญชีด้วยตนเองเท่านั้น
+          suspend_html: ระงับ <strong>@%{acct}</strong> ทำให้โปรไฟล์และเนื้อหาของเขาเข้าถึงไม่ได้และไม่สามารถโต้ตอบด้วย
+        close_report: 'ทำเครื่องหมายรายงาน #%{id} ว่าแก้ปัญหาแล้ว'
+        close_reports_html: ทำเครื่องหมายรายงาน <strong>ทั้งหมด</strong> ต่อ <strong>@%{acct}</strong> ว่าแก้ปัญหาแล้ว
+        delete_data_html: ลบโปรไฟล์และเนื้อหาของ <strong>@%{acct}</strong> ในอีก 30 วันนับจากนี้เว้นแต่มีการเลิกระงับเขาในระหว่างนี้
+        preview_preamble_html: "<strong>@%{acct}</strong> จะได้รับคำเตือนโดยมีเนื้อหาดังต่อไปนี้:"
+        record_strike_html: บันทึกการดำเนินการต่อ <strong>@%{acct}</strong> เพื่อช่วยให้คุณเลื่อนระดับการละเมิดในอนาคตจากบัญชีนี้
+        send_email_html: ส่งอีเมลคำเตือนถึง <strong>@%{acct}</strong>
+        warning_placeholder: การให้เหตุผลเพิ่มเติมที่ไม่จำเป็นสำหรับการกระทำการควบคุม
       target_origin: จุดเริ่มต้นของบัญชีที่ได้รับการรายงาน
       title: รายงาน
       unassign: เลิกมอบหมาย
+      unknown_action_msg: 'การกระทำที่ไม่รู้จัก: %{action}'
       unresolved: ยังไม่ได้แก้ปัญหา
       updated_at: อัปเดตเมื่อ
       view_profile: ดูโปรไฟล์
@@ -647,7 +676,7 @@ th:
         manage_invites: จัดการคำเชิญ
         manage_invites_description: อนุญาตให้ผู้ใช้เรียกดูและปิดใช้งานลิงก์เชิญ
         manage_reports: จัดการรายงาน
-        manage_reports_description: อนุญาตให้ผู้ใช้ตรวจทานรายงานและทำการกระทำการควบคุมกับรายงาน
+        manage_reports_description: อนุญาตให้ผู้ใช้ตรวจทานรายงานและทำการกระทำการควบคุมต่อรายงาน
         manage_roles: จัดการบทบาท
         manage_roles_description: อนุญาตให้ผู้ใช้จัดการและกำหนดบทบาทที่ต่ำกว่าบทบาทของเขา
         manage_rules: จัดการกฎ
@@ -659,7 +688,7 @@ th:
         manage_user_access: จัดการการเข้าถึงของผู้ใช้
         manage_user_access_description: อนุญาตให้ผู้ใช้ปิดใช้งานการรับรองความถูกต้องด้วยสองปัจจัยของผู้ใช้อื่น เปลี่ยนที่อยู่อีเมลของเขา และตั้งรหัสผ่านของเขาใหม่
         manage_users: จัดการผู้ใช้
-        manage_users_description: อนุญาตให้ผู้ใช้ดูรายละเอียดของผู้ใช้อื่น ๆ และทำการกระทำการควบคุมกับผู้ใช้
+        manage_users_description: อนุญาตให้ผู้ใช้ดูรายละเอียดของผู้ใช้อื่น ๆ และทำการกระทำการควบคุมต่อผู้ใช้
         manage_webhooks: จัดการเว็บฮุค
         manage_webhooks_description: อนุญาตให้ผู้ใช้ตั้งค่าเว็บฮุคสำหรับเหตุการณ์การดูแล
         view_audit_log: ดูรายการบันทึกการตรวจสอบ
@@ -686,7 +715,7 @@ th:
         preamble: ปรับแต่งส่วนติดต่อเว็บของ Mastodon
         title: ลักษณะที่ปรากฏ
       branding:
-        preamble: ตราสินค้าของเซิร์ฟเวอร์ของคุณสร้างความแตกต่างตราสินค้าของเซิร์ฟเวอร์ของคุณจากเซิร์ฟเวอร์อื่น ๆ ในเครือข่าย อาจแสดงข้อมูลนี้ข้ามสภาพแวดล้อมที่หลากหลาย เช่น ส่วนติดต่อเว็บของ Mastodon, แอปพลิเคชันเนทีฟ, ในการแสดงตัวอย่างลิงก์ในเว็บไซต์อื่น ๆ และภายในแอปการส่งข้อความ และอื่น ๆ ด้วยเหตุผลนี้ จึงเป็นการดีที่สุดที่จะทำให้ข้อมูลนี้ชัดเจน สั้น และกระชับ
+        preamble: ตราสินค้าของเซิร์ฟเวอร์ของคุณสร้างความแตกต่างของตราสินค้าจากเซิร์ฟเวอร์อื่น ๆ ในเครือข่าย อาจแสดงข้อมูลนี้ข้ามสภาพแวดล้อมที่หลากหลาย เช่น ส่วนติดต่อเว็บของ Mastodon, แอปพลิเคชันเนทีฟ, ในการแสดงตัวอย่างลิงก์ในเว็บไซต์อื่น ๆ และภายในแอปการส่งข้อความ และอื่น ๆ ด้วยเหตุผลนี้ จึงเป็นการดีที่สุดที่จะทำให้ข้อมูลนี้ชัดเจน สั้น และกระชับ
         title: ตราสินค้า
       content_retention:
         preamble: ควบคุมวิธีการจัดเก็บเนื้อหาที่ผู้ใช้สร้างขึ้นใน Mastodon
@@ -699,6 +728,8 @@ th:
         preamble: การแสดงเนื้อหาที่น่าสนใจเป็นเครื่องมือในการเตรียมความพร้อมให้ผู้ใช้ใหม่ที่อาจไม่รู้จักใครก็ตามใน Mastodon ควบคุมวิธีที่คุณลักษณะการค้นพบต่าง ๆ ทำงานในเซิร์ฟเวอร์ของคุณ
         profile_directory: ไดเรกทอรีโปรไฟล์
         public_timelines: เส้นเวลาสาธารณะ
+        publish_discovered_servers: เผยแพร่เซิร์ฟเวอร์ที่ค้นพบ
+        publish_statistics: เผยแพร่สถิติ
         title: การค้นพบ
         trends: แนวโน้ม
       domain_blocks:
@@ -753,6 +784,7 @@ th:
         suspend: "%{name} ได้ระงับบัญชีของ %{target}"
       appeal_approved: อุทธรณ์แล้ว
       appeal_pending: รอดำเนินการการอุทธรณ์
+      appeal_rejected: ปฏิเสธการอุทธรณ์แล้ว
     system_checks:
       database_schema_check:
         message_html: มีการโยกย้ายฐานข้อมูลที่รอดำเนินการ โปรดเรียกใช้การโยกย้ายเพื่อให้แน่ใจว่าแอปพลิเคชันทำงานตามที่คาดไว้
@@ -766,6 +798,12 @@ th:
         message_html: คุณไม่ได้กำหนดกฎของเซิร์ฟเวอร์ใด ๆ
       sidekiq_process_check:
         message_html: ไม่มีกระบวนการ Sidekiq ที่กำลังทำงานสำหรับคิว %{value} โปรดตรวจทานการกำหนดค่า Sidekiq ของคุณ
+      upload_check_privacy_error:
+        action: ตรวจสอบที่นี่สำหรับข้อมูลเพิ่มเติม
+        message_html: "<strong>เว็บเซิร์ฟเวอร์ของคุณกำหนดค่าไม่ถูกต้อง ความเป็นส่วนตัวของผู้ใช้ของคุณตกอยู่ในความเสี่ยง</strong>"
+      upload_check_privacy_error_object_storage:
+        action: ตรวจสอบที่นี่สำหรับข้อมูลเพิ่มเติม
+        message_html: "<strong>ที่เก็บข้อมูลวัตถุของคุณกำหนดค่าไม่ถูกต้อง ความเป็นส่วนตัวของผู้ใช้ของคุณตกอยู่ในความเสี่ยง</strong>"
     tags:
       review: สถานะการตรวจทาน
       updated_msg: อัปเดตการตั้งค่าแฮชแท็กสำเร็จ
@@ -787,6 +825,7 @@ th:
           other: แบ่งปันโดย %{count} คนในช่วงสัปดาห์ที่ผ่านมา
         title: ลิงก์ที่กำลังนิยม
         usage_comparison: แบ่งปัน %{today} ครั้งวันนี้ เทียบกับ %{yesterday} เมื่อวานนี้
+      not_allowed_to_trend: ไม่ได้รับอนุญาตให้ขึ้นแนวโน้ม
       only_allowed: อนุญาตเท่านั้น
       pending_review: การตรวจทานที่รอดำเนินการ
       preview_card_providers:
@@ -915,6 +954,7 @@ th:
   applications:
     created: สร้างแอปพลิเคชันสำเร็จ
     destroyed: ลบแอปพลิเคชันสำเร็จ
+    logout: ออกจากระบบ
     regenerate_token: สร้างโทเคนการเข้าถึงใหม่
     token_regenerated: สร้างโทเคนการเข้าถึงใหม่สำเร็จ
     warning: ระวังเป็นอย่างสูงกับข้อมูลนี้ อย่าแบ่งปันข้อมูลกับใครก็ตาม!
@@ -922,12 +962,14 @@ th:
   auth:
     apply_for_account: ขอบัญชี
     change_password: รหัสผ่าน
+    confirmations:
+      wrong_email_hint: หากที่อยู่อีเมลนั้นไม่ถูกต้อง คุณสามารถเปลี่ยนที่อยู่อีเมลได้ในการตั้งค่าบัญชี
     delete_account: ลบบัญชี
     delete_account_html: หากคุณต้องการลบบัญชีของคุณ คุณสามารถ <a href="%{path}">ดำเนินการต่อที่นี่</a> คุณจะได้รับการถามเพื่อการยืนยัน
     description:
       prefix_invited_by_user: "@%{name} เชิญคุณเข้าร่วมเซิร์ฟเวอร์ Mastodon นี้!"
       prefix_sign_up: ลงทะเบียนใน Mastodon วันนี้!
-      suffix: เมื่อมีบัญชี คุณจะสามารถติดตามผู้คน โพสต์การอัปเดต และแลกเปลี่ยนข้อความกับผู้ใช้จากเซิร์ฟเวอร์ Mastodon และอื่น ๆ!
+      suffix: ด้วยบัญชี คุณจะสามารถติดตามผู้คน โพสต์การอัปเดต และแลกเปลี่ยนข้อความกับผู้ใช้จากเซิร์ฟเวอร์ Mastodon และอื่น ๆ!
     didnt_get_confirmation: ไม่ได้รับคำแนะนำการยืนยัน?
     dont_have_your_security_key: ไม่มีกุญแจความปลอดภัยของคุณ?
     forgot_password: ลืมรหัสผ่านของคุณ?
@@ -949,6 +991,8 @@ th:
     resend_confirmation: ส่งคำแนะนำการยืนยันใหม่
     reset_password: ตั้งรหัสผ่านใหม่
     rules:
+      accept: ยอมรับ
+      back: ย้อนกลับ
       preamble: มีการตั้งและบังคับใช้กฎโดยผู้ควบคุมของ %{domain}
       title: กฎพื้นฐานบางประการ
     security: ความปลอดภัย
@@ -961,7 +1005,7 @@ th:
       preamble_html: ลงชื่อเข้าด้วยข้อมูลประจำตัว <strong>%{domain}</strong> ของคุณ หากบัญชีของคุณได้รับการโฮสต์ในเซิร์ฟเวอร์อื่น คุณจะไม่สามารถเข้าสู่ระบบได้ที่นี่
       title: ลงชื่อเข้า %{domain}
     sign_up:
-      preamble: เมื่อมีบัญชีในเซิร์ฟเวอร์ Mastodon นี้ คุณจะสามารถติดตามบุคคลอื่นใดในเครือข่าย โดยไม่คำนึงถึงที่ซึ่งบัญชีของเขาได้รับการโฮสต์
+      preamble: ด้วยบัญชีในเซิร์ฟเวอร์ Mastodon นี้ คุณจะสามารถติดตามบุคคลอื่นใดในเครือข่าย โดยไม่คำนึงถึงที่ซึ่งบัญชีของเขาได้รับการโฮสต์
       title: มาตั้งค่าของคุณใน %{domain} กันเลย
     status:
       account_status: สถานะบัญชี
@@ -969,7 +1013,7 @@ th:
       functional: บัญชีของคุณทำงานได้อย่างเต็มที่
       pending: ใบสมัครของคุณกำลังรอดำเนินการตรวจทานโดยพนักงานของเรา นี่อาจใช้เวลาสักครู่ คุณจะได้รับอีเมลหากมีการอนุมัติใบสมัครของคุณ
       redirecting_to: บัญชีของคุณไม่ได้ใช้งานเนื่องจากบัญชีกำลังเปลี่ยนเส้นทางไปยัง %{acct} ในปัจจุบัน
-      view_strikes: ดูการดำเนินการที่ผ่านมากับบัญชีของคุณ
+      view_strikes: ดูการดำเนินการที่ผ่านมาต่อบัญชีของคุณ
     too_fast: ส่งแบบฟอร์มเร็วเกินไป ลองอีกครั้ง
     use_security_key: ใช้กุญแจความปลอดภัย
   authorize_follow:
@@ -1041,7 +1085,7 @@ th:
       approve_appeal: อนุมัติการอุทธรณ์
       associated_report: รายงานที่เกี่ยวข้อง
       created_at: ลงวันที่
-      description_html: นี่คือการกระทำที่ใช้กับบัญชีของคุณและคำเตือนที่ส่งถึงคุณโดยพนักงานของ %{instance}
+      description_html: นี่คือการกระทำที่ใช้ต่อบัญชีของคุณและคำเตือนที่ส่งถึงคุณโดยพนักงานของ %{instance}
       recipient: ส่งถึง
       reject_appeal: ปฏิเสธการอุทธรณ์
       status: 'โพสต์ #%{id}'
@@ -1137,8 +1181,6 @@ th:
       index:
         hint: ตัวกรองนี้นำไปใช้เพื่อเลือกโพสต์แต่ละรายการโดยไม่คำนึงถึงเกณฑ์อื่น ๆ คุณสามารถเพิ่มโพสต์เพิ่มเติมไปยังตัวกรองนี้ได้จากส่วนติดต่อเว็บ
         title: โพสต์ที่กรองอยู่
-  footer:
-    trending_now: กำลังนิยม
   generic:
     all: ทั้งหมด
     all_items_on_page_selected_html:
@@ -1157,8 +1199,6 @@ th:
     today: วันนี้
     validation_errors:
       other: ยังมีบางอย่างไม่ถูกต้อง! โปรดตรวจทาน %{count} ข้อผิดพลาดด้านล่าง
-  html_validator:
-    invalid_markup: 'มีมาร์กอัป HTML ที่ไม่ถูกต้อง: %{error}'
   imports:
     errors:
       invalid_csv_file: 'ไฟล์ CSV ไม่ถูกต้อง ข้อผิดพลาด: %{error}'
@@ -1341,7 +1381,11 @@ th:
       unrecognized_emoji: ไม่ใช่อีโมจิที่รู้จัก
   relationships:
     activity: กิจกรรมบัญชี
+    confirm_follow_selected_followers: คุณแน่ใจหรือไม่ว่าต้องการติดตามผู้ติดตามที่เลือก?
+    confirm_remove_selected_followers: คุณแน่ใจหรือไม่ว่าต้องการเอาผู้ติดตามที่เลือกออก?
+    confirm_remove_selected_follows: คุณแน่ใจหรือไม่ว่าต้องการเอาการติดตามที่เลือกออก?
     dormant: ไม่เคลื่อนไหว
+    follow_failure: ไม่สามารถติดตามบัญชีที่เลือกบางส่วน
     follow_selected_followers: ติดตามผู้ติดตามที่เลือก
     followers: ผู้ติดตาม
     following: กำลังติดตาม
@@ -1360,7 +1404,7 @@ th:
     missing_resource: ไม่พบ URL การเปลี่ยนเส้นทางที่จำเป็นสำหรับบัญชีของคุณ
   reports:
     errors:
-      invalid_rules: ไม่ได้อ้างอิงกฎที่ถูกต้อง
+      invalid_rules: ไม่อ้างอิงกฎที่ถูกต้อง
   rss:
     content_warning: 'คำเตือนเนื้อหา:'
     descriptions:
@@ -1381,6 +1425,7 @@ th:
       electron: Electron
       firefox: Firefox
       generic: เบราว์เซอร์ที่ไม่รู้จัก
+      huawei_browser: เบราว์เซอร์ Huawei
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: เบราว์เซอร์ Nokia S40 Ovi
@@ -1390,6 +1435,7 @@ th:
       qq: เบราว์เซอร์ QQ
       safari: Safari
       uc_browser: เบราว์เซอร์ UC
+      unknown_browser: เบราว์เซอร์ที่ไม่รู้จัก
       weibo: Weibo
     current_session: เซสชันปัจจุบัน
     description: "%{browser} ใน %{platform}"
@@ -1402,9 +1448,10 @@ th:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: แพลตฟอร์มที่ไม่รู้จัก
+      unknown_platform: แพลตฟอร์มที่ไม่รู้จัก
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1550,11 +1597,11 @@ th:
   user_mailer:
     appeal_approved:
       action: ไปยังบัญชีของคุณ
-      explanation: อนุมัติการอุทธรณ์การดำเนินการกับบัญชีของคุณเมื่อ %{strike_date} ที่คุณได้ส่งเมื่อ %{appeal_date} แล้ว บัญชีของคุณอยู่ในสถานะที่ดีอีกครั้งหนึ่ง
+      explanation: อนุมัติการอุทธรณ์การดำเนินการต่อบัญชีของคุณเมื่อ %{strike_date} ที่คุณได้ส่งเมื่อ %{appeal_date} แล้ว บัญชีของคุณอยู่ในสถานะที่ดีอีกครั้งหนึ่ง
       subject: อนุมัติการอุทธรณ์ของคุณจาก %{date} แล้ว
       title: อนุมัติการอุทธรณ์แล้ว
     appeal_rejected:
-      explanation: ปฏิเสธการอุทธรณ์การดำเนินการกับบัญชีของคุณเมื่อ %{strike_date} ที่คุณได้ส่งเมื่อ %{appeal_date} แล้ว
+      explanation: ปฏิเสธการอุทธรณ์การดำเนินการต่อบัญชีของคุณเมื่อ %{strike_date} ที่คุณได้ส่งเมื่อ %{appeal_date} แล้ว
       subject: ปฏิเสธการอุทธรณ์ของคุณจาก %{date} แล้ว
       title: ปฏิเสธการอุทธรณ์แล้ว
     backup_ready:
@@ -1575,7 +1622,7 @@ th:
         spam: สแปม
         violation: เนื้อหาละเมิดหลักเกณฑ์ชุมชนดังต่อไปนี้
       explanation:
-        delete_statuses: มีการพบว่าโพสต์บางส่วนของคุณละเมิดหนึ่งหลักเกณฑ์ชุมชนหรือมากกว่าและได้รับการเอาออกโดยผู้ควบคุมของ %{instance} ในเวลาต่อมา
+        delete_statuses: มีการพบว่าโพสต์บางส่วนของคุณละเมิดหลักเกณฑ์ชุมชนจำนวนหนึ่งหรือมากกว่าและได้รับการเอาออกโดยผู้ควบคุมของ %{instance} ในเวลาต่อมา
         disable: คุณไม่สามารถใช้บัญชีของคุณได้อีกต่อไป แต่โปรไฟล์และข้อมูลอื่น ๆ ของคุณยังคงอยู่ในสภาพเดิม คุณสามารถขอข้อมูลสำรองของข้อมูลของคุณ เปลี่ยนการตั้งค่าบัญชี หรือลบบัญชีของคุณ
         mark_statuses_as_sensitive: ทำเครื่องหมายโพสต์บางส่วนของคุณว่าละเอียดอ่อนโดยผู้ควบคุมของ %{instance} แล้ว นี่หมายความว่าผู้คนจะต้องแตะสื่อในโพสต์ก่อนที่จะแสดงตัวอย่าง คุณสามารถทำเครื่องหมายสื่อว่าละเอียดอ่อนด้วยตัวคุณเองเมื่อโพสต์ในอนาคต
         sensitive: จากนี้ไป จะทำเครื่องหมายไฟล์สื่อที่อัปโหลดทั้งหมดของคุณว่าละเอียดอ่อนและซ่อนอยู่หลังการคลิกไปยังคำเตือน
@@ -1606,17 +1653,18 @@ th:
       final_action: เริ่มโพสต์
       final_step: 'เริ่มโพสต์! แม้ว่าไม่มีผู้ติดตาม โพสต์สาธารณะของคุณอาจเห็นโดยผู้อื่น ตัวอย่างเช่น ในเส้นเวลาในเซิร์ฟเวอร์หรือในแฮชแท็ก คุณอาจต้องการแนะนำตัวเองในแฮชแท็ก #introductions'
       full_handle: นามเต็มของคุณ
-      full_handle_hint: นี่คือสิ่งที่คุณจะบอกเพื่อน ๆ ของคุณ เพื่อให้เขาสามารถส่งข้อความหรือติดตามคุณจากเซิร์ฟเวอร์อื่น
+      full_handle_hint: นี่คือสิ่งที่คุณจะบอกเพื่อน ๆ ของคุณเพื่อให้เขาสามารถส่งข้อความหรือติดตามคุณจากเซิร์ฟเวอร์อื่น
       subject: ยินดีต้อนรับสู่ Mastodon
       title: ยินดีต้อนรับ %{name}!
   users:
     follow_limit_reached: คุณไม่สามารถติดตามมากกว่า %{limit} คน
+    go_to_sso_account_settings: ไปยังการตั้งค่าบัญชีของผู้ให้บริการข้อมูลประจำตัวของคุณ
     invalid_otp_token: รหัสสองปัจจัยไม่ถูกต้อง
     otp_lost_help_html: หากคุณสูญเสียการเข้าถึงทั้งสองอย่าง คุณสามารถติดต่อ %{email}
     seamless_external_login: คุณได้เข้าสู่ระบบผ่านบริการภายนอก ดังนั้นจึงไม่มีการตั้งค่ารหัสผ่านและอีเมล
     signed_in_as: 'ลงชื่อเข้าเป็น:'
   verification:
-    explanation_html: 'คุณสามารถ <strong>ยืนยันตัวคุณเองว่าเป็นเจ้าของของลิงก์ในข้อมูลอภิพันธุ์โปรไฟล์ของคุณ</strong> สำหรับสิ่งนั้น เว็บไซต์ที่เชื่อมโยงต้องมีลิงก์ย้อนกลับไปยังโปรไฟล์ Mastodon ของคุณ ลิงก์ย้อนกลับ <strong>ต้อง</strong> มีแอตทริบิวต์ <code>rel="me"</code> เนื้อหาข้อความของลิงก์ไม่สำคัญ นี่คือตัวอย่าง:'
+    explanation_html: 'คุณสามารถ <strong>ยืนยันตัวคุณเองว่าเป็นเจ้าของของลิงก์ในข้อมูลอภิพันธุ์โปรไฟล์ของคุณ</strong> สำหรับสิ่งนั้น เว็บไซต์ที่เชื่อมโยงต้องมีลิงก์ย้อนกลับไปยังโปรไฟล์ Mastodon ของคุณ หลังจากการเพิ่มลิงก์คุณอาจจำเป็นต้องกลับมาที่นี่และบันทึกโปรไฟล์ของคุณใหม่เพื่อให้การตรวจสอบมีผล ลิงก์ย้อนกลับ <strong>ต้อง</strong> มีแอตทริบิวต์ <code>rel="me"</code> เนื้อหาข้อความของลิงก์ไม่สำคัญ นี่คือตัวอย่าง:'
     verification: การตรวจสอบ
   webauthn_credentials:
     add: เพิ่มกุญแจความปลอดภัยใหม่
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index cd377ff54..99aa325cb 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -3,7 +3,7 @@ tr:
   about:
     about_mastodon_html: Mastodon <em>ücretsiz ve açık kaynaklı</em> bir sosyal ağdır. <em>Merkezileştirilmemiş</em> yapısı sayesinde diğer ticari sosyal platformların aksine iletişimininizin tek bir firmada tutulmasının/yönetilmesinin önüne geçer. Güvendiğiniz bir sunucuyu seçerek oradaki kişilerle etkileşimde bulunabilirsiniz. Herkes kendi Mastodon sunucusunu kurabilir ve sorunsuz bir şekilde Mastodon <em>sosyal ağına</em> dahil edebilir.
     contact_missing: Ayarlanmadı
-    contact_unavailable: Yok
+    contact_unavailable: Bulunamadı
     hosted_on: Mastodon %{domain} üzerinde barındırılıyor
     title: Hakkında
   accounts:
@@ -11,7 +11,7 @@ tr:
     followers:
       one: Takipçi
       other: Takipçi
-    following: Takip edilenler
+    following: Takip ediliyor
     instance_actor_flash: Bu hesap, herhangi bir bireysel kullanıcı değil, sunucunun kendisini temsil etmek için kullanılan sanal bir aktördür. Birleştirme amacıyla kullanılmaktadır ve askıya alınmamalıdır.
     last_active: son etkinlik
     link_verified_on: Bu bağlantının mülkiyeti %{date} tarihinde kontrol edildi
@@ -34,7 +34,7 @@ tr:
       add_email_domain_block: E-posta alan adını engelle
       approve: Onayla
       approved_msg: "%{username} adlı kullanıcının kayıt başvurusu başarıyla onaylandı"
-      are_you_sure: Emin misiniz?
+      are_you_sure: Emin misin?
       avatar: Profil resmi
       by_domain: Alan adı
       change_email:
@@ -56,10 +56,10 @@ tr:
       delete: Veriyi sil
       deleted: Silindi
       demote: Düşür
-      destroyed_msg: "%{username} verilerinin hemen silinmesi için kuyruğa alındı"
-      disable: Devre dışı
+      destroyed_msg: "%{username} adlı kullanıcının verilerinin silinmesi sıraya alındı"
+      disable: Dondur
       disable_sign_in_token_auth: E-posta token doğrulamayı devre dışı bırak
-      disable_two_factor_authentication: 2AD kapat
+      disable_two_factor_authentication: 2 aşamalı doğrulamayı kapat
       disabled: Kapalı
       display_name: Görünen isim
       domain: Alan adı
@@ -70,14 +70,14 @@ tr:
       enable_sign_in_token_auth: E-posta token doğrulamayı etkinleştir
       enabled: Etkin
       enabled_msg: "%{username} hesabı başarıyla çözüldü"
-      followers: Takipçiler
+      followers: Takipçi
       follows: Takip edilen
       header: Üstbilgi
       inbox_url: Gelen kutusu bağlantısı
       invite_request_text: Katılma gerekçeleri
       invited_by: Tarafından davet edildi
       ip: IP
-      joined: Katıldı
+      joined: Katılma tarihi
       location:
         all: Hepsi
         local: Yerel
@@ -91,9 +91,10 @@ tr:
       moderation:
         active: Etkin
         all: Hepsi
-        pending: Bekliyor
+        disabled: Devre dışı bırakıldı
+        pending: Bekleyen
         silenced: Sınırlı
-        suspended: Uzaklaştırılanlar
+        suspended: Askıya Alınan
         title: Denetim
       moderation_notes: Denetleme notları
       most_recent_activity: Son aktivite
@@ -111,7 +112,7 @@ tr:
       promote: Yükselt
       protocol: Protokol
       public: Herkese açık
-      push_subscription_expires: PuSH aboneliği dolumu
+      push_subscription_expires: PuSH aboneliği sona erdi
       redownload: Profili yenile
       redownloaded_msg: "%{username} kullanıcısının profili kökenden başarıyla yenilendi"
       reject: Reddet
@@ -133,6 +134,7 @@ tr:
       search: Ara
       search_same_email_domain: Aynı e-posta alan adına sahip diğer kullanıcılar
       search_same_ip: Aynı IP adresine sahip diğer kullanıcılar
+      security: Güvenlik
       security_measures:
         only_password: Sadece parola
         password_and_2fa: Parola ve İki aşamalı doğrulama
@@ -144,7 +146,7 @@ tr:
         targeted_reports: Başkaları tarafından şikayet edildi
       silence: Sustur
       silenced: Susturulmuş
-      statuses: Durumlar
+      statuses: Gönderiler
       strikes: Önceki eylemler
       subscribe: Abone ol
       suspend: Askıya al
@@ -168,7 +170,7 @@ tr:
       whitelisted: Beyaz listede
     action_logs:
       action_types:
-        approve_appeal: İtirazı Kabul Et
+        approve_appeal: İtirazı Onayla
         approve_user: Kullanıcıyı Onayla
         assigned_to_self_report: Raporu Ata
         change_email_user: Kullanıcı E-postasını Değiştir
@@ -427,6 +429,7 @@ tr:
         resolve: Alan adı çözümleme
         title: Yeni e-posta kara liste girişi
       no_email_domain_block_selected: Seçim yapılmadığından hiç bir e-posta alan adı engeli değiştirilmedi
+      not_permitted: İzin verilmedi
       resolved_dns_records_hint_html: Alan adı aşağıdaki MX alan adlarına çözümleniyor, ancak hiç biri nihayetinde e-posta kabulünden sorum değil. Bir MX alan adını engellemek, görünen alan adı farklı osa bile aynı MX alan adını kullanan e-posta adreslerinden gelen kayıtları engelleyecektir. <strong>Başlıca e-posta sağlayıcıları engellememek için dikkatli olun.</strong>
       resolved_through_html: "%{domain} aracılığıyla çözümlendi"
       title: E-posta kara listesi
@@ -441,6 +444,7 @@ tr:
         private_comment_description_html: 'İçe aktarılan engellerin nereden geldiğini izlemenize olanak sağlamak için, içe aktarılan engeller şu özel yorum ile oluşturulacak: <q>%{comment}</q>'
         private_comment_template: "%{source} kaynağından %{date} tarihinde içe aktarıldı"
         title: Domain bloklarını içe aktar
+      invalid_domain_block: 'Bir veya daha fazla alan adı engeli şu hata(lar)dan dolayı atlandı: %{error}'
       new:
         title: Domain bloklarını içe aktar
       no_file: Dosya seçilmedi
@@ -472,6 +476,7 @@ tr:
       content_policies:
         comment: İç not
         description_html: Bu alan adındaki ve alt alan adlarındaki tüm hesaplara uygulanacak içerik ilkeleri belirleyebilirsiniz.
+        limited_federation_mode_description_html: Bu alan adıyla birleştirmeye izin verip vermediğinizi seçebilirsiniz.
         policies:
           reject_media: Medyayı reddet
           reject_reports: Şikayetleri reddet
@@ -575,19 +580,23 @@ tr:
         mark_as_sensitive_description_html: Bildirilen gönderilerdeki medya dosyaları hassas olarak işaretlenecek ve aynı hesabın gelecekteki ihlallerinde daha yetkili makamlara bildirmenize yardımcı olmak için bir eylem kaydedilecek.
         other_description_html: Hesabın davranışını denetlemek ve bildirilen hesabın iletişimini yapılandırmak için daha fazla seçenek görün.
         resolve_description_html: Bildirilen hesap için bir şey yapılmayacak, eylem kaydedilmeyecek ve bildirim kapatılacak.
-        silence_description_html: Profil sadece halihazırda takip edenler ve elle bakanlarca görünecek, böylece erişimi ciddi bir şekilde kısıtlanacak. Her zaman geri alınabilir.
-        suspend_description_html: Profil ve tüm içeriği sonunda silinene kadar erişilmez olacak. Hesapla etkileşim mümkün olmayacak. 30 gün boyunca geri alınabilir.
+        silence_description_html: Bu hesap sadece halihazırda takip edenler ve elle bakanlarca görünecek, böylece erişimi ciddi bir şekilde kısıtlanacak. Her zaman geri alınabilir. Bu hesaba yönelik tüm bildirimleri kapatır.
+        suspend_description_html: Bu hesap ve tüm içeriği erişilmez olacak ve nihayetinde silinecek ve bu hesapla etkileşim mümkün olmayacaktır. 30 gün içinde geri alınabilir. Bu hesaba yönelik tüm bildiriimleri kapatır.
       actions_description_html: Bu bildirimi çözmek için ne yapılması gerektiğine karar verin. Bildirilen hesap için ceza işlemi yaparsanız, <strong>İstenmeyen</strong> kategorisi seçilmemişse, onlara bir e-posta duyurusu gönderilecektir.
+      actions_description_remote_html: Bu bildirimi çözmek için hangi eylemi yapmak istediğinize karar verin. Bu yalnızca <strong>sizin</strong> sunucunuzun bu uzak hesapla nasıl etkileşeğini ve içeriğiyle ne yapacağını etkiler.
       add_to_report: Bildirime daha fazlasını ekle
       are_you_sure: Emin misiniz?
       assign_to_self: Bana ata
       assigned: Denetleyici atandı
       by_target_domain: Şikayet edilen hesabın alan adı
+      cancel: İptal et
       category: Kategori
       category_description_html: Bu hesap ve/veya içeriğin bildirilme gerekçesi, bildirilen hesapla iletişimde alıntılanacaktır
       comment:
         none: Yok
       comment_description_html: 'Daha fazla bilgi vermek için %{name} şunu yazdı:'
+      confirm: Onayla
+      confirm_action: "%{acct} üzerindeki denetleme eylemini onayla"
       created_at: Şikayet edildi
       delete_and_resolve: Gönderileri sil
       forwarded: İletildi
@@ -604,6 +613,7 @@ tr:
         placeholder: Hangi işlemlerin yapıldığını, ya da diğer ilgili güncellemeleri açıklayın...
         title: Notlar
       notes_description_html: Kendiniz ve diğer moderatörler için not bırakın veya notları görüntüleyin
+      processed_msg: "#%{id} Bildirimi başarıyla işlendi"
       quick_actions_description_html: 'Hemen bir şey yapın veya bildirilen içeriği görmek için aşağı kaydırın:'
       remote_user_placeholder: "%{instance}'dan uzak kullanıcı"
       reopen: Şikayeti tekrar aç
@@ -616,9 +626,28 @@ tr:
       status: Durum
       statuses: Bildirilen içerik
       statuses_description_html: İncitici içerik, bildirilen hesapla iletişimde alıntılanacaktır
+      summary:
+        action_preambles:
+          delete_html: "<strong>@%{acct}</strong> hesabının bazı gönderilerini <strong>kaldıracaksınız</strong>, böylece:"
+          mark_as_sensitive_html: "<strong>@%{acct}</strong> hesabının bazı gönderilerini <strong>hassas</strong> olarak <strong>işaretleyeceksiniz</strong>, böylece:"
+          silence_html: "<strong>@%{acct}</strong> hesabını <strong>sınırlayacaksınız</strong>, böylece:"
+          suspend_html: "<strong>@%{acct}</strong> hesabını <strong>askıya alacaksınız</strong>, böylece:"
+        actions:
+          delete_html: Kuralı ihlal eden gönderileri kaldır
+          mark_as_sensitive_html: Kuralı ihlal eden gönderilerin medyasını hassas olarak işaretle
+          silence_html: "<strong>@%{acct}</strong> hesabının erişimini oldukça kısıtla: profili ve içeriği sadece onları halihazırda takip edenler veya manuel olarak hesabına erişenlerce mümkün olacak"
+          suspend_html: "<strong>@%{acct}</strong> hesabını askıya al, profilini ve içeriğini erişilmez ve etkileşimi imkansız yap"
+        close_report: "#%{id} bildirimini çözüldü olarak işaretle"
+        close_reports_html: "<strong>@%{acct}</strong> hesabına yönelik <strong>tüm</strong> bildirimleri çözüldü olarak işaretle"
+        delete_data_html: İlgili sürede askıdan alınması kaldırılmazsa <strong>@%{acct}</strong> hesabının profilini ve içeriğini şu andan itibaren 30 gün içinde sil
+        preview_preamble_html: "<strong>@%{acct}</strong> aşağıdaki içerikle bir uyarı alacaktır:"
+        record_strike_html: "<strong>@%{acct}</strong> hesabına karşı bir eylem kaydet, böylece bu hesabın gelecekteki ihlallerini üst makama taşımanıza yardımcı olacaktır"
+        send_email_html: "<strong>@%{acct}</strong> adlı kullanıcıya uyarı e-maili gönder"
+        warning_placeholder: İsteğe bağlı ek nedenden denetim eylemi.
       target_origin: Şikayet edilen hesabın kökeni
       title: Şikayetler
       unassign: Atamayı geri al
+      unknown_action_msg: 'Bilinmeyen eylem: %{action}'
       unresolved: Giderilmedi
       updated_at: Güncellendi
       view_profile: Profili görüntüle
@@ -713,6 +742,8 @@ tr:
         preamble: İlginç içeriği gezinmek, Mastodon'da kimseyi tanımayan yeni kullanıcıları alıştırmak için oldukça etkilidir. Sunucunuzdaki çeşitli keşif özelliklerinin nasıl çalıştığını denetleyin.
         profile_directory: Profil dizini
         public_timelines: Genel zaman çizelgeleri
+        publish_discovered_servers: Keşfedilen sunucuları yayınla
+        publish_statistics: İstatistikleri yayınla
         title: Keşfet
         trends: Öne çıkanlar
       domain_blocks:
@@ -767,6 +798,7 @@ tr:
         suspend: "%{name}, %{target} kullanıcısının hesabını askıya aldı"
       appeal_approved: İtiraz Edildi
       appeal_pending: İtiraz bekliyor
+      appeal_rejected: İtiraz reddedildi
     system_checks:
       database_schema_check:
         message_html: Beklemede olan veritabanı güncellemeleri mevcut. Uygulamanın beklenildiği gibi çalışması için lütfen onları çalıştırın
@@ -780,6 +812,12 @@ tr:
         message_html: Herhangi bir sunucu kuralı belirlemediniz.
       sidekiq_process_check:
         message_html: "%{value} kuyruk(lar)ı için herhangi bir Sidekiq süreci çalışmıyor. Lütfen Sidekiq yapılandırmanızı gözden geçirin"
+      upload_check_privacy_error:
+        action: Daha fazla bilgi için buraya tıklayın
+        message_html: "<strong>Web sunucunuz hatalı yapılandırılmış. Kullanıcılarınızın gizliliği tehlikede.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Daha fazla bilgi için buraya göz atın
+        message_html: "<strong>Nesne depolamanız hatalı yapılandırılmış. Kullanıcılarınızın gizliliği tehlikede.</strong>"
     tags:
       review: Durumu gözden geçir
       updated_msg: Etiket ayarları başarıyla güncellendi
@@ -802,6 +840,7 @@ tr:
           other: Geçen hafta %{count} kişi paylaştı
         title: Öne çıkan bağlantılar
         usage_comparison: Bugün %{today} kere paylaşıldı, dün %{yesterday} kere paylaşılmıştı
+      not_allowed_to_trend: Öne çıkma izni yok
       only_allowed: Sadece izin verilenler
       pending_review: İnceleme bekliyor
       preview_card_providers:
@@ -933,6 +972,7 @@ tr:
   applications:
     created: Uygulama başarıyla oluşturuldu
     destroyed: Uygulama başarıyla silindi
+    logout: Oturumu kapat
     regenerate_token: Erişim belirtecini yeniden oluştur
     token_regenerated: Erişim belirteci başarıyla oluşturuldu
     warning: Bu verilere çok dikkat edin. Asla kimseyle paylaşmayın!
@@ -940,6 +980,8 @@ tr:
   auth:
     apply_for_account: Bir hesap talep et
     change_password: Parola
+    confirmations:
+      wrong_email_hint: Eğer bu e-posta adresi doğru değilse, hesap ayarlarında değiştirebilirsiniz.
     delete_account: Hesabı sil
     delete_account_html: Hesabını silmek istersen, <a href="%{path}">buradan devam edebilirsin</a>. Onay istenir.
     description:
@@ -967,6 +1009,8 @@ tr:
     resend_confirmation: Onaylama talimatlarını tekrar gönder
     reset_password: Parolayı sıfırla
     rules:
+      accept: Onayla
+      back: Geri
       preamble: Bunlar, %{domain} moderatörleri tarafından ayarlanmış ve uygulanmıştır.
       title: Bazı temel kurallar.
     security: Güvenlik
@@ -1114,7 +1158,7 @@ tr:
   featured_tags:
     add_new: Yeni ekle
     errors:
-      limit: Zaten azami hashtag miktarı belirlediniz
+      limit: Zaten azami etiket sayısını öne çıkardınız
     hint_html: "<strong>Öne çıkan etiketler nelerdir?</strong> Genel profilinizde belirgin bir şekilde görüntülenirler ve kişilerin genel yayınlarınıza özellikle bu etiketler altında göz atmalarına izin verir. Yaratıcı çalışmaları veya uzun vadeli projeleri takip etmek için harika bir araçtır."
   filters:
     contexts:
@@ -1158,8 +1202,6 @@ tr:
       index:
         hint: Bu filtre diğer ölçütlerden bağımsız olarak tekil gönderileri seçmek için uygulanıyor. Web arayüzünü kullanarak bu filtreye daha fazla gönderi ekleyebilirsiniz.
         title: Filtrelenmiş gönderiler
-  footer:
-    trending_now: Şu an gündemde
   generic:
     all: Tümü
     all_items_on_page_selected_html:
@@ -1182,8 +1224,6 @@ tr:
     validation_errors:
       one: Bir şeyler ters gitti! Lütfen aşağıdaki hatayı gözden geçiriniz
       other: Bir şeyler ters gitti! Lütfen aşağıdaki %{count} hatayı gözden geçiriniz
-  html_validator:
-    invalid_markup: 'geçersiz HTML markup içermektedir: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Geçersiz CSV dosyası. Hata: %{error}'
@@ -1226,7 +1266,7 @@ tr:
     title: İnsanları davet et
   lists:
     errors:
-      limit: En yüksek liste sayısına ulaştınız
+      limit: Azami liste sayısına ulaştınız
   login_activities:
     authentication_methods:
       otp: iki aşamalı kimlik doğrulama uygulaması
@@ -1367,7 +1407,11 @@ tr:
       unrecognized_emoji: tanınan bir emoji değil
   relationships:
     activity: Hesap etkinliği
+    confirm_follow_selected_followers: Seçili takipçileri takip etmek istediğinizden emin misiniz?
+    confirm_remove_selected_followers: Seçili takipçileri kaldırmak istediğinizden emin misiniz?
+    confirm_remove_selected_follows: Seçili takipleri kaldırmak istediğinizden emin misiniz?
     dormant: Uykuda
+    follow_failure: Seçilen hesaplardan bazıları takip edilemedi.
     follow_selected_followers: Seçili takipçileri takip et
     followers: Takipçiler
     following: Takip edilenler
@@ -1407,6 +1451,7 @@ tr:
       electron: Electron
       firefox: Firefox
       generic: Bilinmeyen tarayıcı
+      huawei_browser: Huawei Tarayıcı
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1416,6 +1461,7 @@ tr:
       qq: QQ Browser
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Bilinmeyen Tarayıcı
       weibo: Weibo
     current_session: Geçerli oturum
     description: "%{platform} - %{browser}"
@@ -1428,9 +1474,10 @@ tr:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: bilinmeyen platform
+      unknown_platform: Bilinmeyen Platform
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1543,7 +1590,7 @@ tr:
       '7889238': 3 ay
     min_age_label: Zaman eşiği
     min_favs: Şundan daha fazla beğenilen gönderileri sakla
-    min_favs_hint: Bu belirtilenden daha fazla beğeni alan gönderilerinizin herhangi birini silmez. Beğeni sayısından bağımsız olarak gönderilerin silinmesi için burayı boş bırakın
+    min_favs_hint: En az bu belirtilen kadar beğeni alan gönderilerinizin herhangi birini silmez. Beğeni sayısından bağımsız olarak gönderilerin silinmesi için burayı boş bırakın
     min_reblogs: Şundan daha fazla teşvik edilen gönderileri sakla
     min_reblogs_hint: Bu belirtilenden daha fazla teşvik edilen gönderilerinizin herhangi birini silmez. Teşvik sayısından bağımsız olarak gönderilerin silinmesi için burayı boş bırakın
   stream_entries:
@@ -1643,12 +1690,13 @@ tr:
       title: Gemiye hoşgeldin, %{name}!
   users:
     follow_limit_reached: "%{limit} kişiden daha fazlasını takip edemezsiniz"
+    go_to_sso_account_settings: Kimlik sağlayıcı hesap ayarlarına gidin
     invalid_otp_token: Geçersiz iki adımlı doğrulama kodu
     otp_lost_help_html: Her ikisine de erişiminizi kaybettiyseniz, %{email} ile irtibata geçebilirsiniz
     seamless_external_login: Harici bir servis aracılığıyla oturum açtınız, bu nedenle parola ve e-posta ayarları mevcut değildir.
     signed_in_as: 'Oturum açtı:'
   verification:
-    explanation_html: '<strong>Profil meta verisindeki bağlantıların sahibi olarak kendinizi doğrulayabilirsiniz</strong>. Bunun için, link verilen web sitesi Mastodon profilinize geri bir link içermelidir. Geri link bir <code>rel="me"</code> özelliğine sahip <strong>olmalıdır</strong>. Bağlantının metin içeriği önemli değildir. İşte bir örnek:'
+    explanation_html: '<strong>Profil meta verisindeki bağlantıların sahibi olarak kendinizi doğrulayabilirsiniz</strong>. Bunun için, bağlantısı verilen web sitesi Mastodon profilinize bir bağlantı içermelidir. Doğrulamanın gerçekleşmesi için bağlantıyı ekledikten sonra buraya gelip profilinizi tekrar kaydetmelisiniz. Geri bağlantı bir <code>rel="me"</code> özelliğine sahip <strong>olmalıdır</strong>. Bağlantının metin içeriği önemli değildir. İşte bir örnek:'
     verification: Doğrulama
   webauthn_credentials:
     add: Yeni güvenlik anahtarı ekle
diff --git a/config/locales/tt.yml b/config/locales/tt.yml
index b2986602d..d260e8be2 100644
--- a/config/locales/tt.yml
+++ b/config/locales/tt.yml
@@ -2,16 +2,20 @@
 tt:
   about:
     contact_unavailable: Юк
+    title: Проект турында
   accounts:
     follow: Языл
     following: Язылгансыз
   admin:
     accounts:
+      approve: Хуплау
       avatar: Аватар
       by_domain: Домен
       change_email:
         label: Emailны үзгәртү
         submit: Emailны үзгәртү
+      change_role:
+        no_role: Рольсез
       confirm: Раслау
       deleted: Бетерелде
       domain: Домен
@@ -19,6 +23,7 @@ tt:
       email: Эл. почта
       header: Башлам
       ip: ІР
+      joined: Кушылды
       location:
         all: Бөтенесе
         local: Җирле
@@ -28,8 +33,13 @@ tt:
         all: Бөтенесе
       perform_full_suspension: Искә алмау
       reset: Ташлату
+      role: Роль
       search: Эзләү
+      security: Иминлек
+      security_measures:
+        only_password: Серсүз генә
       sensitive: Sizmäle
+      title: Аккаунтлар
       username: Кулланучы исеме
       web: Веб
     action_logs:
@@ -40,10 +50,10 @@ tt:
       copy: Күчереп алу
       delete: Бетерү
       disable: Cүндерү
-      disabled: Cүндерелгән
-      enable: Кабызу
+      disabled: Cүнек
+      enable: Кушу
       list: Исемлек
-      upload: Йөкләү
+      upload: Йөкләтү
     domain_blocks:
       domain: Домен
       new:
@@ -101,7 +111,7 @@ tt:
   application_mailer:
     salutation: "%{name},"
   auth:
-    change_password: Парол
+    change_password: Серсүз
     login: Керү
     providers:
       cas: САS
@@ -136,14 +146,12 @@ tt:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
   exports:
     archive_takeout:
       date: Көне
-      size: Olılıq
+      size: Зурлык
     bookmarks: Кыстыргычлар
     csv: СSV
   filters:
@@ -160,7 +168,7 @@ tt:
   imports:
     types:
       bookmarks: Кыстыргычлар
-    upload: Йөкләү
+    upload: Йөкләтү
   invites:
     expired: Гамәлдән чыкты
     expires_in:
@@ -174,17 +182,15 @@ tt:
   number:
     human:
       decimal_units:
-        format: "%n%u"
+        format: "%n %u"
   otp_authentication:
-    enable: Кабызу
+    enable: Кушу
   pagination:
     next: Киләсе
     prev: Алдыгы
     truncate: "&hellip;"
-  preferences:
-    other: Башка
   relationships:
-    following: Язылгансыз
+    following: Язылулар
   sessions:
     browser: Браузер
     browsers:
@@ -202,7 +208,7 @@ tt:
       qq: QQ Brоwser
       safari: Safаri
       weibo: Weibо
-    description: "%{browser} - %{platform}"
+    description: "%{platform} платформасында %{browser}"
     ip: ІР
     platforms:
       adobe_air: Adobе Air
@@ -215,10 +221,10 @@ tt:
       windows_mobile: Windows Mоbile
       windows_phone: Windоws Phone
   settings:
-    account: Хисап язмасы
-    appearance: Küreneş
-    development: Эшләнмә
-    edit_profile: Профильны үзгәртү
+    account: Аккаунт
+    appearance: Тышкы кыяфәт
+    development: Ясаучылар өчен
+    edit_profile: Профильне үзгәртү
     import: Импортлау
     preferences: Caylaw
     profile: Профиль
@@ -248,7 +254,7 @@ tt:
     edit: Үзгәртү
   user_mailer:
     warning:
-      reason: 'Сәбаб:'
+      reason: 'Сәбәп:'
       title:
         none: Игътибар
   webauthn_credentials:
diff --git a/config/locales/ug.yml b/config/locales/ug.yml
index 779d4d226..ea5bdb5c8 100644
--- a/config/locales/ug.yml
+++ b/config/locales/ug.yml
@@ -6,7 +6,5 @@ ug:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/uk.yml b/config/locales/uk.yml
index 69ac1d68a..62736d23d 100644
--- a/config/locales/uk.yml
+++ b/config/locales/uk.yml
@@ -95,6 +95,7 @@ uk:
       moderation:
         active: Активний
         all: Усі
+        disabled: Вимкнено
         pending: Очікують
         silenced: Обмежені
         suspended: Призупинені
@@ -139,6 +140,7 @@ uk:
       search: Пошук
       search_same_email_domain: Інші користувачі з тим самим поштовим доменом
       search_same_ip: Інші користувачі з тим самим IP
+      security: Безпека
       security_measures:
         only_password: Лише пароль
         password_and_2fa: Пароль та 2FA
@@ -443,6 +445,7 @@ uk:
         resolve: Розв'язати домен
         title: Нове блокування поштового домену
       no_email_domain_block_selected: Жодні налаштування блокування доменів електронної пошти не було змінено, оскільки жоден з них не було обрано
+      not_permitted: Не дозволено
       resolved_dns_records_hint_html: Ім'я домену резолвиться в наступні домени MX, які в кінцевому рахунку відповідають за прийняття електронної пошти. Блокування домену MX заблокує реєстрацію з будь-якої e-mail адреси, яка використовує однаковий домен MX, навіть якщо доменне ім'я буде інакше. <strong>Будьте обережні, щоб не блокувати великих поштових провайдерів.</strong>
       resolved_through_html: Розв'язано через %{domain}
       title: Чорний список поштових доменів
@@ -457,6 +460,7 @@ uk:
         private_comment_description_html: 'Щоб допомогти вам відстежувати, звідки беруться імпортовані блокування, вони створюються за допомогою наступного приватного коментаря: <q>%{comment}</q>'
         private_comment_template: Імпортовано з %{source} %{date}
         title: Імпорт блокувань домену
+      invalid_domain_block: 'Один або кілька доменних блоків пропущено через такі помилки: %{error}'
       new:
         title: Імпорт блокувань домену
       no_file: Файл не вибрано
@@ -492,6 +496,7 @@ uk:
       content_policies:
         comment: Внутрішня примітка
         description_html: Ви можете визначити правила вмісту що буде застосовано до всіх облікових записів із цього домену та будь-якого з його піддоменів.
+        limited_federation_mode_description_html: Ви можете вибрати, чи дозволити федерацію з цим доменом.
         policies:
           reject_media: Відхилити медіа
           reject_reports: Відхилити скарги
@@ -599,19 +604,23 @@ uk:
         mark_as_sensitive_description_html: Медіа у дописах, на які скаржилися будуть позначені делікатними, а попередження буде записано, щоб допомогти вам підвищити рівень майбутніх порушень того самого облікового запису.
         other_description_html: Більше опцій керування поведінкою облікового запису і налаштування комунікації з обліковим записом, на який поскаржилися.
         resolve_description_html: Не буде застосовано жодних дій проти облікового запису, на який скаржилися, не буде записано попередження, а скаргу буде закрито.
-        silence_description_html: Профіль буде видимий лише тим, хто вже стежить за ним або знайде його самостійно, сильно обмежуючи його знаходження. Можна потім скасувати.
-        suspend_description_html: Профіль і весь його вміст буде недоступним, поки його не буде видалено. Взаємодія з обліковим записом буде неможливою.
+        silence_description_html: Обліковий запис буде видно лише тим, хто вже за ним слідкує, або знайде його вручну, що значно обмежує його охоплення. Може завжди бути повернуто. Закриває всі скарги на цей обліковий запис.
+        suspend_description_html: Обліковий запис і весь його вміст будуть недоступними й врешті-решт видалені, і взаємодіяти з ним буде неможливо. Відновлення можливе протягом 30 днів. Закриває всі скарги на цей обліковий запис.
       actions_description_html: Визначте, які дії слід вжити для розв'язання цієї скарги. Якщо ви оберете каральні дії проти зареєстрованого облікового запису, про них буде надіслано сповіщення електронним листом, крім випадків, коли вибрано категорію <strong>Спам</strong>.
+      actions_description_remote_html: Визначте, які дії слід вжити для розв'язання цього звіту. Це вплине тільки на те, як <strong>ваш</strong> сервер з'єднується з цим віддаленим обліковим записом і обробляє його вміст.
       add_to_report: Додати ще подробиць до скарги
       are_you_sure: Ви впевнені?
       assign_to_self: Призначити мені
       assigned: Призначений модератор
       by_target_domain: Домен облікового запису, на який скаржаться
+      cancel: Скасувати
       category: Категорія
       category_description_html: Причина скарги на цей обліковий запис та/або вміст, яку буде вказано у звіті
       comment:
         none: Немає
       comment_description_html: 'Щоб надати більше відомостей, %{name} пише:'
+      confirm: Підтвердити
+      confirm_action: Підтвердьте модераційну дію щодо @%{acct}
       created_at: Створено
       delete_and_resolve: Видалити дописи
       forwarded: Переслано
@@ -628,6 +637,7 @@ uk:
         placeholder: Опишіть, які дії були виконані, або інші зміни, що стосуються справи...
         title: Примітки
       notes_description_html: Переглядайте та залишайте примітки для інших модераторів та для себе на майбутнє
+      processed_msg: 'Звіт #%{id} успішно оброблено'
       quick_actions_description_html: 'Виберіть швидку дію або гортайте вниз, щоб побачити матеріал, на який надійшла скарга:'
       remote_user_placeholder: віддалений користувач із %{instance}
       reopen: Перевідкрити скаргу
@@ -640,9 +650,28 @@ uk:
       status: Стан
       statuses: Вміст, на який поскаржилися
       statuses_description_html: Замінений вміст буде цитований у спілкуванні з обліковим записом, на який поскаржилися
+      summary:
+        action_preambles:
+          delete_html: 'Ви збираєтеся <strong>вилучити</strong> деякі з дописів <strong>@%{acct}</strong>. Це буде:'
+          mark_as_sensitive_html: 'Ви збираєтеся <strong>позначити</strong> деякі з дописів <strong>@%{acct}</strong> <strong>делікатними</strong>. Це буде:'
+          silence_html: 'Ви збираєтеся <strong>обмежити</strong> обліковий запис <strong>@%{acct}</strong>. Це буде:'
+          suspend_html: 'Ви збираєтесь <strong>призупинити</strong> обліковий запис <strong>@%%{acct}</strong>. Це буде:'
+        actions:
+          delete_html: Вилучити образливі дописи
+          mark_as_sensitive_html: Позначити медіа образливих дописів делікатними
+          silence_html: Досягнуто жорсткого обмеження <strong>@%{acct}</strong>, зробивши їхній профіль і вміст видимим тільки людям, які вже слідують за ними або вручну шукають його профіль
+          suspend_html: Призупинити <strong>@%{acct}</strong>, зробити їх профіль і вміст недоступним та унеможливити взаємодію
+        close_report: 'Позначити звіт #%{id} розв''язаним'
+        close_reports_html: Позначити <strong>всі</strong> звіти проти <strong>@%{acct}</strong> розв'язаними
+        delete_data_html: Видаліть профіль <strong>@%{acct}</strong> і вміст за останні 30 днів, якщо вони не дія не скасується тим часом
+        preview_preamble_html: "<strong>@%{acct}</strong> отримає попередження з таким вмістом:"
+        record_strike_html: Запис попередження проти <strong>@%{acct}</strong>, що допоможе вам посилити ваші майбутні санкції проти цього облікового запису
+        send_email_html: Надіслати <strong>@%{acct}</strong> попереджувального електронного листа
+        warning_placeholder: Додаткові причини дії модерації.
       target_origin: Походження облікового запису, на який скаржаться
       title: Скарги
       unassign: Зняти призначення
+      unknown_action_msg: 'Невідома дія: %{action}'
       unresolved: Невирішені
       updated_at: Оновлені
       view_profile: Переглянути профіль
@@ -741,6 +770,8 @@ uk:
         preamble: Показ цікавих матеріалів відіграє важливу роль у залученні нових користувачів, які, можливо, не знають нікого з Mastodon. Контролюйте роботу різних функцій виявлення на вашому сервері.
         profile_directory: Каталог профілів
         public_timelines: Публічна стрічка
+        publish_discovered_servers: Опублікувати знайдені сервери
+        publish_statistics: Публікувати статистику
         title: Виявлення
         trends: Популярні
       domain_blocks:
@@ -795,6 +826,7 @@ uk:
         suspend: "%{name} заморожує обліковий запис %{target}"
       appeal_approved: Оскаржено
       appeal_pending: Оскарження в очікуванні
+      appeal_rejected: Апеляцію відхилено
     system_checks:
       database_schema_check:
         message_html: Існують відкладені перенесення бази даних. Запустіть їх, щоб забезпечити очікувану роботу програми
@@ -808,6 +840,12 @@ uk:
         message_html: Ви не визначили будь-які правила сервера.
       sidekiq_process_check:
         message_html: Не працює процес Sidekiq для %{value} черги. Перегляньте конфігурації вашого Sidekiq
+      upload_check_privacy_error:
+        action: Перегляньте подробиці тут
+        message_html: "<strong>Ваш вебсервер неправильно налаштований. Приватність ваших користувачів піддається ризику</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Перегляньте подробиці тут
+        message_html: "<strong>Ваше сховище об'єктів неправильно налаштоване. Приватність ваших користувачів піддається ризику</strong>"
     tags:
       review: Переглянути допис
       updated_msg: Параметри хештеґів успішно оновлені
@@ -832,6 +870,7 @@ uk:
           other: Поширили %{count} людей за останній тиждень
         title: Популярні посилання
         usage_comparison: Сьогодні поширено %{today} разів, у порівнянні з %{yesterday} вчора
+      not_allowed_to_trend: Не допускається тенденція
       only_allowed: Тільки дозволене
       pending_review: Очікує перевірки
       preview_card_providers:
@@ -969,6 +1008,7 @@ uk:
   applications:
     created: Застосунок успішно створений
     destroyed: Застосунок успішно видалений
+    logout: Вийти
     regenerate_token: Перегенерувати токен доступу
     token_regenerated: Токен доступу успішне перегенеровано
     warning: Будьте дуже обережні з цими даними. Ніколи не діліться ними ні з ким!
@@ -976,6 +1016,8 @@ uk:
   auth:
     apply_for_account: Запит облікового запису
     change_password: Пароль
+    confirmations:
+      wrong_email_hint: Якщо ця адреса електронної пошти неправильна, можна змінити її в налаштуваннях облікового запису.
     delete_account: Видалити обліковий запис
     delete_account_html: Якщо ви хочете видалити свій обліковий запис, ви можете <a href="%{path}">перейти сюди</a>. Вас попросять підтвердити дію.
     description:
@@ -1003,6 +1045,8 @@ uk:
     resend_confirmation: Повторно відправити інструкції з підтвердження
     reset_password: Скинути пароль
     rules:
+      accept: Прийняти
+      back: Назад
       preamble: Вони налаштовані та закріплені модераторами %{domain}.
       title: Деякі основні правила.
     security: Зміна паролю
@@ -1200,8 +1244,6 @@ uk:
       index:
         hint: Цей фільтр застосовується для вибору окремих дописів, незалежно від інших критеріїв. Ви можете додавати більше дописів до цього фільтра з вебінтерфейсу.
         title: Відфільтровані дописи
-  footer:
-    trending_now: Актуальні
   generic:
     all: Усі
     all_items_on_page_selected_html:
@@ -1232,8 +1274,6 @@ uk:
       many: Щось досі не гаразд! Перегляньте %{count} повідомлень про помилки
       one: Щось досі не гаразд! Перегляньте повідомлення про помилку
       other: Щось досі не гаразд! Перегляньте %{count} повідомлень про помилки
-  html_validator:
-    invalid_markup: 'містить неприпустиму HTML розмітку: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Хибний файл CSV. Помилка: %{error}'
@@ -1419,7 +1459,11 @@ uk:
       unrecognized_emoji: не є розпізнаним емоджі
   relationships:
     activity: Діяльність облікового запису
+    confirm_follow_selected_followers: Ви справді бажаєте підписатися на обраних підписників?
+    confirm_remove_selected_followers: Ви дійсно бажаєте видалити вибраних підписників?
+    confirm_remove_selected_follows: Ви дійсно хочете вилучити вибрані підписки?
     dormant: Неактивні
+    follow_failure: Не вдалося підписатися на деякі вибрані облікові записи.
     follow_selected_followers: Стежити за вибраними підписниками
     followers: Підписники
     following: Підписник
@@ -1459,6 +1503,7 @@ uk:
       electron: Electron
       firefox: Firefox
       generic: Невідомий браузер
+      huawei_browser: Huawei Браузер
       ie: Internet Explorer
       micro_messenger: MicroMessenger
       nokia: Nokia S40 Ovi Browser
@@ -1468,6 +1513,7 @@ uk:
       qq: QQ Browser
       safari: Сафарі
       uc_browser: UC Browser
+      unknown_browser: Невідомий браузер
       weibo: Weibo
     current_session: Поточний сеанс
     description: "%{browser} на %{platform}"
@@ -1480,9 +1526,10 @@ uk:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: macOS
-      other: невідома платформа
+      unknown_platform: Невідома Платформа
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1607,7 +1654,7 @@ uk:
       '7889238': 3 місяці
     min_age_label: Поріг давності
     min_favs: Залишати дописи в обраному більше ніж
-    min_favs_hint: Не видаляти ваших дописів, що отримали більше вказаної кількості додавання до закладок. Залиште порожнім, щоб видаляти дописи незалежно від кількості їхнього додавання до обраних
+    min_favs_hint: Не видаляти ваших дописів, що були поширені більш ніж вказану кількість разів. Залиште порожнім, щоб видаляти дописи, попри кількість їхніх поширень
     min_reblogs: Залишати дописи, поширені більше ніж
     min_reblogs_hint: Не видаляти ваших дописів, що були поширені більш ніж вказану кількість разів. Залиште порожнім, щоб видаляти дописи, попри кількість їхніх поширень
   stream_entries:
@@ -1707,12 +1754,13 @@ uk:
       title: Ласкаво просимо, %{name}!
   users:
     follow_limit_reached: Не можна слідкувати більш ніж за %{limit} людей
+    go_to_sso_account_settings: Перейдіть до налаштувань облікового запису постачальника ідентифікації
     invalid_otp_token: Введено неправильний код
     otp_lost_help_html: Якщо ви втратили доступ до обох, ви можете отримати доступ з %{email}
     seamless_external_login: Ви увійшли за допомогою зовнішнього сервісу, тому налаштування паролю та електронної пошти недоступні.
     signed_in_as: 'Ви увійшли як:'
   verification:
-    explanation_html: 'Ви можете <strong>підтвердити володіння посиланнями в метаданих вашого профілю</strong>. Для цього на зазначеному сайті повинне міститися посилання на ваш профіль Mastodon. Посилання <strong>повинне</strong> мати атрибут <code>rel="me"</code>. Текстовий вміст посилання не має значення. Ось приклад:'
+    explanation_html: 'Ви можете <strong>підтвердити володіння посиланнями в метаданих вашого профілю</strong>. Для цього на зазначеному сайті повинен міститися посилання на ваш профіль Mastodon. Посилання <strong>повинне</strong> мати атрибут <code>rel="me"</code>. Текстовий вміст посилання не має значення. Ось приклад:'
     verification: Підтвердження
   webauthn_credentials:
     add: Додати новий ключ безпеки
diff --git a/config/locales/ur.yml b/config/locales/ur.yml
index 31c2292bb..a5ae83fd4 100644
--- a/config/locales/ur.yml
+++ b/config/locales/ur.yml
@@ -6,7 +6,5 @@ ur:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/uz.yml b/config/locales/uz.yml
new file mode 100644
index 000000000..caed654ec
--- /dev/null
+++ b/config/locales/uz.yml
@@ -0,0 +1,49 @@
+---
+uz:
+  about:
+    title: Haqida
+  accounts:
+    follow: Obuna bo‘lish
+  admin:
+    accounts:
+      display_name: Ko'rsatiladigan nom
+      domain: Domen
+      email: Email
+      email_status: Email holati
+      enable: Muzlatishtan olish
+      enabled: Yoqilgan
+      followers: Obunachilar
+      follows: Obuna
+      invited_by: Tomonidan taklif qilingan
+      ip: IP
+      joined: Qo'shilgan
+      location:
+        all: Barchasi
+        local: Mahalliy
+        remote: Masofadagi
+        title: Qayerda
+      login_status: Login holati
+      media_attachments: Media qo'shimchalari
+      memorialize: Xotiraga aylantiring
+      memorialized: Yodga olingan
+      memorialized_msg: "%{username} esdalik hisobiga muvaffaqiyatli aylantirildi"
+      moderation:
+        active: Aktiv
+        all: Barchasi
+        pending: Navbatdagi
+        silenced: Cheklangan
+        suspended: To'xtatilgan
+        title: Moderatsiya
+      moderation_notes: Moderatsiya eslatmalari
+      most_recent_activity: Eng oxirgi faoliyat
+      most_recent_ip: Eng oxirgi IP
+      perform_full_suspension: To'xtatilgan
+      reject: Rad etish
+  errors:
+    '400': The request you submitted was invalid or malformed.
+    '403': You don't have permission to view this page.
+    '404': The page you are looking for isn't here.
+    '406': This page is not available in the requested format.
+    '410': The page you were looking for doesn't exist here anymore.
+    '429': Too many requests
+    '503': The page could not be served due to a temporary server failure.
diff --git a/config/locales/vi.yml b/config/locales/vi.yml
index 62d1a64c4..a1a225701 100644
--- a/config/locales/vi.yml
+++ b/config/locales/vi.yml
@@ -89,6 +89,7 @@ vi:
       moderation:
         active: Hoạt động
         all: Tất cả
+        disabled: Đã tắt
         pending: Chờ
         silenced: Hạn chế
         suspended: Vô hiệu hóa
@@ -130,6 +131,7 @@ vi:
       search: Tìm kiếm
       search_same_email_domain: Tra cứu email
       search_same_ip: Tra cứu IP
+      security: Bảo mật
       security_measures:
         only_password: Chỉ mật khẩu
         password_and_2fa: Mật khẩu và 2FA
@@ -419,6 +421,7 @@ vi:
         resolve: Xử lý tên miền
         title: Chặn tên miền email mới
       no_email_domain_block_selected: Không có chặn e-mail nào thay đổi vì không có mục nào được chọn
+      not_permitted: Không cho phép
       resolved_dns_records_hint_html: Tên miền phân giải thành các tên miền MX sau, các tên miền này chịu trách nhiệm cuối cùng trong việc chấp nhận email. Chặn tên miền MX sẽ chặn đăng ký từ bất kỳ địa chỉ email nào sử dụng cùng một tên miền MX, ngay cả khi tên miền hiển thị là khác. <strong>Cẩn thận đừng chặn những dịch vụ email lớn.</strong>
       resolved_through_html: Đã xử lý thông qua %{domain}
       title: Tên miền email đã chặn
@@ -433,6 +436,7 @@ vi:
         private_comment_description_html: 'Để giúp bạn theo dõi nguồn gốc của các chặn tên miền đã nhập, các tên miền chặn đã nhập sẽ được tạo bằng nhận xét riêng tư sau: <q>%{comment}</q>'
         private_comment_template: Nhập từ %{source} vào %{date}
         title: Nhập máy chủ chặn
+      invalid_domain_block: 'Một hoặc nhiều tên miền đã bị bỏ qua do (các) lỗi sau: %{error}'
       new:
         title: Nhập máy chủ chặn
       no_file: Không có tập tin nào được chọn
@@ -462,6 +466,7 @@ vi:
       content_policies:
         comment: Lưu ý nội bộ
         description_html: Bạn có thể xác định các chính sách nội dung sẽ được áp dụng cho tất cả các tài khoản trên máy chủ này và bất kỳ tên miền phụ nào của nó.
+        limited_federation_mode_description_html: Bạn có thể chọn nếu muốn cho phép liên hợp với máy chủ này.
         policies:
           reject_media: Từ chối media
           reject_reports: Từ chối báo cáo
@@ -564,18 +569,22 @@ vi:
         other_description_html: Tùy chọn cách kiểm soát và giao tiếp với tài khoản bị báo cáo.
         resolve_description_html: Không áp dụng trừng phạt nào và đóng báo cáo.
         silence_description_html: Chỉ hiển thị trang với những người đã theo dõi hoặc tìm kiếm thủ công.
-        suspend_description_html: Đóng băng hồ sơ và chặn truy cập tất cả các nội dung cho đến khi chúng bị xóa hoàn toàn. Đảo ngược trong vòng 30 ngày.
+        suspend_description_html: Tài khoản và tất cả nội dung của nó sẽ không thể truy cập được và cuối cùng sẽ bị xóa, đồng thời không thể tương tác với tài khoản đó. Có thể đảo ngược trong vòng 30 ngày. Đóng tất cả các báo cáo đối với tài khoản này.
       actions_description_html: Nếu áp đặt trừng phạt, một email thông báo sẽ được gửi cho người này, ngoại trừ <strong>Spam</strong>.
+      actions_description_remote_html: Chọn hành động cần thực hiện để xử lý báo cáo này. Điều này sẽ chỉ ảnh hưởng đến cách máy chủ <strong>của bạn</strong> giao tiếp với tài khoản này và xử lý nội dung của nó.
       add_to_report: Bổ sung báo cáo
       are_you_sure: Bạn có chắc không?
       assign_to_self: Giao cho tôi
       assigned: Người xử lý
       by_target_domain: Tên tài khoản bị báo cáo
+      cancel: Hủy bỏ
       category: Phân loại
       category_description_html: Lý do tài khoản hoặc nội dung này bị báo cáo sẽ được trích dẫn khi giao tiếp với họ
       comment:
         none: Không có mô tả
       comment_description_html: "%{name} cho biết thêm:"
+      confirm: Xác nhận
+      confirm_action: Xác nhận kiểm duyệt với %{acct}
       created_at: Báo cáo lúc
       delete_and_resolve: Xóa tút
       forwarded: Chuyển tiếp
@@ -592,6 +601,7 @@ vi:
         placeholder: Mô tả vi phạm của người này, hướng xử lý và những cập nhật liên quan khác...
         title: Lưu ý
       notes_description_html: Xem và để lại lưu ý cho các kiểm duyệt viên khác
+      processed_msg: 'Báo cáo #%{id} đã được xử lý thành công'
       quick_actions_description_html: 'Kiểm duyệt nhanh hoặc kéo xuống để xem nội dung bị báo cáo:'
       remote_user_placeholder: người ở %{instance}
       reopen: Mở lại báo cáo
@@ -604,9 +614,28 @@ vi:
       status: Trạng thái
       statuses: Nội dung bị báo cáo
       statuses_description_html: Lý do tài khoản hoặc nội dung này bị báo cáo sẽ được trích dẫn khi giao tiếp với họ
+      summary:
+        action_preambles:
+          delete_html: 'Bạn sắp <strong>xóa</strong> vài tút của <strong>@%{acct}</strong>. Việc này sẽ:'
+          mark_as_sensitive_html: 'Bạn sắp <strong>đánh dấu</strong> vài tút của <strong>@%{acct}</strong> là <strong>nhạy cảm</strong>. Việc này sẽ:'
+          silence_html: 'Bạn sắp <strong>hạn chế</strong> <strong>@%{acct}</strong>. Việc này sẽ:'
+          suspend_html: 'Bạn sắp <strong>vô hiệu hóa</strong> <strong>@%{acct}</strong>. Việc này sẽ:'
+        actions:
+          delete_html: Xóa các tút vi phạm
+          mark_as_sensitive_html: Đánh dấu media trong tút vi phạm là nhạy cảm
+          silence_html: Hạn chế mức ảnh hưởng của <strong>@%{acct}</strong> bằng cách làm cho trang và nội dung của họ chỉ hiển thị với những người đã theo dõi họ hoặc tìm kiếm theo cách thủ công
+          suspend_html: Vô hiệu hóa <strong>@%{acct}</strong>, làm cho trang và nội dung của họ không thể truy cập và không thể tương tác
+        close_report: 'Đánh dấu báo cáo #%{id} đã xử lý xong'
+        close_reports_html: Đánh dấu <strong>tất cả</strong> báo cáo chống lại <strong>@%{acct}</strong> đã xử lý xong
+        delete_data_html: Xóa trang <strong>@%{acct}</strong> và nội dung 30 ngày kể từ bây giờ trừ khi bỏ vô hiệu hóa
+        preview_preamble_html: "<strong>@%{acct}</strong> sẽ nhận được cảnh báo với nội dung như sau:"
+        record_strike_html: Lưu lại cảnh cáo <strong>@%{acct}</strong> để giúp bạn đánh giá các vi phạm trong tương lai từ tài khoản này
+        send_email_html: Gửi <strong>@%{acct}</strong> một email cảnh báo
+        warning_placeholder: Lý do bổ sung cho hành động kiểm duyệt.
       target_origin: Nguồn báo cáo
       title: Báo cáo
       unassign: Bỏ qua
+      unknown_action_msg: 'Hành động chưa biết: %{action}'
       unresolved: Chờ xử lý
       updated_at: Cập nhật lúc
       view_profile: Xem trang
@@ -699,6 +728,8 @@ vi:
         preamble: Hiển thị nội dung thú vị là công cụ để thu hút người dùng mới, những người có thể không quen bất kỳ ai trong Mastodon. Kiểm soát cách các tính năng khám phá hoạt động trên máy chủ của bạn.
         profile_directory: Cộng đồng
         public_timelines: Bảng tin
+        publish_discovered_servers: Công khai các máy chủ được phát hiện
+        publish_statistics: Công khai số liệu thống kê
         title: Khám phá
         trends: Thịnh hành
       domain_blocks:
@@ -753,6 +784,7 @@ vi:
         suspend: "%{name} đã vô hiệu hóa %{target}"
       appeal_approved: Đã khiếu nại
       appeal_pending: Đang kháng cáo
+      appeal_rejected: Khiếu nại bị từ chối
     system_checks:
       database_schema_check:
         message_html: Có cơ sở dữ liệu đang chờ xử lý. Xin khởi động nó để ứng dụng có thể hoạt động một cách ổn định nhất
@@ -766,6 +798,12 @@ vi:
         message_html: Bạn chưa cập nhật nội quy máy chủ.
       sidekiq_process_check:
         message_html: Sidekiq không hoạt động khi truy vấn %{value}. Hãy kiểm tra lại cấu hình Sidekiq
+      upload_check_privacy_error:
+        action: Nhấn vào đây để biết thêm thông tin
+        message_html: "<strong>Máy chủ web của bạn bị cấu hình sai. Có nguy cơ ảnh hưởng bảo mật của người dùng.</strong>"
+      upload_check_privacy_error_object_storage:
+        action: Nhấn vào đây để biết thêm thông tin
+        message_html: "<strong>Lưu trữ đối tượng của bạn bị cấu hình sai. Có nguy cơ ảnh hưởng bảo mật của người dùng.</strong>"
     tags:
       review: Phê duyệt
       updated_msg: Hashtag đã được cập nhật thành công
@@ -787,6 +825,7 @@ vi:
           other: "%{count} người chia sẻ tuần rồi"
         title: Liên kết nổi bật
         usage_comparison: Chia sẻ %{today} lần hôm nay, so với %{yesterday} lần hôm qua
+      not_allowed_to_trend: Không được phép thành xu hướng
       only_allowed: Chỉ cho phép
       pending_review: Đang chờ
       preview_card_providers:
@@ -915,6 +954,7 @@ vi:
   applications:
     created: Đơn đăng ký được tạo thành công
     destroyed: Đã xóa đơn đăng ký
+    logout: Đăng xuất
     regenerate_token: Tạo lại mã truy cập
     token_regenerated: Mã truy cập được tạo lại thành công
     warning: Hãy rất cẩn thận với dữ liệu này. Không bao giờ chia sẻ nó với bất cứ ai!
@@ -922,6 +962,8 @@ vi:
   auth:
     apply_for_account: Xin đăng ký
     change_password: Mật khẩu
+    confirmations:
+      wrong_email_hint: Nếu địa chỉ email đó không chính xác, bạn có thể thay đổi nó trong cài đặt tài khoản.
     delete_account: Xóa tài khoản
     delete_account_html: Nếu bạn muốn xóa tài khoản của mình, hãy <a href="%{path}">yêu cầu tại đây</a>. Bạn sẽ được yêu cầu xác nhận.
     description:
@@ -949,6 +991,8 @@ vi:
     resend_confirmation: Gửi lại email xác minh
     reset_password: Đặt lại mật khẩu
     rules:
+      accept: Chấp nhận
+      back: Quay lại
       preamble: Được ban hành và áp dụng bởi quản trị máy chủ %{domain}.
       title: Nội quy máy chủ.
     security: Bảo mật
@@ -1137,8 +1181,6 @@ vi:
       index:
         hint: Bộ lọc này áp dụng để chọn các tút riêng lẻ bất kể các tiêu chí khác. Bạn có thể thêm các tút khác vào bộ lọc này từ giao diện web.
         title: Những tút đã lọc
-  footer:
-    trending_now: Thịnh hành
   generic:
     all: Tất cả
     all_items_on_page_selected_html:
@@ -1157,8 +1199,6 @@ vi:
     today: hôm nay
     validation_errors:
       other: Đã có %{count} lỗi xảy ra! Xem chi tiết bên dưới
-  html_validator:
-    invalid_markup: 'chứa đánh dấu HTML không hợp lệ: %{error}'
   imports:
     errors:
       invalid_csv_file: 'Tập tin CSV không hợp lệ. Lỗi: %{error}'
@@ -1341,7 +1381,11 @@ vi:
       unrecognized_emoji: không phải là emoji
   relationships:
     activity: Tương tác
+    confirm_follow_selected_followers: Bạn có chắc muốn theo dõi những người đã chọn?
+    confirm_remove_selected_followers: Bạn có chắc muốn bỏ theo dõi những người đã chọn?
+    confirm_remove_selected_follows: Bạn có chắc muốn xoá những người theo dõi bạn đã chọn không?
     dormant: Chưa
+    follow_failure: Không thể theo dõi một số tài khoản đã chọn.
     follow_selected_followers: Theo dõi những người đã chọn
     followers: Người theo dõi
     following: Đang theo dõi
@@ -1381,6 +1425,7 @@ vi:
       electron: Electron
       firefox: Firefox
       generic: Trình duyệt khác
+      huawei_browser: Huawei Browser
       ie: Internet Explorer
       micro_messenger: MicroMes hành khách
       nokia: Trình duyệt Nokia S40 Ovi
@@ -1390,6 +1435,7 @@ vi:
       qq: QQ
       safari: Safari
       uc_browser: UC Browser
+      unknown_browser: Trình duyệt khác
       weibo: Weibo
     current_session: Phiên hiện tại
     description: "%{browser} trên %{platform}"
@@ -1402,9 +1448,10 @@ vi:
       chrome_os: ChromeOS
       firefox_os: Hệ điều hành Firefox
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: nền tảng khác
+      unknown_platform: Nền tảng khác
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Điện thoại Windows
@@ -1611,6 +1658,7 @@ vi:
       title: Xin chào %{name}!
   users:
     follow_limit_reached: Bạn chỉ có thể theo dõi tối đa %{limit} người
+    go_to_sso_account_settings: Thiết lập tài khoản nhà cung cấp danh tính
     invalid_otp_token: Mã xác minh 2 bước không hợp lệ
     otp_lost_help_html: Nếu bạn mất quyền truy cập vào cả hai, bạn có thể đăng nhập bằng %{email}
     seamless_external_login: Bạn đã đăng nhập thông qua một dịch vụ bên ngoài, vì vậy mật khẩu và email không khả dụng.
diff --git a/config/locales/zgh.yml b/config/locales/zgh.yml
index d29daf18c..4750a6cee 100644
--- a/config/locales/zgh.yml
+++ b/config/locales/zgh.yml
@@ -89,9 +89,7 @@ zgh:
     '404': The page you are looking for isn't here.
     '406': This page is not available in the requested format.
     '410': The page you were looking for doesn't exist here anymore.
-    '422': 
     '429': Too many requests
-    '500': 
     '503': The page could not be served due to a temporary server failure.
   exports:
     archive_takeout:
diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml
index bc1a7994a..f85c6c73a 100644
--- a/config/locales/zh-CN.yml
+++ b/config/locales/zh-CN.yml
@@ -14,7 +14,7 @@ zh-CN:
     instance_actor_flash: 该账号用来代表虚拟角色,并不代表个人用户,仅代表服务器本身。该账号用于达成互联之目的,不应该被停用。
     last_active: 最近活动
     link_verified_on: 此链接的所有权已在 %{date} 检查
-    nothing_here: 这里什么都没有!
+    nothing_here: 空空如也!
     pin_errors:
       following: 你必须关注你要推荐的人
     posts:
@@ -57,7 +57,7 @@ zh-CN:
       destroyed_msg: "%{username} 的数据已被安排至删除队列"
       disable: 冻结
       disable_sign_in_token_auth: 禁用电子邮件令牌认证
-      disable_two_factor_authentication: 停用两步认证
+      disable_two_factor_authentication: 停用双重认证
       disabled: 已冻结
       display_name: 昵称
       domain: 域名
@@ -89,6 +89,7 @@ zh-CN:
       moderation:
         active: 活跃
         all: 全部
+        disabled: 已禁用
         pending: 待审核
         silenced: 受限
         suspended: 已封禁
@@ -130,6 +131,7 @@ zh-CN:
       search: 搜索
       search_same_email_domain: 其他具有相同电子邮箱域名的用户
       search_same_ip: 具有相同IP的其他用户
+      security: 安全性
       security_measures:
         only_password: 仅密码
         password_and_2fa: 密码和双重认证
@@ -137,8 +139,8 @@ zh-CN:
       sensitized: 已标记为敏感内容
       shared_inbox_url: 公用收件箱(Shared Inbox)URL
       show:
-        created_reports: 这个帐户提交的举报
-        targeted_reports: 针对这个帐户的举报
+        created_reports: 这个账户提交的举报
+        targeted_reports: 针对这个账户的举报
       silence: 隐藏
       silenced: 已隐藏
       statuses: 嘟文
@@ -305,12 +307,12 @@ zh-CN:
       unpublished_msg: 公告已取消发布!
       updated_msg: 公告已成功更新!
     custom_emojis:
-      assign_category: 指定分类
+      assign_category: 指定类别
       by_domain: 域名
       copied_msg: 已成功将表情复制到本站
       copy: 复制
       copy_failed_msg: 无法将表情复制到本站
-      create_new_category: 新建分类
+      create_new_category: 新建类别
       created_msg: 表情添加成功!
       delete: 删除
       destroyed_msg: 表情删除成功!
@@ -419,6 +421,7 @@ zh-CN:
         resolve: 解析域名
         title: 添加电子邮件域名屏蔽
       no_email_domain_block_selected: 没有任何项目被选中,因此未能更改电子邮件域名屏蔽列表
+      not_permitted: 未允许
       resolved_dns_records_hint_html: 该域名解析的 MX 记录所指向的域名如下,这些域名被用于接收电子邮件。 即使电子邮件地址域名与 MX 域名不同,屏蔽一个 MX 域名意味着阻止任何使用相同 MX 域名的电子邮件地址注册本站账户。 <strong>请小心不要误屏蔽主要的电子邮件提供商。</strong>
       resolved_through_html: 通过 %{domain} 解析
       title: 电子邮件域名屏蔽
@@ -433,6 +436,7 @@ zh-CN:
         private_comment_description_html: 为了帮助您追踪域名列表来源,导入的域名列表将被添加如下的私人注释:<q>%{comment}</q>
         private_comment_template: 从 %{source} 导入 %{date}
         title: 导入域名列表
+      invalid_domain_block: 由于以下错误,一个或多个域名屏蔽被跳过: %{error}
       new:
         title: 导入域名列表
       no_file: 没有选择文件
@@ -462,6 +466,7 @@ zh-CN:
       content_policies:
         comment: 内部备注
         description_html: 你可以设置应用于此域名所有账号和其所有子域名的内容策略。
+        limited_federation_mode_description_html: 您可以选择是否允许与该联邦联合。
         policies:
           reject_media: 拒收媒体
           reject_reports: 拒收举报
@@ -563,19 +568,23 @@ zh-CN:
         mark_as_sensitive_description_html: 被举报的嘟文将被标记为敏感,同时该账号将被标记一次处罚,以供未来同一账号再次违规时参考。
         other_description_html: 查看更多控制该账号行为的选项,并自定义编写与被举报账号的通信。
         resolve_description_html: 不会对被举报账号采取任何动作,举报将被关闭,也不会留下处罚记录。
-        silence_description_html: 只有关注或手工搜索此账号才能查看其资料,将严重限制其触达范围。可随时撤销。
-        suspend_description_html: 此账号及其内容将无法访问,并最终被删除。也无法与该账号交互。可在30天内撤销。
+        silence_description_html: 只有关注或手工搜索此账号才能查看其资料,将严重限制其触达范围。可随时撤销。关闭针对此帐户的所有举报。
+        suspend_description_html: 该帐户及其所有内容将无法访问并最终被删除,且无法与该帐户进行互动。 在 30 天内可随时撤销。关闭针对此帐户的所有举报。
       actions_description_html: 决定采取何种措施处理此举报。如果对被举报账号采取惩罚性措施,将向其发送一封电子邮件通知。但若选中<strong>垃圾信息</strong>类别则不会发送通知。
+      actions_description_remote_html: 决定采取何种行动来解决此举报。 这只会影响<strong>您的</strong>服务器如何与该远程帐户的通信并处理其内容。
       add_to_report: 增加更多举报内容
       are_you_sure: 你确定吗?
       assign_to_self: 接管
       assigned: 已接管的监察员
       by_target_domain: 被举报账户的域名
+      cancel: 取消
       category: 类别
       category_description_html: 在与被举报账户的通信时,将引用该账号和/或内容被举报的原因
       comment:
         none: 没有
       comment_description_html: "%{name} 补充道:"
+      confirm: 确认
+      confirm_action: 确认对 @%{acct} 的管理操作
       created_at: 举报时间
       delete_and_resolve: 删除嘟文
       forwarded: 已转发
@@ -592,6 +601,7 @@ zh-CN:
         placeholder: 描述已经执行的操作,或其他任何相关的跟进情况…
         title: 备注
       notes_description_html: 查看备注或向其他监察员留言
+      processed_msg: '举报 #%{id} 处理成功'
       quick_actions_description_html: 快捷选择操作或向下滚动以查看举报内容:
       remote_user_placeholder: 来自 %{instance} 的远程实例用户
       reopen: 重开举报
@@ -604,9 +614,28 @@ zh-CN:
       status: 状态
       statuses: 被举报内容
       statuses_description_html: 在与该账号的通信中将引用违规内容
+      summary:
+        action_preambles:
+          delete_html: 您即将<strong>删除</strong> <strong>@%{acct}</strong> 的一些帖子。 这将:
+          mark_as_sensitive_html: 您即将 <strong>标记</strong> <strong>@%{acct}</strong> 的帖一些子为 <strong>敏感</strong>。这将:
+          silence_html: 您即将<strong>限制</strong> <strong>@%{acct}</strong> 的帐户。 这将:
+          suspend_html: 您即将<strong>暂停</strong> <strong>@%{acct}</strong> 的帐户。 这将:
+        actions:
+          delete_html: 删除违规帖子
+          mark_as_sensitive_html: 将违规帖子的媒体标记为敏感
+          silence_html: 严格限制 <strong>@%{acct}</strong> 的影响力,方法是让他们的个人资料和内容仅对已经关注他们的人可见,或手动查找其个人资料时
+          suspend_html: 暂停 <strong>@%{acct}</strong>,使他们的个人资料和内容无法访问,也无法与之互动
+        close_report: '将报告 #%{id} 标记为已解决'
+        close_reports_html: 将针对 <strong>@%{acct}</strong> 的<strong>所有</strong> 报告标记为已解决
+        delete_data_html: 从现在起 30 天后删除 <strong>@%{acct}</strong> 的个人资料和内容,除非他们同时解除暂停。
+        preview_preamble_html: "<strong>@%{acct}</strong> 将收到包含以下内容的警告:"
+        record_strike_html: 记录一次针对 <strong>@%{acct}</strong> 的警示,以帮助您在这个帐户上的未来违规事件中得到重视。
+        send_email_html: 向 <strong>@%{acct}</strong> 发送警告电子邮件
+        warning_placeholder: 可选的补充理由,以说明调整的情况。
       target_origin: 被举报账号的来源
       title: 举报
       unassign: 取消接管
+      unknown_action_msg: 未知操作:%{action}
       unresolved: 未处理
       updated_at: 更新时间
       view_profile: 查看资料
@@ -699,6 +728,8 @@ zh-CN:
         preamble: 露出有趣的内容有助于新加入 Mastodon 的用户融入。可在这里控制多种发现功能如何在你的服务器上工作。
         profile_directory: 个人资料目录
         public_timelines: 公共时间轴
+        publish_discovered_servers: 已公开实例的服务器
+        publish_statistics: 发布统计数据
         title: 发现
         trends: 流行趋势
       domain_blocks:
@@ -753,6 +784,7 @@ zh-CN:
         suspend: "%{name} 封禁了用户 %{target}"
       appeal_approved: 已申诉
       appeal_pending: 申诉待处理
+      appeal_rejected: 申诉已驳回
     system_checks:
       database_schema_check:
         message_html: 有待处理的数据库迁移。请运行它们以确保应用程序正常运行。
@@ -766,6 +798,12 @@ zh-CN:
         message_html: 你没有定义任何服务器规则。
       sidekiq_process_check:
         message_html: "%{value} 队列未运行任何 Sidekiq 进程。请检查你的 Sidekiq 配置"
+      upload_check_privacy_error:
+        action: 点击这里查看更多信息
+        message_html: "<strong>您的网站服务器配置错误,您用户的隐私处于危险中。</strong>"
+      upload_check_privacy_error_object_storage:
+        action: 点击这里查看更多信息
+        message_html: "<strong>您的对象存储空间配置错误,您用户的隐私处于危险中。</strong>"
     tags:
       review: 审核状态
       updated_msg: 话题标签设置更新成功
@@ -787,6 +825,7 @@ zh-CN:
           other: 过去一周内被 %{count} 个人分享过
         title: 热门链接
         usage_comparison: 今日被分享 %{today} 次,前一日为 %{yesterday} 次
+      not_allowed_to_trend: 不允许的趋势
       only_allowed: 仅显示已允许的内容
       pending_review: 待审核
       preview_card_providers:
@@ -869,7 +908,7 @@ zh-CN:
       next_steps: 你可以批准此申诉并撤销该审核结果,也可以忽略此申诉。
       subject: "%{username} 对 %{instance} 的审核结果提出了申诉"
     new_pending_account:
-      body: 新帐户的详细信息如下。你可以批准或拒绝此申请。
+      body: 新账户的详细信息如下。你可以批准或拒绝此申请。
       subject: 在 %{instance} 上有新账号 (%{username}) 需要审核
     new_report:
       body: "%{reporter} 举报了用户 %{target}"
@@ -915,6 +954,7 @@ zh-CN:
   applications:
     created: 应用创建成功
     destroyed: 应用删除成功
+    logout: 退出登录
     regenerate_token: 重置访问令牌
     token_regenerated: 访问令牌重置成功
     warning: 一定小心,千万不要把它分享给任何人!
@@ -922,6 +962,8 @@ zh-CN:
   auth:
     apply_for_account: 申请账号
     change_password: 密码
+    confirmations:
+      wrong_email_hint: 如果该电子邮件地址不正确,您可以在帐户设置中进行更改。
     delete_account: 删除帐户
     delete_account_html: 如果你想删除你的帐户,请<a href="%{path}">点击这里继续</a>。你需要确认你的操作。
     description:
@@ -936,7 +978,7 @@ zh-CN:
     link_to_webauth: 使用你的安全密钥设备
     log_in_with: 通过外部服务登录
     login: 登录
-    logout: 登出
+    logout: 退出登录
     migrate_account: 迁移到另一个账户
     migrate_account_html: 如果你希望引导他人关注另一个账号,请<a href="%{path}">点击这里进行设置</a>。
     or_log_in_with: 或通过外部服务登录
@@ -949,6 +991,8 @@ zh-CN:
     resend_confirmation: 重新发送确认邮件
     reset_password: 重置密码
     rules:
+      accept: 接受
+      back: 返回
       preamble: 这些由 %{domain} 监察员设置和执行。
       title: 一些基本规则。
     security: 账户安全
@@ -961,14 +1005,14 @@ zh-CN:
       preamble_html: 使用您在 <strong>%{domain}</strong> 的账户和密码登录。如果您的账户托管在其他的服务器上,您将无法在此登录。
       title: 登录到 %{domain}
     sign_up:
-      preamble: 使用此 Mastodon 服务器上的帐号,您将能够关注网络上的任何其他人,无论他们的帐号托管在哪里的主机。
+      preamble: 有了这个Mastodon服务器上的账户,您就可以关注Mastodon网络上的任何其他人,无论他们的账户在哪里。
       title: 让我们在 %{domain} 上开始。
     status:
       account_status: 账户状态
       confirming: 等待电子邮件确认完成。
       functional: 你的账号可以正常使用了。
       pending: 工作人员正在审核你的申请。这需要花点时间。在申请被批准后,你将收到一封电子邮件。
-      redirecting_to: 你的帐户无效,因为它已被设置为跳转到 %{acct}
+      redirecting_to: 你的账户无效,因为它已被设置为跳转到 %{acct}
       view_strikes: 查看针对你账号的记录
     too_fast: 表单提交过快,请重试。
     use_security_key: 使用安全密钥
@@ -1024,7 +1068,7 @@ zh-CN:
       email_change_html: 你可以 <a href="%{path}">更换邮箱地址</a> 无需删除账号
       email_contact_html: 如果它还没送到,你可以发邮件给 <a href="mailto:%{email}">%{email}</a> 寻求帮助。
       email_reconfirmation_html: 如果你没有收到确认邮件,请点击 <a href="%{path}">重新发送</a> 。
-      irreversible: 你将无法恢复或重新激活你的帐户
+      irreversible: 你将无法恢复或重新激活你的账户
       more_details_html: 更多细节,请查看 <a href="%{terms_path}">隐私政策</a> 。
       username_available: 你的用户名现在又可以使用了
       username_unavailable: 你的用户名仍将无法使用
@@ -1096,7 +1140,7 @@ zh-CN:
   featured_tags:
     add_new: 添加新条目
     errors:
-      limit: 你所推荐的话题标签数已达上限
+      limit: 您所推荐的话题标签数已达上限
     hint_html: "<strong>什么是精选话题标签?</strong> 它们被显示在你的公开个人资料中的突出位置,人们可以在这些标签下浏览你的公共嘟文。 它们是跟踪创作或长期项目的进度的重要工具。"
   filters:
     contexts:
@@ -1137,8 +1181,6 @@ zh-CN:
       index:
         hint: 无论其他条件如何,此过滤器适用于选用个别嘟文。你可以从网页界面中向此过滤器加入更多嘟文。
         title: 过滤的嘟文
-  footer:
-    trending_now: 现在流行
   generic:
     all: 全部
     all_items_on_page_selected_html:
@@ -1157,8 +1199,6 @@ zh-CN:
     today: 今天
     validation_errors:
       other: 出错啦!检查一下下面 %{count} 处出错的地方吧
-  html_validator:
-    invalid_markup: '包含无效的 HTML 标记: %{error}'
   imports:
     errors:
       invalid_csv_file: '无效的 CSV 文件。错误: %{error}'
@@ -1200,7 +1240,7 @@ zh-CN:
     title: 邀请用户
   lists:
     errors:
-      limit: 你所建立的列表数量已经达到上限
+      limit: 您已达到列表数量的上限
   login_activities:
     authentication_methods:
       otp: 双因素认证应用
@@ -1237,13 +1277,13 @@ zh-CN:
     past_migrations: 迁移记录
     proceed_with_move: 移动关注者
     redirected_msg: 你的账号现在会跳转至 %{acct}
-    redirecting_to: 你的帐户被跳转到了 %{acct}。
+    redirecting_to: 你的账户正在跳转到 %{acct}。
     set_redirect: 设置跳转
     warning:
       backreference_required: 新账号必须先引用当前账号
       before: 在继续前,请仔细阅读下列说明:
       cooldown: 移动后会有一个冷却期,在此期间你将无法再次移动
-      disabled_account: 此后,你的当前帐户将无法使用。但是,你仍然有权导出数据或者重新激活。
+      disabled_account: 此后,你的当前账户将无法使用。但是,你仍然有权导出数据或者重新激活。
       followers: 这步操作将把所有关注者从当前账户移动到新账户
       only_redirect_html: 或者,你可以<a href="%{path}">只在你的账号资料上设置一个跳转</a>。
       other_data: 不会自动移动其它数据
@@ -1341,7 +1381,11 @@ zh-CN:
       unrecognized_emoji: 不是一个可识别的表情
   relationships:
     activity: 账号活动
+    confirm_follow_selected_followers: 您确定想要关注所选的关注者吗?
+    confirm_remove_selected_followers: 您确定想要取关所选的关注者吗?
+    confirm_remove_selected_follows: 您确定要删除选定的关注着吗?
     dormant: 休眠
+    follow_failure: 无法关注选中的部分账户。
     follow_selected_followers: 关注选中的关注者
     followers: 关注者
     following: 正在关注
@@ -1381,6 +1425,7 @@ zh-CN:
       electron: Electron
       firefox: 火狐
       generic: 未知浏览器
+      huawei_browser: 华为浏览器
       ie: IE 浏览器
       micro_messenger: 微信
       nokia: Nokia S40 Ovi 浏览器
@@ -1390,6 +1435,7 @@ zh-CN:
       qq: QQ浏览器
       safari: Safari
       uc_browser: UC 浏览器
+      unknown_browser: 未知浏览器
       weibo: 新浪微博
     current_session: 当前会话
     description: "%{platform} 上的 %{browser}"
@@ -1402,9 +1448,10 @@ zh-CN:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: 未知平台
+      unknown_platform: 未知平台
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1511,7 +1558,7 @@ zh-CN:
       '7889238': 3个月
     min_age_label: 过期阈值
     min_favs: 保留如下嘟文:喜欢数超过
-    min_favs_hint: 喜欢数超过该阈值的的嘟文不会被删除。如果留空,则无论嘟文获得多少喜欢,都将被删除。
+    min_favs_hint: 喜欢数超过该阈值的的嘟文都不会被删除。如果留空,则无论嘟文获得多少喜欢,都将被删除。
     min_reblogs: 保留如下嘟文:转嘟数超过
     min_reblogs_hint: 转嘟数超过该阈值的的嘟文不会被删除。如果留空,则无论嘟文获得多少转嘟,都将被删除。
   stream_entries:
@@ -1585,19 +1632,19 @@ zh-CN:
       statuses: 被引用的嘟文:
       subject:
         delete_statuses: 你在 %{acct} 的嘟文已被删除
-        disable: 你的帐户 %{acct} 已被冻结
+        disable: 你的账户 %{acct} 已被冻结
         mark_statuses_as_sensitive: 你在 %{acct} 的嘟文已被标记为敏感内容
         none: 对 %{acct} 的警告
         sensitive: 你在 %{acct} 的嘟文今后将被标记为敏感内容
-        silence: 你的帐户 %{acct} 已被隐藏
-        suspend: 你的帐户 %{acct} 已被封禁。
+        silence: 你的账户 %{acct} 已被隐藏
+        suspend: 你的账户 %{acct} 已被封禁
       title:
         delete_statuses: 嘟文已删除
         disable: 账号已冻结
         mark_statuses_as_sensitive: 嘟文已被标记为敏感内容
         none: 警示
         sensitive: 账户已被标记为敏感内容
-        silence: 帐户被隐藏
+        silence: 账户被隐藏
         suspend: 账号被封禁
     welcome:
       edit_profile_action: 设置个人资料
@@ -1611,12 +1658,13 @@ zh-CN:
       title: "%{name},欢迎你的加入!"
   users:
     follow_limit_reached: 你不能关注超过 %{limit} 个人
+    go_to_sso_account_settings: 转到您的身份提供商进行账户设置
     invalid_otp_token: 输入的双重认证代码无效
     otp_lost_help_html: 如果你不慎丢失了所有的代码,请联系 %{email} 寻求帮助
     seamless_external_login: 因为你是通过外部服务登录的,所以密码和电子邮件地址设置都不可用。
     signed_in_as: 当前登录的账户:
   verification:
-    explanation_html: 你可以 <strong>验证自己是个人资料元数据中的某个链接的所有者</strong>。 为此,被链接网站必须包含一个到你的 Mastodon 主页的链接。链接中 <strong>必须</strong> 包括 <code>rel="me"</code> 属性。链接的文本内容可以随意填写。例如:
+    explanation_html: 您可以 <strong>验证自己是个人资料元数据中的链接的所有者</strong> 。为此,链接的网站必须包含返回到您的 Mastodon 个人资料的链接。添加链接后,您可能需要回到这里并重新保存个人资料以使验证生效。 <strong>必须</strong> 在返回链接上使用 <code>rel="me"</code> 属性。 链接的文本内容无关紧要。以下是一个示例:
     verification: 验证
   webauthn_credentials:
     add: 添加新的安全密钥
diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml
index f26a8df08..566156620 100644
--- a/config/locales/zh-HK.yml
+++ b/config/locales/zh-HK.yml
@@ -89,6 +89,7 @@ zh-HK:
       moderation:
         active: 活躍
         all: 全部
+        disabled: 已停用
         pending: 處理中
         silenced: 受限
         suspended: 已停權
@@ -130,6 +131,7 @@ zh-HK:
       search: 搜尋
       search_same_email_domain: 其他有相同電郵網域的使用者
       search_same_ip: 其他有相同 IP 位址的使用者
+      security: 安全性
       security_measures:
         only_password: 僅密碼
         password_and_2fa: 密碼和兩步認證
@@ -419,6 +421,7 @@ zh-HK:
         resolve: 解析網域
         title: 新增電郵網域阻隔
       no_email_domain_block_selected: 未選取任何電郵網域限制,因此並未變更
+      not_permitted: 無權限
       resolved_dns_records_hint_html: 域名解析為以下 MX 網域,這些網域主要用來接收電子郵件。封鎖一個 MX 網域,即使域名看起來不同,也會阻止任何用相同 MX 網域的電郵地址進行註冊。<strong>注意不要封鎖主要的電子郵件供應商。</strong>
       resolved_through_html: 透過 %{domain} 解析
       title: 電郵網域阻隔
@@ -433,6 +436,7 @@ zh-HK:
         private_comment_description_html: 為了幫助你追蹤匯入封鎖的源頭,匯入的封鎖會在建立時將會有以下的私密備註:<q>%{comment}</q>
         private_comment_template: 於 %{date} 從 %{source} 匯入
         title: 匯入封鎖的網域
+      invalid_domain_block: 由於以下錯誤,已略過一個或多個域名封鎖:%{error}
       new:
         title: 匯入封鎖的網域
       no_file: 未選擇檔案
@@ -462,6 +466,7 @@ zh-HK:
       content_policies:
         comment: 內部備註
         description_html: 你可以自行制訂內容政策,政策將用於此網域和其子網域的所有帳號。
+        limited_federation_mode_description_html: 你可選擇是否允許此網域加入跨站。
         policies:
           reject_media: 拒絕接收媒體
           reject_reports: 拒絕檢舉
@@ -563,19 +568,23 @@ zh-HK:
         mark_as_sensitive_description_html: 被檢舉的帖文中的媒體會被標記為敏感內容,並記錄一次警告,以便日後加快處理同一帳號的違規行為。
         other_description_html: 查看更多控制帳號行為,以及自訂與被檢舉帳號溝通的選項。
         resolve_description_html: 不會對被檢舉的帳號採取任何動作,也不會記錄警告,檢舉報告將會結案。
-        silence_description_html: 此個人檔案將會只有追蹤者和透過手動搜尋才可以看見,大幅限制了它的曝光。你可隨時恢復它。
-        suspend_description_html: 直到最後刪除之前,此個人檔案及其內容將無法被存取。你將無法與此帳號互動。這動作可以在 30 天內還原。
+        silence_description_html: 此帳號將會只有追蹤者或手動搜尋它的人才可以看見,大幅限制了它的曝光。你可隨時恢復它。關閉所有對此帳號的檢舉報告。
+        suspend_description_html: 此帳號及其所有內容將不可再被存取,並且最終會被刪除,無法再進行互動。可於 30 天內撤銷此動作。關閉所有對此帳號的檢舉報告。
       actions_description_html: 決定對此檢舉採取哪種措施。如果對被檢舉帳號採取懲罰措施,除非選取了<strong>垃圾訊息</strong>分類,否則將向他們發送一封電郵通知。
+      actions_description_remote_html: 決定對此檢舉採取哪種動作。這只會影響<strong>你的伺服器</strong>與此遠端帳號的通訊和處理其內容的方式。
       add_to_report: 加入更多到檢舉
       are_you_sure: 你確認嗎?
       assign_to_self: 指派給自己
       assigned: 指派版主
       by_target_domain: 被舉報帳號的域名
+      cancel: 取消
       category: 分類
       category_description_html: 此帳號及/或內容被檢舉的原因,將會被引用在與被檢舉帳號的通訊中。
       comment:
         none: 沒有
       comment_description_html: 為了提供更多資訊,%{name} 寫道:
+      confirm: 確定
+      confirm_action: 確認對 @%{acct} 執行管理操作
       created_at: 日期
       delete_and_resolve: 刪除帖文
       forwarded: 已轉寄
@@ -592,6 +601,7 @@ zh-HK:
         placeholder: 記錄已執行的動作,或其他相關的更新……
         title: 筆記
       notes_description_html: 查看並給其他管理員和日後的自己留下筆記
+      processed_msg: '已成功處理檢舉報告 #%{id}'
       quick_actions_description_html: 採取快捷操作或向下捲動以查看檢舉內容:
       remote_user_placeholder: 來自 %{instance} 的遠端使用者
       reopen: 重開舉報個案
@@ -604,9 +614,28 @@ zh-HK:
       status: 狀態
       statuses: 被檢舉的內容
       statuses_description_html: 違規內容將被引用在被檢舉帳號的通訊中
+      summary:
+        action_preambles:
+          delete_html: 你即將<strong>移除</strong> <strong>@%{acct}</strong> 的部份帖文。這會:
+          mark_as_sensitive_html: 你即將把 <strong>@%{acct}</strong> 的部份帖文<strong>標記</strong>為<strong>敏感內容</strong>。這會:
+          silence_html: 你即將<strong>限制</strong> <strong>@%{acct}</strong> 的帳號。這會:
+          suspend_html: 你即將把 <strong>@%{acct}</strong> 的帳號<strong>停權</strong>。這會:
+        actions:
+          delete_html: 移除違規帖文
+          mark_as_sensitive_html: 將違規帖文的媒體標記為敏感內容
+          silence_html: 僅讓 <strong>@%{acct}</strong> 的追蹤者和手動搜尋其個人檔案的人看到他的個人檔案和內容,從而大幅限制其曝光。
+          suspend_html: 把 <strong>@%{acct}</strong> 停權,使其個人檔案和內容無法被存取和互動。
+        close_report: '將檢舉報告 #%{id} 標記為已解決'
+        close_reports_html: 將針對 <strong>@%{acct}</strong> 的<strong>所有</strong>檢舉報告標記為已解決
+        delete_data_html: 從現在起 30 天後移除 <strong>@%{acct}</strong> 的個人檔案和內容,除非在此期間撤銷對他們的停權。
+        preview_preamble_html: "<strong>@%{acct}</strong> 將收到警告,內容如下:"
+        record_strike_html: 記錄對 <strong>@%{acct}</strong> 的警告,有助你加快處理此帳號日後的違規行為
+        send_email_html: 向 <strong>@%{acct}</strong> 發送警告郵件
+        warning_placeholder: 選填審核動作的補充理由。
       target_origin: 被檢舉帳號來源
       title: 舉報
       unassign: 取消指派
+      unknown_action_msg: 未知的動作:%{action}
       unresolved: 未處理
       updated_at: 更新
       view_profile: 查看個人檔案
@@ -699,6 +728,8 @@ zh-HK:
         preamble: 呈現有趣的內容有助於吸引不認識 Mastodon 的使用者新手上路。控制各種探索功能在你的伺服器上的運作方式。
         profile_directory: 個人檔案目錄
         public_timelines: 公共時間軸
+        publish_discovered_servers: 公開已知的伺服器
+        publish_statistics: 公佈統計數據
         title: 探索
         trends: 熱門
       domain_blocks:
@@ -753,6 +784,7 @@ zh-HK:
         suspend: "%{name} 將 %{target} 的帳號停權"
       appeal_approved: 已申訴
       appeal_pending: 申訴待審中
+      appeal_rejected: 申訴被駁回
     system_checks:
       database_schema_check:
         message_html: 有待處理的資料庫轉移 (database migration) 。請執行以確保應用程式行為合乎預期
@@ -766,6 +798,12 @@ zh-HK:
         message_html: 您尚未定義任何伺服器規則
       sidekiq_process_check:
         message_html: 沒有 %{value} 佇列的 Sidekiq 處理程序。請檢查 Sidekiq 設定檔案
+      upload_check_privacy_error:
+        action: 在此查看更多資訊
+        message_html: "<strong>你的網絡伺服器配置錯誤。你的使用者的私隱有危險。</strong>"
+      upload_check_privacy_error_object_storage:
+        action: 在此查看更多資訊
+        message_html: "<strong>你的對象儲存配置錯誤。你的使用者的私隱有危險。</strong>"
     tags:
       review: 審核文章
       updated_msg: 成功更新主題標籤設定
@@ -787,6 +825,7 @@ zh-HK:
           other: 上週有 %{count} 人分享
         title: 熱門連結
         usage_comparison: 今天被分享了 %{today} 次,相較於昨天 %{yesterday} 次
+      not_allowed_to_trend: 不允許登上趨勢
       only_allowed: 僅允許
       pending_review: 等待審核中
       preview_card_providers:
@@ -915,6 +954,7 @@ zh-HK:
   applications:
     created: 已建立應用程式
     destroyed: 已刪除應用程式
+    logout: 登出
     regenerate_token: 重設 token
     token_regenerated: 已重設 token
     warning: 警告,不要把它分享給任何人!
@@ -922,6 +962,8 @@ zh-HK:
   auth:
     apply_for_account: 申請帳號
     change_password: 密碼
+    confirmations:
+      wrong_email_hint: 如果該電郵地址不正確,你可以在帳號設定中修改。
     delete_account: 刪除帳號
     delete_account_html: 如果你想刪除你的帳號,請<a href="%{path}">點擊這裡繼續</a>。你需要確認你的操作。
     description:
@@ -949,6 +991,8 @@ zh-HK:
     resend_confirmation: 重發確認指示電郵
     reset_password: 重設密碼
     rules:
+      accept: 接受
+      back: 返回
       preamble: 這些是由 %{domain} 管理員制訂和執行的。
       title: 一些基本規則。
     security: 登入資訊
@@ -1096,7 +1140,7 @@ zh-HK:
   featured_tags:
     add_new: 新增
     errors:
-      limit: 你所推薦的標籤數量已經達到上限
+      limit: 你推薦的標籤數量已達上限
     hint_html: "<strong>甚麼是推薦主題標籤?</strong> 它們會被顯示在你的個人資料頁面,讓其他人可以根據標籤瀏覽你的公開文章,令人們更易找到你創作或者長期作品!"
   filters:
     contexts:
@@ -1137,8 +1181,6 @@ zh-HK:
       index:
         hint: 不管其他條件如何,此篩選器會套用於所選的個別帖文。你可以在網頁介面上加入更多帖文到此篩選器。
         title: 篩選帖文
-  footer:
-    trending_now: 今期流行
   generic:
     all: 全部
     all_items_on_page_selected_html:
@@ -1157,8 +1199,6 @@ zh-HK:
     today: 今天
     validation_errors:
       other: 提交的資料有 %{count} 項問題
-  html_validator:
-    invalid_markup: 含有無效的HTML標記:%{error}
   imports:
     errors:
       invalid_csv_file: 無效的 CSV 檔案。錯誤:%{error}
@@ -1200,7 +1240,7 @@ zh-HK:
     title: 邀請用戶
   lists:
     errors:
-      limit: 你所建立的列表數量已經達到上限
+      limit: 你已達到列表數量的上限
   login_activities:
     authentication_methods:
       otp: 兩步認證軟體
@@ -1341,7 +1381,11 @@ zh-HK:
       unrecognized_emoji: 不能識別這個emoji
   relationships:
     activity: 帳戶活動
+    confirm_follow_selected_followers: 你確定要追蹤選取的追蹤者嗎?
+    confirm_remove_selected_followers: 你確定要移除選取的追蹤者嗎?
+    confirm_remove_selected_follows: 你確定要移除選取的追蹤者嗎?
     dormant: 潛在
+    follow_failure: 無法追蹤部份已選的帳號。
     follow_selected_followers: 關注所選的追隨者
     followers: 追隨者
     following: 正在關注
@@ -1381,6 +1425,7 @@ zh-HK:
       electron: Electron 瀏覽器
       firefox: Firefox 瀏覽器
       generic: 未知的瀏覽器
+      huawei_browser: 華為瀏覽器
       ie: Internet Explorer 瀏覽器
       micro_messenger: 微信
       nokia: Nokia S40 Ovi 瀏覽器
@@ -1390,6 +1435,7 @@ zh-HK:
       qq: QQ瀏覽器
       safari: Safari 瀏覽器
       uc_browser: UC 瀏覽器
+      unknown_browser: 未知的瀏覽器
       weibo: 新浪微博
     current_session: 目前的作業階段
     description: "%{platform} 上的 %{browser}"
@@ -1402,9 +1448,10 @@ zh-HK:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: 未知平台
+      unknown_platform: 未知平台
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1511,7 +1558,7 @@ zh-HK:
       '7889238': 3 個月
     min_age_label: 按時間篩選
     min_favs: 保留超過嘟文最愛門檻
-    min_favs_hint: 如果您嘟文已收到超過最愛門檻則不會刪除。留白表示不論最愛數量皆刪除嘟文。
+    min_favs_hint: 收到此數量或更多最愛的帖文將不會被刪除。 留空可無視最愛數量刪除帖子
     min_reblogs: 保留超過嘟文轉嘟門檻
     min_reblogs_hint: 如果您嘟文已收到超過轉嘟門檻則不會刪除。留白表示不論轉嘟數量皆刪除嘟文。
   stream_entries:
@@ -1611,12 +1658,13 @@ zh-HK:
       title: 歡迎 %{name} 加入!
   users:
     follow_limit_reached: 你不能關注多於%{limit} 人
+    go_to_sso_account_settings: 前往你身份提供者的帳號設定
     invalid_otp_token: 雙重認證碼不正確
     otp_lost_help_html: 如果這兩者你均無法登入,你可以聯繫 %{email}
     seamless_external_login: 因為你正在使用第三方服務登入,所以不能設定密碼和電郵。
     signed_in_as: 目前登入的帳戶:
   verification:
-    explanation_html: 你可以<strong>認證個人資料頁面的元數據 (Metadata) 連結是屬於你的</strong>。要認證,那些連結的目的地網站必須有一條回到你 Mastodon 個人頁面的連結,而且連結<strong>必須</strong>具有<code>rel="me"</code>屬性。連結的文字內容都不會影響認證。這裏有一個例子:
+    explanation_html: 你可以<strong>在你的個人檔案元數據中驗證自己是連結的擁有者</strong>。為此,連接的網站必須包含一條回到你 Mastodon 個人檔案的連結。加入連結後,你可能需要回到這裏,再次儲存你的個人檔案以便驗證生效。返回的連結<strong>必須</strong>有一個<code>rel="me"</code>的屬性。當中的文字內容並不重要。以下有一個例子:
     verification: 驗證
   webauthn_credentials:
     add: 新增安全密鑰裝置
diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml
index 1674ad756..ef027e8f2 100644
--- a/config/locales/zh-TW.yml
+++ b/config/locales/zh-TW.yml
@@ -11,10 +11,10 @@ zh-TW:
     followers:
       other: 跟隨者
     following: 正在跟隨
-    instance_actor_flash: 這個帳號是一個用來代表此伺服器的虛擬執行者,而非真實使用者。它用途為聯邦宇宙且不應被停權。
+    instance_actor_flash: 此帳號是用來代表此伺服器的虛擬執行者,而非個別使用者。它的用途為維繫聯邦宇宙,且不應被停權。
     last_active: 上次活躍時間
     link_verified_on: 此連結的所有權已在 %{date} 檢查過
-    nothing_here: 暫時沒有內容可供顯示!
+    nothing_here: 暫時沒有內容可供顯示!
     pin_errors:
       following: 您只能推薦您正在跟隨的使用者。
     posts:
@@ -23,17 +23,17 @@ zh-TW:
   admin:
     account_actions:
       action: 執行動作
-      title: 在 %{acct} 執行管理員動作
+      title: 對 %{acct} 執行站務動作
     account_moderation_notes:
-      create: 記錄
-      created_msg: 已新增管理備忘!
-      destroyed_msg: 成功刪除管理備忘!
+      create: 新增站務記錄
+      created_msg: 已成功新增管理備註!
+      destroyed_msg: 已成功刪除管理備註!
     accounts:
       add_email_domain_block: 將電子郵件網域加入黑名單
       approve: 批准
-      approved_msg: 成功審核了%{username} 的新帳號申請
-      are_you_sure: 您確定嗎?
-      avatar: 頭像
+      approved_msg: 已成功審核 %{username} 的新帳號申請
+      are_you_sure: 您確定嗎?
+      avatar: 大頭貼
       by_domain: 站點
       change_email:
         changed_msg: 電子郵件已成功變更!
@@ -54,7 +54,7 @@ zh-TW:
       delete: 刪除資料
       deleted: 已刪除
       demote: 降級
-      destroyed_msg: 即將刪除 %{username} 的數據
+      destroyed_msg: 即將刪除 %{username} 的資料
       disable: 停用
       disable_sign_in_token_auth: 停用電子郵件 token 驗證
       disable_two_factor_authentication: 停用兩階段認證
@@ -71,7 +71,7 @@ zh-TW:
       followers: 跟隨者
       follows: 正在跟隨
       header: 開頭
-      inbox_url: 收件箱 (Inbox) URL
+      inbox_url: 收件匣 (Inbox) URL
       invite_request_text: 加入原因
       invited_by: 邀請者
       ip: IP 位址
@@ -85,18 +85,19 @@ zh-TW:
       media_attachments: 多媒體附件
       memorialize: 設定為追悼帳號
       memorialized: 被悼念的
-      memorialized_msg: 成功將%{username} 的帳號變為紀念帳號
+      memorialized_msg: 成功將%{username} 的帳號變為追悼帳號
       moderation:
         active: 活躍
         all: 全部
+        disabled: 已停用
         pending: 等待中
         silenced: 受限的
         suspended: 已停權
         title: 站務
-      moderation_notes: 管理備忘
+      moderation_notes: 站務備註
       most_recent_activity: 最近活動
       most_recent_ip: 最近 IP 位址
-      no_account_selected: 未選取任何帳號,因此未變更
+      no_account_selected: 因未選取任何帳號,所以什麼事都沒發生
       no_limits_imposed: 未受限制
       no_role_assigned: 未指派角色
       not_subscribed: 未訂閱
@@ -112,17 +113,17 @@ zh-TW:
       redownload: 重新整理個人檔案
       redownloaded_msg: 成功重新載入%{username} 的個人檔案頁面
       reject: 拒絕
-      rejected_msg: 成功拒絕了%{username} 的新帳號申請
+      rejected_msg: 已成功婉拒 %{username} 的新帳號申請
       remote_suspension_irreversible: 此帳號之資料已被不可逆地刪除。
       remote_suspension_reversible_hint_html: 這個帳號已於此伺服器被停權,所有資料將會於 %{date} 被刪除。在此之前,遠端伺服器可以完全回復此的帳號。如果您想即時刪除這個帳號的資料,您可以在下面進行操作。
       remove_avatar: 取消大頭貼
       remove_header: 移除開頭
-      removed_avatar_msg: 成功刪除了 %{username} 的大頭貼
-      removed_header_msg: 成功刪除了 %{username} 的封面圖片
+      removed_avatar_msg: 已成功刪除 %{username} 的大頭貼
+      removed_header_msg: 已成功刪除 %{username} 的封面圖片
       resend_confirmation:
         already_confirmed: 此使用者已被確認
         send: 重新發送驗證信
-        success: 驗證信發送成功!
+        success: 驗證信發送成功!
       reset: 重設
       reset_password: 重設密碼
       resubscribe: 重新訂閱
@@ -130,14 +131,15 @@ zh-TW:
       search: 搜尋
       search_same_email_domain: 其他有同個電子郵件網域的使用者
       search_same_ip: 其他有同個 IP 的使用者
+      security: 安全性
       security_measures:
         only_password: 僅使用密碼
-        password_and_2fa: 密碼及二重因素驗證
-      sensitive: 敏感内容
+        password_and_2fa: 密碼及兩階段驗證 (2FA)
+      sensitive: 強制標記為敏感内容
       sensitized: 已標記為敏感內容
-      shared_inbox_url: 共享收件箱網址
+      shared_inbox_url: 共享收件匣 URL
       show:
-        created_reports: 建立檢舉
+        created_reports: 新增檢舉
         targeted_reports: 由其他人檢舉
       silence: 靜音
       silenced: 已靜音
@@ -146,18 +148,18 @@ zh-TW:
       subscribe: 訂閱
       suspend: 停權
       suspended: 已停權
-      suspension_irreversible: 已永久刪除這個帳號的數據。雖然這個帳號的數據已被永久刪除,但是您仍然可以取消暫停這個帳號。
-      suspension_reversible_hint_html: 這個帳號已被暫停,所有數據將會在 %{date} 被刪除。在此之前,您可以完全回復您的帳號。如果您想即時刪除這個帳號的數據,您可以在下面進行操作。
+      suspension_irreversible: 已永久刪除此帳號的資料。您可以取消這個帳號的停權狀態,但無法還原已刪除的資料。
+      suspension_reversible_hint_html: 這個帳號已被停權,所有資料將會於 %{date} 被刪除。在此之前,此帳號可以被完全復原。如果您想即時刪除這個帳號的資料,可以在下面進行操作。
       title: 帳號
       unblock_email: 解除封鎖電子郵件地址
       unblocked_email_msg: 成功解除封鎖 %{username} 的電子郵件地址
       unconfirmed_email: 未確認的電子郵件地址
-      undo_sensitized: 取消敏感狀態
+      undo_sensitized: 取消強制標記為敏感內容
       undo_silenced: 取消靜音
       undo_suspension: 取消停權
       unsilenced_msg: 成功解除 %{username} 的帳號限制
       unsubscribe: 取消訂閱
-      unsuspended_msg: 成功取消暫停 %{username} 的帳號
+      unsuspended_msg: 已成功取消停權 %{username} 的帳號
       username: 使用者名稱
       view_domain: 查看站台概要
       warn: 警告
@@ -171,23 +173,23 @@ zh-TW:
         change_email_user: 變更使用者的電子郵件地址
         change_role_user: 變更使用者角色
         confirm_user: 確認使用者
-        create_account_warning: 建立警告
-        create_announcement: 建立公告
+        create_account_warning: 新增警告
+        create_announcement: 新增公告
         create_canonical_email_block: 新增 E-mail 封鎖
-        create_custom_emoji: 建立自訂顏文字
-        create_domain_allow: 建立允許網域
-        create_domain_block: 建立阻擋網域
+        create_custom_emoji: 新增自訂顏文字
+        create_domain_allow: 新增允許網域
+        create_domain_block: 新增網域封鎖
         create_email_domain_block: 新增電子郵件網域封鎖
         create_ip_block: 新增IP規則
         create_unavailable_domain: 新增無法存取的網域
-        create_user_role: 建立角色
-        demote_user: 把用戶降級
+        create_user_role: 新增角色
+        demote_user: 將用戶降級
         destroy_announcement: 刪除公告
         destroy_canonical_email_block: 刪除 E-mail 封鎖
         destroy_custom_emoji: 刪除自訂顏文字
         destroy_domain_allow: 刪除允許網域
-        destroy_domain_block: 刪除阻擋網域
-        destroy_email_domain_block: 刪除阻擋電郵網域
+        destroy_domain_block: 刪除網域封鎖
+        destroy_email_domain_block: 刪除電子郵件網域封鎖
         destroy_instance: 清除網域
         destroy_ip_block: 刪除 IP 規則
         destroy_status: 刪除狀態
@@ -200,8 +202,8 @@ zh-TW:
         enable_custom_emoji: 啓用自訂顏文字
         enable_sign_in_token_auth_user: 啟用使用者電子郵件 token 驗證
         enable_user: 啓用帳號
-        memorialize_account: 設定成紀念帳號
-        promote_user: 把用戶升級
+        memorialize_account: 設定成追悼帳號
+        promote_user: 將用戶升級
         reject_appeal: 駁回申訴
         reject_user: 回絕使用者
         remove_avatar_user: 刪除大頭貼
@@ -209,79 +211,79 @@ zh-TW:
         resend_user: 重新發送驗證信
         reset_password_user: 重設密碼
         resolve_report: 消除舉報
-        sensitive_account: 把您的帳號的媒體標記為敏感內容
+        sensitive_account: 將媒體強制標記為敏感內容
         silence_account: 靜音帳號
         suspend_account: 停權帳號
         unassigned_report: 取消指派舉報
         unblock_email_account: 解除封鎖電子郵件地址
-        unsensitive_account: 取消把您的帳號的媒體設定為敏感內容
+        unsensitive_account: 取消將媒體強制標記為敏感內容
         unsilence_account: 取消帳號的靜音狀態
         unsuspend_account: 取消帳號的暫停狀態
         update_announcement: 更新公告
         update_custom_emoji: 更新自訂顏文字
-        update_domain_block: 更新封鎖網域
+        update_domain_block: 更新網域封鎖
         update_ip_block: 更新 IP 規則
         update_status: 更新狀態
         update_user_role: 更新角色
       actions:
-        approve_appeal_html: "%{name} 批准了來自 %{target} 的審核決定申訴"
-        approve_user_html: "%{name} 批准了從 %{target} 而來的註冊"
+        approve_appeal_html: "%{name} 已批准來自 %{target} 的審核決定申訴"
+        approve_user_html: "%{name} 已批准從 %{target} 而來的註冊"
         assigned_to_self_report_html: "%{name} 將報告 %{target} 指派給自己"
-        change_email_user_html: "%{name} 變更了使用者 %{target} 的電子郵件地址"
-        change_role_user_html: "%{name} 變更了 %{target} 的角色"
-        confirm_user_html: "%{name} 確認了使用者 %{target} 的電子郵件位址"
+        change_email_user_html: "%{name} 已變更使用者 %{target} 的電子郵件地址"
+        change_role_user_html: "%{name} 已變更 %{target} 的角色"
+        confirm_user_html: "%{name} 已確認使用者 %{target} 的電子郵件位址"
         create_account_warning_html: "%{name} 已對 %{target} 送出警告"
-        create_announcement_html: "%{name} 新增了公告 %{target}"
-        create_canonical_email_block_html: "%{name} 已封鎖了 hash 為 %{target} 之 e-mail"
-        create_custom_emoji_html: "%{name} 上傳了新自訂表情符號 %{target}"
+        create_announcement_html: "%{name} 已新增公告 %{target}"
+        create_canonical_email_block_html: "%{name} 已封鎖 hash 為 %{target} 的 e-mail"
+        create_custom_emoji_html: "%{name} 已上傳新自訂表情符號 %{target}"
         create_domain_allow_html: "%{name} 允許 %{target} 網域加入聯邦宇宙"
-        create_domain_block_html: "%{name} 封鎖了網域 %{target}"
-        create_email_domain_block_html: "%{name} 封鎖了電子郵件網域 %{target}"
-        create_ip_block_html: "%{name} 已經設定了IP %{target} 的規則"
+        create_domain_block_html: "%{name} 已封鎖網域 %{target}"
+        create_email_domain_block_html: "%{name} 已封鎖電子郵件網域 %{target}"
+        create_ip_block_html: "%{name} 已設定 IP %{target} 的規則"
         create_unavailable_domain_html: "%{name} 停止發送至網域 %{target}"
-        create_user_role_html: "%{name} 建立了 %{target} 角色"
+        create_user_role_html: "%{name} 已新增 %{target} 角色"
         demote_user_html: "%{name} 將使用者 %{target} 降級"
-        destroy_announcement_html: "%{name} 刪除了公告 %{target}"
-        destroy_canonical_email_block_html: "%{name} 取消了 hash 為 %{target} 之 e-mail 的封鎖"
-        destroy_custom_emoji_html: "%{name} 刪除了表情符號 %{target}"
+        destroy_announcement_html: "%{name} 已刪除公告 %{target}"
+        destroy_canonical_email_block_html: "%{name} 已解除封鎖 hash 為 %{target} 的電子郵件"
+        destroy_custom_emoji_html: "%{name} 已刪除表情符號 %{target}"
         destroy_domain_allow_html: "%{name} 不允許與網域 %{target} 加入聯邦宇宙"
-        destroy_domain_block_html: "%{name} 取消了對網域 %{target} 的封鎖"
-        destroy_email_domain_block_html: "%{name} 取消了對電子郵件網域 %{target} 的封鎖"
-        destroy_instance_html: "%{name} 清除了網域 %{target}"
-        destroy_ip_block_html: "%{name} 刪除了 IP %{target} 的規則"
-        destroy_status_html: "%{name} 刪除了 %{target} 的嘟文"
-        destroy_unavailable_domain_html: "%{name} 恢復了對網域 %{target} 的發送"
-        destroy_user_role_html: "%{name} 刪除了 %{target} 角色"
-        disable_2fa_user_html: "%{name} 停用了使用者 %{target} 的兩階段認證"
-        disable_custom_emoji_html: "%{name} 停用了自訂表情符號 %{target}"
-        disable_sign_in_token_auth_user_html: "%{name} 停用了 %{target} 之使用者電子郵件 token 驗證"
+        destroy_domain_block_html: "%{name} 已解除封鎖網域 %{target}"
+        destroy_email_domain_block_html: "%{name} 已解除封鎖電子郵件網域 %{target}"
+        destroy_instance_html: "%{name} 已清除網域 %{target}"
+        destroy_ip_block_html: "%{name} 已刪除 IP %{target} 的規則"
+        destroy_status_html: "%{name} 已刪除 %{target} 的嘟文"
+        destroy_unavailable_domain_html: "%{name} 已恢復對網域 %{target} 的發送"
+        destroy_user_role_html: "%{name} 已刪除 %{target} 角色"
+        disable_2fa_user_html: "%{name} 已停用使用者 %{target} 的兩階段認證 (2FA) "
+        disable_custom_emoji_html: "%{name} 已停用自訂表情符號 %{target}"
+        disable_sign_in_token_auth_user_html: "%{name} 已停用 %{target} 之使用者電子郵件 token 驗證"
         disable_user_html: "%{name} 將使用者 %{target} 設定為禁止登入"
-        enable_custom_emoji_html: "%{name} 啟用了自訂表情符號 %{target}"
-        enable_sign_in_token_auth_user_html: "%{name} 啟用了 %{target} 之使用者電子郵件 token 驗證"
+        enable_custom_emoji_html: "%{name} 已啟用自訂表情符號 %{target}"
+        enable_sign_in_token_auth_user_html: "%{name} 已啟用 %{target} 之使用者電子郵件 token 驗證"
         enable_user_html: "%{name} 將使用者 %{target} 設定為允許登入"
         memorialize_account_html: "%{name} 將 %{target} 設定為追悼帳號"
-        promote_user_html: "%{name} 對使用者 %{target} 進行了晉級操作"
-        reject_appeal_html: "%{name} 回絕了來自 %{target} 的審核決定申訴"
-        reject_user_html: "%{name} 回絕了從 %{target} 而來的註冊"
-        remove_avatar_user_html: "%{name} 移除了 %{target} 的大頭貼"
+        promote_user_html: "%{name} 對使用者 %{target} 已進行晉級操作"
+        reject_appeal_html: "%{name} 已回絕來自 %{target} 的審核決定申訴"
+        reject_user_html: "%{name} 已回絕從 %{target} 而來的註冊"
+        remove_avatar_user_html: "%{name} 已移除 %{target} 的大頭貼"
         reopen_report_html: "%{name} 重新開啟 %{target} 的檢舉"
         resend_user_html: "%{name} 已重新發送驗證信給 %{target}"
-        reset_password_user_html: "%{name} 重新設定了使用者 %{target} 的密碼"
-        resolve_report_html: "%{name} 處理了 %{target} 的檢舉"
+        reset_password_user_html: "%{name} 已重新設定使用者 %{target} 的密碼"
+        resolve_report_html: "%{name} 已處理 %{target} 的檢舉"
         sensitive_account_html: "%{name} 將 %{target} 的媒體檔案標記為敏感內容"
-        silence_account_html: "%{name} 靜音了使用者 %{target}"
-        suspend_account_html: "%{name} 停權了使用者 %{target}"
+        silence_account_html: "%{name} 已靜音使用者 %{target}"
+        suspend_account_html: "%{name} 已停權 %{target} 的帳號"
         unassigned_report_html: "%{name} 取消指派 %{target} 的檢舉"
-        unblock_email_account_html: "%{name} 解除封鎖了 %{target} 的電子郵件地址"
-        unsensitive_account_html: "%{name} 將 %{target} 的媒體檔案的敏感狀態取消"
-        unsilence_account_html: "%{name} 取消了使用者 %{target} 的靜音狀態"
-        unsuspend_account_html: "%{name} 取消了使用者 %{target} 的停權狀態"
-        update_announcement_html: "%{name} 更新了公告 %{target}"
-        update_custom_emoji_html: "%{name} 更新了自訂表情符號 %{target}"
-        update_domain_block_html: "%{name} 更新了 %{target} 之網域封鎖"
-        update_ip_block_html: "%{name} 已經更新了 IP %{target} 之規則"
-        update_status_html: "%{name} 更新了 %{target} 的嘟文"
-        update_user_role_html: "%{name} 變更了 %{target} 角色"
+        unblock_email_account_html: "%{name} 已解除封鎖 %{target} 的電子郵件地址"
+        unsensitive_account_html: "%{name} 將 %{target} 的媒體檔案取消標記為敏感內容"
+        unsilence_account_html: "%{name} 已取消使用者 %{target} 的靜音狀態"
+        unsuspend_account_html: "%{name} 已取消停權 %{target} 的帳號"
+        update_announcement_html: "%{name} 已更新公告 %{target}"
+        update_custom_emoji_html: "%{name} 已更新自訂表情符號 %{target}"
+        update_domain_block_html: "%{name} 已更新 %{target} 之網域封鎖"
+        update_ip_block_html: "%{name} 已變更 IP %{target} 之規則"
+        update_status_html: "%{name} 已更新 %{target} 的嘟文"
+        update_user_role_html: "%{name} 已變更 %{target} 角色"
       deleted_account: 已刪除帳號
       empty: 找不到 log
       filter_by_action: 按動作過濾
@@ -294,39 +296,39 @@ zh-TW:
       empty: 找不到公告。
       live: 直播
       new:
-        create: 建立公告
+        create: 新增公告
         title: 新增公告
-      publish: 發佈
+      publish: 發布
       published_msg: 成功發布公告!
       scheduled_for: 排定 %{time}
       scheduled_msg: 公告已排定公開!
       title: 公告
-      unpublish: 取消發佈
+      unpublish: 取消發布
       unpublished_msg: 成功取消發布公告!
       updated_msg: 成功更新公告!
     custom_emojis:
       assign_category: 指定分類
       by_domain: 站點
-      copied_msg: 成功將表情複製到本地
+      copied_msg: 成功建立 emoji 表情符號之本地備份
       copy: 複製
       copy_failed_msg: 無法將表情複製到本地
       create_new_category: 建立新分類
-      created_msg: 已新增表情符號!
+      created_msg: 已新增表情符號!
       delete: 刪除
-      destroyed_msg: 已刪除表情符號!
+      destroyed_msg: 已刪除表情符號!
       disable: 停用
       disabled: 已停用
-      disabled_msg: 已停用表情符號
+      disabled_msg: 已停用該表情符號
       emoji: 表情符號
       enable: 啟用
       enabled: 已啟用
-      enabled_msg: 已啟用表情符號
+      enabled_msg: 已啟用該表情符號
       image_hint: 檔案大小最大至 %{size} 之 PNG 或 GIF
       list: 列表
       listed: 已顯示
       new:
         title: 加入新的自訂表情符號
-      no_emoji_selected: 未選取任何 emoji,因此未變更
+      no_emoji_selected: 未選取任何 emoji,所以什麼事都沒發生
       not_permitted: 您無權執行此操作
       overwrite: 覆蓋
       shortcode: 短代碼
@@ -336,7 +338,7 @@ zh-TW:
       unlist: 不公開
       unlisted: 已隱藏
       update_failed_msg: 無法更新表情符號
-      updated_msg: 已更新表情符號!
+      updated_msg: 已更新表情符號!
       upload: 上傳新的表情符號
     dashboard:
       active_users: 活躍使用者
@@ -372,13 +374,13 @@ zh-TW:
       import: 匯入
       undo: 從聯邦宇宙白名單移除
     domain_blocks:
-      add_new: 新增欲封鎖域名
-      created_msg: 正在進行站點封鎖
-      destroyed_msg: 已撤銷站點封鎖
+      add_new: 新增網域黑名單
+      created_msg: 正在進行網域封鎖
+      destroyed_msg: 已撤銷網域封鎖
       domain: 站點
       edit: 更改封鎖的站台
-      existing_domain_block: 您已對 %{name} 施加了更嚴格的限制。
-      existing_domain_block_html: 您已經對 %{name} 施加了更嚴格的限制,您需要先把他<a href="%{unblock_url}">取消封鎖</a>。
+      existing_domain_block: 您已對 %{name} 施加更嚴格的限制。
+      existing_domain_block_html: 您已對 %{name} 施加更嚴格的限制,您需要先 <a href="%{unblock_url}">解除封鎖</a>。
       export: 匯出
       import: 匯入
       new:
@@ -389,21 +391,21 @@ zh-TW:
           noop: 無
           silence: 靜音
           suspend: 停權
-        title: 新增封鎖站點
-      no_domain_block_selected: 因未選取項目,而未更改網域黑名單
+        title: 新增網域黑名單
+      no_domain_block_selected: 因未選取網域黑名單,所以什麼事都沒發生
       not_permitted: 您無權執行此操作
       obfuscate: 混淆網域名稱
       obfuscate_hint: 若啟用網域廣告列表限制,於列表部份混淆網域名稱
       private_comment: 私人留言
-      private_comment_hint: 請提供更多有關此站台限制的資訊以供版主作內部參考。
+      private_comment_hint: 請提供更多有關此站台限制的資訊以供管理員作內部參考。
       public_comment: 公開留言
       public_comment_hint: 如果您已經啟用站台限制列表的公告,請為一般大眾提供更多有關此站台限制的資訊。
       reject_media: 拒絕媒體檔案
       reject_media_hint: 刪除本地快取的媒體檔案,並且不再接收來自該站點的任何媒體檔案。與停權無關
       reject_reports: 拒絕檢舉
-      reject_reports_hint: 忽略所有來自此站點的檢舉。與停權無關
-      undo: 復原欲封鎖域名
-      view: 顯示阻擋的網域
+      reject_reports_hint: 忽略所有來自此網域的檢舉。與停權無關
+      undo: 撤銷網域封鎖
+      view: 顯示已封鎖網域
     email_domain_blocks:
       add_new: 加入新項目
       attempts_over_week:
@@ -413,12 +415,13 @@ zh-TW:
       dns:
         types:
           mx: MX 記錄
-      domain: 站點
+      domain: 網域
       new:
-        create: 新增站點
+        create: 新增網域
         resolve: 解析網域
         title: 新增電子郵件黑名單項目
-      no_email_domain_block_selected: 因未選取項目,而未更改電子郵件網域黑名單
+      no_email_domain_block_selected: 因未選取電子郵件網域黑名單,所以什麼事都沒發生
+      not_permitted: 無權限
       resolved_dns_records_hint_html: 網域名稱解析為以下 MX 網域,這些網域最終負責接收電子郵件。封鎖 MX 網域將會封鎖任何來自使用相同 MX 網域的電子郵件註冊,即便可見的域名是不同的也一樣。<strong>請注意,不要封鎖主要的電子郵件服務提供商。</strong>
       resolved_through_html: 透過 %{domain} 解析
       title: 電子郵件黑名單
@@ -433,12 +436,13 @@ zh-TW:
         private_comment_description_html: 為了幫助您追蹤匯入黑名單之來源,匯入黑名單建立時將隨附以下私密備註:<q>%{comment}</q>
         private_comment_template: 於 %{date} 由 %{source} 匯入
         title: 匯入網域黑名單
+      invalid_domain_block: 由於此錯誤,以致一個或多個網域封鎖被略過:%{error}
       new:
         title: 匯入網域黑名單
       no_file: 尚未選擇檔案
     follow_recommendations:
       description_html: |-
-        <strong>跟隨建議幫助新使用者們快速找到有趣的內容</strong>. 當使用者沒有與其他帳號有足夠多的互動以建立個人化跟隨建議時,這些帳號將會被推荐。這些帳號將基於某選定語言之高互動和高本地跟隨者數量帳號而
+        <strong>跟隨建議幫助新使用者們快速找到有趣的內容</strong>。當使用者沒有與其他帳號有足夠多的互動以建立個人化跟隨建議時,這些帳號將會被推薦。這些帳號將基於某選定語言之高互動和高本地跟隨者數量帳號而
         每日重新更新。
       language: 對於語言
       status: 狀態
@@ -464,6 +468,7 @@ zh-TW:
       content_policies:
         comment: 內部備註
         description_html: 您可以定義對所有此網域帳號及其子網域所實施之內容政策。
+        limited_federation_mode_description_html: 您可以選擇是否允許此網域加入聯邦宇宙。
         policies:
           reject_media: 拒絕多媒體
           reject_reports: 拒絕檢舉
@@ -531,7 +536,7 @@ zh-TW:
         '94670856': 3 年
       new:
         title: 建立新的 IP 規則
-      no_ip_block_selected: 因為沒有選擇任何 IP 規則,所以什麼事都沒發生
+      no_ip_block_selected: 因未選取任何 IP 規則,所以什麼事都沒發生
       title: IP 規則
     relationships:
       title: "%{acct} 的關係"
@@ -544,7 +549,7 @@ zh-TW:
       enable: 啟用
       enable_hint: 啟用後,您的伺服器將訂閱該中繼的所有公開文章,並將會此伺服器的公開文章發送給它。
       enabled: 已啟用
-      inbox_url: 中繼URL
+      inbox_url: 中繼 URL
       pending: 等待中繼站審核
       save_and_enable: 儲存並啟用
       setup: 設定中繼連結
@@ -552,8 +557,8 @@ zh-TW:
       status: 狀態
       title: 中繼
     report_notes:
-      created_msg: 檢舉記錄建立成功!
-      destroyed_msg: 檢舉記錄刪除成功!
+      created_msg: 已成功新增檢舉備註!
+      destroyed_msg: 已成功刪除檢舉備註!
     reports:
       account:
         notes:
@@ -565,19 +570,23 @@ zh-TW:
         mark_as_sensitive_description_html: 被檢舉的嘟文中的媒體將會被標記為敏感內容,並將會記錄一次警告,以協助您升級同一帳號未來的違規行為。
         other_description_html: 檢視更多控制帳號行為以及自訂檢舉帳號通知之選項。
         resolve_description_html: 被檢舉的帳號將不被採取任何行動,不會加以刪除線標記,並且此份報告將被關閉。
-        silence_description_html: 個人頁面僅會對已跟隨帳號之使用者或手動查詢可見,將大幅度限制觸及範圍。此設定可隨時被還原。
-        suspend_description_html: 個人頁面及其內容將無法被存取,直到其最終被刪除。將無法與此帳號互動。此設定 30 日內可被還原。
+        silence_description_html: 此帳號僅會對已跟隨帳號之使用者或手動查詢可見,將大幅度限制觸及範圍。此設定可隨時被還原。關閉所有對此帳號之檢舉報告。
+        suspend_description_html: 此帳號及其所有內容將不可被存取並且最終被移除,並且無法與之進行互動。三十天內可以撤銷此動作。關閉所有對此帳號之檢舉報告。
       actions_description_html: 決定應對此報告採取何種行動。若您對檢舉之帳號採取懲罰措施,則將對他們發送 e-mail 通知,如非選擇了 <strong>垃圾郵件</strong> 類別。
+      actions_description_remote_html: 決定將對此檢舉報告採取何種動作。這將僅作用於<strong>您的伺服器</strong>與此遠端帳號及其內容之通訊行為。
       add_to_report: 加入更多至報告
-      are_you_sure: 您確定嗎?
+      are_you_sure: 您確定嗎?
       assign_to_self: 指派給自己
-      assigned: 指派負責人
+      assigned: 指派站務
       by_target_domain: 檢舉帳號之網域
+      cancel: 取消
       category: 分類
       category_description_html: 此帳號及/或被檢舉內容之原因會被引用在檢舉帳號通知中
       comment:
         none: 無
       comment_description_html: 提供更多資訊,%{name} 寫道:
+      confirm: 確認
+      confirm_action: 確認對 @%{acct} 執行站務動作
       created_at: 日期
       delete_and_resolve: 刪除嘟文
       forwarded: 已轉寄
@@ -587,13 +596,14 @@ zh-TW:
       mark_as_unresolved: 標記為「未解決」
       no_one_assigned: 沒有人
       notes:
-        create: 建立記錄
-        create_and_resolve: 建立記錄並標記為「已解決」
-        create_and_unresolve: 建立記錄並標記「未解決」
+        create: 新增備註
+        create_and_resolve: 新增備註並標記為「已解決」
+        create_and_unresolve: 新增備註並標記「未解決」
         delete: 刪除
         placeholder: 記錄已執行的動作,或其他相關的更新...
-        title: 註記
-      notes_description_html: 檢視及留下些給其他管理員和未來的自己的註記
+        title: 備註
+      notes_description_html: 檢視及留下些給其他管理員和未來的自己的備註
+      processed_msg: '檢舉報告 #%{id} 已被成功處理'
       quick_actions_description_html: 採取一個快速行動,或者下捲以檢視檢舉內容:
       remote_user_placeholder: 來自 %{instance} 之遠端使用者
       reopen: 重開檢舉
@@ -601,14 +611,33 @@ zh-TW:
       reported_account: 被檢舉使用者
       reported_by: 檢舉人
       resolved: 已解決
-      resolved_msg: 檢舉已處理!
+      resolved_msg: 檢舉報告已處理完成!
       skip_to_actions: 跳過行動
       status: 嘟文
       statuses: 被檢舉的內容
       statuses_description_html: 侵犯性違規內容會被引用在檢舉帳號通知中
+      summary:
+        action_preambles:
+          delete_html: 您將要 <strong>移除</strong> 某些 <strong>@%{acct}</strong> 之嘟文。此將會:
+          mark_as_sensitive_html: 您將要 <strong>標記</strong> 某些 <strong>@%{acct}</strong> 之嘟文為 <strong>敏感內容 </strong>。此將會:
+          silence_html: 您將要 <strong>限制</strong> <strong>@%{acct}</strong> 之帳號。此將會:
+          suspend_html: 您將要 <strong>停權</strong> <strong>@%{acct}</strong> 之帳號。此將會:
+        actions:
+          delete_html: 移除違反規則之嘟文
+          mark_as_sensitive_html: 將違反規則之嘟文多媒體標記為敏感內容
+          silence_html: 藉由標記他們的個人檔案與內容為僅可見於已跟隨帳號或手動查詢此個人檔案,此將嚴格地限制 <strong>@%{acct}</strong> 之觸及率
+          suspend_html: 停權 <strong>@%{acct}</strong>,將他們的個人檔案與內容標記為無法存取及無法與之互動
+        close_report: '將檢舉報告 #%{id} 標記為已處理'
+        close_reports_html: 將 <strong>所有</strong> 對於 <strong>@%{acct}</strong> 之檢舉報告標記為已處理
+        delete_data_html: 於即日起 30 天後刪除 <strong>@%{acct}</strong>之個人檔案與內容,除非他們於期限前被解除暫停
+        preview_preamble_html: "<strong>@%{acct}</strong> 將收到關於以下內容之警告:"
+        record_strike_html: 紀錄關於 <strong>@%{acct}</strong>之警示有助於您升級對此帳號未來違規處理
+        send_email_html: 寄一封警告 e-mail 給 <strong>@%{acct}</strong>
+        warning_placeholder: 選填之其他站務動作理由。
       target_origin: 檢舉帳號之來源
       title: 檢舉
       unassign: 取消指派
+      unknown_action_msg: 未知的動作:%{action}
       unresolved: 未解決
       updated_at: 更新
       view_profile: 檢視個人檔案頁面
@@ -686,7 +715,7 @@ zh-TW:
         title: 關於
       appearance:
         preamble: 客製化 Mastodon 網頁介面。
-        title: 外觀設定
+        title: 外觀
       branding:
         preamble: 您的伺服器品牌使之從聯邦宇宙網路中其他伺服器間凸顯自己。此資訊可能於各種不同的環境中顯示,例如 Mastodon 網頁介面、原生應用程式、其他網頁上的連結預覽或是其他通訊應用程式等等。因此,請盡可能保持此資訊簡潔明朗。
         title: 品牌化
@@ -701,6 +730,8 @@ zh-TW:
         preamble: 呈現有趣的內容有助於 Mastodon 上一人不識的新手上路。控制各種不同的分類在您伺服器上如何被探索到。
         profile_directory: 個人檔案目錄
         public_timelines: 公開時間軸
+        publish_discovered_servers: 公開已知伺服器列表
+        publish_statistics: 公開統計資料
         title: 探索
         trends: 熱門趨勢
       domain_blocks:
@@ -735,7 +766,7 @@ zh-TW:
       media:
         title: 媒體檔案
       metadata: 詮釋資料
-      no_status_selected: 因未選擇嘟文而未變更。
+      no_status_selected: 因未選取嘟文,所以什麼事都沒發生。
       open: 公開嘟文
       original_status: 原始嘟文
       reblogs: 轉嘟
@@ -746,18 +777,19 @@ zh-TW:
       with_media: 含有媒體檔案
     strikes:
       actions:
-        delete_statuses: "%{name} 刪除了 %{target} 的嘟文"
-        disable: "%{name} 凍結了 %{target} 的帳號"
+        delete_statuses: "%{name} 已刪除 %{target} 的嘟文"
+        disable: "%{name} 已凍結 %{target} 的帳號"
         mark_statuses_as_sensitive: "%{name} 將 %{target} 的嘟文標記為敏感內容"
         none: "%{name} 已對 %{target} 送出警告"
         sensitive: "%{name} 將 %{target} 的帳號標記為含有敏感內容"
-        silence: "%{name} 限制了 %{target} 的帳號"
-        suspend: "%{name} 將 %{target} 的帳號停權"
+        silence: "%{name} 已限制 %{target} 的帳號"
+        suspend: "%{name} 已將 %{target} 的帳號停權"
       appeal_approved: 已申訴
       appeal_pending: 申訴待審中
+      appeal_rejected: 申訴被駁回
     system_checks:
       database_schema_check:
-        message_html: 有挂起的数据库迁移,请运行它们以确保应用程序按照预期运行。
+        message_html: 發現尚待處理的資料庫遷移 (database migration)。請執行它們以確保應用程式如期運行。
       elasticsearch_running_check:
         message_html: 無法連接 Elasticsearch。請檢查是否正在執行中,或者已關閉全文搜尋。
       elasticsearch_version_check:
@@ -765,9 +797,15 @@ zh-TW:
         version_comparison: Elasticsearch %{running_version} 版正在執行,需要 %{required_version} 版。
       rules_check:
         action: 管理服务器规则
-        message_html: 您没有定義任何伺服器規則。
+        message_html: 您尚未定義任何伺服器規則。
       sidekiq_process_check:
         message_html: 沒有佇列 %{value} 的 Sidekiq 行程,請檢查您的 Sidekiq 設定組態
+      upload_check_privacy_error:
+        action: 檢查這裡以取得更多資訊
+        message_html: "<strong>您的網頁伺服器設定錯誤。您的使用者隱私正暴露於風險之中。</strong>"
+      upload_check_privacy_error_object_storage:
+        action: 檢查這裡以取得更多資訊
+        message_html: "<strong>您的物件資料儲存空間 (object storage) 設定錯誤。您的使用者隱私正暴露於風險之中。</strong>"
     tags:
       review: 審核嘟文
       updated_msg: 成功更新主題標籤設定
@@ -782,13 +820,14 @@ zh-TW:
         description_html: 這些連結是正在被您伺服器上看到該嘟文之帳號大量分享。這些連結可以幫助您的使用者探索現在世界上正在發生的事情。除非您核准該發行者,連結將不被公開展示。您也可以核准或駁回個別連結。
         disallow: 不允許連結
         disallow_provider: 不允許發行者
-        no_link_selected: 未選取任何鏈結,因此未變更
+        no_link_selected: 因未選取任何鏈結,所以什麼事都沒發生
         publishers:
-          no_publisher_selected: 未選取任何發行者,因此未變更
+          no_publisher_selected: 因未選取任何發行者,所以什麼事都沒發生
         shared_by_over_week:
           other: 上週被 %{count} 名使用者分享
         title: 熱門連結
         usage_comparison: 於今日被 %{today} 人分享,相較於昨日 %{yesterday} 人
+      not_allowed_to_trend: 不允許登上熱門
       only_allowed: 僅允許
       pending_review: 等待審核中
       preview_card_providers:
@@ -803,7 +842,7 @@ zh-TW:
         description_html: 這些是您伺服器上已知被正在大量分享及加入最愛之嘟文。這些嘟文能幫助您伺服器上舊雨新知發現更多帳號來跟隨。除非您核准該作者且作者允許他們的帳號被推薦至其他人,嘟文將不被公開展示。您可以核准或駁回個別嘟文。
         disallow: 不允許嘟文
         disallow_account: 不允許作者
-        no_status_selected: 未選取任何熱門嘟文,因此未變更
+        no_status_selected: 因未選取任何熱門嘟文,所以什麼事都沒發生
         not_discoverable: 嘟文作者選擇不被發現
         shared_by:
           other: 分享過或/及收藏過 %{friendly_count} 次
@@ -818,7 +857,7 @@ zh-TW:
           tag_uses_measure: 總使用次數
         description_html: 這些主題標籤正在您的伺服器上大量嘟文中出現。這些主題標籤能幫助您的使用者發現人們正集中討論的內容。除非您核准,主題標籤將不被公開展示。
         listable: 能被建議
-        no_tag_selected: 未選取任何主題標籤,因此未變更
+        no_tag_selected: 因未選取任何主題標籤,所以什麼事都沒發生
         not_listable: 不能被建議
         not_trendable: 不會登上熱門
         not_usable: 不可被使用
@@ -836,7 +875,7 @@ zh-TW:
       add_new: 新增
       delete: 刪除
       edit_preset: 編輯預設警告
-      empty: 您未曾定義任何預設警告
+      empty: 您尚未定義任何預設警告。
       title: 管理預設警告
     webhooks:
       add_new: 新增端點
@@ -874,8 +913,8 @@ zh-TW:
       body: 以下是新帳號的詳細資訊。您可以同意或拒絕這項申請。
       subject: "%{instance} 上有新帳號 (%{username}) 待審核"
     new_report:
-      body: "%{reporter} 檢舉了使用者 %{target}"
-      body_remote: 來自 %{domain} 的使用者檢舉了使用者 %{target}
+      body: "%{reporter} 已檢舉使用者 %{target}"
+      body_remote: 來自 %{domain} 的使用者已檢舉使用者 %{target}
       subject: 來自 %{instance} 的使用者檢舉(#%{id})
     new_trends:
       body: 以下項目需要經過審核才能公開顯示:
@@ -911,24 +950,27 @@ zh-TW:
     notification_preferences: 變更電子郵件設定
     salutation: "%{name}、"
     settings: 變更電子郵件設定︰%{link}
-    view: '進入瀏覽:'
+    view: 進入瀏覽:
     view_profile: 檢視個人檔案
     view_status: 檢視嘟文
   applications:
-    created: 已建立應用
-    destroyed: 已刪除應用
-    regenerate_token: 重設 token
-    token_regenerated: 已重設 token
-    warning: 警告,不要把它分享給任何人!
+    created: 已建立應用程式
+    destroyed: 已刪除應用程式
+    logout: 登出
+    regenerate_token: 重新產生存取 token
+    token_regenerated: 已重新產生存取 token
+    warning: 警告,不要將它分享給任何人!
     your_token: 您的 access token
   auth:
     apply_for_account: 申請帳號
     change_password: 密碼
+    confirmations:
+      wrong_email_hint: 若電子郵件地址不正確,您可以於帳號設定中更改。
     delete_account: 刪除帳號
     delete_account_html: 如果您欲刪除您的帳號,請<a href="%{path}">點擊這裡繼續</a>。您需要再三確認您的操作。
     description:
       prefix_invited_by_user: "@%{name} 邀請您加入這個 Mastodon 伺服器!"
-      prefix_sign_up: 現在就註冊 Mastodon 帳號吧!
+      prefix_sign_up: 馬上註冊 Mastodon 帳號吧!
       suffix: 有了帳號,就可以從任何 Mastodon 伺服器跟隨任何人、發發廢嘟,並且與任何 Mastodon 伺服器的使用者交流,以及更多!
     didnt_get_confirmation: 沒有收到驗證信?
     dont_have_your_security_key: 找不到您的安全金鑰?
@@ -951,16 +993,18 @@ zh-TW:
     resend_confirmation: 重新寄送確認指引
     reset_password: 重設密碼
     rules:
+      accept: 接受
+      back: 上一頁
       preamble: 這些被 %{domain} 的管管們制定以及實施。
       title: 一些基本守則。
     security: 登入資訊
     set_new_password: 設定新密碼
     setup:
       email_below_hint_html: 如果此電子郵件地址不正確,您可於此修改並接收郵件進行認證。
-      email_settings_hint_html: 請確認 e-mail 是否傳送到 %{email} 。如果不對的話,可以從帳號設定修改。
+      email_settings_hint_html: 請確認 e-mail 是否傳送至 %{email} 。如果電子郵件地址不正確的話,可以從帳號設定修改。
       title: 設定
     sign_in:
-      preamble_html: 請登入您於 <strong>%{domain}</strong> 之帳號密碼。若您的帳號託管於其他伺服器,您將無法於此登入。
+      preamble_html: 請使用您在 <strong>%{domain}</strong> 的帳號密碼登入。若您的帳號託管於其他伺服器,您將無法在此登入。
       title: 登入 %{domain}
     sign_up:
       preamble: 於此 Mastodon 伺服器擁有帳號的話,您將能跟隨聯邦宇宙網路中任何一份子,無論他們的帳號託管於何處。
@@ -969,13 +1013,13 @@ zh-TW:
       account_status: 帳號狀態
       confirming: 等待電子郵件確認完成。
       functional: 您的帳號可以正常使用了。
-      pending: 管管們正在處理您的申請,這可能需要一點時間處理。我們將在申請通過後以電子郵件方式通知您。
+      pending: 管管們正在處理您的申請,這可能需要一點時間處理。我們將於申請通過後以電子郵件方式通知您。
       redirecting_to: 您的帳號因目前重定向至 %{acct} 而被停用。
       view_strikes: 檢視針對您帳號過去的警示
     too_fast: 送出表單的速度太快跟不上,請稍後再試。
     use_security_key: 使用安全金鑰
   authorize_follow:
-    already_following: 您已經跟隨了這個使用者
+    already_following: 您已經跟隨這個使用者
     already_requested: 您早已向該帳號寄送跟隨請求
     error: 對不起,搜尋其他站點使用者出現錯誤
     follow: 跟隨
@@ -1061,11 +1105,11 @@ zh-TW:
       your_appeal_pending: 您已遞交申訴
       your_appeal_rejected: 您的申訴已被駁回
   domain_validator:
-    invalid_domain: 並非一個有效域名
+    invalid_domain: 並非一個有效網域
   errors:
     '400': 您所送出的請求無效或格式不正確。
     '403': 您沒有檢視這個頁面的權限。
-    '404': 您所尋找的網頁不存在。
+    '404': 您要找的頁面不存在。
     '406': 此頁面無法以請求的格式顯示。
     '410': 您所尋找的網頁此處已不存在。
     '422':
@@ -1091,14 +1135,14 @@ zh-TW:
     blocks: 您封鎖的使用者
     bookmarks: 書籤
     csv: CSV
-    domain_blocks: 域名封鎖
+    domain_blocks: 網域封鎖
     lists: 列表
     mutes: 您靜音的使用者
-    storage: 儲存空間大小
+    storage: 匯出檔案大小
   featured_tags:
-    add_new: 追加
+    add_new: 新增
     errors:
-      limit: 您所推薦的標籤數量已經達到上限
+      limit: 您所推薦的主題標籤數量已達上限
     hint_html: "<strong>推薦主題標籤是什麼?</strong> 這些主題標籤將顯示於您的公開個人檔案頁,訪客可以藉此閱覽您標示了這些標籤的嘟文,拿來展示創意作品或者長期更新的專案很好用唷!"
   filters:
     contexts:
@@ -1139,28 +1183,24 @@ zh-TW:
       index:
         hint: 此過濾器會套用至所選之各別嘟文,不管它們有無符合其他條件。您可以從網頁介面中將更多嘟文加入至此過濾器。
         title: 已過濾之嘟文
-  footer:
-    trending_now: 現正熱門
   generic:
     all: 全部
     all_items_on_page_selected_html:
       other: 已選取此頁面上 <strong>%{count}</strong> 個項目。
     all_matching_items_selected_html:
       other: 已選取符合您搜尋的 <strong>%{count}</strong> 個項目。
-    changes_saved_msg: 已成功儲存修改!
+    changes_saved_msg: 已成功儲存變更!
     copy: 複製
     delete: 刪除
     deselect: 取消選擇全部
     none: 無
     order_by: 排序
-    save_changes: 儲存修改
+    save_changes: 儲存變更
     select_all_matching_items:
       other: 選取 %{count} 個符合您搜尋的項目。
     today: 今天
     validation_errors:
       other: 唔…這是什麼鳥?請檢查以下 %{count} 項錯誤
-  html_validator:
-    invalid_markup: 含有無效的 HTML 語法:%{error}
   imports:
     errors:
       invalid_csv_file: 無效的 CSV 檔案。錯誤訊息:%{error}
@@ -1171,12 +1211,12 @@ zh-TW:
       overwrite: 覆蓋
       overwrite_long: 以新的紀錄覆蓋目前紀錄
     preface: 您可以在此匯入您在其他伺服器所匯出的資料檔,包括跟隨的使用者、封鎖的使用者名單。
-    success: 資料檔上傳成功,正在匯入,請稍候
+    success: 資料上傳成功,正在匯入,請稍候
     types:
-      blocking: 您封鎖的使用者名單
+      blocking: 您封鎖的使用者列表
       bookmarks: 書籤
-      domain_blocking: 域名封鎖名單
-      following: 您跟隨的使用者名單
+      domain_blocking: 網域封鎖列表
+      following: 您跟隨的使用者列表
       muting: 您靜音的使用者名單
     upload: 上傳
   invites:
@@ -1202,7 +1242,7 @@ zh-TW:
     title: 邀請使用者
   lists:
     errors:
-      limit: 您所建立的列表數量已經達到上限
+      limit: 您所建立的列表數量已達上限
   login_activities:
     authentication_methods:
       otp: 兩階段驗證應用程式
@@ -1235,7 +1275,7 @@ zh-TW:
     incoming_migrations_html: 要從其他帳號移動到此帳號的話,首先您必須<a href="%{path}">建立帳號別名</a>。
     moved_msg: 您的帳號正被重新導向到 %{acct},您的跟隨者也會同步轉移至該帳號。
     not_redirecting: 您的帳號目前尚未重新導向到任何其他帳號。
-    on_cooldown: 您最近已轉移過您的帳號。此功能將在 %{count} 天後可再度使用。
+    on_cooldown: 您最近已轉移過您的帳號。此功能將於 %{count} 天後可再度使用。
     past_migrations: 以往的轉移紀錄
     proceed_with_move: 移動跟隨者
     redirected_msg: 您的帳號現在指向 %{acct}
@@ -1246,7 +1286,7 @@ zh-TW:
       before: 在進行下一步驟之前,請詳細閱讀以下説明:
       cooldown: 在轉移帳號後會有一段等待時間,在等待時間內您將無法再次轉移
       disabled_account: 之後您的目前帳號將完全無法使用。但您可以存取資料匯出與重新啟用。
-      followers: 此動作將會把目前帳號的所有跟隨者轉移至新帳號
+      followers: 此動作將會將目前帳號的所有跟隨者轉移至新帳號
       only_redirect_html: 或者,您也可以<a href="%{path}">僅在您的個人檔案中設定重新導向</a>。
       other_data: 其他資料並不會自動轉移
       redirect: 您目前的帳號將會在個人檔案頁面新增重新導向公告,並會被排除在搜尋結果之外
@@ -1261,7 +1301,7 @@ zh-TW:
   notification_mailer:
     admin:
       report:
-        subject: "%{name} 送出了一則檢舉報告"
+        subject: "%{name} 已送出一則檢舉報告"
       sign_up:
         subject: "%{name} 已進行註冊"
     favourite:
@@ -1291,7 +1331,7 @@ zh-TW:
     status:
       subject: "%{name} 剛剛嘟文"
     update:
-      subject: "%{name} 編輯了嘟文"
+      subject: "%{name} 已編輯嘟文"
   notifications:
     email_events: 電子郵件通知設定
     email_events_hint: 選取您想接收通知的事件:
@@ -1343,8 +1383,12 @@ zh-TW:
       unrecognized_emoji: 並非一個可識別的 emoji
   relationships:
     activity: 帳號動態
+    confirm_follow_selected_followers: 您確定要跟隨選取的跟隨者嗎?
+    confirm_remove_selected_followers: 您確定要移除選取的跟隨者嗎?
+    confirm_remove_selected_follows: 您確定要取消跟隨這些選取的使用者嗎?
     dormant: 潛水中
-    follow_selected_followers: 跟隨所選的跟隨者
+    follow_failure: 無法跟隨某些所選取的帳號。
+    follow_selected_followers: 跟隨選取的跟隨者
     followers: 跟隨者
     following: 跟隨中
     invited: 已邀請
@@ -1354,9 +1398,9 @@ zh-TW:
     mutual: 跟隨彼此
     primary: 主要
     relationship: 關係
-    remove_selected_domains: 從所選網域中移除所有跟隨者
-    remove_selected_followers: 移除所選的跟隨者
-    remove_selected_follows: 取消跟隨所選使用者
+    remove_selected_domains: 從所選取網域中移除所有跟隨者
+    remove_selected_followers: 移除選取的跟隨者
+    remove_selected_follows: 取消跟隨選取的使用者
     status: 帳號狀態
   remote_follow:
     missing_resource: 無法找到資源
@@ -1370,7 +1414,7 @@ zh-TW:
       tag: '帶有 #%{hashtag} 之公開嘟文'
   scheduled_statuses:
     over_daily_limit: 您已經超過了本日排定發嘟的限額 (%{limit})
-    over_total_limit: 您已經超過了排定發嘟的限額 (%{limit})
+    over_total_limit: 您已經超過排程發嘟的限額 (%{limit})
     too_soon: 嘟文不可以改變過去哦,只能預定未來 (咦)
   sessions:
     activity: 最近活動
@@ -1383,6 +1427,7 @@ zh-TW:
       electron: Electron 瀏覽器
       firefox: Firefox 瀏覽器
       generic: 未知的瀏覽器
+      huawei_browser: 華為瀏覽器
       ie: Internet Explorer 瀏覽器
       micro_messenger: 微信
       nokia: Nokia S40 Ovi 瀏覽器
@@ -1392,6 +1437,7 @@ zh-TW:
       qq: QQ 瀏覽器
       safari: Safari 瀏覽器
       uc_browser: UC 瀏覽器
+      unknown_browser: 未知的瀏覽器
       weibo: 新浪微博
     current_session: 目前的 session
     description: "%{platform} 上的 %{browser}"
@@ -1404,9 +1450,10 @@ zh-TW:
       chrome_os: ChromeOS
       firefox_os: Firefox OS
       ios: iOS
+      kai_os: KaiOS
       linux: Linux
       mac: Mac
-      other: 不明平台
+      unknown_platform: 未知的平台
       windows: Windows
       windows_mobile: Windows Mobile
       windows_phone: Windows Phone
@@ -1418,7 +1465,7 @@ zh-TW:
     account: 帳號
     account_settings: 帳號設定
     aliases: 帳號別名
-    appearance: 外觀設定
+    appearance: 外觀
     authorized_apps: 已授權應用程式
     back: 回到 Mastodon
     delete: 刪除帳號
@@ -1455,10 +1502,10 @@ zh-TW:
     errors:
       in_reply_not_found: 您嘗試回覆的嘟文看起來不存在。
     open_in_web: 以網頁開啟
-    over_character_limit: 超過了 %{max} 字的限制
+    over_character_limit: 已超過 %{max} 字的限制
     pin_errors:
       direct: 無法釘選只有僅提及使用者可見之嘟文
-      limit: 您所置頂的嘟文數量已經達到上限
+      limit: 釘選嘟文的數量已達上限
       ownership: 不能釘選他人的嘟文
       reblog: 不能釘選轉嘟
     poll:
@@ -1476,7 +1523,7 @@ zh-TW:
     visibilities:
       direct: 私訊
       private: 僅限跟隨者
-      private_long: 只有跟隨您的人能看到
+      private_long: 只有跟隨者能看到
       public: 公開
       public_long: 所有人都能看到
       unlisted: 不在公開時間軸顯示
@@ -1486,8 +1533,8 @@ zh-TW:
     enabled_hint: 一旦達到指定的保存期限,就會自動刪除您的嘟文,除非該嘟文符合下列例外
     exceptions: 例外
     explanation: 因為刪除嘟文是耗費資源的操作,當伺服器不那麼忙碌時才會慢慢完成。因此,您的嘟文會在到達保存期限後一段時間才會被刪除。
-    ignore_favs: 忽略最愛
-    ignore_reblogs: 忽略轉嘟
+    ignore_favs: 忽略最愛數
+    ignore_reblogs: 忽略轉嘟數
     interaction_exceptions: 基於互動的例外規則
     interaction_exceptions_explanation: 請注意嘟文是無法保證被刪除的,如果在一次處理過後嘟文低於最愛或轉嘟的門檻。
     keep_direct: 保留私訊
@@ -1536,13 +1583,13 @@ zh-TW:
       time: "%H:%M"
   two_factor_authentication:
     add: 新增
-    disable: 停用
+    disable: 停用兩階段驗證
     disabled_success: 已成功啟用兩階段驗證
     edit: 編輯
     enabled: 兩階段認證已啟用
     enabled_success: 已成功啟用兩階段認證
     generate_recovery_codes: 產生備用驗證碼
-    lost_recovery_codes: 讓您可以在遺失手機時,使用備用驗證碼登入。若您遺失了備用驗證碼,可以在這裏產生一批新的,舊有的備用驗證碼將會失效。
+    lost_recovery_codes: 讓您可以在遺失手機時,使用備用驗證碼登入。若您已遺失備用驗證碼,可於此產生一批新的,舊有的備用驗證碼將會失效。
     methods: 兩步驟方式
     otp: 驗證應用程式
     recovery_codes: 備份備用驗證碼
@@ -1577,10 +1624,10 @@ zh-TW:
         spam: 垃圾訊息
         violation: 該內容違反以下社群規章
       explanation:
-        delete_statuses: 您的某些嘟文被發現違反了一項或多項社群準則,隨後已被 %{instance} 的管理員刪除。
+        delete_statuses: 您的某些嘟文被發現已違反一項或多項社群準則,隨後已被 %{instance} 的管理員刪除。
         disable: 您無法繼續使用您的帳號,但您的個人頁面及其他資料內容保持不變。您可以要求一份您的資料備份,帳號異動設定,或是刪除帳號。
         mark_statuses_as_sensitive: 您的部份嘟文已被 %{instance} 的管理員標記為敏感內容。這代表了人們必須在顯示預覽前點擊嘟文中的媒體。您可以在將來嘟文時自己將媒體標記為敏感內容。
-        sensitive: 由此刻起,您所有上傳的媒體檔案將被標記為敏感內容,並且隱藏於點擊警告之後。
+        sensitive: 您之後上傳的所有媒體檔案會被標為敏感內容,並且被隱藏起來,須點擊警告按鈕才會顯示。
         silence: 您仍然可以使用您的帳號,但僅有已跟隨您的人才能看到您在此伺服器的嘟文,您也可能會從各式探索功能中被排除。但其他人仍可手動跟隨您。
         suspend: 您將不能使用您的帳號,您的個人檔案頁面及其他資料將不再能被存取。您仍可於約 30 日內資料被完全刪除前要求下載您的資料,但我們仍會保留一部份基本資料,以防止有人規避停權處罰。
       reason: 原因:
@@ -1600,7 +1647,7 @@ zh-TW:
         none: 警告
         sensitive: 帳號已標記為敏感內容
         silence: 帳號已被限制
-        suspend: 帳號己被停用
+        suspend: 帳號己被停權
     welcome:
       edit_profile_action: 設定個人檔案
       edit_profile_step: 您可以設定您的個人檔案,包括上傳大頭貼、變更顯示名稱等等。您也可以選擇在新的跟隨者跟隨前,先對他們進行審核。
@@ -1608,17 +1655,18 @@ zh-TW:
       final_action: 開始嘟嘟
       final_step: '開始嘟嘟吧!即使您現在沒有跟隨者,其他人仍然能在本站時間軸、主題標籤等地方,看到您的公開嘟文。試著用 #introductions 這個主題標籤介紹一下自己吧。'
       full_handle: 您的完整帳號名稱
-      full_handle_hint: 您需要把這告訴您的朋友們,這樣他們就能從另一個伺服器向您發送訊息或著跟隨您。
+      full_handle_hint: 您需要將這告訴您的朋友們,這樣他們就能從另一個伺服器向您發送訊息或跟隨您。
       subject: 歡迎來到 Mastodon
       title: "%{name} 誠摯歡迎您的加入!"
   users:
     follow_limit_reached: 您無法跟隨多於 %{limit} 個人
+    go_to_sso_account_settings: 前往您的身分提供商 (identity provider) 之帳號設定
     invalid_otp_token: 兩階段認證碼不正確
     otp_lost_help_html: 如果您無法訪問這兩者,可以透過 %{email} 與我們聯繫
     seamless_external_login: 由於您是由外部系統登入,所以不能設定密碼與電子郵件。
     signed_in_as: 目前登入的帳號:
   verification:
-    explanation_html: 您在 Mastodon 個人檔案頁上所列出的連結,可以用此方式<strong>驗證您確實掌控該連結網頁的內容</strong>。您可以在連結的網頁上加上一個連回 Mastodon 個人檔案頁面的連結,該連結的原始碼 <strong>必須</strong>包含<code>rel="me"</code>屬性。連結的顯示文字可自由發揮,以下為範例:
+    explanation_html: 您可以<strong>透過以個人檔案頁上列出連結的方式,驗證您確實掌控該連結網頁的內容</strong>。為此,您可以在連結的網頁中加入一個連回 Mastodon 個人檔案頁面的連結。新增此連結之後,您可能要回到這裡並重新儲存您的個人檔案以使該驗證生效。該連結的原始碼 <strong>必須</strong>包含<code>rel="me"</code>屬性。連結的顯示文字則可自由發揮,以下為範例:
     verification: 驗證連結
   webauthn_credentials:
     add: 新增安全金鑰
@@ -1627,7 +1675,7 @@ zh-TW:
       success: 您已成功加入安全金鑰。
     delete: 刪除
     delete_confirmation: 您確定要移除這把安全金鑰嗎?
-    description_html: 如果您啟用<strong>安全金鑰驗證</strong>的話,您將在登入時需要使用其中一把安全金鑰。
+    description_html: 如果您啟用<strong>安全金鑰驗證</strong>的話,您將於登入時需要使用其中一把安全金鑰。
     destroy:
       error: 移除安全金鑰時出現了問題。請再試一次。
       success: 您已成功將安全金鑰移除。
diff --git a/config/puma.rb b/config/puma.rb
index e59295445..c4e2b0b85 100644
--- a/config/puma.rb
+++ b/config/puma.rb
@@ -22,3 +22,5 @@ on_worker_boot do
 end
 
 plugin :tmp_restart
+
+set_remote_address(proxy_protocol: :v1) if ENV['PROXY_PROTO_V1'] == 'true'
diff --git a/config/routes.rb b/config/routes.rb
index 8639f0ef5..099933116 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -8,6 +8,7 @@ Rails.application.routes.draw do
   # have alternative format representations requiring separate controllers
   web_app_paths = %w(
     /getting-started
+    /getting-started-misc
     /keyboard-shortcuts
     /home
     /public
@@ -27,6 +28,7 @@ Rails.application.routes.draw do
     /blocks
     /domain_blocks
     /mutes
+    /followed_tags
     /statuses/(*any)
   ).freeze
 
@@ -110,6 +112,8 @@ Rails.application.routes.draw do
 
   resource :inbox, only: [:create], module: :activitypub
 
+  get '/:encoded_at(*path)', to: redirect("/@%{path}"), constraints: { encoded_at: /%40/ }
+
   constraints(username: /[^@\/.]+/) do
     get '/@:username', to: 'accounts#show', as: :short_account
     get '/@:username/with_replies', to: 'accounts#show', as: :short_account_with_replies
@@ -220,6 +224,7 @@ Rails.application.routes.draw do
   resource :statuses_cleanup, controller: :statuses_cleanup, only: [:show, :update]
 
   get '/media_proxy/:id/(*any)', to: 'media_proxy#show', as: :media_proxy, format: false
+  get '/backups/:id/download', to: 'backups#download', as: :download_backup, format: false
 
   resource :authorize_interaction, only: [:show, :create]
   resource :share, only: [:show, :create]
@@ -228,7 +233,7 @@ Rails.application.routes.draw do
     get '/dashboard', to: 'dashboard#index'
 
     resources :domain_allows, only: [:new, :create, :show, :destroy]
-    resources :domain_blocks, only: [:new, :create, :show, :destroy, :update, :edit] do
+    resources :domain_blocks, only: [:new, :create, :destroy, :update, :edit] do
       collection do
         post :batch
       end
@@ -314,7 +319,11 @@ Rails.application.routes.draw do
     end
 
     resources :reports, only: [:index, :show] do
-      resources :actions, only: [:create], controller: 'reports/actions'
+      resources :actions, only: [:create], controller: 'reports/actions' do
+        collection do
+          post :preview
+        end
+      end
 
       member do
         post :assign_to_self
@@ -470,7 +479,9 @@ Rails.application.routes.draw do
         resources :list, only: :show
       end
 
-      resources :streaming, only: [:index]
+      get '/streaming', to: 'streaming#index'
+      get '/streaming/(*any)', to: 'streaming#index'
+
       resources :custom_emojis, only: [:index]
       resources :suggestions, only: [:index, :destroy]
       resources :scheduled_statuses, only: [:index, :show, :update, :destroy]
@@ -542,6 +553,7 @@ Rails.application.routes.draw do
         resources :domain_blocks, only: [:index], controller: 'instances/domain_blocks'
         resource :privacy_policy, only: [:show], controller: 'instances/privacy_policies'
         resource :extended_description, only: [:show], controller: 'instances/extended_descriptions'
+        resource :translation_languages, only: [:show], controller: 'instances/translation_languages'
         resource :activity, only: [:show], controller: 'instances/activity'
       end
 
diff --git a/config/settings.yml b/config/settings.yml
index 1f44fd308..65eee7516 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -12,54 +12,17 @@ defaults: &defaults
   registrations_mode: 'open'
   profile_directory: true
   closed_registrations_message: ''
-  open_deletion: true
   timeline_preview: false
-  min_invite_role: 'admin'
   show_staff_badge: true
-  default_sensitive: false
-  unfollow_modal: false
-  boost_modal: false
-  favourite_modal: false
-  delete_modal: true
-  auto_play_gif: false
-  display_media: 'default'
-  expand_spoilers: false
   preview_sensitive_media: false
-  reduce_motion: false
-  disable_swiping: false
-  show_application: false
-  system_font_ui: false
-  system_emoji_font: false
   noindex: false
-  hide_followers_count: false
   flavour: 'glitch'
   skin: 'default'
-  aggregate_reblogs: true
-  advanced_layout: false
-  use_blurhash: true
-  use_pending_items: false
   trends: true
+  trends_as_landing_page: true
   trendable_by_default: false
   trending_status_cw: true
-  crop_images: true
-  notification_emails:
-    follow: true
-    reblog: false
-    favourite: false
-    mention: true
-    follow_request: true
-    digest: true
-    report: true
-    pending_account: true
-    trending_tag: true
-    trending_link: false
-    trending_status: false
-    appeal: true
-  always_send_emails: false
-  interactions:
-    must_be_follower: false
-    must_be_following: false
-    must_be_following_dm: false
+  hide_followers_count: false
   reserved_usernames:
     - admin
     - support
diff --git a/config/sidekiq.yml b/config/sidekiq.yml
index b8739aab3..2278329a5 100644
--- a/config/sidekiq.yml
+++ b/config/sidekiq.yml
@@ -9,52 +9,52 @@
   - [scheduler]
 :scheduler:
   :listened_queues_only: true
-:schedule:
-  scheduled_statuses_scheduler:
-    every: '5m'
-    class: Scheduler::ScheduledStatusesScheduler
-    queue: scheduler
-  trends_refresh_scheduler:
-    every: '5m'
-    class: Scheduler::Trends::RefreshScheduler
-    queue: scheduler
-  trends_review_notifications_scheduler:
-    every: '6h'
-    class: Scheduler::Trends::ReviewNotificationsScheduler
-    queue: scheduler
-  indexing_scheduler:
-    every: '5m'
-    class: Scheduler::IndexingScheduler
-    queue: scheduler
-  vacuum_scheduler:
-    cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *'
-    class: Scheduler::VacuumScheduler
-    queue: scheduler
-  follow_recommendations_scheduler:
-    cron: '<%= Random.rand(0..59) %> <%= Random.rand(6..9) %> * * *'
-    class: Scheduler::FollowRecommendationsScheduler
-    queue: scheduler
-  user_cleanup_scheduler:
-    cron: '<%= Random.rand(0..59) %> <%= Random.rand(4..6) %> * * *'
-    class: Scheduler::UserCleanupScheduler
-    queue: scheduler
-  ip_cleanup_scheduler:
-    cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *'
-    class: Scheduler::IpCleanupScheduler
-    queue: scheduler
-  pghero_scheduler:
-    cron: '0 0 * * *'
-    class: Scheduler::PgheroScheduler
-    queue: scheduler
-  instance_refresh_scheduler:
-    cron: '0 * * * *'
-    class: Scheduler::InstanceRefreshScheduler
-    queue: scheduler
-  accounts_statuses_cleanup_scheduler:
-    interval: 1 minute
-    class: Scheduler::AccountsStatusesCleanupScheduler
-    queue: scheduler
-  suspended_user_cleanup_scheduler:
-    interval: 1 minute
-    class: Scheduler::SuspendedUserCleanupScheduler
-    queue: scheduler
+  :schedule:
+    scheduled_statuses_scheduler:
+      every: '5m'
+      class: Scheduler::ScheduledStatusesScheduler
+      queue: scheduler
+    trends_refresh_scheduler:
+      every: '5m'
+      class: Scheduler::Trends::RefreshScheduler
+      queue: scheduler
+    trends_review_notifications_scheduler:
+      every: '6h'
+      class: Scheduler::Trends::ReviewNotificationsScheduler
+      queue: scheduler
+    indexing_scheduler:
+      every: '5m'
+      class: Scheduler::IndexingScheduler
+      queue: scheduler
+    vacuum_scheduler:
+      cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *'
+      class: Scheduler::VacuumScheduler
+      queue: scheduler
+    follow_recommendations_scheduler:
+      cron: '<%= Random.rand(0..59) %> <%= Random.rand(6..9) %> * * *'
+      class: Scheduler::FollowRecommendationsScheduler
+      queue: scheduler
+    user_cleanup_scheduler:
+      cron: '<%= Random.rand(0..59) %> <%= Random.rand(4..6) %> * * *'
+      class: Scheduler::UserCleanupScheduler
+      queue: scheduler
+    ip_cleanup_scheduler:
+      cron: '<%= Random.rand(0..59) %> <%= Random.rand(3..5) %> * * *'
+      class: Scheduler::IpCleanupScheduler
+      queue: scheduler
+    pghero_scheduler:
+      cron: '0 0 * * *'
+      class: Scheduler::PgheroScheduler
+      queue: scheduler
+    instance_refresh_scheduler:
+      cron: '0 * * * *'
+      class: Scheduler::InstanceRefreshScheduler
+      queue: scheduler
+    accounts_statuses_cleanup_scheduler:
+      interval: 1 minute
+      class: Scheduler::AccountsStatusesCleanupScheduler
+      queue: scheduler
+    suspended_user_cleanup_scheduler:
+      interval: 1 minute
+      class: Scheduler::SuspendedUserCleanupScheduler
+      queue: scheduler
diff --git a/config/webpack/generateLocalePacks.js b/config/webpack/generateLocalePacks.js
index b1b818159..fedf0c7a1 100644
--- a/config/webpack/generateLocalePacks.js
+++ b/config/webpack/generateLocalePacks.js
@@ -13,7 +13,7 @@ const { existsSync, readdirSync, writeFileSync } = require('fs');
 const { join, resolve } = require('path');
 const rimraf = require('rimraf');
 const mkdirp = require('mkdirp');
-const { flavours } = require('./configuration.js');
+const { flavours } = require('./configuration');
 
 module.exports = Object.keys(flavours).reduce(function (map, entry) {
   const flavour = flavours[entry];
@@ -43,13 +43,13 @@ module.exports = Object.keys(flavours).reduce(function (map, entry) {
       // first try react-intl
       `node_modules/react-intl/locale-data/${baseLocale}.js`,
       // then check locales/locale-data
-      `app/javascript/locales/locale-data/${baseLocale}.js`,
+      `app/javascript/mastodon/locales/locale-data/${baseLocale}.js`,
       // fall back to English (this is what react-intl does anyway)
       'node_modules/react-intl/locale-data/en.js',
     ].filter(
-      filename => existsSync(filename)
+      filename => existsSync(filename),
     ).map(
-      filename => filename.replace(/(?:node_modules|app\/javascript)\//, '')
+      filename => filename.replace(/(?:node_modules|app\/javascript)\//, ''),
     )[0];
     const localeContent = `//
 // locales/${entry}/${locale}.js
diff --git a/config/webpack/rules/babel.js b/config/webpack/rules/babel.js
index 4d25748ee..8b6205a5c 100644
--- a/config/webpack/rules/babel.js
+++ b/config/webpack/rules/babel.js
@@ -2,7 +2,7 @@ const { join, resolve } = require('path');
 const { env, settings } = require('../configuration');
 
 module.exports = {
-  test: /\.(js|jsx|mjs)$/,
+  test: /\.(js|jsx|mjs|ts|tsx)$/,
   include: [
     settings.source_path,
     ...settings.resolved_paths,
diff --git a/config/webpack/shared.js b/config/webpack/shared.js
index bbf9f51f1..405858d0c 100644
--- a/config/webpack/shared.js
+++ b/config/webpack/shared.js
@@ -81,7 +81,7 @@ module.exports = {
           },
           minChunks: 2,
           minSize: 0,
-          test: /^(?!.*[\\\/]node_modules[\\\/]react-intl[\\\/]).+$/,
+          test: /^(?!.*[\\/]node_modules[\\/]react-intl[\\/]).+$/,
         },
       },
     },
diff --git a/config/webpacker.yml b/config/webpacker.yml
index 9accd6152..f5c93a684 100644
--- a/config/webpacker.yml
+++ b/config/webpacker.yml
@@ -36,6 +36,9 @@ default: &default
   extensions:
     - .mjs
     - .js
+    - .jsx
+    - .ts
+    - .tsx
     - .sass
     - .scss
     - .css
@@ -75,7 +78,7 @@ development:
 test:
   <<: *default
 
-  # CircleCI precompiles packs prior to running the tests.
+  # CI precompiles packs prior to running the tests.
   # Also avoids race conditions in parallel_tests.
   compile: false
 
diff --git a/db/migrate/20160305115639_add_devise_to_users.rb b/db/migrate/20160305115639_add_devise_to_users.rb
index 0e12e6053..fa1e521b2 100644
--- a/db/migrate/20160305115639_add_devise_to_users.rb
+++ b/db/migrate/20160305115639_add_devise_to_users.rb
@@ -2,7 +2,7 @@ class AddDeviseToUsers < ActiveRecord::Migration[4.2]
   def self.up
     change_table(:users) do |t|
       ## Database authenticatable
-      t.string :encrypted_password, null: false, default: ""
+      t.string :encrypted_password, null: false, default: ''
 
       ## Recoverable
       t.string   :reset_password_token
diff --git a/db/migrate/20161006213403_rails_settings_migration.rb b/db/migrate/20161006213403_rails_settings_migration.rb
index 9d565cb5c..02932610c 100644
--- a/db/migrate/20161006213403_rails_settings_migration.rb
+++ b/db/migrate/20161006213403_rails_settings_migration.rb
@@ -1,8 +1,8 @@
 MIGRATION_BASE_CLASS = if ActiveRecord::VERSION::MAJOR >= 5
-  ActiveRecord::Migration[5.0]
-else
-  ActiveRecord::Migration[4.2]
-end
+                         ActiveRecord::Migration[5.0]
+                       else
+                         ActiveRecord::Migration[4.2]
+                       end
 
 class RailsSettingsMigration < MIGRATION_BASE_CLASS
   def self.up
@@ -12,7 +12,7 @@ class RailsSettingsMigration < MIGRATION_BASE_CLASS
       t.references :target, null: false, polymorphic: true, index: { name: 'index_settings_on_target_type_and_target_id' }
       t.timestamps null: true
     end
-    add_index :settings, [ :target_type, :target_id, :var ], unique: true
+    add_index :settings, [:target_type, :target_id, :var], unique: true
   end
 
   def self.down
diff --git a/db/migrate/20161122163057_remove_unneeded_indexes.rb b/db/migrate/20161122163057_remove_unneeded_indexes.rb
index 3832b878d..12cc9c5b2 100644
--- a/db/migrate/20161122163057_remove_unneeded_indexes.rb
+++ b/db/migrate/20161122163057_remove_unneeded_indexes.rb
@@ -1,7 +1,7 @@
 class RemoveUnneededIndexes < ActiveRecord::Migration[5.0]
   def change
-    remove_index :notifications, name: "index_notifications_on_account_id"
-    remove_index :settings, name: "index_settings_on_target_type_and_target_id"
-    remove_index :statuses_tags, name: "index_statuses_tags_on_tag_id"
+    remove_index :notifications, name: 'index_notifications_on_account_id'
+    remove_index :settings, name: 'index_settings_on_target_type_and_target_id'
+    remove_index :statuses_tags, name: 'index_statuses_tags_on_tag_id'
   end
 end
diff --git a/db/migrate/20170125145934_add_spoiler_text_to_statuses.rb b/db/migrate/20170125145934_add_spoiler_text_to_statuses.rb
index 2c43210ba..39cd41c00 100644
--- a/db/migrate/20170125145934_add_spoiler_text_to_statuses.rb
+++ b/db/migrate/20170125145934_add_spoiler_text_to_statuses.rb
@@ -1,5 +1,5 @@
 class AddSpoilerTextToStatuses < ActiveRecord::Migration[5.0]
   def change
-    add_column :statuses, :spoiler_text, :text, default: "", null: false
+    add_column :statuses, :spoiler_text, :text, default: '', null: false
   end
 end
diff --git a/db/migrate/20170610000000_add_statuses_index_on_account_id_id.rb b/db/migrate/20170610000000_add_statuses_index_on_account_id_id.rb
index 3e74346a8..86e425559 100644
--- a/db/migrate/20170610000000_add_statuses_index_on_account_id_id.rb
+++ b/db/migrate/20170610000000_add_statuses_index_on_account_id_id.rb
@@ -6,7 +6,7 @@ class AddStatusesIndexOnAccountIdId < ActiveRecord::Migration[5.1]
     # of an account to show them in his status page is one of the most
     # significant examples.
     # Add this index to improve the performance in such cases.
-    add_index 'statuses', ['account_id', 'id'], algorithm: :concurrently, name: 'index_statuses_on_account_id_id'
+    add_index 'statuses', %w(account_id id), algorithm: :concurrently, name: 'index_statuses_on_account_id_id'
 
     remove_index 'statuses', algorithm: :concurrently, column: 'account_id', name: 'index_statuses_on_account_id'
   end
diff --git a/db/migrate/20170716191202_add_hide_notifications_to_mute.rb b/db/migrate/20170716191202_add_hide_notifications_to_mute.rb
index de7d2a4a2..a498396b7 100644
--- a/db/migrate/20170716191202_add_hide_notifications_to_mute.rb
+++ b/db/migrate/20170716191202_add_hide_notifications_to_mute.rb
@@ -1,5 +1,15 @@
+require Rails.root.join('lib', 'mastodon', 'migration_helpers')
+
 class AddHideNotificationsToMute < ActiveRecord::Migration[5.1]
-  def change
-    add_column :mutes, :hide_notifications, :boolean, default: false, null: false
+  include Mastodon::MigrationHelpers
+
+  disable_ddl_transaction!
+
+  def up
+    add_column_with_default :mutes, :hide_notifications, :boolean, default: true, allow_null: false
+  end
+
+  def down
+    remove_column :mutes, :hide_notifications
   end
 end
diff --git a/db/migrate/20170914032032_default_existing_mutes_to_hiding_notifications.rb b/db/migrate/20170914032032_default_existing_mutes_to_hiding_notifications.rb
index 8e6cac455..d9866dfde 100644
--- a/db/migrate/20170914032032_default_existing_mutes_to_hiding_notifications.rb
+++ b/db/migrate/20170914032032_default_existing_mutes_to_hiding_notifications.rb
@@ -1,8 +1,13 @@
+# frozen_string_literal: true
+
+# This migration is glitch-soc-only because mutes were originally developed in
+# glitch-soc and the default value changed when submitting the code upstream.
+
+# This migration originally changed existing values to `true`, but this has
+# been dropped as to not cause issues when migrating from upstream.
+
 class DefaultExistingMutesToHidingNotifications < ActiveRecord::Migration[5.1]
   def up
     change_column_default :mutes, :hide_notifications, from: false, to: true
-
-    # Unfortunately if this is applied sometime after the one to add the table we lose some data, so this is irreversible.
-    Mute.update_all(hide_notifications: true)
   end
 end
diff --git a/db/migrate/20170918125918_ids_to_bigints.rb b/db/migrate/20170918125918_ids_to_bigints.rb
index bf875e4e5..e3fc34a51 100644
--- a/db/migrate/20170918125918_ids_to_bigints.rb
+++ b/db/migrate/20170918125918_ids_to_bigints.rb
@@ -1,7 +1,9 @@
-require Rails.root.join('lib', 'mastodon', 'migration_helpers')
+require_relative '../../lib/mastodon/migration_helpers'
+require_relative '../../lib/mastodon/migration_warning'
 
 class IdsToBigints < ActiveRecord::Migration[5.1]
   include Mastodon::MigrationHelpers
+  include Mastodon::MigrationWarning
 
   disable_ddl_transaction!
 
@@ -69,24 +71,12 @@ class IdsToBigints < ActiveRecord::Migration[5.1]
     ]
     included_columns << [:deprecated_preview_cards, :id] if table_exists?(:deprecated_preview_cards)
 
-    # Print out a warning that this will probably take a while.
-    if $stdout.isatty
-      say ''
-      say 'WARNING: This migration may take a *long* time for large instances'
-      say 'It will *not* lock tables for any significant time, but it may run'
-      say 'for a very long time. We will pause for 10 seconds to allow you to'
-      say 'interrupt this migration if you are not ready.'
-      say ''
-      say 'This migration has some sections that can be safely interrupted'
-      say 'and restarted later, and will tell you when those are occurring.'
-      say ''
-      say 'For more information, see https://github.com/mastodon/mastodon/pull/5088'
+    migration_duration_warning(<<~EXPLANATION)
+      This migration has some sections that can be safely interrupted
+      and restarted later, and will tell you when those are occurring.
 
-      10.downto(1) do |i|
-        say "Continuing in #{i} second#{i == 1 ? '' : 's'}...", true
-        sleep 1
-      end
-    end
+      For more information, see https://github.com/mastodon/mastodon/pull/5088
+    EXPLANATION
 
     tables = included_columns.map(&:first).uniq
     table_sizes = {}
diff --git a/db/migrate/20170920032311_fix_reblogs_in_feeds.rb b/db/migrate/20170920032311_fix_reblogs_in_feeds.rb
index 4ab68e8f3..7e2db0ff3 100644
--- a/db/migrate/20170920032311_fix_reblogs_in_feeds.rb
+++ b/db/migrate/20170920032311_fix_reblogs_in_feeds.rb
@@ -1,6 +1,6 @@
 class FixReblogsInFeeds < ActiveRecord::Migration[5.1]
   def up
-    redis = Redis.current
+    redis = RedisConfiguration.pool.checkout
     fm = FeedManager.instance
 
     # Old scheme:
diff --git a/db/migrate/20170927215609_add_description_to_media_attachments.rb b/db/migrate/20170927215609_add_description_to_media_attachments.rb
index db8d76566..9c3312373 100644
--- a/db/migrate/20170927215609_add_description_to_media_attachments.rb
+++ b/db/migrate/20170927215609_add_description_to_media_attachments.rb
@@ -1,4 +1,4 @@
-class AddDescriptionToMediaAttachments < ActiveRecord::Migration[5.1]
+class AddDescriptionToMediaAttachments < ActiveRecord::Migration[5.2]
   def change
     add_column :media_attachments, :description, :text
   end
diff --git a/db/migrate/20170928082043_create_email_domain_blocks.rb b/db/migrate/20170928082043_create_email_domain_blocks.rb
index 1f0fb7587..2baed54ef 100644
--- a/db/migrate/20170928082043_create_email_domain_blocks.rb
+++ b/db/migrate/20170928082043_create_email_domain_blocks.rb
@@ -1,4 +1,4 @@
-class CreateEmailDomainBlocks < ActiveRecord::Migration[5.1]
+class CreateEmailDomainBlocks < ActiveRecord::Migration[5.2]
   def change
     create_table :email_domain_blocks do |t|
       t.string :domain, null: false
diff --git a/db/migrate/20171005102658_create_account_moderation_notes.rb b/db/migrate/20171005102658_create_account_moderation_notes.rb
index 010b94586..afa2f5f25 100644
--- a/db/migrate/20171005102658_create_account_moderation_notes.rb
+++ b/db/migrate/20171005102658_create_account_moderation_notes.rb
@@ -1,4 +1,4 @@
-class CreateAccountModerationNotes < ActiveRecord::Migration[5.1]
+class CreateAccountModerationNotes < ActiveRecord::Migration[5.2]
   def change
     create_table :account_moderation_notes do |t|
       t.text :content, null: false
diff --git a/db/migrate/20171005171936_add_disabled_to_custom_emojis.rb b/db/migrate/20171005171936_add_disabled_to_custom_emojis.rb
index 067a7bee0..7cf007ae9 100644
--- a/db/migrate/20171005171936_add_disabled_to_custom_emojis.rb
+++ b/db/migrate/20171005171936_add_disabled_to_custom_emojis.rb
@@ -1,6 +1,6 @@
 require Rails.root.join('lib', 'mastodon', 'migration_helpers')
 
-class AddDisabledToCustomEmojis < ActiveRecord::Migration[5.1]
+class AddDisabledToCustomEmojis < ActiveRecord::Migration[5.2]
   include Mastodon::MigrationHelpers
 
   disable_ddl_transaction!
diff --git a/db/migrate/20171006142024_add_uri_to_custom_emojis.rb b/db/migrate/20171006142024_add_uri_to_custom_emojis.rb
index 04dfcf397..ff62aed20 100644
--- a/db/migrate/20171006142024_add_uri_to_custom_emojis.rb
+++ b/db/migrate/20171006142024_add_uri_to_custom_emojis.rb
@@ -1,4 +1,4 @@
-class AddUriToCustomEmojis < ActiveRecord::Migration[5.1]
+class AddUriToCustomEmojis < ActiveRecord::Migration[5.2]
   def change
     add_column :custom_emojis, :uri, :string
     add_column :custom_emojis, :image_remote_url, :string
diff --git a/db/migrate/20171009222537_create_keyword_mutes.rb b/db/migrate/20171009222537_create_keyword_mutes.rb
index 66411ba1d..77c88b0a5 100644
--- a/db/migrate/20171009222537_create_keyword_mutes.rb
+++ b/db/migrate/20171009222537_create_keyword_mutes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class CreateKeywordMutes < ActiveRecord::Migration[5.1]
   def change
     create_table :keyword_mutes do |t|
diff --git a/db/migrate/20171010023049_add_foreign_key_to_account_moderation_notes.rb b/db/migrate/20171010023049_add_foreign_key_to_account_moderation_notes.rb
index cdcd15934..a2c0fbcc4 100644
--- a/db/migrate/20171010023049_add_foreign_key_to_account_moderation_notes.rb
+++ b/db/migrate/20171010023049_add_foreign_key_to_account_moderation_notes.rb
@@ -1,4 +1,4 @@
-class AddForeignKeyToAccountModerationNotes < ActiveRecord::Migration[5.1]
+class AddForeignKeyToAccountModerationNotes < ActiveRecord::Migration[5.2]
   def change
     safety_assured { add_foreign_key :account_moderation_notes, :accounts }
   end
diff --git a/db/migrate/20171010025614_change_accounts_nonnullable_in_account_moderation_notes.rb b/db/migrate/20171010025614_change_accounts_nonnullable_in_account_moderation_notes.rb
index 1d7a0086c..62725c88d 100644
--- a/db/migrate/20171010025614_change_accounts_nonnullable_in_account_moderation_notes.rb
+++ b/db/migrate/20171010025614_change_accounts_nonnullable_in_account_moderation_notes.rb
@@ -1,4 +1,4 @@
-class ChangeAccountsNonnullableInAccountModerationNotes < ActiveRecord::Migration[5.1]
+class ChangeAccountsNonnullableInAccountModerationNotes < ActiveRecord::Migration[5.2]
   def change
     safety_assured do
       change_column_null :account_moderation_notes, :account_id, false
diff --git a/db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb b/db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb
index 60a287101..5f7c60a3e 100644
--- a/db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb
+++ b/db/migrate/20171020084748_add_visible_in_picker_to_custom_emoji.rb
@@ -1,7 +1,7 @@
-class AddVisibleInPickerToCustomEmoji < ActiveRecord::Migration[5.1]
+class AddVisibleInPickerToCustomEmoji < ActiveRecord::Migration[5.2]
   def change
-    safety_assured {
+    safety_assured do
       add_column :custom_emojis, :visible_in_picker, :boolean, default: true, null: false
-    }
+    end
   end
 end
diff --git a/db/migrate/20171021191900_move_keyword_mutes_into_glitch_namespace.rb b/db/migrate/20171021191900_move_keyword_mutes_into_glitch_namespace.rb
index 269bb49d6..b6ea537c2 100644
--- a/db/migrate/20171021191900_move_keyword_mutes_into_glitch_namespace.rb
+++ b/db/migrate/20171021191900_move_keyword_mutes_into_glitch_namespace.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class MoveKeywordMutesIntoGlitchNamespace < ActiveRecord::Migration[5.1]
   def change
     safety_assured do
diff --git a/db/migrate/20171028221157_add_reblogs_to_follows.rb b/db/migrate/20171028221157_add_reblogs_to_follows.rb
index 3b2e46ed8..773904249 100644
--- a/db/migrate/20171028221157_add_reblogs_to_follows.rb
+++ b/db/migrate/20171028221157_add_reblogs_to_follows.rb
@@ -1,6 +1,6 @@
 require Rails.root.join('lib', 'mastodon', 'migration_helpers')
 
-class AddReblogsToFollows < ActiveRecord::Migration[5.1]
+class AddReblogsToFollows < ActiveRecord::Migration[5.2]
   include Mastodon::MigrationHelpers
 
   disable_ddl_transaction!
diff --git a/db/migrate/20171107143332_add_memorial_to_accounts.rb b/db/migrate/20171107143332_add_memorial_to_accounts.rb
index f3e012ce8..4fb1216a1 100644
--- a/db/migrate/20171107143332_add_memorial_to_accounts.rb
+++ b/db/migrate/20171107143332_add_memorial_to_accounts.rb
@@ -1,6 +1,6 @@
 require Rails.root.join('lib', 'mastodon', 'migration_helpers')
 
-class AddMemorialToAccounts < ActiveRecord::Migration[5.1]
+class AddMemorialToAccounts < ActiveRecord::Migration[5.2]
   include Mastodon::MigrationHelpers
 
   disable_ddl_transaction!
diff --git a/db/migrate/20171107143624_add_disabled_to_users.rb b/db/migrate/20171107143624_add_disabled_to_users.rb
index a71cac1c6..fb736786d 100644
--- a/db/migrate/20171107143624_add_disabled_to_users.rb
+++ b/db/migrate/20171107143624_add_disabled_to_users.rb
@@ -1,6 +1,6 @@
 require Rails.root.join('lib', 'mastodon', 'migration_helpers')
 
-class AddDisabledToUsers < ActiveRecord::Migration[5.1]
+class AddDisabledToUsers < ActiveRecord::Migration[5.2]
   include Mastodon::MigrationHelpers
 
   disable_ddl_transaction!
diff --git a/db/migrate/20171109012327_add_moderator_to_accounts.rb b/db/migrate/20171109012327_add_moderator_to_accounts.rb
index ddd87583a..70b0b598c 100644
--- a/db/migrate/20171109012327_add_moderator_to_accounts.rb
+++ b/db/migrate/20171109012327_add_moderator_to_accounts.rb
@@ -1,6 +1,6 @@
 require Rails.root.join('lib', 'mastodon', 'migration_helpers')
 
-class AddModeratorToAccounts < ActiveRecord::Migration[5.1]
+class AddModeratorToAccounts < ActiveRecord::Migration[5.2]
   include Mastodon::MigrationHelpers
 
   disable_ddl_transaction!
diff --git a/db/migrate/20171114080328_add_index_domain_to_email_domain_blocks.rb b/db/migrate/20171114080328_add_index_domain_to_email_domain_blocks.rb
index 84a341510..34dc6ff00 100644
--- a/db/migrate/20171114080328_add_index_domain_to_email_domain_blocks.rb
+++ b/db/migrate/20171114080328_add_index_domain_to_email_domain_blocks.rb
@@ -1,4 +1,4 @@
-class AddIndexDomainToEmailDomainBlocks < ActiveRecord::Migration[5.1]
+class AddIndexDomainToEmailDomainBlocks < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   def change
diff --git a/db/migrate/20171114231651_create_lists.rb b/db/migrate/20171114231651_create_lists.rb
index 21285e901..b7761abe4 100644
--- a/db/migrate/20171114231651_create_lists.rb
+++ b/db/migrate/20171114231651_create_lists.rb
@@ -1,4 +1,4 @@
-class CreateLists < ActiveRecord::Migration[5.1]
+class CreateLists < ActiveRecord::Migration[5.2]
   def change
     create_table :lists do |t|
       t.references :account, foreign_key: { on_delete: :cascade }
diff --git a/db/migrate/20171116161857_create_list_accounts.rb b/db/migrate/20171116161857_create_list_accounts.rb
index b76c90651..7dbb78d8d 100644
--- a/db/migrate/20171116161857_create_list_accounts.rb
+++ b/db/migrate/20171116161857_create_list_accounts.rb
@@ -1,4 +1,4 @@
-class CreateListAccounts < ActiveRecord::Migration[5.1]
+class CreateListAccounts < ActiveRecord::Migration[5.2]
   def change
     create_table :list_accounts do |t|
       t.belongs_to :list, foreign_key: { on_delete: :cascade }, null: false
diff --git a/db/migrate/20171118012443_add_moved_to_account_id_to_accounts.rb b/db/migrate/20171118012443_add_moved_to_account_id_to_accounts.rb
index 586ef6f02..893972848 100644
--- a/db/migrate/20171118012443_add_moved_to_account_id_to_accounts.rb
+++ b/db/migrate/20171118012443_add_moved_to_account_id_to_accounts.rb
@@ -1,4 +1,4 @@
-class AddMovedToAccountIdToAccounts < ActiveRecord::Migration[5.1]
+class AddMovedToAccountIdToAccounts < ActiveRecord::Migration[5.2]
   def change
     add_column :accounts, :moved_to_account_id, :bigint, null: true, default: nil
     safety_assured { add_foreign_key :accounts, :accounts, column: :moved_to_account_id, on_delete: :nullify }
diff --git a/db/migrate/20171119172437_create_admin_action_logs.rb b/db/migrate/20171119172437_create_admin_action_logs.rb
index b690735d2..80d5a3dba 100644
--- a/db/migrate/20171119172437_create_admin_action_logs.rb
+++ b/db/migrate/20171119172437_create_admin_action_logs.rb
@@ -1,4 +1,4 @@
-class CreateAdminActionLogs < ActiveRecord::Migration[5.1]
+class CreateAdminActionLogs < ActiveRecord::Migration[5.2]
   def change
     create_table :admin_action_logs do |t|
       t.belongs_to :account, foreign_key: { on_delete: :cascade }
diff --git a/db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb b/db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb
index 131e54b72..a02ffe09d 100644
--- a/db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb
+++ b/db/migrate/20171122120436_add_index_account_and_reblog_of_id_to_statuses.rb
@@ -1,4 +1,4 @@
-class AddIndexAccountAndReblogOfIdToStatuses < ActiveRecord::Migration[5.1]
+class AddIndexAccountAndReblogOfIdToStatuses < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   def up
diff --git a/db/migrate/20171125024930_create_invites.rb b/db/migrate/20171125024930_create_invites.rb
index bcf03bd72..2e814babf 100644
--- a/db/migrate/20171125024930_create_invites.rb
+++ b/db/migrate/20171125024930_create_invites.rb
@@ -1,4 +1,4 @@
-class CreateInvites < ActiveRecord::Migration[5.1]
+class CreateInvites < ActiveRecord::Migration[5.2]
   def change
     create_table :invites do |t|
       t.belongs_to :user, foreign_key: { on_delete: :cascade }
diff --git a/db/migrate/20171125031751_add_invite_id_to_users.rb b/db/migrate/20171125031751_add_invite_id_to_users.rb
index 9cfb0c542..2ff6c3430 100644
--- a/db/migrate/20171125031751_add_invite_id_to_users.rb
+++ b/db/migrate/20171125031751_add_invite_id_to_users.rb
@@ -1,4 +1,4 @@
-class AddInviteIdToUsers < ActiveRecord::Migration[5.1]
+class AddInviteIdToUsers < ActiveRecord::Migration[5.2]
   def change
     safety_assured { add_reference :users, :invite, null: true, default: nil, foreign_key: { on_delete: :nullify }, index: false }
   end
diff --git a/db/migrate/20171125185353_add_index_reblog_of_id_and_account_to_statuses.rb b/db/migrate/20171125185353_add_index_reblog_of_id_and_account_to_statuses.rb
index 37662eaa5..8952387b5 100644
--- a/db/migrate/20171125185353_add_index_reblog_of_id_and_account_to_statuses.rb
+++ b/db/migrate/20171125185353_add_index_reblog_of_id_and_account_to_statuses.rb
@@ -1,4 +1,4 @@
-class AddIndexReblogOfIdAndAccountToStatuses < ActiveRecord::Migration[5.1]
+class AddIndexReblogOfIdAndAccountToStatuses < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   def change
diff --git a/db/migrate/20171125190735_remove_old_reblog_index_on_statuses.rb b/db/migrate/20171125190735_remove_old_reblog_index_on_statuses.rb
index 68146c5ce..dc7e09a25 100644
--- a/db/migrate/20171125190735_remove_old_reblog_index_on_statuses.rb
+++ b/db/migrate/20171125190735_remove_old_reblog_index_on_statuses.rb
@@ -1,4 +1,4 @@
-class RemoveOldReblogIndexOnStatuses < ActiveRecord::Migration[5.1]
+class RemoveOldReblogIndexOnStatuses < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   def up
diff --git a/db/migrate/20171129172043_add_index_on_stream_entries.rb b/db/migrate/20171129172043_add_index_on_stream_entries.rb
index 181c4f288..4580fb42f 100644
--- a/db/migrate/20171129172043_add_index_on_stream_entries.rb
+++ b/db/migrate/20171129172043_add_index_on_stream_entries.rb
@@ -1,4 +1,4 @@
-class AddIndexOnStreamEntries < ActiveRecord::Migration[5.1]
+class AddIndexOnStreamEntries < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   def change
diff --git a/db/migrate/20171130000000_add_embed_url_to_preview_cards.rb b/db/migrate/20171130000000_add_embed_url_to_preview_cards.rb
index 8fcabef9f..811f6ceae 100644
--- a/db/migrate/20171130000000_add_embed_url_to_preview_cards.rb
+++ b/db/migrate/20171130000000_add_embed_url_to_preview_cards.rb
@@ -1,6 +1,6 @@
 require Rails.root.join('lib', 'mastodon', 'migration_helpers')
 
-class AddEmbedURLToPreviewCards < ActiveRecord::Migration[5.1]
+class AddEmbedURLToPreviewCards < ActiveRecord::Migration[5.2]
   include Mastodon::MigrationHelpers
 
   disable_ddl_transaction!
diff --git a/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb b/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb
index ac86c9e77..e8e878611 100644
--- a/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb
+++ b/db/migrate/20171201000000_change_account_id_nonnullable_in_lists.rb
@@ -1,4 +1,4 @@
-class ChangeAccountIdNonnullableInLists < ActiveRecord::Migration[5.1]
+class ChangeAccountIdNonnullableInLists < ActiveRecord::Migration[5.2]
   def change
     safety_assured do
       change_column_null :lists, :account_id, false
diff --git a/db/migrate/20171210213213_add_local_only_flag_to_statuses.rb b/db/migrate/20171210213213_add_local_only_flag_to_statuses.rb
index af1e29d6a..010503b10 100644
--- a/db/migrate/20171210213213_add_local_only_flag_to_statuses.rb
+++ b/db/migrate/20171210213213_add_local_only_flag_to_statuses.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AddLocalOnlyFlagToStatuses < ActiveRecord::Migration[5.1]
   def change
     add_column :statuses, :local_only, :boolean
diff --git a/db/migrate/20171212195226_remove_duplicate_indexes_in_lists.rb b/db/migrate/20171212195226_remove_duplicate_indexes_in_lists.rb
index 03f2591a8..f3007c77c 100644
--- a/db/migrate/20171212195226_remove_duplicate_indexes_in_lists.rb
+++ b/db/migrate/20171212195226_remove_duplicate_indexes_in_lists.rb
@@ -1,6 +1,6 @@
-class RemoveDuplicateIndexesInLists < ActiveRecord::Migration[5.1]
+class RemoveDuplicateIndexesInLists < ActiveRecord::Migration[5.2]
   def change
-    remove_index :list_accounts, name: "index_list_accounts_on_account_id"
-    remove_index :list_accounts, name: "index_list_accounts_on_list_id"
+    remove_index :list_accounts, name: 'index_list_accounts_on_account_id'
+    remove_index :list_accounts, name: 'index_list_accounts_on_list_id'
   end
 end
diff --git a/db/migrate/20171226094803_more_faster_index_on_notifications.rb b/db/migrate/20171226094803_more_faster_index_on_notifications.rb
index 0273a4e7c..e0e73b27a 100644
--- a/db/migrate/20171226094803_more_faster_index_on_notifications.rb
+++ b/db/migrate/20171226094803_more_faster_index_on_notifications.rb
@@ -1,4 +1,4 @@
-class MoreFasterIndexOnNotifications < ActiveRecord::Migration[5.1]
+class MoreFasterIndexOnNotifications < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   def change
diff --git a/db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb b/db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb
index 401fc5e62..eb277d3bc 100644
--- a/db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb
+++ b/db/migrate/20180106000232_add_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb
@@ -1,4 +1,4 @@
-class AddIndexOnStatusesForApiV1AccountsAccountIdStatuses < ActiveRecord::Migration[5.1]
+class AddIndexOnStatusesForApiV1AccountsAccountIdStatuses < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   def change
diff --git a/db/migrate/20180109143959_add_remember_token_to_users.rb b/db/migrate/20180109143959_add_remember_token_to_users.rb
index 662905bcb..f244fc6f6 100644
--- a/db/migrate/20180109143959_add_remember_token_to_users.rb
+++ b/db/migrate/20180109143959_add_remember_token_to_users.rb
@@ -1,4 +1,4 @@
-class AddRememberTokenToUsers < ActiveRecord::Migration[5.1]
+class AddRememberTokenToUsers < ActiveRecord::Migration[5.2]
   def change
     add_column :users, :remember_token, :string, null: true
   end
diff --git a/db/migrate/20180204034416_create_identities.rb b/db/migrate/20180204034416_create_identities.rb
index f6f5da910..68288aadf 100644
--- a/db/migrate/20180204034416_create_identities.rb
+++ b/db/migrate/20180204034416_create_identities.rb
@@ -1,7 +1,7 @@
-class CreateIdentities < ActiveRecord::Migration[5.0]
+class CreateIdentities < ActiveRecord::Migration[5.2]
   def change
-    create_table :identities do |t|
-      t.references :user, foreign_key: { on_delete: :cascade }
+    create_table :identities, id: :integer do |t|
+      t.references :user, type: :integer, foreign_key: { on_delete: :cascade }
       t.string :provider, null: false, default: ''
       t.string :uid, null: false, default: ''
 
diff --git a/db/migrate/20180206000000_change_user_id_nonnullable.rb b/db/migrate/20180206000000_change_user_id_nonnullable.rb
index 2d2cf20d3..119638387 100644
--- a/db/migrate/20180206000000_change_user_id_nonnullable.rb
+++ b/db/migrate/20180206000000_change_user_id_nonnullable.rb
@@ -1,4 +1,4 @@
-class ChangeUserIdNonnullable < ActiveRecord::Migration[5.1]
+class ChangeUserIdNonnullable < ActiveRecord::Migration[5.2]
   def change
     safety_assured do
       change_column_null :invites, :user_id, false
diff --git a/db/migrate/20180211015820_create_backups.rb b/db/migrate/20180211015820_create_backups.rb
index 9725a3e9f..4aaeed83b 100644
--- a/db/migrate/20180211015820_create_backups.rb
+++ b/db/migrate/20180211015820_create_backups.rb
@@ -1,4 +1,4 @@
-class CreateBackups < ActiveRecord::Migration[5.1]
+class CreateBackups < ActiveRecord::Migration[5.2]
   def change
     create_table :backups do |t|
       t.references :user, foreign_key: { on_delete: :nullify }
diff --git a/db/migrate/20180304013859_add_featured_collection_url_to_accounts.rb b/db/migrate/20180304013859_add_featured_collection_url_to_accounts.rb
index 1964b5121..46842a9e3 100644
--- a/db/migrate/20180304013859_add_featured_collection_url_to_accounts.rb
+++ b/db/migrate/20180304013859_add_featured_collection_url_to_accounts.rb
@@ -1,4 +1,4 @@
-class AddFeaturedCollectionURLToAccounts < ActiveRecord::Migration[5.1]
+class AddFeaturedCollectionURLToAccounts < ActiveRecord::Migration[5.2]
   def change
     add_column :accounts, :featured_collection_url, :string
   end
diff --git a/db/migrate/20180310000000_change_columns_in_notifications_nonnullable.rb b/db/migrate/20180310000000_change_columns_in_notifications_nonnullable.rb
index dba789207..8577d0235 100644
--- a/db/migrate/20180310000000_change_columns_in_notifications_nonnullable.rb
+++ b/db/migrate/20180310000000_change_columns_in_notifications_nonnullable.rb
@@ -1,4 +1,4 @@
-class ChangeColumnsInNotificationsNonnullable < ActiveRecord::Migration[5.1]
+class ChangeColumnsInNotificationsNonnullable < ActiveRecord::Migration[5.2]
   def change
     safety_assured do
       change_column_null :notifications, :activity_id, false
diff --git a/db/migrate/20180402031200_add_assigned_account_id_to_reports.rb b/db/migrate/20180402031200_add_assigned_account_id_to_reports.rb
index e2d1371d2..27cbd6c2a 100644
--- a/db/migrate/20180402031200_add_assigned_account_id_to_reports.rb
+++ b/db/migrate/20180402031200_add_assigned_account_id_to_reports.rb
@@ -1,4 +1,4 @@
-class AddAssignedAccountIdToReports < ActiveRecord::Migration[5.1]
+class AddAssignedAccountIdToReports < ActiveRecord::Migration[5.2]
   def change
     safety_assured { add_reference :reports, :assigned_account, null: true, default: nil, foreign_key: { on_delete: :nullify, to_table: :accounts }, index: false }
   end
diff --git a/db/migrate/20180402040909_create_report_notes.rb b/db/migrate/20180402040909_create_report_notes.rb
index 429cb4534..5d5a33627 100644
--- a/db/migrate/20180402040909_create_report_notes.rb
+++ b/db/migrate/20180402040909_create_report_notes.rb
@@ -1,4 +1,4 @@
-class CreateReportNotes < ActiveRecord::Migration[5.1]
+class CreateReportNotes < ActiveRecord::Migration[5.2]
   def change
     create_table :report_notes do |t|
       t.text :content, null: false
diff --git a/db/migrate/20180410204633_add_fields_to_accounts.rb b/db/migrate/20180410204633_add_fields_to_accounts.rb
index 5b8c17480..a1b9504b6 100644
--- a/db/migrate/20180410204633_add_fields_to_accounts.rb
+++ b/db/migrate/20180410204633_add_fields_to_accounts.rb
@@ -1,4 +1,4 @@
-class AddFieldsToAccounts < ActiveRecord::Migration[5.1]
+class AddFieldsToAccounts < ActiveRecord::Migration[5.2]
   def change
     add_column :accounts, :fields, :jsonb
   end
diff --git a/db/migrate/20180410220657_create_bookmarks.rb b/db/migrate/20180410220657_create_bookmarks.rb
index bc79022e4..aba21f5ea 100644
--- a/db/migrate/20180410220657_create_bookmarks.rb
+++ b/db/migrate/20180410220657_create_bookmarks.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # This migration is a duplicate of 20180831171112 and may get ignored, see
 # config/initializers/0_duplicate_migrations.rb
 
diff --git a/db/migrate/20180514130000_improve_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb b/db/migrate/20180514130000_improve_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb
index b29e62803..48d156bef 100644
--- a/db/migrate/20180514130000_improve_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb
+++ b/db/migrate/20180514130000_improve_index_on_statuses_for_api_v1_accounts_account_id_statuses.rb
@@ -1,12 +1,12 @@
 # frozen_string_literal: true
 
-class ImproveIndexOnStatusesForApiV1AccountsAccountIdStatuses < ActiveRecord::Migration[5.1]
+class ImproveIndexOnStatusesForApiV1AccountsAccountIdStatuses < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   def change
-  #  These changes ware reverted by migration 20180514140000.
-  #  add_index :statuses, [:account_id, :id, :visibility], where: 'visibility IN (0, 1, 2)', algorithm: :concurrently
-  #  add_index :statuses, [:account_id, :id], where: 'visibility = 3', algorithm: :concurrently
-  #  remove_index :statuses, column: [:account_id, :id, :visibility, :updated_at], order: { id: :desc }, algorithm: :concurrently, name: :index_statuses_20180106
+    #  These changes were reverted by migration 20180514140000.
+    #  add_index :statuses, [:account_id, :id, :visibility], where: 'visibility IN (0, 1, 2)', algorithm: :concurrently
+    #  add_index :statuses, [:account_id, :id], where: 'visibility = 3', algorithm: :concurrently
+    #  remove_index :statuses, column: [:account_id, :id, :visibility, :updated_at], order: { id: :desc }, algorithm: :concurrently, name: :index_statuses_20180106
   end
 end
diff --git a/db/migrate/20180514140000_revert_index_change_on_statuses_for_api_v1_accounts_account_id_statuses.rb b/db/migrate/20180514140000_revert_index_change_on_statuses_for_api_v1_accounts_account_id_statuses.rb
index ccd2ec7ea..242ae7410 100644
--- a/db/migrate/20180514140000_revert_index_change_on_statuses_for_api_v1_accounts_account_id_statuses.rb
+++ b/db/migrate/20180514140000_revert_index_change_on_statuses_for_api_v1_accounts_account_id_statuses.rb
@@ -1,15 +1,15 @@
 # frozen_string_literal: true
 
-class RevertIndexChangeOnStatusesForApiV1AccountsAccountIdStatuses < ActiveRecord::Migration[5.1]
+class RevertIndexChangeOnStatusesForApiV1AccountsAccountIdStatuses < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   def change
     safety_assured do
-      add_index :statuses, [:account_id, :id, :visibility, :updated_at], order: { id: :desc }, algorithm: :concurrently, name: :index_statuses_20180106 unless index_name_exists?(:statuses, "index_statuses_20180106")
+      add_index :statuses, [:account_id, :id, :visibility, :updated_at], order: { id: :desc }, algorithm: :concurrently, name: :index_statuses_20180106 unless index_name_exists?(:statuses, 'index_statuses_20180106')
     end
 
     # These index may not exists (see migration 20180514130000)
     remove_index :statuses, column: [:account_id, :id, :visibility], where: 'visibility IN (0, 1, 2)', algorithm: :concurrently if index_exists?(:statuses, [:account_id, :id, :visibility], where: 'visibility IN (0, 1, 2)')
-    remove_index :statuses, column: [:account_id, :id], where: 'visibility = 3', algorithm: :concurrently if index_exists?(:statuses, ["account_id", "id"], where: "(visibility = 3)")
+    remove_index :statuses, column: [:account_id, :id], where: 'visibility = 3', algorithm: :concurrently if index_exists?(:statuses, %w(account_id id), where: '(visibility = 3)')
   end
 end
diff --git a/db/migrate/20180528141303_fix_accounts_unique_index.rb b/db/migrate/20180528141303_fix_accounts_unique_index.rb
index 3e33e2cac..1e67b4bb4 100644
--- a/db/migrate/20180528141303_fix_accounts_unique_index.rb
+++ b/db/migrate/20180528141303_fix_accounts_unique_index.rb
@@ -1,4 +1,8 @@
+require_relative '../../lib/mastodon/migration_warning'
+
 class FixAccountsUniqueIndex < ActiveRecord::Migration[5.2]
+  include Mastodon::MigrationWarning
+
   class Account < ApplicationRecord
     # Dummy class, to make migration possible across version changes
     has_one :user, inverse_of: :account
@@ -35,22 +39,11 @@ class FixAccountsUniqueIndex < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   def up
-    if $stdout.isatty
-      say ''
-      say 'WARNING: This migration may take a *long* time for large instances'
-      say 'It will *not* lock tables for any significant time, but it may run'
-      say 'for a very long time. We will pause for 10 seconds to allow you to'
-      say 'interrupt this migration if you are not ready.'
-      say ''
-      say 'This migration will irreversibly delete user accounts with duplicate'
-      say 'usernames. You may use the `rake mastodon:maintenance:find_duplicate_usernames`'
-      say 'task to manually deal with such accounts before running this migration.'
-
-      10.downto(1) do |i|
-        say "Continuing in #{i} second#{i == 1 ? '' : 's'}...", true
-        sleep 1
-      end
-    end
+    migration_duration_warning(<<~EXPLANATION)
+      This migration will irreversibly delete user accounts with duplicate
+      usernames. You may use the `rake mastodon:maintenance:find_duplicate_usernames`
+      task to manually deal with such accounts before running this migration.
+    EXPLANATION
 
     duplicates = Account.connection.select_all('SELECT string_agg(id::text, \',\') AS ids FROM accounts GROUP BY lower(username), lower(domain) HAVING count(*) > 1').to_ary
 
@@ -106,21 +99,17 @@ class FixAccountsUniqueIndex < ActiveRecord::Migration[5.2]
     # to check for (and skip past) uniqueness errors
     [Favourite, Follow, FollowRequest, Block, Mute].each do |klass|
       klass.where(account_id: duplicate_account.id).find_each do |record|
-        begin
-          record.update_attribute(:account_id, main_account.id)
-        rescue ActiveRecord::RecordNotUnique
-          next
-        end
+        record.update_attribute(:account_id, main_account.id)
+      rescue ActiveRecord::RecordNotUnique
+        next
       end
     end
 
     [Follow, FollowRequest, Block, Mute].each do |klass|
       klass.where(target_account_id: duplicate_account.id).find_each do |record|
-        begin
-          record.update_attribute(:target_account_id, main_account.id)
-        rescue ActiveRecord::RecordNotUnique
-          next
-        end
+        record.update_attribute(:target_account_id, main_account.id)
+      rescue ActiveRecord::RecordNotUnique
+        next
       end
     end
   end
diff --git a/db/migrate/20180604000556_add_apply_to_mentions_flag_to_keyword_mutes.rb b/db/migrate/20180604000556_add_apply_to_mentions_flag_to_keyword_mutes.rb
index cd97d0f20..8078a07bf 100644
--- a/db/migrate/20180604000556_add_apply_to_mentions_flag_to_keyword_mutes.rb
+++ b/db/migrate/20180604000556_add_apply_to_mentions_flag_to_keyword_mutes.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'mastodon/migration_helpers'
 
 class AddApplyToMentionsFlagToKeywordMutes < ActiveRecord::Migration[5.2]
diff --git a/db/migrate/20180707193142_migrate_filters.rb b/db/migrate/20180707193142_migrate_filters.rb
index 067c53357..8f6b3e1bb 100644
--- a/db/migrate/20180707193142_migrate_filters.rb
+++ b/db/migrate/20180707193142_migrate_filters.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
 class MigrateFilters < ActiveRecord::Migration[5.2]
   class GlitchKeywordMute < ApplicationRecord
     # Dummy class, as we removed Glitch::KeywordMute
-    belongs_to :account, required: true
+    belongs_to :account, optional: false
     validates_presence_of :keyword
   end
 
@@ -15,7 +17,7 @@ class MigrateFilters < ActiveRecord::Migration[5.2]
     private
 
     def clean_up_contexts
-      self.context = Array(context).map(&:strip).map(&:presence).compact
+      self.context = Array(context).map(&:strip).filter_map(&:presence)
     end
   end
 
@@ -27,7 +29,8 @@ class MigrateFilters < ActiveRecord::Migration[5.2]
         phrase: filter.keyword,
         context: filter.apply_to_mentions ? %w(home public notifications) : %w(home public),
         whole_word: filter.whole_word,
-        irreversible: true)
+        irreversible: true
+      )
     end
   end
 
@@ -48,7 +51,8 @@ class MigrateFilters < ActiveRecord::Migration[5.2]
       GlitchKeywordMute.where(account: filter.account).create!(
         keyword: filter.phrase,
         whole_word: filter.whole_word,
-        apply_to_mentions: filter.context.include?('notifications'))
+        apply_to_mentions: filter.context.include?('notifications')
+      )
     end
   end
 end
diff --git a/db/migrate/20180812173710_copy_status_stats.rb b/db/migrate/20180812173710_copy_status_stats.rb
index ff10c18d9..45eb9501c 100644
--- a/db/migrate/20180812173710_copy_status_stats.rb
+++ b/db/migrate/20180812173710_copy_status_stats.rb
@@ -19,7 +19,7 @@ class CopyStatusStats < ActiveRecord::Migration[5.2]
 
   def supports_upsert?
     version = select_one("SELECT current_setting('server_version_num') AS v")['v'].to_i
-    version >= 90500
+    version >= 90_500
   end
 
   def up_fast
@@ -43,12 +43,10 @@ class CopyStatusStats < ActiveRecord::Migration[5.2]
     # We cannot use bulk INSERT or overarching transactions here because of possible
     # uniqueness violations that we need to skip over
     Status.unscoped.select('id, reblogs_count, favourites_count, created_at, updated_at').find_each do |status|
-      begin
-        params = [[nil, status.id], [nil, status.reblogs_count], [nil, status.favourites_count], [nil, status.created_at], [nil, status.updated_at]]
-        exec_insert('INSERT INTO status_stats (status_id, reblogs_count, favourites_count, created_at, updated_at) VALUES ($1, $2, $3, $4, $5)', nil, params)
-      rescue ActiveRecord::RecordNotUnique
-        next
-      end
+      params = [[nil, status.id], [nil, status.reblogs_count], [nil, status.favourites_count], [nil, status.created_at], [nil, status.updated_at]]
+      exec_insert('INSERT INTO status_stats (status_id, reblogs_count, favourites_count, created_at, updated_at) VALUES ($1, $2, $3, $4, $5)', nil, params)
+    rescue ActiveRecord::RecordNotUnique
+      next
     end
   end
 end
diff --git a/db/migrate/20180831171112_create_bookmarks.rb b/db/migrate/20180831171112_create_bookmarks.rb
index 5d587b7e9..9f6bfae57 100644
--- a/db/migrate/20180831171112_create_bookmarks.rb
+++ b/db/migrate/20180831171112_create_bookmarks.rb
@@ -1,7 +1,7 @@
 # This migration is a duplicate of 20180410220657 and may get ignored, see
 # config/initializers/0_duplicate_migrations.rb
 
-class CreateBookmarks < ActiveRecord::Migration[5.1]
+class CreateBookmarks < ActiveRecord::Migration[5.2]
   def change
     create_table :bookmarks do |t|
       t.references :account, null: false
diff --git a/db/migrate/20181024224956_migrate_account_conversations.rb b/db/migrate/20181024224956_migrate_account_conversations.rb
index 9e6497d81..e4dcdb18b 100644
--- a/db/migrate/20181024224956_migrate_account_conversations.rb
+++ b/db/migrate/20181024224956_migrate_account_conversations.rb
@@ -1,4 +1,8 @@
+require_relative '../../lib/mastodon/migration_warning'
+
 class MigrateAccountConversations < ActiveRecord::Migration[5.2]
+  include Mastodon::MigrationWarning
+
   disable_ddl_transaction!
 
   class Mention < ApplicationRecord
@@ -62,19 +66,7 @@ class MigrateAccountConversations < ActiveRecord::Migration[5.2]
   end
 
   def up
-    if $stdout.isatty
-      say ''
-      say 'WARNING: This migration may take a *long* time for large instances'
-      say 'It will *not* lock tables for any significant time, but it may run'
-      say 'for a very long time. We will pause for 10 seconds to allow you to'
-      say 'interrupt this migration if you are not ready.'
-      say ''
-
-      10.downto(1) do |i|
-        say "Continuing in #{i} second#{i == 1 ? '' : 's'}...", true
-        sleep 1
-      end
-    end
+    migration_duration_warning
 
     migrated  = 0
     last_time = Time.zone.now
@@ -100,8 +92,7 @@ class MigrateAccountConversations < ActiveRecord::Migration[5.2]
     end
   end
 
-  def down
-  end
+  def down; end
 
   private
 
diff --git a/db/migrate/20181116173541_copy_account_stats.rb b/db/migrate/20181116173541_copy_account_stats.rb
index 8e27eb11b..f908575cb 100644
--- a/db/migrate/20181116173541_copy_account_stats.rb
+++ b/db/migrate/20181116173541_copy_account_stats.rb
@@ -19,7 +19,7 @@ class CopyAccountStats < ActiveRecord::Migration[5.2]
 
   def supports_upsert?
     version = select_one("SELECT current_setting('server_version_num') AS v")['v'].to_i
-    version >= 90500
+    version >= 90_500
   end
 
   def up_fast
@@ -43,12 +43,10 @@ class CopyAccountStats < ActiveRecord::Migration[5.2]
     # We cannot use bulk INSERT or overarching transactions here because of possible
     # uniqueness violations that we need to skip over
     Account.unscoped.select('id, statuses_count, following_count, followers_count, created_at, updated_at').find_each do |account|
-      begin
-        params = [[nil, account.id], [nil, account[:statuses_count]], [nil, account[:following_count]], [nil, account[:followers_count]], [nil, account.created_at], [nil, account.updated_at]]
-        exec_insert('INSERT INTO account_stats (account_id, statuses_count, following_count, followers_count, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)', nil, params)
-      rescue ActiveRecord::RecordNotUnique
-        next
-      end
+      params = [[nil, account.id], [nil, account[:statuses_count]], [nil, account[:following_count]], [nil, account[:followers_count]], [nil, account.created_at], [nil, account.updated_at]]
+      exec_insert('INSERT INTO account_stats (account_id, statuses_count, following_count, followers_count, created_at, updated_at) VALUES ($1, $2, $3, $4, $5, $6)', nil, params)
+    rescue ActiveRecord::RecordNotUnique
+      next
     end
   end
 end
diff --git a/db/migrate/20190306145741_add_lock_version_to_polls.rb b/db/migrate/20190306145741_add_lock_version_to_polls.rb
index 5bb8cd3b4..c9fa471ad 100644
--- a/db/migrate/20190306145741_add_lock_version_to_polls.rb
+++ b/db/migrate/20190306145741_add_lock_version_to_polls.rb
@@ -21,4 +21,3 @@ class AddLockVersionToPolls < ActiveRecord::Migration[5.2]
     remove_column :polls, :lock_version
   end
 end
-
diff --git a/db/migrate/20190314181829_migrate_open_registrations_setting.rb b/db/migrate/20190314181829_migrate_open_registrations_setting.rb
index e5fe95009..d2f6bf2c1 100644
--- a/db/migrate/20190314181829_migrate_open_registrations_setting.rb
+++ b/db/migrate/20190314181829_migrate_open_registrations_setting.rb
@@ -2,6 +2,7 @@ class MigrateOpenRegistrationsSetting < ActiveRecord::Migration[5.2]
   def up
     open_registrations = Setting.find_by(var: 'open_registrations')
     return if open_registrations.nil? || open_registrations.value
+
     setting = Setting.where(var: 'registrations_mode').first_or_initialize(var: 'registrations_mode')
     setting.update(value: 'none')
   end
@@ -9,6 +10,7 @@ class MigrateOpenRegistrationsSetting < ActiveRecord::Migration[5.2]
   def down
     registrations_mode = Setting.find_by(var: 'registrations_mode')
     return if registrations_mode.nil?
+
     setting = Setting.where(var: 'open_registrations').first_or_initialize(var: 'open_registrations')
     setting.update(value: registrations_mode.value == 'open')
   end
diff --git a/db/migrate/20190512200918_add_content_type_to_statuses.rb b/db/migrate/20190512200918_add_content_type_to_statuses.rb
index efbe2caa7..31c1a4f17 100644
--- a/db/migrate/20190512200918_add_content_type_to_statuses.rb
+++ b/db/migrate/20190512200918_add_content_type_to_statuses.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AddContentTypeToStatuses < ActiveRecord::Migration[5.2]
   def change
     add_column :statuses, :content_type, :string
diff --git a/db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb b/db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb
index 72b7c609d..1c18b85cb 100644
--- a/db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb
+++ b/db/migrate/20190529143559_preserve_old_layout_for_existing_users.rb
@@ -8,10 +8,10 @@ class PreserveOldLayoutForExistingUsers < ActiveRecord::Migration[5.2]
 
     User.where(User.arel_table[:current_sign_in_at].gteq(1.month.ago)).find_each do |user|
       next if Setting.unscoped.where(thing_type: 'User', thing_id: user.id, var: 'advanced_layout').exists?
+
       user.settings.advanced_layout = true
     end
   end
 
-  def down
-  end
+  def down; end
 end
diff --git a/db/migrate/20190807135426_add_comments_to_domain_blocks.rb b/db/migrate/20190807135426_add_comments_to_domain_blocks.rb
index b660a71ad..79b9f0212 100644
--- a/db/migrate/20190807135426_add_comments_to_domain_blocks.rb
+++ b/db/migrate/20190807135426_add_comments_to_domain_blocks.rb
@@ -4,4 +4,3 @@ class AddCommentsToDomainBlocks < ActiveRecord::Migration[5.2]
     add_column :domain_blocks, :public_comment, :text
   end
 end
-
diff --git a/db/migrate/20191031163205_change_list_account_follow_nullable.rb b/db/migrate/20191031163205_change_list_account_follow_nullable.rb
index 65ff93365..43ebfe892 100644
--- a/db/migrate/20191031163205_change_list_account_follow_nullable.rb
+++ b/db/migrate/20191031163205_change_list_account_follow_nullable.rb
@@ -1,4 +1,4 @@
-class ChangeListAccountFollowNullable < ActiveRecord::Migration[5.1]
+class ChangeListAccountFollowNullable < ActiveRecord::Migration[5.2]
   def change
     safety_assured do
       change_column_null :list_accounts, :follow_id, true
diff --git a/db/migrate/20200312162302_add_status_ids_to_announcements.rb b/db/migrate/20200312162302_add_status_ids_to_announcements.rb
index 42aa6513d..704d3773e 100644
--- a/db/migrate/20200312162302_add_status_ids_to_announcements.rb
+++ b/db/migrate/20200312162302_add_status_ids_to_announcements.rb
@@ -3,4 +3,3 @@ class AddStatusIdsToAnnouncements < ActiveRecord::Migration[5.2]
     add_column :announcements, :status_ids, :bigint, array: true
   end
 end
-
diff --git a/db/migrate/20200407202420_migrate_unavailable_inboxes.rb b/db/migrate/20200407202420_migrate_unavailable_inboxes.rb
index 92a3acb5d..8f9c68794 100644
--- a/db/migrate/20200407202420_migrate_unavailable_inboxes.rb
+++ b/db/migrate/20200407202420_migrate_unavailable_inboxes.rb
@@ -2,7 +2,8 @@ class MigrateUnavailableInboxes < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   def up
-    urls = Redis.current.smembers('unavailable_inboxes')
+    redis = RedisConfiguration.pool.checkout
+    urls = redis.smembers('unavailable_inboxes')
 
     hosts = urls.map do |url|
       Addressable::URI.parse(url).normalized_host
@@ -14,7 +15,7 @@ class MigrateUnavailableInboxes < ActiveRecord::Migration[5.2]
       UnavailableDomain.create(domain: host)
     end
 
-    Redis.current.del(*(['unavailable_inboxes'] + Redis.current.keys('exhausted_deliveries:*')))
+    redis.del(*(['unavailable_inboxes'] + redis.keys('exhausted_deliveries:*')))
   end
 
   def down; end
diff --git a/db/migrate/20200510110808_reset_web_app_secret.rb b/db/migrate/20200510110808_reset_web_app_secret.rb
index b274844c5..8c0c06a83 100644
--- a/db/migrate/20200510110808_reset_web_app_secret.rb
+++ b/db/migrate/20200510110808_reset_web_app_secret.rb
@@ -10,6 +10,5 @@ class ResetWebAppSecret < ActiveRecord::Migration[5.2]
     web_app.save!
   end
 
-  def down
-  end
+  def down; end
 end
diff --git a/db/migrate/20200510181721_remove_duplicated_indexes_pghero.rb b/db/migrate/20200510181721_remove_duplicated_indexes_pghero.rb
index 1d6ba1fe9..59bb1b9e2 100644
--- a/db/migrate/20200510181721_remove_duplicated_indexes_pghero.rb
+++ b/db/migrate/20200510181721_remove_duplicated_indexes_pghero.rb
@@ -19,4 +19,3 @@ class RemoveDuplicatedIndexesPghero < ActiveRecord::Migration[5.2]
     add_index :markers, :user_id, name: :index_markers_on_user_id                                       unless index_exists?(:markers, :user_id, name: :index_markers_on_user_id)
   end
 end
-
diff --git a/db/migrate/20200521180606_encrypted_message_ids_to_timestamp_ids.rb b/db/migrate/20200521180606_encrypted_message_ids_to_timestamp_ids.rb
index 24d43a0bf..c5c80b795 100644
--- a/db/migrate/20200521180606_encrypted_message_ids_to_timestamp_ids.rb
+++ b/db/migrate/20200521180606_encrypted_message_ids_to_timestamp_ids.rb
@@ -6,7 +6,7 @@ class EncryptedMessageIdsToTimestampIds < ActiveRecord::Migration[5.2]
   end
 
   def down
-    execute("LOCK encrypted_messages")
+    execute('LOCK encrypted_messages')
     execute("SELECT setval('encrypted_messages_id_seq', (SELECT MAX(id) FROM encrypted_messages))")
     execute("ALTER TABLE encrypted_messages ALTER COLUMN id SET DEFAULT nextval('encrypted_messages_id_seq')")
   end
diff --git a/db/migrate/20200620164023_add_fixed_lowercase_index_to_accounts.rb b/db/migrate/20200620164023_add_fixed_lowercase_index_to_accounts.rb
index 652ce9752..b350ee9f2 100644
--- a/db/migrate/20200620164023_add_fixed_lowercase_index_to_accounts.rb
+++ b/db/migrate/20200620164023_add_fixed_lowercase_index_to_accounts.rb
@@ -16,7 +16,7 @@ class AddFixedLowercaseIndexToAccounts < ActiveRecord::Migration[5.2]
       add_index :accounts, "lower (username), COALESCE(lower(domain), '')", name: 'index_accounts_on_username_and_domain_lower', unique: true, algorithm: :concurrently
     rescue ActiveRecord::RecordNotUnique
       remove_index :accounts, name: 'index_accounts_on_username_and_domain_lower'
-      raise CorruptionError.new('index_accounts_on_username_and_domain_lower')
+      raise CorruptionError, 'index_accounts_on_username_and_domain_lower'
     end
 
     remove_index :accounts, name: 'old_index_accounts_on_username_and_domain_lower' if index_name_exists?(:accounts, 'old_index_accounts_on_username_and_domain_lower')
diff --git a/db/migrate/20200622213645_media_attachment_ids_to_timestamp_ids.rb b/db/migrate/20200622213645_media_attachment_ids_to_timestamp_ids.rb
index 5c6865b92..7c141e7af 100644
--- a/db/migrate/20200622213645_media_attachment_ids_to_timestamp_ids.rb
+++ b/db/migrate/20200622213645_media_attachment_ids_to_timestamp_ids.rb
@@ -1,4 +1,4 @@
-class MediaAttachmentIdsToTimestampIds < ActiveRecord::Migration[5.1]
+class MediaAttachmentIdsToTimestampIds < ActiveRecord::Migration[5.2]
   def up
     # Set up the media_attachments.id column to use our timestamp-based IDs.
     safety_assured do
@@ -10,7 +10,7 @@ class MediaAttachmentIdsToTimestampIds < ActiveRecord::Migration[5.1]
   end
 
   def down
-    execute("LOCK media_attachments")
+    execute('LOCK media_attachments')
     execute("SELECT setval('media_attachments_id_seq', (SELECT MAX(id) FROM media_attachments))")
     execute("ALTER TABLE media_attachments ALTER COLUMN id SET DEFAULT nextval('media_attachments_id_seq')")
   end
diff --git a/db/migrate/20200628133322_create_account_notes.rb b/db/migrate/20200628133322_create_account_notes.rb
index 664727e60..022e0ff3a 100644
--- a/db/migrate/20200628133322_create_account_notes.rb
+++ b/db/migrate/20200628133322_create_account_notes.rb
@@ -10,4 +10,3 @@ class CreateAccountNotes < ActiveRecord::Migration[5.2]
     end
   end
 end
-
diff --git a/db/migrate/20200917192924_add_notify_to_follows.rb b/db/migrate/20200917192924_add_notify_to_follows.rb
index d27471c44..342eaa38d 100644
--- a/db/migrate/20200917192924_add_notify_to_follows.rb
+++ b/db/migrate/20200917192924_add_notify_to_follows.rb
@@ -1,6 +1,6 @@
 require Rails.root.join('lib', 'mastodon', 'migration_helpers')
 
-class AddNotifyToFollows < ActiveRecord::Migration[5.1]
+class AddNotifyToFollows < ActiveRecord::Migration[5.2]
   include Mastodon::MigrationHelpers
 
   disable_ddl_transaction!
diff --git a/db/migrate/20210306164523_account_ids_to_timestamp_ids.rb b/db/migrate/20210306164523_account_ids_to_timestamp_ids.rb
index 39cd4cdea..b287c60dd 100644
--- a/db/migrate/20210306164523_account_ids_to_timestamp_ids.rb
+++ b/db/migrate/20210306164523_account_ids_to_timestamp_ids.rb
@@ -1,4 +1,4 @@
-class AccountIdsToTimestampIds < ActiveRecord::Migration[5.1]
+class AccountIdsToTimestampIds < ActiveRecord::Migration[5.2]
   def up
     # Set up the accounts.id column to use our timestamp-based IDs.
     safety_assured do
@@ -10,7 +10,7 @@ class AccountIdsToTimestampIds < ActiveRecord::Migration[5.1]
   end
 
   def down
-    execute("LOCK accounts")
+    execute('LOCK accounts')
     execute("SELECT setval('accounts_id_seq', (SELECT MAX(id) FROM accounts))")
     execute("ALTER TABLE accounts ALTER COLUMN id SET DEFAULT nextval('accounts_id_seq')")
   end
diff --git a/db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb b/db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb
index b3ee11d09..a3cc854d7 100644
--- a/db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb
+++ b/db/migrate/20210421121431_add_case_insensitive_btree_index_to_tags.rb
@@ -10,7 +10,8 @@ class AddCaseInsensitiveBtreeIndexToTags < ActiveRecord::Migration[5.2]
       safety_assured { execute 'CREATE UNIQUE INDEX CONCURRENTLY index_tags_on_name_lower_btree ON tags (lower(name) text_pattern_ops)' }
     rescue ActiveRecord::StatementInvalid => e
       remove_index :tags, name: 'index_tags_on_name_lower_btree'
-      raise CorruptionError.new('index_tags_on_name_lower_btree') if e.is_a?(ActiveRecord::RecordNotUnique)
+      raise CorruptionError, 'index_tags_on_name_lower_btree' if e.is_a?(ActiveRecord::RecordNotUnique)
+
       raise e
     end
 
diff --git a/db/migrate/20210722120340_create_account_statuses_cleanup_policies.rb b/db/migrate/20210722120340_create_account_statuses_cleanup_policies.rb
index 28cfb6ef5..db168676a 100644
--- a/db/migrate/20210722120340_create_account_statuses_cleanup_policies.rb
+++ b/db/migrate/20210722120340_create_account_statuses_cleanup_policies.rb
@@ -17,4 +17,3 @@ class CreateAccountStatusesCleanupPolicies < ActiveRecord::Migration[6.1]
     end
   end
 end
-
diff --git a/db/migrate/20220209175231_add_content_type_to_status_edits.rb b/db/migrate/20220209175231_add_content_type_to_status_edits.rb
index 0e4e52fcb..bb414535d 100644
--- a/db/migrate/20220209175231_add_content_type_to_status_edits.rb
+++ b/db/migrate/20220209175231_add_content_type_to_status_edits.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class AddContentTypeToStatusEdits < ActiveRecord::Migration[6.1]
   def change
     add_column :status_edits, :content_type, :string
diff --git a/db/migrate/20220613110834_add_action_to_custom_filters.rb b/db/migrate/20220613110834_add_action_to_custom_filters.rb
index 9427a66fc..c1daf3c94 100644
--- a/db/migrate/20220613110834_add_action_to_custom_filters.rb
+++ b/db/migrate/20220613110834_add_action_to_custom_filters.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 require Rails.root.join('lib', 'mastodon', 'migration_helpers')
 
 class AddActionToCustomFilters < ActiveRecord::Migration[6.1]
diff --git a/db/migrate/20230215074327_add_settings_to_users.rb b/db/migrate/20230215074327_add_settings_to_users.rb
new file mode 100644
index 000000000..ff5308f42
--- /dev/null
+++ b/db/migrate/20230215074327_add_settings_to_users.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class AddSettingsToUsers < ActiveRecord::Migration[6.1]
+  def change
+    add_column :users, :settings, :text
+  end
+end
diff --git a/db/migrate/20230215074423_move_user_settings.rb b/db/migrate/20230215074423_move_user_settings.rb
new file mode 100644
index 000000000..8dbca9ecd
--- /dev/null
+++ b/db/migrate/20230215074423_move_user_settings.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+class MoveUserSettings < ActiveRecord::Migration[6.1]
+  class User < ApplicationRecord; end
+
+  MAPPING = {
+    default_privacy: 'default_privacy',
+    default_sensitive: 'web.default_sensitive',
+    default_language: 'default_language',
+    noindex: 'noindex',
+    theme: 'theme',
+    trends: 'web.trends',
+    unfollow_modal: 'web.unfollow_modal',
+    boost_modal: 'web.reblog_modal',
+    delete_modal: 'web.delete_modal',
+    auto_play_gif: 'web.auto_play',
+    display_media: 'web.display_media',
+    expand_spoilers: 'web.expand_content_warnings',
+    reduce_motion: 'web.reduce_motion',
+    disable_swiping: 'web.disable_swiping',
+    show_application: 'show_application',
+    system_font_ui: 'web.use_system_font',
+    aggregate_reblogs: 'aggregate_reblogs',
+    advanced_layout: 'web.advanced_layout',
+    use_blurhash: 'web.use_blurhash',
+    use_pending_items: 'web.use_pending_items',
+    crop_images: 'web.crop_images',
+    notification_emails: {
+      follow: 'notification_emails.follow',
+      reblog: 'notification_emails.reblog',
+      favourite: 'notification_emails.favourite',
+      mention: 'notification_emails.mention',
+      follow_request: 'notification_emails.follow_request',
+      report: 'notification_emails.report',
+      pending_account: 'notification_emails.pending_account',
+      trending_tag: 'notification_emails.trends',
+      appeal: 'notification_emails.appeal',
+    }.freeze,
+    always_send_emails: 'always_send_emails',
+    interactions: {
+      must_be_follower: 'interactions.must_be_follower',
+      must_be_following: 'interactions.must_be_following',
+      must_be_following_dm: 'interactions.must_be_following_dm',
+    }.freeze,
+  }.freeze
+
+  class LegacySetting < ApplicationRecord
+    self.table_name = 'settings'
+
+    def var
+      self[:var]&.to_sym
+    end
+
+    def value
+      YAML.safe_load(self[:value], permitted_classes: [ActiveSupport::HashWithIndifferentAccess, Symbol]) if self[:value].present?
+    end
+  end
+
+  def up
+    User.find_each do |user|
+      previous_settings = LegacySetting.where(thing_type: 'User', thing_id: user.id).index_by(&:var)
+
+      user_settings = {}
+
+      MAPPING.each do |legacy_key, new_key|
+        value = previous_settings[legacy_key]&.value
+
+        next if value.blank?
+
+        if value.is_a?(Hash)
+          value.each do |nested_key, nested_value|
+            user_settings[MAPPING[legacy_key][nested_key.to_sym]] = nested_value
+          end
+        else
+          user_settings[new_key] = value
+        end
+      end
+
+      user.update_column('settings', Oj.dump(user_settings)) # rubocop:disable Rails/SkipsModelValidations
+    end
+  end
+
+  def down; end
+end
diff --git a/db/migrate/20230215074424_move_glitch_user_settings.rb b/db/migrate/20230215074424_move_glitch_user_settings.rb
new file mode 100644
index 000000000..76fafdd76
--- /dev/null
+++ b/db/migrate/20230215074424_move_glitch_user_settings.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+class MoveGlitchUserSettings < ActiveRecord::Migration[6.1]
+  class User < ApplicationRecord; end
+
+  MAPPING = {
+    favourite_modal: 'web.favourite_modal',
+    system_emoji_font: 'web.use_system_emoji_font',
+    hide_followers_count: 'hide_followers_count',
+    default_content_type: 'default_content_type',
+    flavour: 'flavour',
+    skin: 'skin',
+    notification_emails: {
+      trending_link: 'notification_emails.link_trends',
+      trending_status: 'notification_emails.status_trends',
+    }.freeze,
+  }.freeze
+
+  class LegacySetting < ApplicationRecord
+    self.table_name = 'settings'
+
+    def var
+      self[:var]&.to_sym
+    end
+
+    def value
+      YAML.safe_load(self[:value], permitted_classes: [ActiveSupport::HashWithIndifferentAccess, Symbol]) if self[:value].present?
+    end
+  end
+
+  def up
+    User.find_each do |user|
+      previous_settings = LegacySetting.where(thing_type: 'User', thing_id: user.id).index_by(&:var)
+
+      user_settings = Oj.load(user.settings || '{}')
+      user_settings.delete('theme')
+
+      MAPPING.each do |legacy_key, new_key|
+        value = previous_settings[legacy_key]&.value
+
+        next if value.blank?
+
+        if value.is_a?(Hash)
+          value.each do |nested_key, nested_value|
+            user_settings[MAPPING[legacy_key][nested_key.to_sym]] = nested_value
+          end
+        else
+          user_settings[new_key] = value
+        end
+      end
+
+      user.update_column('settings', Oj.dump(user_settings)) # rubocop:disable Rails/SkipsModelValidations
+    end
+  end
+
+  def down; end
+end
diff --git a/db/post_migrate/20180813160548_post_migrate_filters.rb b/db/post_migrate/20180813160548_post_migrate_filters.rb
index 588548c1d..82acf13d5 100644
--- a/db/post_migrate/20180813160548_post_migrate_filters.rb
+++ b/db/post_migrate/20180813160548_post_migrate_filters.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 class PostMigrateFilters < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
@@ -5,7 +7,5 @@ class PostMigrateFilters < ActiveRecord::Migration[5.2]
     drop_table :glitch_keyword_mutes if table_exists? :glitch_keyword_mutes
   end
 
-  def down
-  end
+  def down; end
 end
-
diff --git a/db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb b/db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb
index a46349cb7..615f35cd0 100644
--- a/db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb
+++ b/db/post_migrate/20190511152737_remove_suspended_silenced_account_fields.rb
@@ -34,6 +34,7 @@ class RemoveSuspendedSilencedAccountFields < ActiveRecord::Migration[5.2]
       remove_column :accounts, :suspended, :boolean, null: false, default: false
       remove_column :accounts, :silenced, :boolean, null: false, default: false
     end
+    Account.reset_column_information
   end
 
   def down
diff --git a/db/post_migrate/20200917193528_migrate_notifications_type.rb b/db/post_migrate/20200917193528_migrate_notifications_type.rb
index 88e423084..9dc9ecd48 100644
--- a/db/post_migrate/20200917193528_migrate_notifications_type.rb
+++ b/db/post_migrate/20200917193528_migrate_notifications_type.rb
@@ -4,12 +4,12 @@ class MigrateNotificationsType < ActiveRecord::Migration[5.2]
   disable_ddl_transaction!
 
   TYPES_TO_MIGRATE = {
-    'Mention'       => :mention,
-    'Status'        => :reblog,
-    'Follow'        => :follow,
+    'Mention' => :mention,
+    'Status' => :reblog,
+    'Follow' => :follow,
     'FollowRequest' => :follow_request,
-    'Favourite'     => :favourite,
-    'Poll'          => :poll,
+    'Favourite' => :favourite,
+    'Poll' => :poll,
   }.freeze
 
   def up
diff --git a/db/post_migrate/20210308133107_remove_subscription_expires_at_from_accounts.rb b/db/post_migrate/20210308133107_remove_subscription_expires_at_from_accounts.rb
index 53e24ef26..511104cef 100644
--- a/db/post_migrate/20210308133107_remove_subscription_expires_at_from_accounts.rb
+++ b/db/post_migrate/20210308133107_remove_subscription_expires_at_from_accounts.rb
@@ -1,7 +1,7 @@
-class RemoveSubscriptionExpiresAtFromAccounts < ActiveRecord::Migration[5.0]
+class RemoveSubscriptionExpiresAtFromAccounts < ActiveRecord::Migration[5.2]
   def change
     safety_assured do
-      remove_column :accounts, :subscription_expires_at, :datetime, null: true, default: nil
+      remove_column :accounts, :subscription_expires_at, :datetime, null: true, default: nil, precision: nil
     end
   end
 end
diff --git a/db/post_migrate/20220613110802_remove_whole_word_from_custom_filters.rb b/db/post_migrate/20220613110802_remove_whole_word_from_custom_filters.rb
index 7ef0749e5..99c3366a2 100644
--- a/db/post_migrate/20220613110802_remove_whole_word_from_custom_filters.rb
+++ b/db/post_migrate/20220613110802_remove_whole_word_from_custom_filters.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 require Rails.root.join('lib', 'mastodon', 'migration_helpers')
 
 class RemoveWholeWordFromCustomFilters < ActiveRecord::Migration[6.1]
diff --git a/db/post_migrate/20220613110903_remove_irreversible_from_custom_filters.rb b/db/post_migrate/20220613110903_remove_irreversible_from_custom_filters.rb
index 6ed8bcfee..1c366ee53 100644
--- a/db/post_migrate/20220613110903_remove_irreversible_from_custom_filters.rb
+++ b/db/post_migrate/20220613110903_remove_irreversible_from_custom_filters.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 require Rails.root.join('lib', 'mastodon', 'migration_helpers')
 
 class RemoveIrreversibleFromCustomFilters < ActiveRecord::Migration[6.1]
diff --git a/db/post_migrate/20220729171123_fix_custom_filter_keywords_id_seq.rb b/db/post_migrate/20220729171123_fix_custom_filter_keywords_id_seq.rb
index 7ed34a3ef..eb437c86c 100644
--- a/db/post_migrate/20220729171123_fix_custom_filter_keywords_id_seq.rb
+++ b/db/post_migrate/20220729171123_fix_custom_filter_keywords_id_seq.rb
@@ -5,7 +5,7 @@ class FixCustomFilterKeywordsIdSeq < ActiveRecord::Migration[6.1]
 
   def up
     # 20220613110711 manually inserts items with set `id` in the database, but
-    # we also need to bump the sequence number, otherwise 
+    # we also need to bump the sequence number, otherwise
     safety_assured do
       execute <<-SQL.squish
         BEGIN;
diff --git a/db/post_migrate/20221101190723_backfill_admin_action_logs.rb b/db/post_migrate/20221101190723_backfill_admin_action_logs.rb
index 48ef1e6e3..fa2ddbbca 100644
--- a/db/post_migrate/20221101190723_backfill_admin_action_logs.rb
+++ b/db/post_migrate/20221101190723_backfill_admin_action_logs.rb
@@ -62,7 +62,7 @@ class BackfillAdminActionLogs < ActiveRecord::Migration[6.1]
     # Cannot use usual polymorphic support because of namespacing issues
     belongs_to :status, foreign_key: :target_id
     belongs_to :account, foreign_key: :target_id
-    belongs_to :user, foreign_key: :user_id
+    belongs_to :user
     belongs_to :domain_block, foreign_key: :target_id
     belongs_to :domain_allow, foreign_key: :target_id
     belongs_to :email_domain_block, foreign_key: :target_id
@@ -79,11 +79,13 @@ class BackfillAdminActionLogs < ActiveRecord::Migration[6.1]
     safety_assured do
       AdminActionLog.includes(:account).where(target_type: 'Account', human_identifier: nil).find_each do |log|
         next if log.account.nil?
+
         log.update_attribute('human_identifier', log.account.acct)
       end
 
       AdminActionLog.includes(user: :account).where(target_type: 'User', human_identifier: nil).find_each do |log|
         next if log.user.nil?
+
         log.update_attribute('human_identifier', log.user.account.acct)
         log.update_attribute('route_param', log.user.account_id)
       end
@@ -92,57 +94,68 @@ class BackfillAdminActionLogs < ActiveRecord::Migration[6.1]
 
       AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log|
         next if log.domain_block.nil?
+
         log.update_attribute('human_identifier', log.domain_block.domain)
       end
 
       AdminActionLog.includes(:domain_allow).where(target_type: 'DomainAllow').find_each do |log|
         next if log.domain_allow.nil?
+
         log.update_attribute('human_identifier', log.domain_allow.domain)
       end
 
       AdminActionLog.includes(:email_domain_block).where(target_type: 'EmailDomainBlock').find_each do |log|
         next if log.email_domain_block.nil?
+
         log.update_attribute('human_identifier', log.email_domain_block.domain)
       end
 
       AdminActionLog.includes(:unavailable_domain).where(target_type: 'UnavailableDomain').find_each do |log|
         next if log.unavailable_domain.nil?
+
         log.update_attribute('human_identifier', log.unavailable_domain.domain)
       end
 
       AdminActionLog.includes(status: :account).where(target_type: 'Status', human_identifier: nil).find_each do |log|
         next if log.status.nil?
+
         log.update_attribute('human_identifier', log.status.account.acct)
         log.update_attribute('permalink', log.status.uri)
       end
 
       AdminActionLog.includes(account_warning: :account).where(target_type: 'AccountWarning', human_identifier: nil).find_each do |log|
         next if log.account_warning.nil?
+
         log.update_attribute('human_identifier', log.account_warning.account.acct)
       end
 
       AdminActionLog.includes(:announcement).where(target_type: 'Announcement', human_identifier: nil).find_each do |log|
         next if log.announcement.nil?
+
         log.update_attribute('human_identifier', log.announcement.text)
       end
 
       AdminActionLog.includes(:ip_block).where(target_type: 'IpBlock', human_identifier: nil).find_each do |log|
         next if log.ip_block.nil?
+
         log.update_attribute('human_identifier', "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}")
       end
 
       AdminActionLog.includes(:custom_emoji).where(target_type: 'CustomEmoji', human_identifier: nil).find_each do |log|
         next if log.custom_emoji.nil?
+
         log.update_attribute('human_identifier', log.custom_emoji.shortcode)
       end
 
       AdminActionLog.includes(:canonical_email_block).where(target_type: 'CanonicalEmailBlock', human_identifier: nil).find_each do |log|
         next if log.canonical_email_block.nil?
+
         log.update_attribute('human_identifier', log.canonical_email_block.canonical_email_hash)
       end
 
       AdminActionLog.includes(appeal: :account).where(target_type: 'Appeal', human_identifier: nil).find_each do |log|
         next if log.appeal.nil?
+
         log.update_attribute('human_identifier', log.appeal.account.acct)
         log.update_attribute('route_param', log.appeal.account_warning_id)
       end
diff --git a/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb b/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb
index 279053ab9..9c7ac7120 100644
--- a/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb
+++ b/db/post_migrate/20221206114142_backfill_admin_action_logs_again.rb
@@ -62,7 +62,7 @@ class BackfillAdminActionLogsAgain < ActiveRecord::Migration[6.1]
     # Cannot use usual polymorphic support because of namespacing issues
     belongs_to :status, foreign_key: :target_id
     belongs_to :account, foreign_key: :target_id
-    belongs_to :user, foreign_key: :user_id
+    belongs_to :user
     belongs_to :domain_block, foreign_key: :target_id
     belongs_to :domain_allow, foreign_key: :target_id
     belongs_to :email_domain_block, foreign_key: :target_id
@@ -79,11 +79,13 @@ class BackfillAdminActionLogsAgain < ActiveRecord::Migration[6.1]
     safety_assured do
       AdminActionLog.includes(:account).where(target_type: 'Account', human_identifier: nil).find_each do |log|
         next if log.account.nil?
+
         log.update_attribute('human_identifier', log.account.acct)
       end
 
       AdminActionLog.includes(user: :account).where(target_type: 'User', human_identifier: nil).find_each do |log|
         next if log.user.nil?
+
         log.update_attribute('human_identifier', log.user.account.acct)
         log.update_attribute('route_param', log.user.account_id)
       end
@@ -92,57 +94,68 @@ class BackfillAdminActionLogsAgain < ActiveRecord::Migration[6.1]
 
       AdminActionLog.includes(:domain_block).where(target_type: 'DomainBlock').find_each do |log|
         next if log.domain_block.nil?
+
         log.update_attribute('human_identifier', log.domain_block.domain)
       end
 
       AdminActionLog.includes(:domain_allow).where(target_type: 'DomainAllow').find_each do |log|
         next if log.domain_allow.nil?
+
         log.update_attribute('human_identifier', log.domain_allow.domain)
       end
 
       AdminActionLog.includes(:email_domain_block).where(target_type: 'EmailDomainBlock').find_each do |log|
         next if log.email_domain_block.nil?
+
         log.update_attribute('human_identifier', log.email_domain_block.domain)
       end
 
       AdminActionLog.includes(:unavailable_domain).where(target_type: 'UnavailableDomain').find_each do |log|
         next if log.unavailable_domain.nil?
+
         log.update_attribute('human_identifier', log.unavailable_domain.domain)
       end
 
       AdminActionLog.includes(status: :account).where(target_type: 'Status', human_identifier: nil).find_each do |log|
         next if log.status.nil?
+
         log.update_attribute('human_identifier', log.status.account.acct)
         log.update_attribute('permalink', log.status.uri)
       end
 
       AdminActionLog.includes(account_warning: :account).where(target_type: 'AccountWarning', human_identifier: nil).find_each do |log|
         next if log.account_warning.nil?
+
         log.update_attribute('human_identifier', log.account_warning.account.acct)
       end
 
       AdminActionLog.includes(:announcement).where(target_type: 'Announcement', human_identifier: nil).find_each do |log|
         next if log.announcement.nil?
+
         log.update_attribute('human_identifier', log.announcement.text)
       end
 
       AdminActionLog.includes(:ip_block).where(target_type: 'IpBlock', human_identifier: nil).find_each do |log|
         next if log.ip_block.nil?
+
         log.update_attribute('human_identifier', "#{log.ip_block.ip}/#{log.ip_block.ip.prefix}")
       end
 
       AdminActionLog.includes(:custom_emoji).where(target_type: 'CustomEmoji', human_identifier: nil).find_each do |log|
         next if log.custom_emoji.nil?
+
         log.update_attribute('human_identifier', log.custom_emoji.shortcode)
       end
 
       AdminActionLog.includes(:canonical_email_block).where(target_type: 'CanonicalEmailBlock', human_identifier: nil).find_each do |log|
         next if log.canonical_email_block.nil?
+
         log.update_attribute('human_identifier', log.canonical_email_block.canonical_email_hash)
       end
 
       AdminActionLog.includes(appeal: :account).where(target_type: 'Appeal', human_identifier: nil).find_each do |log|
         next if log.appeal.nil?
+
         log.update_attribute('human_identifier', log.appeal.account.acct)
         log.update_attribute('route_param', log.appeal.account_warning_id)
       end
diff --git a/db/schema.rb b/db/schema.rb
index 7462bf2c7..7d894b1aa 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 2022_12_06_114142) do
+ActiveRecord::Schema.define(version: 2023_02_15_074424) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -1063,6 +1063,7 @@ ActiveRecord::Schema.define(version: 2022_12_06_114142) do
     t.inet "sign_up_ip"
     t.boolean "skip_sign_in_token"
     t.bigint "role_id"
+    t.text "settings"
     t.index ["account_id"], name: "index_users_on_account_id"
     t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
     t.index ["created_by_application_id"], name: "index_users_on_created_by_application_id", where: "(created_by_application_id IS NOT NULL)"
diff --git a/db/seeds.rb b/db/seeds.rb
index 1ca300de7..c01e83f1d 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -1,5 +1,7 @@
 # frozen_string_literal: true
 
-Dir[Rails.root.join('db', 'seeds', '*.rb')].sort.each do |seed|
-  load seed
+Chewy.strategy(:mastodon) do
+  Dir[Rails.root.join('db', 'seeds', '*.rb')].sort.each do |seed|
+    load seed
+  end
 end
diff --git a/db/seeds/02_instance_actor.rb b/db/seeds/02_instance_actor.rb
index 39186b273..f9aa372f1 100644
--- a/db/seeds/02_instance_actor.rb
+++ b/db/seeds/02_instance_actor.rb
@@ -1 +1 @@
-Account.create_with(actor_type: 'Application', locked: true, username: ENV['LOCAL_DOMAIN'] || Rails.configuration.x.local_domain).find_or_create_by(id: -99)
+Account.create_with(actor_type: 'Application', locked: true, username: 'mastodon.internal').find_or_create_by(id: -99)
diff --git a/dist/nginx.conf b/dist/nginx.conf
index 5bc960e25..bed4bd3db 100644
--- a/dist/nginx.conf
+++ b/dist/nginx.conf
@@ -39,7 +39,7 @@ server {
 
   keepalive_timeout    70;
   sendfile             on;
-  client_max_body_size 80m;
+  client_max_body_size 99m;
 
   root /home/mastodon/live/public;
 
diff --git a/docker-compose.yml b/docker-compose.yml
index c534286c7..f603c2f7e 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -56,7 +56,7 @@ services:
 
   web:
     build: .
-    image: tootsuite/mastodon
+    image: ghcr.io/mastodon/mastodon
     restart: always
     env_file: .env.production
     command: bash -c "rm -f /mastodon/tmp/pids/server.pid; bundle exec rails s -p 3000"
@@ -77,7 +77,7 @@ services:
 
   streaming:
     build: .
-    image: tootsuite/mastodon
+    image: ghcr.io/mastodon/mastodon
     restart: always
     env_file: .env.production
     command: node ./streaming
@@ -95,7 +95,7 @@ services:
 
   sidekiq:
     build: .
-    image: tootsuite/mastodon
+    image: ghcr.io/mastodon/mastodon
     restart: always
     env_file: .env.production
     command: bundle exec sidekiq
diff --git a/jest.config.js b/jest.config.js
index 177e05f98..1eb143a59 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,9 +1,7 @@
-module.exports = {
-  'testEnvironment': 'jsdom',
-  'projects': [
-    '<rootDir>/app/javascript/mastodon',
-  ],
-  'testPathIgnorePatterns': [
+/** @type {import('jest').Config} */
+const config = {
+  testEnvironment: 'jsdom',
+  testPathIgnorePatterns: [
     '<rootDir>/node_modules/',
     '<rootDir>/vendor/',
     '<rootDir>/config/',
@@ -12,22 +10,17 @@ module.exports = {
     '<rootDir>/tmp/',
     '<rootDir>/app/javascript/themes/',
   ],
-  'setupFiles': [
-    'raf/polyfill',
-  ],
-  'setupFilesAfterEnv': [
-    '<rootDir>/app/javascript/mastodon/test_setup.js',
-  ],
-  'collectCoverageFrom': [
+  setupFiles: ['raf/polyfill'],
+  setupFilesAfterEnv: ['<rootDir>/app/javascript/mastodon/test_setup.js'],
+  collectCoverageFrom: [
     'app/javascript/mastodon/**/*.js',
     '!app/javascript/mastodon/features/emoji/emoji_compressed.js',
     '!app/javascript/mastodon/locales/locale-data/*.js',
     '!app/javascript/mastodon/service_worker/entry.js',
     '!app/javascript/mastodon/test_setup.js',
   ],
-  'coverageDirectory': '<rootDir>/coverage',
-  'moduleDirectories': [
-    '<rootDir>/node_modules',
-    '<rootDir>/app/javascript',
-  ],
+  coverageDirectory: '<rootDir>/coverage',
+  moduleDirectories: ['<rootDir>/node_modules', '<rootDir>/app/javascript'],
 };
+
+module.exports = config;
diff --git a/lib/active_record/database_tasks_extensions.rb b/lib/active_record/database_tasks_extensions.rb
index e274f476d..d52186113 100644
--- a/lib/active_record/database_tasks_extensions.rb
+++ b/lib/active_record/database_tasks_extensions.rb
@@ -11,7 +11,7 @@ module ActiveRecord
         ActiveRecord::Base.establish_connection(db_config)
         Mastodon::Snowflake.define_timestamp_id
 
-        original_load_schema.bind(self).call(db_config, *args)
+        original_load_schema.bind_call(self, db_config, *args)
 
         Mastodon::Snowflake.ensure_id_sequences_exist
       end
diff --git a/lib/assets/wordmark.light.css b/lib/assets/wordmark.light.css
index 9a601f972..b8c9993fd 100644
--- a/lib/assets/wordmark.light.css
+++ b/lib/assets/wordmark.light.css
@@ -1 +1,3 @@
-use { color: #000 !important; }
+use {
+  color: #000 !important;
+}
diff --git a/lib/chewy/strategy/bypass_with_warning.rb b/lib/chewy/strategy/bypass_with_warning.rb
new file mode 100644
index 000000000..eb6fbaab1
--- /dev/null
+++ b/lib/chewy/strategy/bypass_with_warning.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Chewy
+  class Strategy
+    class BypassWithWarning < Base
+      def update(...)
+        Rails.logger.warn 'Chewy update without a root strategy' unless @warning_issued
+        @warning_issued = true
+      end
+    end
+  end
+end
diff --git a/lib/cli.rb b/lib/cli.rb
index 35c00e736..ac235cf03 100644
--- a/lib/cli.rb
+++ b/lib/cli.rb
@@ -121,7 +121,7 @@ module Mastodon
 
       prompt.warn('Do NOT interrupt this process...')
 
-      delete_account = ->(account) do
+      delete_account = lambda do |account|
         payload = ActiveModelSerializers::SerializableResource.new(
           account,
           serializer: ActivityPub::DeleteActorSerializer,
@@ -131,7 +131,7 @@ module Mastodon
         json = Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(account))
 
         unless options[:dry_run]
-          ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
+          ActivityPub::DeliveryWorker.push_bulk(inboxes, limit: 1_000) do |inbox_url|
             [json, account.id, inbox_url]
           end
 
diff --git a/lib/mastodon/accounts_cli.rb b/lib/mastodon/accounts_cli.rb
index 34afbc699..a6532541e 100644
--- a/lib/mastodon/accounts_cli.rb
+++ b/lib/mastodon/accounts_cli.rb
@@ -372,16 +372,16 @@ module Mastodon
     option :concurrency, type: :numeric, default: 5, aliases: [:c]
     option :verbose, type: :boolean, aliases: [:v]
     option :dry_run, type: :boolean
-    desc 'refresh [USERNAME]', 'Fetch remote user data and files'
+    desc 'refresh [USERNAMES]', 'Fetch remote user data and files'
     long_desc <<-LONG_DESC
       Fetch remote user data and files for one or multiple accounts.
 
       With the --all option, all remote accounts will be processed.
       Through the --domain option, this can be narrowed down to a
-      specific domain only. Otherwise, a single remote account must
-      be specified with USERNAME.
+      specific domain only. Otherwise, remote accounts must be
+      specified with space-separated USERNAMES.
     LONG_DESC
-    def refresh(username = nil)
+    def refresh(*usernames)
       dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
 
       if options[:domain] || options[:all]
@@ -397,19 +397,25 @@ module Mastodon
         end
 
         say("Refreshed #{processed} accounts#{dry_run}", :green, true)
-      elsif username.present?
-        username, domain = username.split('@')
-        account = Account.find_remote(username, domain)
+      elsif !usernames.empty?
+        usernames.each do |user|
+          user, domain = user.split('@')
+          account = Account.find_remote(user, domain)
+
+          if account.nil?
+            say('No such account', :red)
+            exit(1)
+          end
 
-        if account.nil?
-          say('No such account', :red)
-          exit(1)
-        end
+          next if options[:dry_run]
 
-        unless options[:dry_run]
-          account.reset_avatar!
-          account.reset_header!
-          account.save
+          begin
+            account.reset_avatar!
+            account.reset_header!
+            account.save
+          rescue Mastodon::UnexpectedResponseError
+            say("Account failed: #{user}@#{domain}", :red)
+          end
         end
 
         say("OK#{dry_run}", :green)
@@ -490,14 +496,12 @@ module Mastodon
         scope = Account.where(id: ::Follow.where(account: account).select(:target_account_id))
 
         scope.find_each do |target_account|
-          begin
-            UnfollowService.new.call(account, target_account)
-          rescue => e
-            progress.log pastel.red("Error processing #{target_account.id}: #{e}")
-          ensure
-            progress.increment
-            processed += 1
-          end
+          UnfollowService.new.call(account, target_account)
+        rescue => e
+          progress.log pastel.red("Error processing #{target_account.id}: #{e}")
+        ensure
+          progress.increment
+          processed += 1
         end
 
         BootstrapTimelineWorker.perform_async(account.id)
@@ -507,14 +511,12 @@ module Mastodon
         scope = Account.where(id: ::Follow.where(target_account: account).select(:account_id))
 
         scope.find_each do |target_account|
-          begin
-            UnfollowService.new.call(target_account, account)
-          rescue => e
-            progress.log pastel.red("Error processing #{target_account.id}: #{e}")
-          ensure
-            progress.increment
-            processed += 1
-          end
+          UnfollowService.new.call(target_account, account)
+        rescue => e
+          progress.log pastel.red("Error processing #{target_account.id}: #{e}")
+        ensure
+          progress.increment
+          processed += 1
         end
       end
 
@@ -631,7 +633,7 @@ module Mastodon
           exit(1)
         end
 
-        unless options[:force] || migration.target_acount_id == account.moved_to_account_id
+        unless options[:force] || migration.target_account_id == account.moved_to_account_id
           say('The specified account is not redirecting to its last migration target. Use --force if you want to replay the migration anyway', :red)
           exit(1)
         end
diff --git a/lib/mastodon/cli_helper.rb b/lib/mastodon/cli_helper.rb
index a78a28e27..ab1351ae8 100644
--- a/lib/mastodon/cli_helper.rb
+++ b/lib/mastodon/cli_helper.rb
@@ -42,17 +42,17 @@ module Mastodon
 
         items.each do |item|
           futures << Concurrent::Future.execute(executor: pool) do
-            begin
-              if !progress.total.nil? && progress.progress + 1 > progress.total
-                # The number of items has changed between start and now,
-                # since there is no good way to predict the final count from
-                # here, just change the progress bar to an indeterminate one
+            if !progress.total.nil? && progress.progress + 1 > progress.total
+              # The number of items has changed between start and now,
+              # since there is no good way to predict the final count from
+              # here, just change the progress bar to an indeterminate one
 
-                progress.total = nil
-              end
+              progress.total = nil
+            end
 
-              progress.log("Processing #{item.id}") if options[:verbose]
+            progress.log("Processing #{item.id}") if options[:verbose]
 
+            Chewy.strategy(:mastodon) do
               result = ActiveRecord::Base.connection_pool.with_connection do
                 yield(item)
               ensure
@@ -61,11 +61,11 @@ module Mastodon
               end
 
               aggregate.increment(result) if result.is_a?(Integer)
-            rescue => e
-              progress.log pastel.red("Error processing #{item.id}: #{e}")
-            ensure
-              progress.increment
             end
+          rescue => e
+            progress.log pastel.red("Error processing #{item.id}: #{e}")
+          ensure
+            progress.increment
           end
         end
 
diff --git a/lib/mastodon/domains_cli.rb b/lib/mastodon/domains_cli.rb
index 77364ffbb..05f08f462 100644
--- a/lib/mastodon/domains_cli.rb
+++ b/lib/mastodon/domains_cli.rb
@@ -18,6 +18,8 @@ module Mastodon
     option :dry_run, type: :boolean
     option :limited_federation_mode, type: :boolean
     option :by_uri, type: :boolean
+    option :include_subdomains, type: :boolean
+    option :purge_domain_blocks, type: :boolean
     desc 'purge [DOMAIN...]', 'Remove accounts from a DOMAIN without a trace'
     long_desc <<-LONG_DESC
       Remove all accounts from a given DOMAIN without leaving behind any
@@ -33,40 +35,75 @@ module Mastodon
       that has the handle `foo@bar.com` but whose profile is at the URL
       `https://mastodon-bar.com/users/foo`, would be purged by either
       `tootctl domains purge bar.com` or `tootctl domains purge --by-uri mastodon-bar.com`.
+
+      When the --include-subdomains option is given, not only DOMAIN is deleted, but all
+      subdomains as well. Note that this may be considerably slower.
+
+      When the --purge-domain-blocks option is given, also purge matching domain blocks.
     LONG_DESC
     def purge(*domains)
-      dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
-
-      scope = begin
-        if options[:limited_federation_mode]
-          Account.remote.where.not(domain: DomainAllow.pluck(:domain))
-        elsif !domains.empty?
-          if options[:by_uri]
-            domains.map { |domain| Account.remote.where(Account.arel_table[:uri].matches("https://#{domain}/%", false, true)) }.reduce(:or)
-          else
-            Account.remote.where(domain: domains)
-          end
+      dry_run            = options[:dry_run] ? ' (DRY RUN)' : ''
+      domains            = domains.map { |domain| TagManager.instance.normalize_domain(domain) }
+      account_scope      = Account.none
+      domain_block_scope = DomainBlock.none
+      emoji_scope        = CustomEmoji.none
+
+      # Sanity check on command arguments
+      if options[:limited_federation_mode] && !domains.empty?
+        say('DOMAIN parameter not supported with --limited-federation-mode', :red)
+        exit(1)
+      elsif domains.empty? && !options[:limited_federation_mode]
+        say('No domain(s) given', :red)
+        exit(1)
+      end
+
+      # Build scopes from command arguments
+      if options[:limited_federation_mode]
+        account_scope = Account.remote.where.not(domain: DomainAllow.select(:domain))
+        emoji_scope   = CustomEmoji.remote.where.not(domain: DomainAllow.select(:domain))
+      else
+        # Handle wildcard subdomains
+        subdomain_patterns = domains.filter_map { |domain| "%.#{Account.sanitize_sql_like(domain[2..])}" if domain.start_with?('*.') }
+        domains = domains.filter { |domain| !domain.start_with?('*.') }
+        # Handle --include-subdomains
+        subdomain_patterns += domains.map { |domain| "%.#{Account.sanitize_sql_like(domain)}" } if options[:include_subdomains]
+        uri_patterns = (domains.map { |domain| Account.sanitize_sql_like(domain) } + subdomain_patterns).map { |pattern| "https://#{pattern}/%" }
+
+        if options[:purge_domain_blocks]
+          domain_block_scope = DomainBlock.where(domain: domains)
+          domain_block_scope = domain_block_scope.or(DomainBlock.where(DomainBlock.arel_table[:domain].matches_any(subdomain_patterns))) unless subdomain_patterns.empty?
+        end
+
+        if options[:by_uri]
+          account_scope = Account.remote.where(Account.arel_table[:uri].matches_any(uri_patterns, false, true))
+          emoji_scope   = CustomEmoji.remote.where(CustomEmoji.arel_table[:uri].matches_any(uri_patterns, false, true))
         else
-          say('No domain(s) given', :red)
-          exit(1)
+          account_scope = Account.remote.where(domain: domains)
+          account_scope = account_scope.or(Account.remote.where(Account.arel_table[:domain].matches_any(subdomain_patterns))) unless subdomain_patterns.empty?
+          emoji_scope   = CustomEmoji.where(domain: domains)
+          emoji_scope   = emoji_scope.or(CustomEmoji.remote.where(CustomEmoji.arel_table[:uri].matches_any(subdomain_patterns))) unless subdomain_patterns.empty?
         end
       end
 
-      processed, = parallelize_with_progress(scope) do |account|
+      # Actually perform the deletions
+      processed, = parallelize_with_progress(account_scope) do |account|
         DeleteAccountService.new.call(account, reserve_username: false, skip_side_effects: true) unless options[:dry_run]
       end
 
-      DomainBlock.where(domain: domains).destroy_all unless options[:dry_run]
-
       say("Removed #{processed} accounts#{dry_run}", :green)
 
-      custom_emojis = CustomEmoji.where(domain: domains)
-      custom_emojis_count = custom_emojis.count
-      custom_emojis.destroy_all unless options[:dry_run]
+      if options[:purge_domain_blocks]
+        domain_block_count = domain_block_scope.count
+        domain_block_scope.in_batches.destroy_all unless options[:dry_run]
+        say("Removed #{domain_block_count} domain blocks#{dry_run}", :green)
+      end
+
+      custom_emojis_count = emoji_scope.count
+      emoji_scope.in_batches.destroy_all unless options[:dry_run]
 
       Instance.refresh unless options[:dry_run]
 
-      say("Removed #{custom_emojis_count} custom emojis", :green)
+      say("Removed #{custom_emojis_count} custom emojis#{dry_run}", :green)
     end
 
     option :concurrency, type: :numeric, default: 50, aliases: [:c]
@@ -102,7 +139,7 @@ module Mastodon
 
       pool = Concurrent::ThreadPoolExecutor.new(min_threads: 0, max_threads: options[:concurrency], idletime: 10, auto_terminate: true, max_queue: 0)
 
-      work_unit = ->(domain) do
+      work_unit = lambda do |domain|
         next if stats.key?(domain)
         next if options[:exclude_suspended] && domain.match?(blocked_domains)
 
@@ -111,6 +148,7 @@ module Mastodon
         begin
           Request.new(:get, "https://#{domain}/api/v1/instance").perform do |res|
             next unless res.code == 200
+
             stats[domain] = Oj.load(res.to_s)
           end
 
@@ -124,9 +162,10 @@ module Mastodon
 
           Request.new(:get, "https://#{domain}/api/v1/instance/activity").perform do |res|
             next unless res.code == 200
+
             stats[domain]['activity'] = Oj.load(res.to_s)
           end
-        rescue StandardError
+        rescue
           failed.increment
         ensure
           processed.increment
diff --git a/lib/mastodon/emoji_cli.rb b/lib/mastodon/emoji_cli.rb
index a3e947909..88065c2a3 100644
--- a/lib/mastodon/emoji_cli.rb
+++ b/lib/mastodon/emoji_cli.rb
@@ -49,7 +49,7 @@ module Mastodon
           next if filename.start_with?('._')
 
           shortcode    = [options[:prefix], filename, options[:suffix]].compact.join
-          custom_emoji = CustomEmoji.local.find_by("LOWER(shortcode) = ?", shortcode.downcase)
+          custom_emoji = CustomEmoji.local.find_by('LOWER(shortcode) = ?', shortcode.downcase)
 
           if custom_emoji && !options[:overwrite]
             skipped += 1
@@ -68,7 +68,7 @@ module Mastodon
             failed += 1
             say('Failure/Error: ', :red)
             say(entry.full_name)
-            say('    ' + custom_emoji.errors[:image].join(', '), :red)
+            say("    #{custom_emoji.errors[:image].join(', ')}", :red)
           end
         end
       end
diff --git a/lib/mastodon/feeds_cli.rb b/lib/mastodon/feeds_cli.rb
index 428d63a44..fcfb48740 100644
--- a/lib/mastodon/feeds_cli.rb
+++ b/lib/mastodon/feeds_cli.rb
@@ -53,11 +53,7 @@ module Mastodon
     desc 'clear', 'Remove all home and list feeds from Redis'
     def clear
       keys = redis.keys('feed:*')
-
-      redis.pipelined do
-        keys.each { |key| redis.del(key) }
-      end
-
+      redis.del(keys)
       say('OK', :green)
     end
   end
diff --git a/lib/mastodon/ip_blocks_cli.rb b/lib/mastodon/ip_blocks_cli.rb
index 5c38c1aca..08939c092 100644
--- a/lib/mastodon/ip_blocks_cli.rb
+++ b/lib/mastodon/ip_blocks_cli.rb
@@ -79,13 +79,11 @@ module Mastodon
       skipped   = 0
 
       addresses.each do |address|
-        ip_blocks = begin
-          if options[:force]
-            IpBlock.where('ip >>= ?', address)
-          else
-            IpBlock.where('ip <<= ?', address)
-          end
-        end
+        ip_blocks = if options[:force]
+                      IpBlock.where('ip >>= ?', address)
+                    else
+                      IpBlock.where('ip <<= ?', address)
+                    end
 
         if ip_blocks.empty?
           say("#{address} is not yet blocked", :yellow)
diff --git a/lib/mastodon/maintenance_cli.rb b/lib/mastodon/maintenance_cli.rb
index 85937da81..ff8f6ddda 100644
--- a/lib/mastodon/maintenance_cli.rb
+++ b/lib/mastodon/maintenance_cli.rb
@@ -13,8 +13,8 @@ module Mastodon
       true
     end
 
-    MIN_SUPPORTED_VERSION = 2019_10_01_213028 # rubocop:disable Style/NumericLiterals
-    MAX_SUPPORTED_VERSION = 2022_11_04_133904 # rubocop:disable Style/NumericLiterals
+    MIN_SUPPORTED_VERSION = 2019_10_01_213028
+    MAX_SUPPORTED_VERSION = 2022_11_04_133904
 
     # Stubs to enjoy ActiveRecord queries while not depending on a particular
     # version of the code/database
@@ -98,11 +98,9 @@ module Mastodon
 
         owned_classes.each do |klass|
           klass.where(account_id: other_account.id).find_each do |record|
-            begin
-              record.update_attribute(:account_id, id)
-            rescue ActiveRecord::RecordNotUnique
-              next
-            end
+            record.update_attribute(:account_id, id)
+          rescue ActiveRecord::RecordNotUnique
+            next
           end
         end
 
@@ -111,11 +109,9 @@ module Mastodon
 
         target_classes.each do |klass|
           klass.where(target_account_id: other_account.id).find_each do |record|
-            begin
-              record.update_attribute(:target_account_id, id)
-            rescue ActiveRecord::RecordNotUnique
-              next
-            end
+            record.update_attribute(:target_account_id, id)
+          rescue ActiveRecord::RecordNotUnique
+            next
           end
         end
 
@@ -209,7 +205,7 @@ module Mastodon
       end
 
       @prompt.say 'Restoring index_accounts_on_username_and_domain_lower…'
-      if ActiveRecord::Migrator.current_version < 20200620164023 # rubocop:disable Style/NumericLiterals
+      if ActiveRecord::Migrator.current_version < 2020_06_20_164023
         ActiveRecord::Base.connection.add_index :accounts, 'lower (username), lower(domain)', name: 'index_accounts_on_username_and_domain_lower', unique: true
       else
         ActiveRecord::Base.connection.add_index :accounts, "lower (username), COALESCE(lower(domain), '')", name: 'index_accounts_on_username_and_domain_lower', unique: true
@@ -252,7 +248,7 @@ module Mastodon
         end
       end
 
-      if ActiveRecord::Migrator.current_version < 20220118183010 # rubocop:disable Style/NumericLiterals
+      if ActiveRecord::Migrator.current_version < 2022_01_18_183010
         ActiveRecord::Base.connection.select_all("SELECT string_agg(id::text, ',') AS ids FROM users WHERE remember_token IS NOT NULL GROUP BY remember_token HAVING count(*) > 1").each do |row|
           users = User.where(id: row['ids'].split(',')).sort_by(&:updated_at).reverse.drop(1)
           @prompt.warn "Unsetting remember token for those accounts: #{users.map(&:account).map(&:acct).join(', ')}"
@@ -275,9 +271,9 @@ module Mastodon
       @prompt.say 'Restoring users indexes…'
       ActiveRecord::Base.connection.add_index :users, ['confirmation_token'], name: 'index_users_on_confirmation_token', unique: true
       ActiveRecord::Base.connection.add_index :users, ['email'], name: 'index_users_on_email', unique: true
-      ActiveRecord::Base.connection.add_index :users, ['remember_token'], name: 'index_users_on_remember_token', unique: true if ActiveRecord::Migrator.current_version < 20220118183010
+      ActiveRecord::Base.connection.add_index :users, ['remember_token'], name: 'index_users_on_remember_token', unique: true if ActiveRecord::Migrator.current_version < 2022_01_18_183010
 
-      if ActiveRecord::Migrator.current_version < 20220310060641 # rubocop:disable Style/NumericLiterals
+      if ActiveRecord::Migrator.current_version < 2022_03_10_060641
         ActiveRecord::Base.connection.add_index :users, ['reset_password_token'], name: 'index_users_on_reset_password_token', unique: true
       else
         ActiveRecord::Base.connection.add_index :users, ['reset_password_token'], name: 'index_users_on_reset_password_token', unique: true, where: 'reset_password_token IS NOT NULL', opclass: :text_pattern_ops
@@ -293,7 +289,7 @@ module Mastodon
       end
 
       @prompt.say 'Restoring account domain blocks indexes…'
-      ActiveRecord::Base.connection.add_index :account_domain_blocks, ['account_id', 'domain'], name: 'index_account_domain_blocks_on_account_id_and_domain', unique: true
+      ActiveRecord::Base.connection.add_index :account_domain_blocks, %w(account_id domain), name: 'index_account_domain_blocks_on_account_id_and_domain', unique: true
     end
 
     def deduplicate_account_identity_proofs!
@@ -307,7 +303,7 @@ module Mastodon
       end
 
       @prompt.say 'Restoring account identity proofs indexes…'
-      ActiveRecord::Base.connection.add_index :account_identity_proofs, ['account_id', 'provider', 'provider_username'], name: 'index_account_proofs_on_account_and_provider_and_username', unique: true
+      ActiveRecord::Base.connection.add_index :account_identity_proofs, %w(account_id provider provider_username), name: 'index_account_proofs_on_account_and_provider_and_username', unique: true
     end
 
     def deduplicate_announcement_reactions!
@@ -321,7 +317,7 @@ module Mastodon
       end
 
       @prompt.say 'Restoring announcement_reactions indexes…'
-      ActiveRecord::Base.connection.add_index :announcement_reactions, ['account_id', 'announcement_id', 'name'], name: 'index_announcement_reactions_on_account_id_and_announcement_id', unique: true
+      ActiveRecord::Base.connection.add_index :announcement_reactions, %w(account_id announcement_id name), name: 'index_announcement_reactions_on_account_id_and_announcement_id', unique: true
     end
 
     def deduplicate_conversations!
@@ -340,7 +336,7 @@ module Mastodon
       end
 
       @prompt.say 'Restoring conversations indexes…'
-      if ActiveRecord::Migrator.current_version < 20220307083603 # rubocop:disable Style/NumericLiterals
+      if ActiveRecord::Migrator.current_version < 2022_03_07_083603
         ActiveRecord::Base.connection.add_index :conversations, ['uri'], name: 'index_conversations_on_uri', unique: true
       else
         ActiveRecord::Base.connection.add_index :conversations, ['uri'], name: 'index_conversations_on_uri', unique: true, where: 'uri IS NOT NULL', opclass: :text_pattern_ops
@@ -363,7 +359,7 @@ module Mastodon
       end
 
       @prompt.say 'Restoring custom_emojis indexes…'
-      ActiveRecord::Base.connection.add_index :custom_emojis, ['shortcode', 'domain'], name: 'index_custom_emojis_on_shortcode_and_domain', unique: true
+      ActiveRecord::Base.connection.add_index :custom_emojis, %w(shortcode domain), name: 'index_custom_emojis_on_shortcode_and_domain', unique: true
     end
 
     def deduplicate_custom_emoji_categories!
@@ -457,7 +453,7 @@ module Mastodon
       end
 
       @prompt.say 'Restoring media_attachments indexes…'
-      if ActiveRecord::Migrator.current_version < 20220310060626 # rubocop:disable Style/NumericLiterals
+      if ActiveRecord::Migrator.current_version < 2022_03_10_060626
         ActiveRecord::Base.connection.add_index :media_attachments, ['shortcode'], name: 'index_media_attachments_on_shortcode', unique: true
       else
         ActiveRecord::Base.connection.add_index :media_attachments, ['shortcode'], name: 'index_media_attachments_on_shortcode', unique: true, where: 'shortcode IS NOT NULL', opclass: :text_pattern_ops
@@ -490,7 +486,7 @@ module Mastodon
       end
 
       @prompt.say 'Restoring statuses indexes…'
-      if ActiveRecord::Migrator.current_version < 20220310060706 # rubocop:disable Style/NumericLiterals
+      if ActiveRecord::Migrator.current_version < 2022_03_10_060706
         ActiveRecord::Base.connection.add_index :statuses, ['uri'], name: 'index_statuses_on_uri', unique: true
       else
         ActiveRecord::Base.connection.add_index :statuses, ['uri'], name: 'index_statuses_on_uri', unique: true, where: 'uri IS NOT NULL', opclass: :text_pattern_ops
@@ -512,7 +508,7 @@ module Mastodon
       end
 
       @prompt.say 'Restoring tags indexes…'
-      if ActiveRecord::Migrator.current_version < 20210421121431
+      if ActiveRecord::Migrator.current_version < 2021_04_21_121431
         ActiveRecord::Base.connection.add_index :tags, 'lower((name)::text)', name: 'index_tags_on_name_lower', unique: true
       else
         ActiveRecord::Base.connection.execute 'CREATE UNIQUE INDEX CONCURRENTLY index_tags_on_name_lower_btree ON tags (lower(name) text_pattern_ops)'
@@ -554,7 +550,7 @@ module Mastodon
       @prompt.warn 'All those accounts are distinct accounts but only the most recently-created one is fully-functional.'
 
       accounts.each_with_index do |account, idx|
-        @prompt.say '%2d. %s: created at: %s; updated at: %s; last logged in at: %s; statuses: %5d; last status at: %s' % [idx, account.username, account.created_at, account.updated_at, account.user&.last_sign_in_at&.to_s || 'N/A', account.account_stat&.statuses_count || 0, account.account_stat&.last_status_at || 'N/A']
+        @prompt.say format('%2d. %s: created at: %s; updated at: %s; last logged in at: %s; statuses: %5d; last status at: %s', idx, account.username, account.created_at, account.updated_at, account.user&.last_sign_in_at&.to_s || 'N/A', account.account_stat&.statuses_count || 0, account.account_stat&.last_status_at || 'N/A')
       end
 
       @prompt.say 'Please chose the one to keep unchanged, other ones will be automatically renamed.'
@@ -601,11 +597,9 @@ module Mastodon
       owned_classes = [ConversationMute, AccountConversation]
       owned_classes.each do |klass|
         klass.where(conversation_id: duplicate_conv.id).find_each do |record|
-          begin
-            record.update_attribute(:account_id, main_conv.id)
-          rescue ActiveRecord::RecordNotUnique
-            next
-          end
+          record.update_attribute(:account_id, main_conv.id)
+        rescue ActiveRecord::RecordNotUnique
+          next
         end
       end
     end
@@ -629,47 +623,37 @@ module Mastodon
       owned_classes << Bookmark if ActiveRecord::Base.connection.table_exists?(:bookmarks)
       owned_classes.each do |klass|
         klass.where(status_id: duplicate_status.id).find_each do |record|
-          begin
-            record.update_attribute(:status_id, main_status.id)
-          rescue ActiveRecord::RecordNotUnique
-            next
-          end
-        end
-      end
-
-      StatusPin.where(account_id: main_status.account_id, status_id: duplicate_status.id).find_each do |record|
-        begin
           record.update_attribute(:status_id, main_status.id)
         rescue ActiveRecord::RecordNotUnique
           next
         end
       end
 
+      StatusPin.where(account_id: main_status.account_id, status_id: duplicate_status.id).find_each do |record|
+        record.update_attribute(:status_id, main_status.id)
+      rescue ActiveRecord::RecordNotUnique
+        next
+      end
+
       Status.where(in_reply_to_id: duplicate_status.id).find_each do |record|
-        begin
-          record.update_attribute(:in_reply_to_id, main_status.id)
-        rescue ActiveRecord::RecordNotUnique
-          next
-        end
+        record.update_attribute(:in_reply_to_id, main_status.id)
+      rescue ActiveRecord::RecordNotUnique
+        next
       end
 
       Status.where(reblog_of_id: duplicate_status.id).find_each do |record|
-        begin
-          record.update_attribute(:reblog_of_id, main_status.id)
-        rescue ActiveRecord::RecordNotUnique
-          next
-        end
+        record.update_attribute(:reblog_of_id, main_status.id)
+      rescue ActiveRecord::RecordNotUnique
+        next
       end
     end
 
     def merge_tags!(main_tag, duplicate_tag)
       [FeaturedTag].each do |klass|
         klass.where(tag_id: duplicate_tag.id).find_each do |record|
-          begin
-            record.update_attribute(:tag_id, main_tag.id)
-          rescue ActiveRecord::RecordNotUnique
-            next
-          end
+          record.update_attribute(:tag_id, main_tag.id)
+        rescue ActiveRecord::RecordNotUnique
+          next
         end
       end
     end
diff --git a/lib/mastodon/media_cli.rb b/lib/mastodon/media_cli.rb
index 24cc98964..b2dfe58d5 100644
--- a/lib/mastodon/media_cli.rb
+++ b/lib/mastodon/media_cli.rb
@@ -35,7 +35,6 @@ module Mastodon
       follow status. By default, only accounts that are not followed by or
       following anyone locally are pruned.
     DESC
-    # rubocop:disable Metrics/PerceivedComplexity
     def remove
       if options[:prune_profiles] && options[:remove_headers]
         say('--prune-profiles and --remove-headers should not be specified simultaneously', :red, true)
@@ -116,13 +115,11 @@ module Mastodon
 
         loop do
           objects = begin
-            begin
-              bucket.objects(start_after: last_key, prefix: prefix).limit(1000).map { |x| x }
-            rescue => e
-              progress.log(pastel.red("Error fetching list of files: #{e}"))
-              progress.log("If you want to continue from this point, add --start-after=#{last_key} to your command") if last_key
-              break
-            end
+            bucket.objects(start_after: last_key, prefix: prefix).limit(1000).map { |x| x }
+          rescue => e
+            progress.log(pastel.red("Error fetching list of files: #{e}"))
+            progress.log("If you want to continue from this point, add --start-after=#{last_key} to your command") if last_key
+            break
           end
 
           break if objects.empty?
@@ -226,7 +223,6 @@ module Mastodon
 
       say("Removed #{removed} orphans (approx. #{number_to_human_size(reclaimed_bytes)})#{dry_run}", :green, true)
     end
-    # rubocop:enable Metrics/PerceivedComplexity
 
     option :account, type: :string
     option :domain, type: :string
@@ -277,9 +273,7 @@ module Mastodon
         exit(1)
       end
 
-      if options[:days].present?
-        scope = scope.where('media_attachments.id > ?', Mastodon::Snowflake.id_at(options[:days].days.ago, with_random: false))
-      end
+      scope = scope.where('media_attachments.id > ?', Mastodon::Snowflake.id_at(options[:days].days.ago, with_random: false)) if options[:days].present?
 
       processed, aggregate = parallelize_with_progress(scope) do |media_attachment|
         next if media_attachment.remote_url.blank? || (!options[:force] && media_attachment.file_file_name.present?)
diff --git a/lib/mastodon/migration_helpers.rb b/lib/mastodon/migration_helpers.rb
index 2ab8150ec..5a252b351 100644
--- a/lib/mastodon/migration_helpers.rb
+++ b/lib/mastodon/migration_helpers.rb
@@ -289,8 +289,6 @@ module Mastodon
     # determines this method to be too complex while there's no way to make it
     # less "complex" without introducing extra methods (which actually will
     # make things _more_ complex).
-    #
-    # rubocop: disable Metrics/AbcSize
     def update_column_in_batches(table_name, column, value)
       if transaction_open?
         raise 'update_column_in_batches can not be run inside a transaction, ' \
@@ -573,7 +571,7 @@ module Mastodon
             o.conname as name,
             o.confdeltype as on_delete
           from pg_constraint o
-          left join pg_class f on f.oid = o.confrelid 
+          left join pg_class f on f.oid = o.confrelid
           left join pg_class c on c.oid = o.conrelid
           left join pg_class m on m.oid = o.conrelid
           where o.contype = 'f'
diff --git a/lib/mastodon/migration_warning.rb b/lib/mastodon/migration_warning.rb
new file mode 100644
index 000000000..227f6705d
--- /dev/null
+++ b/lib/mastodon/migration_warning.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+module Mastodon
+  module MigrationWarning
+    WARNING_SECONDS = 10
+
+    DEFAULT_WARNING = <<~WARNING_MESSAGE
+      WARNING: This migration may take a *long* time for large instances.
+      It will *not* lock tables for any significant time, but it may run
+      for a very long time. We will pause for #{WARNING_SECONDS} seconds to allow you to
+      interrupt this migration if you are not ready.
+    WARNING_MESSAGE
+
+    def migration_duration_warning(explanation = nil)
+      return unless valid_environment?
+
+      announce_warning(explanation)
+
+      announce_countdown
+    end
+
+    private
+
+    def announce_countdown
+      WARNING_SECONDS.downto(1) do |i|
+        say "Continuing in #{i} second#{i == 1 ? '' : 's'}...", true
+        sleep 1
+      end
+    end
+
+    def valid_environment?
+      $stdout.isatty && Rails.env.production?
+    end
+
+    def announce_warning(explanation)
+      announce_message prepare_message(explanation)
+    end
+
+    def announce_message(text)
+      say ''
+      text.each_line do |line|
+        say(line)
+      end
+      say ''
+    end
+
+    def prepare_message(explanation)
+      if explanation.blank?
+        DEFAULT_WARNING
+      else
+        DEFAULT_WARNING + "\n#{explanation}"
+      end
+    end
+  end
+end
diff --git a/lib/mastodon/premailer_webpack_strategy.rb b/lib/mastodon/premailer_webpack_strategy.rb
index 56ef09c1a..5c297d4d0 100644
--- a/lib/mastodon/premailer_webpack_strategy.rb
+++ b/lib/mastodon/premailer_webpack_strategy.rb
@@ -13,7 +13,7 @@ module PremailerWebpackStrategy
             HTTP.get(url).to_s
           else
             url = url[1..-1] if url.start_with?('/')
-            File.read(Rails.root.join('public', url))
+            Rails.public_path.join(url).read
           end
 
     css.gsub(/url\(\//, "url(#{asset_host}/")
diff --git a/lib/mastodon/redis_config.rb b/lib/mastodon/redis_config.rb
index 3522fa11e..2dbe76f83 100644
--- a/lib/mastodon/redis_config.rb
+++ b/lib/mastodon/redis_config.rb
@@ -1,17 +1,17 @@
 # frozen_string_literal: true
 
 def setup_redis_env_url(prefix = nil, defaults = true)
-  prefix = prefix.to_s.upcase + '_' unless prefix.nil?
+  prefix = "#{prefix.to_s.upcase}_" unless prefix.nil?
   prefix = '' if prefix.nil?
 
-  return if ENV[prefix + 'REDIS_URL'].present?
+  return if ENV["#{prefix}REDIS_URL"].present?
 
-  password = ENV.fetch(prefix + 'REDIS_PASSWORD') { '' if defaults }
-  host     = ENV.fetch(prefix + 'REDIS_HOST') { 'localhost' if defaults }
-  port     = ENV.fetch(prefix + 'REDIS_PORT') { 6379 if defaults }
-  db       = ENV.fetch(prefix + 'REDIS_DB') { 0 if defaults }
+  password = ENV.fetch("#{prefix}REDIS_PASSWORD") { '' if defaults }
+  host     = ENV.fetch("#{prefix}REDIS_HOST") { 'localhost' if defaults }
+  port     = ENV.fetch("#{prefix}REDIS_PORT") { 6379 if defaults }
+  db       = ENV.fetch("#{prefix}REDIS_DB") { 0 if defaults }
 
-  ENV[prefix + 'REDIS_URL'] = begin
+  ENV["#{prefix}REDIS_URL"] = begin
     if [password, host, port, db].all?(&:nil?)
       ENV['REDIS_URL']
     else
@@ -27,7 +27,7 @@ setup_redis_env_url(:cache, false)
 setup_redis_env_url(:sidekiq, false)
 
 namespace         = ENV.fetch('REDIS_NAMESPACE', nil)
-cache_namespace   = namespace ? namespace + '_cache' : 'cache'
+cache_namespace   = namespace ? "#{namespace}_cache" : 'cache'
 sidekiq_namespace = namespace
 
 REDIS_CACHE_PARAMS = {
@@ -35,7 +35,7 @@ REDIS_CACHE_PARAMS = {
   url: ENV['CACHE_REDIS_URL'],
   expires_in: 10.minutes,
   namespace: cache_namespace,
-  pool_size: Sidekiq.server? ? Sidekiq.options[:concurrency] : Integer(ENV['MAX_THREADS'] || 5),
+  pool_size: Sidekiq.server? ? Sidekiq[:concurrency] : Integer(ENV['MAX_THREADS'] || 5),
   pool_timeout: 5,
   connect_timeout: 5,
 }.freeze
@@ -46,6 +46,4 @@ REDIS_SIDEKIQ_PARAMS = {
   namespace: sidekiq_namespace,
 }.freeze
 
-if Rails.env.test?
-  ENV['REDIS_NAMESPACE'] = "mastodon_test#{ENV['TEST_ENV_NUMBER']}"
-end
+ENV['REDIS_NAMESPACE'] = "mastodon_test#{ENV['TEST_ENV_NUMBER']}" if Rails.env.test?
diff --git a/lib/mastodon/search_cli.rb b/lib/mastodon/search_cli.rb
index b206854ab..31e9a3d5a 100644
--- a/lib/mastodon/search_cli.rb
+++ b/lib/mastodon/search_cli.rb
@@ -43,13 +43,11 @@ module Mastodon
         exit(1)
       end
 
-      indices = begin
-        if options[:only]
-          options[:only].map { |str| "#{str.camelize}Index".constantize }
-        else
-          INDICES
-        end
-      end
+      indices = if options[:only]
+                  options[:only].map { |str| "#{str.camelize}Index".constantize }
+                else
+                  INDICES
+                end
 
       pool      = Concurrent::FixedThreadPool.new(options[:concurrency], max_queue: options[:concurrency] * 10)
       importers = indices.index_with { |index| "Importer::#{index.name}Importer".constantize.new(batch_size: options[:batch_size], executor: pool) }
diff --git a/lib/mastodon/sidekiq_middleware.rb b/lib/mastodon/sidekiq_middleware.rb
index c75e8401f..9832e1a27 100644
--- a/lib/mastodon/sidekiq_middleware.rb
+++ b/lib/mastodon/sidekiq_middleware.rb
@@ -3,8 +3,8 @@
 class Mastodon::SidekiqMiddleware
   BACKTRACE_LIMIT = 3
 
-  def call(*)
-    yield
+  def call(*, &block)
+    Chewy.strategy(:mastodon, &block)
   rescue Mastodon::HostValidationError
     # Do not retry
   rescue => e
diff --git a/lib/mastodon/snowflake.rb b/lib/mastodon/snowflake.rb
index fe0dc1722..8030288af 100644
--- a/lib/mastodon/snowflake.rb
+++ b/lib/mastodon/snowflake.rb
@@ -115,7 +115,7 @@ module Mastodon::Snowflake
         # And only those that are using timestamp_id.
         next unless (data = DEFAULT_REGEX.match(id_col.default_function))
 
-        seq_name = data[:seq_prefix] + '_id_seq'
+        seq_name = "#{data[:seq_prefix]}_id_seq"
 
         # If we were on Postgres 9.5+, we could do CREATE SEQUENCE IF
         # NOT EXISTS, but we can't depend on that. Instead, catch the
diff --git a/lib/mastodon/statuses_cli.rb b/lib/mastodon/statuses_cli.rb
index d4c2e6cf2..baab83e29 100644
--- a/lib/mastodon/statuses_cli.rb
+++ b/lib/mastodon/statuses_cli.rb
@@ -93,7 +93,7 @@ module Mastodon
         c.table_name = 'statuses_to_be_deleted'
       end
 
-      Object.const_set('StatusToBeDeleted', klass)
+      Object.const_set(:StatusToBeDeleted, klass)
 
       scope     = StatusToBeDeleted
       processed = 0
@@ -175,7 +175,7 @@ module Mastodon
         c.table_name = 'conversations_to_be_deleted'
       end
 
-      Object.const_set('ConversationsToBeDeleted', klass)
+      Object.const_set(:ConversationsToBeDeleted, klass)
 
       scope     = ConversationsToBeDeleted
       processed = 0
diff --git a/lib/mastodon/upgrade_cli.rb b/lib/mastodon/upgrade_cli.rb
index 570b7e6fa..2b60f9eee 100644
--- a/lib/mastodon/upgrade_cli.rb
+++ b/lib/mastodon/upgrade_cli.rb
@@ -50,16 +50,14 @@ module Mastodon
             styles << :original unless styles.include?(:original)
 
             styles.each do |style|
-              success = begin
-                case Paperclip::Attachment.default_options[:storage]
-                when :s3
-                  upgrade_storage_s3(progress, attachment, style)
-                when :fog
-                  upgrade_storage_fog(progress, attachment, style)
-                when :filesystem
-                  upgrade_storage_filesystem(progress, attachment, style)
-                end
-              end
+              success = case Paperclip::Attachment.default_options[:storage]
+                        when :s3
+                          upgrade_storage_s3(progress, attachment, style)
+                        when :fog
+                          upgrade_storage_fog(progress, attachment, style)
+                        when :filesystem
+                          upgrade_storage_filesystem(progress, attachment, style)
+                        end
 
               upgraded = true if style == :original && success
 
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index 3340d0bf5..603e2b88d 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -9,7 +9,7 @@ module Mastodon
     end
 
     def minor
-      0
+      1
     end
 
     def patch
diff --git a/lib/paperclip/attachment_extensions.rb b/lib/paperclip/attachment_extensions.rb
index d66a17623..7f82138aa 100644
--- a/lib/paperclip/attachment_extensions.rb
+++ b/lib/paperclip/attachment_extensions.rb
@@ -8,7 +8,7 @@ module Paperclip
 
     # monkey-patch to avoid unlinking too avoid unlinking source file too early
     # see https://github.com/kreeti/kt-paperclip/issues/64
-    def post_process_style(name, style) #:nodoc:
+    def post_process_style(name, style) # :nodoc:
       raise "Style #{name} has no processors defined." if style.processors.blank?
 
       intermediate_files = []
@@ -16,16 +16,16 @@ module Paperclip
       # if we're processing the original, close + unlink the source tempfile
       intermediate_files << original if name == :original
 
-      @queued_for_write[name] = style.processors.
-                                inject(original) do |file, processor|
+      @queued_for_write[name] = style.processors
+                                     .inject(original) do |file, processor|
         file = Paperclip.processor(processor).make(file, style.processor_options, self)
         intermediate_files << file unless file == original
         file
       end
 
       unadapted_file = @queued_for_write[name]
-      @queued_for_write[name] = Paperclip.io_adapters.
-                                for(@queued_for_write[name], @options[:adapter_options])
+      @queued_for_write[name] = Paperclip.io_adapters
+                                         .for(@queued_for_write[name], @options[:adapter_options])
       unadapted_file.close if unadapted_file.respond_to?(:close)
       @queued_for_write[name]
     rescue Paperclip::Errors::NotIdentifiedByImageMagickError => e
diff --git a/lib/paperclip/color_extractor.rb b/lib/paperclip/color_extractor.rb
index d3b8e1022..2e8dc04fd 100644
--- a/lib/paperclip/color_extractor.rb
+++ b/lib/paperclip/color_extractor.rb
@@ -79,8 +79,8 @@ module Paperclip
     private
 
     def w3c_contrast(color1, color2)
-      luminance1 = color1.to_xyz.y * 0.01 + 0.05
-      luminance2 = color2.to_xyz.y * 0.01 + 0.05
+      luminance1 = (color1.to_xyz.y * 0.01) + 0.05
+      luminance2 = (color2.to_xyz.y * 0.01) + 0.05
 
       if luminance1 > luminance2
         luminance1 / luminance2
@@ -109,11 +109,11 @@ module Paperclip
 
         case max
         when r
-          h = (g - b) / d + (g < b ? 6.0 : 0)
+          h = ((g - b) / d) + (g < b ? 6.0 : 0)
         when g
-          h = (b - r) / d + 2.0
+          h = ((b - r) / d) + 2.0
         when b
-          h = (r - g) / d + 4.0
+          h = ((r - g) / d) + 4.0
         end
 
         h /= 6.0
@@ -126,9 +126,9 @@ module Paperclip
       t += 1 if t.negative?
       t -= 1 if t > 1
 
-      return (p + (q - p) * 6 * t) if t < 1 / 6.0
+      return (p + ((q - p) * 6 * t)) if t < 1 / 6.0
       return q if t < 1 / 2.0
-      return (p + (q - p) * (2 / 3.0 - t) * 6) if t < 2 / 3.0
+      return (p + ((q - p) * ((2 / 3.0) - t) * 6)) if t < 2 / 3.0
 
       p
     end
@@ -147,11 +147,11 @@ module Paperclip
         g = l.to_f
         b = l.to_f # achromatic
       else
-        q = l < 0.5 ? l * (s + 1) : l + s - l * s
-        p = 2 * l - q
-        r = hue_to_rgb(p, q, h + 1 / 3.0)
+        q = l < 0.5 ? l * (s + 1) : l + s - (l * s)
+        p = (2 * l) - q
+        r = hue_to_rgb(p, q, h + (1 / 3.0))
         g = hue_to_rgb(p, q, h)
-        b = hue_to_rgb(p, q, h - 1 / 3.0)
+        b = hue_to_rgb(p, q, h - (1 / 3.0))
       end
 
       [(r * 255).round, (g * 255).round, (b * 255).round]
@@ -161,13 +161,11 @@ module Paperclip
     def lighten_or_darken(color, by)
       hue, saturation, light = rgb_to_hsl(color.r, color.g, color.b)
 
-      light = begin
-        if light < 50
-          [100, light + by].min
-        else
-          [0, light - by].max
-        end
-      end
+      light = if light < 50
+                [100, light + by].min
+              else
+                [0, light - by].max
+              end
 
       ColorDiff::Color::RGB.new(*hsl_to_rgb(hue, saturation, light))
     end
@@ -185,7 +183,7 @@ module Paperclip
     end
 
     def rgb_to_hex(rgb)
-      '#%02x%02x%02x' % [rgb.r, rgb.g, rgb.b]
+      format('#%02x%02x%02x', rgb.r, rgb.g, rgb.b)
     end
   end
 end
diff --git a/lib/paperclip/gif_transcoder.rb b/lib/paperclip/gif_transcoder.rb
index d14465c01..32bdb8a86 100644
--- a/lib/paperclip/gif_transcoder.rb
+++ b/lib/paperclip/gif_transcoder.rb
@@ -57,7 +57,7 @@ class GifReader
           end
 
           # Skip lzw min code size
-          raise InvalidValue unless s.read(1).unpack('C')[0] >= 2
+          raise InvalidValue unless s.read(1).unpack1('C') >= 2
 
           # Skip image data sub-blocks
           skip_sub_blocks!(s)
@@ -77,7 +77,7 @@ class GifReader
   private
 
   def skip_extension_block!(file)
-    if EXTENSION_LABELS.include?(file.read(1).unpack('C')[0])
+    if EXTENSION_LABELS.include?(file.read(1).unpack1('C'))
       block_size, = file.read(1).unpack('C')
       file.seek(block_size, IO::SEEK_CUR)
     end
@@ -109,7 +109,7 @@ module Paperclip
       final_file = Paperclip::Transcoder.make(file, options, attachment)
 
       if options[:style] == :original
-        attachment.instance.file_file_name    = File.basename(attachment.instance.file_file_name, '.*') + '.mp4'
+        attachment.instance.file_file_name    = "#{File.basename(attachment.instance.file_file_name, '.*')}.mp4"
         attachment.instance.file_content_type = 'video/mp4'
         attachment.instance.type              = MediaAttachment.types[:gifv]
       end
diff --git a/lib/paperclip/type_corrector.rb b/lib/paperclip/type_corrector.rb
index 17e2fc5da..030b98b12 100644
--- a/lib/paperclip/type_corrector.rb
+++ b/lib/paperclip/type_corrector.rb
@@ -7,7 +7,7 @@ module Paperclip
     def make
       return @file unless options[:format]
 
-      target_extension = '.' + options[:format]
+      target_extension = ".#{options[:format]}"
       extension        = File.extname(attachment.instance_read(:file_name))
 
       return @file unless options[:style] == :original && target_extension && extension != target_extension
diff --git a/lib/public_file_server_middleware.rb b/lib/public_file_server_middleware.rb
new file mode 100644
index 000000000..3799230a2
--- /dev/null
+++ b/lib/public_file_server_middleware.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'action_dispatch/middleware/static'
+
+class PublicFileServerMiddleware
+  SERVICE_WORKER_TTL = 7.days.to_i
+  CACHE_TTL          = 28.days.to_i
+
+  def initialize(app)
+    @app = app
+    @file_handler = ActionDispatch::FileHandler.new(Rails.application.paths['public'].first)
+  end
+
+  def call(env)
+    file = @file_handler.attempt(env)
+
+    # If the request is not a static file, move on!
+    return @app.call(env) if file.nil?
+
+    status, headers, response = file
+
+    # Set cache headers on static files. Some paths require different cache headers
+    headers['Cache-Control'] = begin
+      request_path = env['REQUEST_PATH']
+
+      if request_path.start_with?('/sw.js')
+        "public, max-age=#{SERVICE_WORKER_TTL}, must-revalidate"
+      elsif request_path.start_with?(paperclip_root_url)
+        "public, max-age=#{CACHE_TTL}, immutable"
+      else
+        "public, max-age=#{CACHE_TTL}, must-revalidate"
+      end
+    end
+
+    [status, headers, response]
+  end
+
+  private
+
+  def paperclip_root_url
+    ENV.fetch('PAPERCLIP_ROOT_URL', '/system')
+  end
+end
diff --git a/lib/rails/engine_extensions.rb b/lib/rails/engine_extensions.rb
index 4848b15f2..1f5c2cd6c 100644
--- a/lib/rails/engine_extensions.rb
+++ b/lib/rails/engine_extensions.rb
@@ -2,8 +2,8 @@ module Rails
   module EngineExtensions
     # Rewrite task loading code to filter digitalocean.rake task
     def run_tasks_blocks(app)
-      Railtie.instance_method(:run_tasks_blocks).bind(self).call(app)
-      paths["lib/tasks"].existent.reject { |ext| ext.end_with?('digitalocean.rake') }.sort.each { |ext| load(ext) }
+      Railtie.instance_method(:run_tasks_blocks).bind_call(self, app)
+      paths['lib/tasks'].existent.reject { |ext| ext.end_with?('digitalocean.rake') }.sort.each { |ext| load(ext) }
     end
   end
 end
diff --git a/lib/sanitize_ext/sanitize_config.rb b/lib/sanitize_ext/sanitize_config.rb
index 55eb17097..82834fd68 100644
--- a/lib/sanitize_ext/sanitize_config.rb
+++ b/lib/sanitize_ext/sanitize_config.rb
@@ -49,9 +49,9 @@ class Sanitize
         node.content = "[🖼  #{node['alt']}]"
       else
         url = node['href']
-        prefix = url.match(/\Ahttps?:\/\/(www\.)?/).to_s
+        prefix = url.match(%r{\Ahttps?://(www\.)?}).to_s
         text   = url[prefix.length, 30]
-        text   = text + "…" if url[prefix.length..-1].length > 30
+        text += '…' if url.length - prefix.length > 30
         node.content = "[🖼  #{text}]"
       end
     end
@@ -61,13 +61,11 @@ class Sanitize
 
       current_node = env[:node]
 
-      scheme = begin
-        if current_node['href'] =~ Sanitize::REGEX_PROTOCOL
-          Regexp.last_match(1).downcase
-        else
-          :relative
-        end
-      end
+      scheme = if current_node['href'] =~ Sanitize::REGEX_PROTOCOL
+                 Regexp.last_match(1).downcase
+               else
+                 :relative
+               end
 
       current_node.replace(Nokogiri::XML::Text.new(current_node.text, current_node.document)) unless LINK_PROTOCOLS.include?(scheme)
     end
@@ -76,12 +74,11 @@ class Sanitize
       elements: %w(p br span a abbr del pre blockquote code b strong u sub sup i em h1 h2 h3 h4 h5 ul ol li details summary),
 
       attributes: {
-        'a'          => %w(href rel class title),
-        'span'       => %w(class),
-        'abbr'       => %w(title),
+        'a' => %w(href rel class title),
+        'span' => %w(class),
         'blockquote' => %w(cite),
-        'ol'         => %w(start reversed),
-        'li'         => %w(value),
+        'ol' => %w(start reversed),
+        'li' => %w(value),
       },
 
       add_attributes: {
@@ -92,7 +89,7 @@ class Sanitize
       },
 
       protocols: {
-        'a'          => { 'href' => LINK_PROTOCOLS },
+        'a' => { 'href' => LINK_PROTOCOLS },
         'blockquote' => { 'cite' => LINK_PROTOCOLS },
       },
 
@@ -109,17 +106,17 @@ class Sanitize
 
       attributes: merge(
         RELAXED[:attributes],
-        'audio'  => %w(controls),
-        'embed'  => %w(height src type width),
+        'audio' => %w(controls),
+        'embed' => %w(height src type width),
         'iframe' => %w(allowfullscreen frameborder height scrolling src width),
         'source' => %w(src type),
-        'video'  => %w(controls height loop width),
-        'div'    => [:data]
+        'video' => %w(controls height loop width),
+        'div' => [:data]
       ),
 
       protocols: merge(
         RELAXED[:protocols],
-        'embed'  => { 'src' => HTTP_PROTOCOLS },
+        'embed' => { 'src' => HTTP_PROTOCOLS },
         'iframe' => { 'src' => HTTP_PROTOCOLS },
         'source' => { 'src' => HTTP_PROTOCOLS }
       )
@@ -130,7 +127,7 @@ class Sanitize
 
       node = env[:node]
 
-      rel = (node['rel'] || '').split(' ') & ['tag']
+      rel = (node['rel'] || '').split & ['tag']
       rel += ['nofollow', 'noopener', 'noreferrer'] unless TagManager.instance.local_url?(node['href'])
 
       if rel.empty?
diff --git a/lib/tasks/assets.rake b/lib/tasks/assets.rake
index 5931aae61..76e190f70 100644
--- a/lib/tasks/assets.rake
+++ b/lib/tasks/assets.rake
@@ -3,18 +3,18 @@
 namespace :assets do
   desc 'Generate static pages'
   task generate_static_pages: :environment do
-    class StaticApplicationController < ApplicationController
-      def current_user
-        nil
+    def render_static_page(action, dest:, **opts)
+      renderer = Class.new(ApplicationController) do
+        def current_user
+          nil
+        end
       end
-    end
 
-    def render_static_page(action, dest:, **opts)
-      html = StaticApplicationController.render(action, opts)
+      html = renderer.render(action, opts)
       File.write(dest, html)
     end
 
-    render_static_page 'errors/500', layout: 'error', dest: Rails.root.join('public', 'assets', '500.html')
+    render_static_page 'errors/500', layout: 'error', dest: Rails.public_path.join('assets', '500.html')
   end
 end
 
diff --git a/lib/tasks/auto_annotate_models.rake b/lib/tasks/auto_annotate_models.rake
index a374e33ad..4b5997920 100644
--- a/lib/tasks/auto_annotate_models.rake
+++ b/lib/tasks/auto_annotate_models.rake
@@ -3,42 +3,42 @@
 if Rails.env.development?
   task :set_annotation_options do
     Annotate.set_defaults(
-      'routes'                  => 'false',
-      'models'                  => 'true',
-      'position_in_routes'      => 'before',
-      'position_in_class'       => 'before',
-      'position_in_test'        => 'before',
-      'position_in_fixture'     => 'before',
-      'position_in_factory'     => 'before',
-      'position_in_serializer'  => 'before',
-      'show_foreign_keys'       => 'false',
-      'show_indexes'            => 'false',
-      'simple_indexes'          => 'false',
-      'model_dir'               => 'app/models',
-      'root_dir'                => '',
-      'include_version'         => 'false',
-      'require'                 => '',
-      'exclude_tests'           => 'true',
-      'exclude_fixtures'        => 'true',
-      'exclude_factories'       => 'true',
-      'exclude_serializers'     => 'true',
-      'exclude_scaffolds'       => 'true',
-      'exclude_controllers'     => 'true',
-      'exclude_helpers'         => 'true',
-      'ignore_model_sub_dir'    => 'false',
-      'ignore_columns'          => nil,
-      'ignore_routes'           => nil,
-      'ignore_unknown_models'   => 'false',
+      'routes' => 'false',
+      'models' => 'true',
+      'position_in_routes' => 'before',
+      'position_in_class' => 'before',
+      'position_in_test' => 'before',
+      'position_in_fixture' => 'before',
+      'position_in_factory' => 'before',
+      'position_in_serializer' => 'before',
+      'show_foreign_keys' => 'false',
+      'show_indexes' => 'false',
+      'simple_indexes' => 'false',
+      'model_dir' => 'app/models',
+      'root_dir' => '',
+      'include_version' => 'false',
+      'require' => '',
+      'exclude_tests' => 'true',
+      'exclude_fixtures' => 'true',
+      'exclude_factories' => 'true',
+      'exclude_serializers' => 'true',
+      'exclude_scaffolds' => 'true',
+      'exclude_controllers' => 'true',
+      'exclude_helpers' => 'true',
+      'ignore_model_sub_dir' => 'false',
+      'ignore_columns' => nil,
+      'ignore_routes' => nil,
+      'ignore_unknown_models' => 'false',
       'hide_limit_column_types' => 'integer,boolean',
-      'skip_on_db_migrate'      => 'false',
-      'format_bare'             => 'true',
-      'format_rdoc'             => 'false',
-      'format_markdown'         => 'false',
-      'sort'                    => 'false',
-      'force'                   => 'false',
-      'trace'                   => 'false',
-      'wrapper_open'            => nil,
-      'wrapper_close'           => nil
+      'skip_on_db_migrate' => 'false',
+      'format_bare' => 'true',
+      'format_rdoc' => 'false',
+      'format_markdown' => 'false',
+      'sort' => 'false',
+      'force' => 'false',
+      'trace' => 'false',
+      'wrapper_open' => nil,
+      'wrapper_close' => nil
     )
   end
 
diff --git a/lib/tasks/branding.rake b/lib/tasks/branding.rake
index 2eec7c9e1..d1c1c9ded 100644
--- a/lib/tasks/branding.rake
+++ b/lib/tasks/branding.rake
@@ -54,7 +54,7 @@ namespace :branding do
       rsvg_convert.run(size: size, input: favicon_source, output: output_path)
     end
 
-    convert.run(input: favicons, output: Rails.root.join('public', 'favicon.ico'))
+    convert.run(input: favicons, output: Rails.public_path.join('favicon.ico'))
 
     apple_icon_sizes.each do |size|
       rsvg_convert.run(size: size, input: app_icon_source, output: output_dest.join("apple-touch-icon-#{size}x#{size}.png"))
@@ -69,7 +69,7 @@ namespace :branding do
   task generate_app_badge: :environment do
     rsvg_convert = Terrapin::CommandLine.new('rsvg-convert', '--stylesheet :stylesheet -w :size -h :size --keep-aspect-ratio :input -o :output')
     badge_source = Rails.root.join('app', 'javascript', 'images', 'logo-symbol-icon.svg')
-    output_dest  = Rails.root.join('public')
+    output_dest  = Rails.public_path
     stylesheet   = Rails.root.join('lib', 'assets', 'wordmark.light.css')
 
     rsvg_convert.run(stylesheet: stylesheet, input: badge_source, size: 192, output: output_dest.join('badge.png'))
diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake
index ca939fd1f..e8a64b8fb 100644
--- a/lib/tasks/db.rake
+++ b/lib/tasks/db.rake
@@ -4,16 +4,14 @@ namespace :db do
   namespace :migrate do
     desc 'Setup the db or migrate depending on state of db'
     task setup: :environment do
-      begin
-        if ActiveRecord::Migrator.current_version.zero?
-          Rake::Task['db:migrate'].invoke
-          Rake::Task['db:seed'].invoke
-        end
-      rescue ActiveRecord::NoDatabaseError
-        Rake::Task['db:setup'].invoke
-      else
+      if ActiveRecord::Migrator.current_version.zero?
         Rake::Task['db:migrate'].invoke
+        Rake::Task['db:seed'].invoke
       end
+    rescue ActiveRecord::NoDatabaseError
+      Rake::Task['db:setup'].invoke
+    else
+      Rake::Task['db:migrate'].invoke
     end
   end
 
diff --git a/lib/tasks/emojis.rake b/lib/tasks/emojis.rake
index 473aee9cc..fbb2e8d4f 100644
--- a/lib/tasks/emojis.rake
+++ b/lib/tasks/emojis.rake
@@ -1,12 +1,12 @@
 # frozen_string_literal: true
 
 def gen_border(codepoint, color)
-  input = Rails.root.join('public', 'emoji', "#{codepoint}.svg")
-  dest = Rails.root.join('public', 'emoji', "#{codepoint}_border.svg")
+  input = Rails.public_path.join('emoji', "#{codepoint}.svg")
+  dest = Rails.public_path.join('emoji', "#{codepoint}_border.svg")
   doc = File.open(input) { |f| Nokogiri::XML(f) }
   svg = doc.at_css('svg')
   if svg.key?('viewBox')
-    view_box = svg['viewBox'].split(' ').map(&:to_i)
+    view_box = svg['viewBox'].split.map(&:to_i)
     view_box[0] -= 2
     view_box[1] -= 2
     view_box[2] += 4
@@ -36,7 +36,7 @@ end
 
 def codepoints_to_unicode(codepoints)
   if codepoints.include?(' ')
-    codepoints.split(' ').map(&:hex).pack('U*')
+    codepoints.split.map(&:hex).pack('U*')
   else
     [codepoints.hex].pack('U')
   end
@@ -69,7 +69,7 @@ namespace :emojis do
       end
     end
 
-    existence_maps = grouped_codes.map { |c| c.index_with { |cc| File.exist?(Rails.root.join('public', 'emoji', "#{codepoints_to_filename(cc)}.svg")) } }
+    existence_maps = grouped_codes.map { |c| c.index_with { |cc| Rails.public_path.join('emoji', "#{codepoints_to_filename(cc)}.svg").exist? } }
     map = {}
 
     existence_maps.each do |group|
diff --git a/lib/tasks/glitchsoc.rake b/lib/tasks/glitchsoc.rake
index 79e864648..72558fa19 100644
--- a/lib/tasks/glitchsoc.rake
+++ b/lib/tasks/glitchsoc.rake
@@ -1,8 +1,12 @@
+# frozen_string_literal: true
+
 namespace :glitchsoc do
   desc 'Backfill local-only flag on statuses table'
   task backfill_local_only: :environment do
-    Status.local.where(local_only: nil).find_each do |st|
-      ActiveRecord::Base.logger.silence { st.update_attribute(:local_only, st.marked_local_only?) }
+    Status.local.where(local_only: nil).find_each do |status|
+      ActiveRecord::Base.logger.silence do
+        status.update_attribute(:local_only, status.marked_local_only?) # rubocop:disable Rails/SkipsModelValidations
+      end
     end
   end
 end
diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake
index 32040feec..6c0e66922 100644
--- a/lib/tasks/mastodon.rake
+++ b/lib/tasks/mastodon.rake
@@ -92,7 +92,7 @@ namespace :mastodon do
           prompt.ok 'Database configuration works! 🎆'
           db_connection_works = true
           break
-        rescue StandardError => e
+        rescue => e
           prompt.error 'Database connection could not be established with this configuration, try again.'
           prompt.error e.message
           break unless prompt.yes?('Try again?')
@@ -132,7 +132,7 @@ namespace :mastodon do
           redis.ping
           prompt.ok 'Redis configuration works! 🎆'
           break
-        rescue StandardError => e
+        rescue => e
           prompt.error 'Redis connection could not be established with this configuration, try again.'
           prompt.error e.message
           break unless prompt.yes?('Try again?')
@@ -142,7 +142,7 @@ namespace :mastodon do
       prompt.say "\n"
 
       if prompt.yes?('Do you want to store uploaded files on the cloud?', default: false)
-        case prompt.select('Provider', ['DigitalOcean Spaces', 'Amazon S3', 'Wasabi', 'Minio', 'Google Cloud Storage'])
+        case prompt.select('Provider', ['DigitalOcean Spaces', 'Amazon S3', 'Wasabi', 'Minio', 'Google Cloud Storage', 'Storj DCS'])
         when 'DigitalOcean Spaces'
           env['S3_ENABLED'] = 'true'
           env['S3_PROTOCOL'] = 'https'
@@ -257,6 +257,42 @@ namespace :mastodon do
             q.required true
             q.modify :strip
           end
+        when 'Storj DCS'
+          env['S3_ENABLED']  = 'true'
+          env['S3_PROTOCOL'] = 'https'
+          env['S3_REGION']   = 'global'
+
+          env['S3_ENDPOINT'] = prompt.ask('Storj DCS endpoint URL:') do |q|
+            q.required true
+            q.default 'https://gateway.storjshare.io'
+            q.modify :strip
+          end
+
+          env['S3_PROTOCOL'] = env['S3_ENDPOINT'].start_with?('https') ? 'https' : 'http'
+          env['S3_HOSTNAME'] = env['S3_ENDPOINT'].gsub(/\Ahttps?:\/\//, '')
+
+          env['S3_BUCKET'] = prompt.ask('Storj DCS bucket name:') do |q|
+            q.required true
+            q.default "files.#{env['LOCAL_DOMAIN']}"
+            q.modify :strip
+          end
+
+          env['AWS_ACCESS_KEY_ID'] = prompt.ask('Storj Gateway access key (uplink share --register --readonly=false --not-after=none sj://bucket):') do |q|
+            q.required true
+            q.modify :strip
+          end
+
+          env['AWS_SECRET_ACCESS_KEY'] = prompt.ask('Storj Gateway secret key:') do |q|
+            q.required true
+            q.modify :strip
+          end
+
+          linksharing_access_key = prompt.ask('Storj Linksharing access key (uplink share --register --public --readonly=true --disallow-lists --not-after=none sj://bucket):') do |q|
+            q.required true
+            q.modify :strip
+          end
+          env['S3_ALIAS_HOST'] = "link.storjshare.io/raw/#{linksharing_access_key}/#{env['S3_BUCKET']}"
+
         when 'Google Cloud Storage'
           env['S3_ENABLED']             = 'true'
           env['S3_PROTOCOL']            = 'https'
@@ -363,14 +399,14 @@ namespace :mastodon do
           end
 
           ActionMailer::Base.smtp_settings = {
-            port:                 env['SMTP_PORT'],
-            address:              env['SMTP_SERVER'],
-            user_name:            env['SMTP_LOGIN'].presence,
-            password:             env['SMTP_PASSWORD'].presence,
-            domain:               env['LOCAL_DOMAIN'],
-            authentication:       env['SMTP_AUTH_METHOD'] == 'none' ? nil : env['SMTP_AUTH_METHOD'] || :plain,
-            openssl_verify_mode:  env['SMTP_OPENSSL_VERIFY_MODE'],
-            enable_starttls:      enable_starttls,
+            port: env['SMTP_PORT'],
+            address: env['SMTP_SERVER'],
+            user_name: env['SMTP_LOGIN'].presence,
+            password: env['SMTP_PASSWORD'].presence,
+            domain: env['LOCAL_DOMAIN'],
+            authentication: env['SMTP_AUTH_METHOD'] == 'none' ? nil : env['SMTP_AUTH_METHOD'] || :plain,
+            openssl_verify_mode: env['SMTP_OPENSSL_VERIFY_MODE'],
+            enable_starttls: enable_starttls,
             enable_starttls_auto: enable_starttls_auto,
           }
 
@@ -381,7 +417,7 @@ namespace :mastodon do
           mail = ActionMailer::Base.new.mail to: send_to, subject: 'Test', body: 'Mastodon SMTP configuration works!'
           mail.deliver
           break
-        rescue StandardError => e
+        rescue => e
           prompt.error 'E-mail could not be sent with this configuration, try again.'
           prompt.error e.message
           break unless prompt.yes?('Try again?')
@@ -409,7 +445,7 @@ namespace :mastodon do
           generated_header << "# using docker-compose or not.\n\n"
         end
 
-        File.write(Rails.root.join('.env.production'), "#{generated_header}#{env_contents}\n")
+        Rails.root.join('.env.production').write("#{generated_header}#{env_contents}\n")
 
         if using_docker
           prompt.ok 'Below is your configuration, save it to an .env.production file outside Docker:'
@@ -427,10 +463,10 @@ namespace :mastodon do
           prompt.say 'Running `RAILS_ENV=production rails db:setup` ...'
           prompt.say "\n\n"
 
-          if !system(env.transform_values(&:to_s).merge({ 'RAILS_ENV' => 'production', 'SAFETY_ASSURED' => '1' }), 'rails db:setup')
-            prompt.error 'That failed! Perhaps your configuration is not right'
-          else
+          if system(env.transform_values(&:to_s).merge({ 'RAILS_ENV' => 'production', 'SAFETY_ASSURED' => '1' }), 'rails db:setup')
             prompt.ok 'Done!'
+          else
+            prompt.error 'That failed! Perhaps your configuration is not right'
           end
         end
 
@@ -443,10 +479,10 @@ namespace :mastodon do
             prompt.say 'Running `RAILS_ENV=production rails assets:precompile` ...'
             prompt.say "\n\n"
 
-            if !system(env.transform_values(&:to_s).merge({ 'RAILS_ENV' => 'production' }), 'rails assets:precompile')
-              prompt.error 'That failed! Maybe you need swap space?'
-            else
+            if system(env.transform_values(&:to_s).merge({ 'RAILS_ENV' => 'production' }), 'rails assets:precompile')
               prompt.say 'Done!'
+            else
+              prompt.error 'That failed! Maybe you need swap space?'
             end
           end
         end
diff --git a/lib/tasks/repo.rake b/lib/tasks/repo.rake
index 795b54c59..888337b4f 100644
--- a/lib/tasks/repo.rake
+++ b/lib/tasks/repo.rake
@@ -5,7 +5,7 @@ REPOSITORY_NAME = 'mastodon/mastodon'
 namespace :repo do
   desc 'Generate the AUTHORS.md file'
   task :authors do
-    file = File.open(Rails.root.join('AUTHORS.md'), 'w')
+    file = Rails.root.join('AUTHORS.md').open('w')
 
     file << <<~HEADER
       Authors
@@ -87,12 +87,12 @@ namespace :repo do
   task check_locales_files: :environment do
     pastel = Pastel.new
 
-    missing_yaml_files = I18n.available_locales.reject { |locale| File.exist?(Rails.root.join('config', 'locales', "#{locale}.yml")) }
-    missing_json_files = I18n.available_locales.reject { |locale| File.exist?(Rails.root.join('app', 'javascript', 'mastodon', 'locales', "#{locale}.json")) }
+    missing_yaml_files = I18n.available_locales.reject { |locale| Rails.root.join('config', 'locales', "#{locale}.yml").exist? }
+    missing_json_files = I18n.available_locales.reject { |locale| Rails.root.join('app', 'javascript', 'mastodon', 'locales', "#{locale}.json").exist? }
 
     locales_in_files = Dir[Rails.root.join('config', 'locales', '*.yml')].map do |path|
-      file_name = File.basename(path)
-      file_name.gsub(/\A(doorkeeper|devise|activerecord|simple_form)\./, '').gsub(/\.yml\z/, '').to_sym
+      file_name = File.basename(path, '.yml')
+      file_name.gsub(/\A(doorkeeper|devise|activerecord|simple_form)\./, '').to_sym
     end.uniq.compact
 
     missing_available_locales = locales_in_files - I18n.available_locales
diff --git a/lib/tasks/statistics.rake b/lib/tasks/statistics.rake
index 82f2b5416..dde7890f6 100644
--- a/lib/tasks/statistics.rake
+++ b/lib/tasks/statistics.rake
@@ -7,13 +7,13 @@ namespace :mastodon do
   task :stats do
     require 'rails/code_statistics'
     [
-      %w(App\ Libraries app/lib),
+      ['App Libraries', 'app/lib'],
       %w(Presenters app/presenters),
       %w(Services app/services),
       %w(Validators app/validators),
       %w(Workers app/workers),
     ].each do |name, dir|
-      ::STATS_DIRECTORIES << [name, Rails.root.join(dir)]
+      STATS_DIRECTORIES << [name, Rails.root.join(dir)]
     end
   end
 end
diff --git a/lib/tasks/tests.rake b/lib/tasks/tests.rake
index 1dd25abb9..35073b78b 100644
--- a/lib/tasks/tests.rake
+++ b/lib/tasks/tests.rake
@@ -53,6 +53,11 @@ namespace :tests do
         puts 'Admin::ActionLog email domain block records not updated as expected'
         exit(1)
       end
+
+      unless User.find(1).settings['notification_emails.favourite'] == true && User.find(1).settings['notification_emails.mention'] == false
+        puts 'User settings not kept as expected'
+        exit(1)
+      end
     end
 
     desc 'Populate the database with test data for 2.4.3'
@@ -98,6 +103,11 @@ namespace :tests do
           (1, 'destroy', 'EmailDomainBlock', 1, now(), now()),
           (1, 'destroy', 'Status', 1, now(), now()),
           (1, 'destroy', 'CustomEmoji', 3, now(), now());
+
+        INSERT INTO "settings"
+          (id, thing_type, thing_id, var, value, created_at, updated_at)
+        VALUES
+          (3, 'User', 1, 'notification_emails', E'--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nfollow: false\nreblog: true\nfavourite: true\nmention: false\nfollow_request: true\ndigest: true\nreport: true\npending_account: false\ntrending_tag: true\nappeal: true\n', now(), now());
       SQL
     end
 
diff --git a/package.json b/package.json
index 8eb7e1d66..1c09ae413 100644
--- a/package.json
+++ b/package.json
@@ -10,13 +10,15 @@
     "build:production": "cross-env RAILS_ENV=production NODE_ENV=production ./bin/webpack",
     "manage:translations": "node ./config/webpack/translationRunner.js",
     "start": "node ./streaming/index.js",
-    "test": "${npm_execpath} run test:lint:js && ${npm_execpath} run test:jest",
+    "test": "${npm_execpath} run test:lint:js && ${npm_execpath} run test:typecheck && ${npm_execpath} run test:jest",
     "test:lint": "${npm_execpath} run test:lint:js && ${npm_execpath} run test:lint:sass",
-    "test:lint:js": "eslint --ext=js . --cache",
-    "test:lint:sass": "stylelint \"**/*.{css,scss}\"",
+    "test:lint:js": "eslint --ext=.js,.jsx,.ts,.tsx . --cache --report-unused-disable-directives",
+    "test:lint:sass": "stylelint \"**/*.{css,scss}\" && prettier --check \"**/*.{css,scss}\"",
+    "test:typecheck": "tsc --noEmit",
     "test:jest": "cross-env NODE_ENV=test jest",
-    "format": "prettier --write \"**/*.{json,yml}\"",
-    "format-check": "prettier --check \"**/*.{json,yml}\""
+    "format": "prettier --write .",
+    "format-check": "prettier --check .",
+    "prepare": "husky install"
   },
   "repository": {
     "type": "git",
@@ -24,36 +26,35 @@
   },
   "private": true,
   "dependencies": {
-    "@babel/core": "^7.20.12",
-    "@babel/plugin-proposal-decorators": "^7.20.7",
-    "@babel/plugin-transform-react-inline-elements": "^7.18.6",
-    "@babel/plugin-transform-runtime": "^7.19.6",
+    "@babel/core": "^7.21.3",
+    "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
+    "@babel/plugin-transform-react-inline-elements": "^7.21.0",
+    "@babel/plugin-transform-runtime": "^7.21.0",
     "@babel/preset-env": "^7.20.2",
     "@babel/preset-react": "^7.18.6",
-    "@babel/runtime": "^7.20.7",
+    "@babel/preset-typescript": "^7.21.0",
+    "@babel/runtime": "^7.21.0",
     "@gamestdio/websocket": "^0.3.2",
-    "@github/webauthn-json": "^0.5.7",
+    "@github/webauthn-json": "^2.1.1",
     "@rails/ujs": "^6.1.7",
     "abortcontroller-polyfill": "^1.7.5",
-    "array-includes": "^3.1.6",
     "atrament": "0.2.4",
     "arrow-key-navigation": "^1.2.0",
-    "atrament": "0.2.4",
-    "autoprefixer": "^9.8.8",
-    "axios": "^1.2.2",
+    "autoprefixer": "^10.4.14",
+    "axios": "^1.3.4",
     "babel-loader": "^8.3.0",
     "babel-plugin-lodash": "^3.3.4",
     "babel-plugin-preval": "^5.1.0",
     "babel-plugin-react-intl": "^6.2.0",
     "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
-    "blurhash": "^2.0.4",
+    "blurhash": "^2.0.5",
     "classnames": "^2.3.2",
     "cocoon-js-vanilla": "^1.3.0",
-    "color-blend": "^3.0.1",
+    "color-blend": "^4.0.0",
     "compression-webpack-plugin": "^6.1.1",
     "cross-env": "^7.0.3",
     "css-loader": "^5.2.7",
-    "cssnano": "^4.1.11",
+    "cssnano": "^6.0.0",
     "detect-passive-events": "^2.0.3",
     "dotenv": "^16.0.3",
     "emoji-mart": "npm:emoji-mart-lazyload@latest",
@@ -64,34 +65,32 @@
     "favico.js": "^0.3.10",
     "file-loader": "^6.2.0",
     "font-awesome": "^4.7.0",
-    "fuzzysort": "^1.9.0",
-    "glob": "^8.0.3",
+    "fuzzysort": "^2.0.4",
+    "glob": "^9.3.4",
     "history": "^4.10.1",
     "http-link-header": "^1.1.0",
-    "immutable": "^4.2.2",
+    "immutable": "^4.3.0",
     "imports-loader": "^1.2.0",
-    "intersection-observer": "^0.12.2",
     "intl": "^1.2.5",
     "intl-messageformat": "^2.2.0",
     "intl-relativeformat": "^6.4.3",
-    "is-nan": "^1.3.2",
     "js-yaml": "^4.1.0",
-    "jsdom": "^21.0.0",
+    "jsdom": "^21.1.1",
     "lodash": "^4.17.21",
     "mark-loader": "^0.1.6",
-    "marky": "^1.2.5",
     "mini-css-extract-plugin": "^1.6.2",
-    "mkdirp": "^1.0.4",
+    "mkdirp": "^2.1.6",
     "npmlog": "^7.0.1",
     "object-assign": "^4.1.1",
     "object.values": "^1.1.6",
     "path-complete-extname": "^1.0.0",
     "pg": "^8.5.0",
+    "pg-connection-string": "^2.5.0",
     "postcss": "^8.4.21",
-    "postcss-loader": "^3.0.0",
+    "postcss-loader": "^4.3.0",
     "promise.prototype.finally": "^3.1.4",
     "prop-types": "^15.8.1",
-    "punycode": "^2.1.0",
+    "punycode": "^2.3.0",
     "react": "^16.14.0",
     "react-dom": "^16.14.0",
     "react-helmet": "^6.1.0",
@@ -106,20 +105,20 @@
     "react-redux-loading-bar": "^5.0.4",
     "react-router-dom": "^4.1.1",
     "react-router-scroll-4": "^1.0.0-beta.1",
-    "react-select": "^5.7.0",
+    "react-select": "^5.7.2",
     "react-sparklines": "^1.7.0",
     "react-swipeable-views": "^0.14.0",
-    "react-textarea-autosize": "^8.4.0",
+    "react-textarea-autosize": "^8.4.1",
     "react-toggle": "^4.1.3",
-    "redis": "^4.0.6 <4.1.0",
-    "redux": "^4.2.0",
+    "redis": "^4.6.5",
+    "redux": "^4.2.1",
     "redux-immutable": "^4.0.0",
     "redux-thunk": "^2.4.2",
     "regenerator-runtime": "^0.13.11",
     "requestidlecallback": "^0.3.0",
     "reselect": "^4.1.7",
-    "rimraf": "^3.0.2",
-    "sass": "^1.57.1",
+    "rimraf": "^4.4.1",
+    "sass": "^1.60.0",
     "sass-loader": "^10.2.0",
     "stacktrace-js": "^2.0.2",
     "stringz": "^2.1.0",
@@ -132,7 +131,7 @@
     "uuid": "^8.3.1",
     "webpack": "^4.46.0",
     "webpack-assets-manifest": "^4.0.6",
-    "webpack-bundle-analyzer": "^4.7.0",
+    "webpack-bundle-analyzer": "^4.8.0",
     "webpack-cli": "^3.3.12",
     "webpack-merge": "^5.8.0",
     "wicg-inert": "^3.1.2",
@@ -142,35 +141,82 @@
     "workbox-strategies": "^6.5.4",
     "workbox-webpack-plugin": "^6.5.4",
     "workbox-window": "^6.5.4",
-    "ws": "^8.12.0"
+    "ws": "^8.12.1"
   },
   "devDependencies": {
-    "@babel/eslint-parser": "^7.19.1",
     "@testing-library/jest-dom": "^5.16.5",
     "@testing-library/react": "^12.1.5",
-    "babel-jest": "^29.3.1",
-    "eslint": "^7.32.0",
-    "eslint-plugin-import": "~2.26.0",
-    "eslint-plugin-jsx-a11y": "~6.6.1",
+    "@types/babel__core": "^7.20.0",
+    "@types/emoji-mart": "^3.0.9",
+    "@types/escape-html": "^1.0.2",
+    "@types/express": "^4.17.17",
+    "@types/http-link-header": "^1.0.3",
+    "@types/intl": "^1.2.0",
+    "@types/jest": "^29.4.2",
+    "@types/js-yaml": "^4.0.5",
+    "@types/lodash": "^4.14.191",
+    "@types/npmlog": "^4.1.4",
+    "@types/object-assign": "^4.0.30",
+    "@types/pg": "^8.6.6",
+    "@types/prop-types": "^15.7.5",
+    "@types/punycode": "^2.1.0",
+    "@types/raf": "^3.4.0",
+    "@types/react": "^16.14.38",
+    "@types/react-dom": "^16.9.18",
+    "@types/react-helmet": "^6.1.6",
+    "@types/react-immutable-proptypes": "^2.1.0",
+    "@types/react-intl": "2.3.18",
+    "@types/react-motion": "^0.0.33",
+    "@types/react-overlays": "^3.1.0",
+    "@types/react-redux": "^7.1.25",
+    "@types/react-router-dom": "^5.3.3",
+    "@types/react-select": "^5.0.1",
+    "@types/react-sparklines": "^1.7.2",
+    "@types/react-swipeable-views": "^0.13.1",
+    "@types/react-test-renderer": "^18.0.0",
+    "@types/react-textarea-autosize": "^8.0.0",
+    "@types/react-toggle": "^4.0.3",
+    "@types/redux-immutable": "^4.0.3",
+    "@types/requestidlecallback": "^0.3.5",
+    "@types/throng": "^4.0.2",
+    "@types/uuid": "^8.3.4",
+    "@types/webpack": "^4.41.33",
+    "@types/yargs": "^17.0.22",
+    "@typescript-eslint/eslint-plugin": "^5.55.0",
+    "@typescript-eslint/parser": "^5.55.0",
+    "babel-jest": "^29.5.0",
+    "eslint": "^8.36.0",
+    "eslint-plugin-import": "~2.27.5",
+    "eslint-plugin-jsx-a11y": "~6.7.1",
     "eslint-plugin-promise": "~6.1.1",
-    "eslint-plugin-react": "~7.31.11",
-    "jest": "^29.3.1",
-    "jest-environment-jsdom": "^29.3.1",
+    "eslint-plugin-react": "~7.32.2",
+    "husky": "^8.0.3",
+    "jest": "^29.5.0",
+    "jest-environment-jsdom": "^29.5.0",
+    "lint-staged": "^13.1.2",
+    "marky": "^1.2.5",
     "postcss-scss": "^4.0.6",
-    "prettier": "^2.8.2",
+    "prettier": "^2.8.7",
     "raf": "^3.4.1",
     "react-intl-translations-manager": "^5.0.3",
     "react-test-renderer": "^16.14.0",
-    "stylelint": "^14.16.1",
-    "stylelint-config-standard-scss": "^6.1.0",
+    "stylelint": "^15.3.0",
+    "stylelint-config-standard-scss": "^7.0.1",
+    "typescript": "^5.0.3",
     "webpack-dev-server": "^3.11.3",
-    "yargs": "^17.6.2"
+    "yargs": "^17.7.1"
   },
   "resolutions": {
-    "kind-of": "^6.0.3"
+    "kind-of": "^6.0.3",
+    "webpack/terser-webpack-plugin": "^4.2.3"
   },
   "optionalDependencies": {
     "bufferutil": "^4.0.7",
-    "utf-8-validate": "^6.0.0"
+    "utf-8-validate": "^6.0.3"
+  },
+  "lint-staged": {
+    "*": "prettier --ignore-unknown --write",
+    "*.{js,jsx,ts,tsx}": "eslint --fix",
+    "*.{css,scss}": "stylelint --fix"
   }
 }
diff --git a/postcss.config.js b/postcss.config.js
index e7749a219..5d58d74e3 100644
--- a/postcss.config.js
+++ b/postcss.config.js
@@ -1,6 +1,6 @@
 module.exports = ({ env }) => ({
-  plugins: {
-    autoprefixer: {},
-    cssnano: env === 'production' ? {} : false,
-  },
+  plugins: [
+    'autoprefixer',
+    env === 'production' ? 'cssnano' : '',
+  ],
 });
diff --git a/spec/chewy/accounts_index_spec.rb b/spec/chewy/accounts_index_spec.rb
new file mode 100644
index 000000000..f9c5922c7
--- /dev/null
+++ b/spec/chewy/accounts_index_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe AccountsIndex do
+  describe 'Searching the index' do
+    before do
+      mock_elasticsearch_response(described_class, raw_response)
+    end
+
+    it 'returns results from a query' do
+      results = described_class.query(match: { name: 'account' })
+
+      expect(results).to eq []
+    end
+  end
+
+  def raw_response
+    {
+      took: 3,
+      hits: {
+        hits: [
+          {
+            _id: '0',
+            _score: 1.6375021,
+          },
+        ],
+      },
+    }
+  end
+end
diff --git a/spec/chewy/statuses_index_spec.rb b/spec/chewy/statuses_index_spec.rb
new file mode 100644
index 000000000..768e9415f
--- /dev/null
+++ b/spec/chewy/statuses_index_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe StatusesIndex do
+  describe 'Searching the index' do
+    before do
+      mock_elasticsearch_response(described_class, raw_response)
+    end
+
+    it 'returns results from a query' do
+      results = described_class.query(match: { name: 'status' })
+
+      expect(results).to eq []
+    end
+  end
+
+  def raw_response
+    {
+      took: 3,
+      hits: {
+        hits: [
+          {
+            _id: '0',
+            _score: 1.6375021,
+          },
+        ],
+      },
+    }
+  end
+end
diff --git a/spec/chewy/tags_index_spec.rb b/spec/chewy/tags_index_spec.rb
new file mode 100644
index 000000000..054589bdf
--- /dev/null
+++ b/spec/chewy/tags_index_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe TagsIndex do
+  describe 'Searching the index' do
+    before do
+      mock_elasticsearch_response(described_class, raw_response)
+    end
+
+    it 'returns results from a query' do
+      results = described_class.query(match: { name: 'tag' })
+
+      expect(results).to eq []
+    end
+  end
+
+  def raw_response
+    {
+      took: 3,
+      hits: {
+        hits: [
+          {
+            _id: '0',
+            _score: 1.6375021,
+          },
+        ],
+      },
+    }
+  end
+end
diff --git a/spec/config/initializers/rack_attack_spec.rb b/spec/config/initializers/rack_attack_spec.rb
index 581021cb9..cc931b21b 100644
--- a/spec/config/initializers/rack_attack_spec.rb
+++ b/spec/config/initializers/rack_attack_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Rack::Attack do
@@ -8,6 +10,17 @@ describe Rack::Attack do
   end
 
   shared_examples 'throttled endpoint' do
+    before do
+      # Rack::Attack periods are not rolling, so avoid flaky tests by setting the time in a way
+      # to avoid crossing period boundaries.
+
+      # The code Rack::Attack uses to set periods is the following:
+      # https://github.com/rack/rack-attack/blob/v6.6.1/lib/rack/attack/cache.rb#L64-L66
+      # So we want to minimize `Time.now.to_i % period`
+
+      travel_to Time.zone.at((Time.now.to_i / period.seconds).to_i * period.seconds)
+    end
+
     context 'when the number of requests is lower than the limit' do
       it 'does not change the request status' do
         limit.times do
@@ -18,11 +31,16 @@ describe Rack::Attack do
     end
 
     context 'when the number of requests is higher than the limit' do
-      it 'returns http too many requests' do
+      it 'returns http too many requests after limit and returns to normal status after period' do
         (limit * 2).times do |i|
           request.call
           expect(last_response.status).to eq(429) if i > limit
         end
+
+        travel period
+
+        request.call
+        expect(last_response.status).to_not eq(429)
       end
     end
   end
@@ -31,26 +49,31 @@ describe Rack::Attack do
 
   describe 'throttle excessive sign-up requests by IP address' do
     context 'through the website' do
-      let(:limit) { 25 }
-      let(:request) { ->() { post path, {}, 'REMOTE_ADDR' => remote_ip } }
+      let(:limit)  { 25 }
+      let(:period) { 5.minutes }
+      let(:request) { -> { post path, {}, 'REMOTE_ADDR' => remote_ip } }
 
       context 'for exact path' do
-        let(:path)  { '/auth' }
+        let(:path) { '/auth' }
+
         it_behaves_like 'throttled endpoint'
       end
 
       context 'for path with format' do
-        let(:path)  { '/auth.html' }
+        let(:path) { '/auth.html' }
+
         it_behaves_like 'throttled endpoint'
       end
     end
 
     context 'through the API' do
-      let(:limit) { 5 }
-      let(:request) { ->() { post path, {}, 'REMOTE_ADDR' => remote_ip } }
+      let(:limit)  { 5 }
+      let(:period) { 30.minutes }
+      let(:request) { -> { post path, {}, 'REMOTE_ADDR' => remote_ip } }
 
       context 'for exact path' do
-        let(:path)  { '/api/v1/accounts' }
+        let(:path) { '/api/v1/accounts' }
+
         it_behaves_like 'throttled endpoint'
       end
 
@@ -66,16 +89,19 @@ describe Rack::Attack do
   end
 
   describe 'throttle excessive sign-in requests by IP address' do
-    let(:limit) { 25 }
-    let(:request) { ->() { post path, {}, 'REMOTE_ADDR' => remote_ip } }
+    let(:limit)  { 25 }
+    let(:period) { 5.minutes }
+    let(:request) { -> { post path, {}, 'REMOTE_ADDR' => remote_ip } }
 
     context 'for exact path' do
-      let(:path)  { '/auth/sign_in' }
+      let(:path) { '/auth/sign_in' }
+
       it_behaves_like 'throttled endpoint'
     end
 
     context 'for path with format' do
-      let(:path)  { '/auth/sign_in.html' }
+      let(:path) { '/auth/sign_in.html' }
+
       it_behaves_like 'throttled endpoint'
     end
   end
diff --git a/spec/controllers/about_controller_spec.rb b/spec/controllers/about_controller_spec.rb
index 97143ec43..ccd28a96c 100644
--- a/spec/controllers/about_controller_spec.rb
+++ b/spec/controllers/about_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AboutController, type: :controller do
diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb
index defa8b2d3..9c38b3032 100644
--- a/spec/controllers/accounts_controller_spec.rb
+++ b/spec/controllers/accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AccountsController, type: :controller do
@@ -8,7 +10,7 @@ RSpec.describe AccountsController, type: :controller do
   shared_examples 'cacheable response' do
     it 'does not set cookies' do
       expect(response.cookies).to be_empty
-      expect(response.headers['Set-Cookies']).to be nil
+      expect(response.headers['Set-Cookies']).to be_nil
     end
 
     it 'does not set sessions' do
diff --git a/spec/controllers/activitypub/claims_controller_spec.rb b/spec/controllers/activitypub/claims_controller_spec.rb
new file mode 100644
index 000000000..f00eeb732
--- /dev/null
+++ b/spec/controllers/activitypub/claims_controller_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe ActivityPub::ClaimsController do
+  let(:account) { Fabricate(:account) }
+
+  describe 'POST #create' do
+    context 'without signature' do
+      before do
+        post :create, params: { account_username: account.username }, body: '{}'
+      end
+
+      it 'returns http not authorized' do
+        expect(response).to have_http_status(401)
+      end
+    end
+  end
+end
diff --git a/spec/controllers/activitypub/collections_controller_spec.rb b/spec/controllers/activitypub/collections_controller_spec.rb
index f78d9abbf..77901131e 100644
--- a/spec/controllers/activitypub/collections_controller_spec.rb
+++ b/spec/controllers/activitypub/collections_controller_spec.rb
@@ -10,7 +10,7 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
   shared_examples 'cacheable response' do
     it 'does not set cookies' do
       expect(response.cookies).to be_empty
-      expect(response.headers['Set-Cookies']).to be nil
+      expect(response.headers['Set-Cookies']).to be_nil
     end
 
     it 'does not set sessions' do
@@ -35,10 +35,11 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
   describe 'GET #show' do
     context 'when id is "featured"' do
       context 'without signature' do
-        let(:remote_account) { nil }
+        subject(:body) { body_as_json }
 
         subject(:response) { get :show, params: { id: 'featured', account_username: account.username } }
-        subject(:body) { body_as_json }
+
+        let(:remote_account) { nil }
 
         it 'returns http success' do
           expect(response).to have_http_status(200)
@@ -60,7 +61,7 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
         end
 
         it 'does not include contents of private pinned status' do
-          expect(response.body).not_to include(private_pinned.text)
+          expect(response.body).to_not include(private_pinned.text)
         end
 
         context 'when account is permanently suspended' do
@@ -115,7 +116,7 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
           end
 
           it 'does not include contents of private pinned status' do
-            expect(response.body).not_to include(private_pinned.text)
+            expect(response.body).to_not include(private_pinned.text)
           end
         end
 
diff --git a/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb b/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb
index c19bb8cae..8357f5f39 100644
--- a/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb
+++ b/spec/controllers/activitypub/followers_synchronizations_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::FollowersSynchronizationsController, type: :controller do
@@ -32,10 +34,11 @@ RSpec.describe ActivityPub::FollowersSynchronizationsController, type: :controll
     end
 
     context 'with signature from example.com' do
-      let(:remote_account) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/instance') }
+      subject(:body) { body_as_json }
 
       subject(:response) { get :show, params: { account_username: account.username } }
-      subject(:body) { body_as_json }
+
+      let(:remote_account) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/instance') }
 
       it 'returns http success' do
         expect(response).to have_http_status(200)
diff --git a/spec/controllers/activitypub/inboxes_controller_spec.rb b/spec/controllers/activitypub/inboxes_controller_spec.rb
index 2f023197b..8d4084648 100644
--- a/spec/controllers/activitypub/inboxes_controller_spec.rb
+++ b/spec/controllers/activitypub/inboxes_controller_spec.rb
@@ -22,10 +22,10 @@ RSpec.describe ActivityPub::InboxesController, type: :controller do
       end
 
       context 'for a specific account' do
-        let(:account) { Fabricate(:account) }
-
         subject(:response) { post :create, params: { account_username: account.username }, body: '{}' }
 
+        let(:account) { Fabricate(:account) }
+
         context 'when account is permanently suspended' do
           before do
             account.suspend!
@@ -68,7 +68,7 @@ RSpec.describe ActivityPub::InboxesController, type: :controller do
         let(:synchronization_collection) { 'https://example.com/followers2' }
 
         it 'does not start a synchronization job' do
-          expect(ActivityPub::FollowersSynchronizationWorker).not_to have_received(:perform_async)
+          expect(ActivityPub::FollowersSynchronizationWorker).to_not have_received(:perform_async)
         end
       end
 
@@ -76,13 +76,13 @@ RSpec.describe ActivityPub::InboxesController, type: :controller do
         let(:synchronization_url) { 'https://example.org/followers' }
 
         it 'does not start a synchronization job' do
-          expect(ActivityPub::FollowersSynchronizationWorker).not_to have_received(:perform_async)
+          expect(ActivityPub::FollowersSynchronizationWorker).to_not have_received(:perform_async)
         end
       end
 
       context 'with matching digest' do
         it 'does not start a synchronization job' do
-          expect(ActivityPub::FollowersSynchronizationWorker).not_to have_received(:perform_async)
+          expect(ActivityPub::FollowersSynchronizationWorker).to_not have_received(:perform_async)
         end
       end
 
diff --git a/spec/controllers/activitypub/outboxes_controller_spec.rb b/spec/controllers/activitypub/outboxes_controller_spec.rb
index 74bf46a5e..167bbcc21 100644
--- a/spec/controllers/activitypub/outboxes_controller_spec.rb
+++ b/spec/controllers/activitypub/outboxes_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::OutboxesController, type: :controller do
@@ -6,7 +8,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
   shared_examples 'cacheable response' do
     it 'does not set cookies' do
       expect(response.cookies).to be_empty
-      expect(response.headers['Set-Cookies']).to be nil
+      expect(response.headers['Set-Cookies']).to be_nil
     end
 
     it 'does not set sessions' do
@@ -33,10 +35,11 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
 
   describe 'GET #show' do
     context 'without signature' do
-      let(:remote_account) { nil }
+      subject(:body) { body_as_json }
 
       subject(:response) { get :show, params: { account_username: account.username, page: page } }
-      subject(:body) { body_as_json }
+
+      let(:remote_account) { nil }
 
       context 'with page not requested' do
         let(:page) { nil }
diff --git a/spec/controllers/activitypub/replies_controller_spec.rb b/spec/controllers/activitypub/replies_controller_spec.rb
index aee1a8b1a..582ef863f 100644
--- a/spec/controllers/activitypub/replies_controller_spec.rb
+++ b/spec/controllers/activitypub/replies_controller_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe ActivityPub::RepliesController, type: :controller do
   shared_examples 'cacheable response' do
     it 'does not set cookies' do
       expect(response.cookies).to be_empty
-      expect(response.headers['Set-Cookies']).to be nil
+      expect(response.headers['Set-Cookies']).to be_nil
     end
 
     it 'does not set sessions' do
@@ -181,6 +181,7 @@ RSpec.describe ActivityPub::RepliesController, type: :controller do
 
   describe 'GET #index' do
     subject(:response) { get :index, params: { account_username: status.account.username, status_id: status.id, only_other_accounts: only_other_accounts } }
+
     let(:only_other_accounts) { nil }
 
     context 'with no signature' do
diff --git a/spec/controllers/admin/account_actions_controller_spec.rb b/spec/controllers/admin/account_actions_controller_spec.rb
new file mode 100644
index 000000000..4eae51c7b
--- /dev/null
+++ b/spec/controllers/admin/account_actions_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::AccountActionsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #new' do
+    let(:account) { Fabricate(:account) }
+
+    it 'returns http success' do
+      get :new, params: { account_id: account.id }
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/account_moderation_notes_controller_spec.rb b/spec/controllers/admin/account_moderation_notes_controller_spec.rb
index d3f3263f8..d2c52f594 100644
--- a/spec/controllers/admin/account_moderation_notes_controller_spec.rb
+++ b/spec/controllers/admin/account_moderation_notes_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::AccountModerationNotesController, type: :controller do
@@ -26,7 +28,7 @@ RSpec.describe Admin::AccountModerationNotesController, type: :controller do
       let(:params) { { account_moderation_note: { target_account_id: target_account.id, content: '' } } }
 
       it 'falls to create a note' do
-        expect { subject }.not_to change { AccountModerationNote.count }
+        expect { subject }.to_not change { AccountModerationNote.count }
         expect(subject).to render_template 'admin/accounts/show'
       end
     end
diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb
index 81d592ddd..b182715b0 100644
--- a/spec/controllers/admin/accounts_controller_spec.rb
+++ b/spec/controllers/admin/accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::AccountsController, type: :controller do
@@ -39,7 +41,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
         username: 'username',
         display_name: 'display name',
         email: 'local-part@domain',
-        ip: '0.0.0.42'
+        ip: '0.0.0.42',
       }
     end
 
@@ -83,8 +85,8 @@ RSpec.describe Admin::AccountsController, type: :controller do
         let(:target_role) { UserRole.find_by(name: 'Admin') }
 
         it 'fails to memorialize account' do
-          is_expected.to have_http_status :forbidden
-          expect(account.reload).not_to be_memorial
+          expect(subject).to have_http_status 403
+          expect(account.reload).to_not be_memorial
         end
       end
 
@@ -92,7 +94,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
         let(:target_role) { UserRole.find_by(name: 'Moderator') }
 
         it 'succeeds in memorializing account' do
-          is_expected.to redirect_to admin_account_path(account.id)
+          expect(subject).to redirect_to admin_account_path(account.id)
           expect(account.reload).to be_memorial
         end
       end
@@ -105,8 +107,8 @@ RSpec.describe Admin::AccountsController, type: :controller do
         let(:target_role) { UserRole.find_by(name: 'Admin') }
 
         it 'fails to memorialize account' do
-          is_expected.to have_http_status :forbidden
-          expect(account.reload).not_to be_memorial
+          expect(subject).to have_http_status 403
+          expect(account.reload).to_not be_memorial
         end
       end
 
@@ -114,8 +116,8 @@ RSpec.describe Admin::AccountsController, type: :controller do
         let(:target_role) { UserRole.find_by(name: 'Moderator') }
 
         it 'fails to memorialize account' do
-          is_expected.to have_http_status :forbidden
-          expect(account.reload).not_to be_memorial
+          expect(subject).to have_http_status 403
+          expect(account.reload).to_not be_memorial
         end
       end
     end
@@ -132,8 +134,8 @@ RSpec.describe Admin::AccountsController, type: :controller do
       let(:role) { UserRole.find_by(name: 'Admin') }
 
       it 'succeeds in enabling account' do
-        is_expected.to redirect_to admin_account_path(account.id)
-        expect(user.reload).not_to be_disabled
+        expect(subject).to redirect_to admin_account_path(account.id)
+        expect(user.reload).to_not be_disabled
       end
     end
 
@@ -141,7 +143,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
       let(:role) { UserRole.everyone }
 
       it 'fails to enable account' do
-        is_expected.to have_http_status :forbidden
+        expect(subject).to have_http_status 403
         expect(user.reload).to be_disabled
       end
     end
@@ -162,12 +164,12 @@ RSpec.describe Admin::AccountsController, type: :controller do
       let(:role) { UserRole.find_by(name: 'Admin') }
 
       it 'succeeds in approving account' do
-        is_expected.to redirect_to admin_accounts_path(status: 'pending')
+        expect(subject).to redirect_to admin_accounts_path(status: 'pending')
         expect(user.reload).to be_approved
       end
 
       it 'logs action' do
-        is_expected.to have_http_status :found
+        expect(subject).to have_http_status 302
 
         log_item = Admin::ActionLog.last
 
@@ -182,8 +184,8 @@ RSpec.describe Admin::AccountsController, type: :controller do
       let(:role) { UserRole.everyone }
 
       it 'fails to approve account' do
-        is_expected.to have_http_status :forbidden
-        expect(user.reload).not_to be_approved
+        expect(subject).to have_http_status 403
+        expect(user.reload).to_not be_approved
       end
     end
   end
@@ -203,11 +205,11 @@ RSpec.describe Admin::AccountsController, type: :controller do
       let(:role) { UserRole.find_by(name: 'Admin') }
 
       it 'succeeds in rejecting account' do
-        is_expected.to redirect_to admin_accounts_path(status: 'pending')
+        expect(subject).to redirect_to admin_accounts_path(status: 'pending')
       end
 
       it 'logs action' do
-        is_expected.to have_http_status :found
+        expect(subject).to have_http_status 302
 
         log_item = Admin::ActionLog.last
 
@@ -222,8 +224,8 @@ RSpec.describe Admin::AccountsController, type: :controller do
       let(:role) { UserRole.everyone }
 
       it 'fails to reject account' do
-        is_expected.to have_http_status :forbidden
-        expect(user.reload).not_to be_approved
+        expect(subject).to have_http_status 403
+        expect(user.reload).to_not be_approved
       end
     end
   end
@@ -242,7 +244,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
       let(:role) { UserRole.find_by(name: 'Admin') }
 
       it 'succeeds in redownloading' do
-        is_expected.to redirect_to admin_account_path(account.id)
+        expect(subject).to redirect_to admin_account_path(account.id)
       end
     end
 
@@ -250,7 +252,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
       let(:role) { UserRole.everyone }
 
       it 'fails to redownload' do
-        is_expected.to have_http_status :forbidden
+        expect(subject).to have_http_status 403
       end
     end
   end
@@ -265,7 +267,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
       let(:role) { UserRole.find_by(name: 'Admin') }
 
       it 'succeeds in removing avatar' do
-        is_expected.to redirect_to admin_account_path(account.id)
+        expect(subject).to redirect_to admin_account_path(account.id)
       end
     end
 
@@ -273,7 +275,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
       let(:role) { UserRole.everyone }
 
       it 'fails to remove avatar' do
-        is_expected.to have_http_status :forbidden
+        expect(subject).to have_http_status 403
       end
     end
   end
@@ -303,7 +305,7 @@ RSpec.describe Admin::AccountsController, type: :controller do
 
       it 'fails to remove avatar' do
         subject
-        expect(response).to have_http_status :forbidden
+        expect(response).to have_http_status 403
       end
     end
   end
diff --git a/spec/controllers/admin/announcements_controller_spec.rb b/spec/controllers/admin/announcements_controller_spec.rb
new file mode 100644
index 000000000..288ac1d71
--- /dev/null
+++ b/spec/controllers/admin/announcements_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::AnnouncementsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/base_controller_spec.rb b/spec/controllers/admin/base_controller_spec.rb
index 44be91951..5fbf8777c 100644
--- a/spec/controllers/admin/base_controller_spec.rb
+++ b/spec/controllers/admin/base_controller_spec.rb
@@ -15,7 +15,7 @@ describe Admin::BaseController, type: :controller do
     sign_in(Fabricate(:user))
     get :success
 
-    expect(response).to have_http_status(:forbidden)
+    expect(response).to have_http_status(403)
   end
 
   it 'renders admin layout as a moderator' do
diff --git a/spec/controllers/admin/change_email_controller_spec.rb b/spec/controllers/admin/change_emails_controller_spec.rb
index cf8a27d39..832998471 100644
--- a/spec/controllers/admin/change_email_controller_spec.rb
+++ b/spec/controllers/admin/change_emails_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::ChangeEmailsController, type: :controller do
@@ -9,8 +11,8 @@ RSpec.describe Admin::ChangeEmailsController, type: :controller do
     sign_in admin
   end
 
-  describe "GET #show" do
-    it "returns http success" do
+  describe 'GET #show' do
+    it 'returns http success' do
       user = Fabricate(:user)
 
       get :show, params: { account_id: user.account.id }
@@ -19,12 +21,12 @@ RSpec.describe Admin::ChangeEmailsController, type: :controller do
     end
   end
 
-  describe "GET #update" do
+  describe 'GET #update' do
     before do
       allow(UserMailer).to receive(:confirmation_instructions).and_return(double('email', deliver_later: nil))
     end
 
-    it "returns http success" do
+    it 'returns http success' do
       user = Fabricate(:user)
 
       previous_email = user.email
@@ -35,7 +37,7 @@ RSpec.describe Admin::ChangeEmailsController, type: :controller do
 
       expect(user.email).to eq previous_email
       expect(user.unconfirmed_email).to eq 'test@example.com'
-      expect(user.confirmation_token).not_to be_nil
+      expect(user.confirmation_token).to_not be_nil
 
       expect(UserMailer).to have_received(:confirmation_instructions).with(user, user.confirmation_token, { to: 'test@example.com' })
 
diff --git a/spec/controllers/admin/confirmations_controller_spec.rb b/spec/controllers/admin/confirmations_controller_spec.rb
index 6268903c4..d05711e27 100644
--- a/spec/controllers/admin/confirmations_controller_spec.rb
+++ b/spec/controllers/admin/confirmations_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::ConfirmationsController, type: :controller do
@@ -55,7 +57,7 @@ RSpec.describe Admin::ConfirmationsController, type: :controller do
       it 'does not resend confirmation mail' do
         expect(subject).to redirect_to admin_accounts_path
         expect(flash[:error]).to eq I18n.t('admin.accounts.resend_confirmation.already_confirmed')
-        expect(UserMailer).not_to have_received(:confirmation_instructions)
+        expect(UserMailer).to_not have_received(:confirmation_instructions)
       end
     end
   end
diff --git a/spec/controllers/admin/custom_emojis_controller_spec.rb b/spec/controllers/admin/custom_emojis_controller_spec.rb
index 06cd0c22d..d40691e1b 100644
--- a/spec/controllers/admin/custom_emojis_controller_spec.rb
+++ b/spec/controllers/admin/custom_emojis_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Admin::CustomEmojisController do
diff --git a/spec/controllers/admin/dashboard_controller_spec.rb b/spec/controllers/admin/dashboard_controller_spec.rb
index 6231a09a2..ab3738fcd 100644
--- a/spec/controllers/admin/dashboard_controller_spec.rb
+++ b/spec/controllers/admin/dashboard_controller_spec.rb
@@ -8,10 +8,10 @@ describe Admin::DashboardController, type: :controller do
   describe 'GET #index' do
     before do
       allow(Admin::SystemCheck).to receive(:perform).and_return([
-        Admin::SystemCheck::Message.new(:database_schema_check),
-        Admin::SystemCheck::Message.new(:rules_check, nil, admin_rules_path),
-        Admin::SystemCheck::Message.new(:sidekiq_process_check, 'foo, bar'),
-      ])
+                                                                  Admin::SystemCheck::Message.new(:database_schema_check),
+                                                                  Admin::SystemCheck::Message.new(:rules_check, nil, admin_rules_path),
+                                                                  Admin::SystemCheck::Message.new(:sidekiq_process_check, 'foo, bar'),
+                                                                ])
       sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin'))
     end
 
diff --git a/spec/controllers/admin/disputes/appeals_controller_spec.rb b/spec/controllers/admin/disputes/appeals_controller_spec.rb
index 712657791..576a0c12b 100644
--- a/spec/controllers/admin/disputes/appeals_controller_spec.rb
+++ b/spec/controllers/admin/disputes/appeals_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::Disputes::AppealsController, type: :controller do
diff --git a/spec/controllers/admin/domain_allows_controller_spec.rb b/spec/controllers/admin/domain_allows_controller_spec.rb
index 6c4e67787..2a0f47145 100644
--- a/spec/controllers/admin/domain_allows_controller_spec.rb
+++ b/spec/controllers/admin/domain_allows_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::DomainAllowsController, type: :controller do
diff --git a/spec/controllers/admin/domain_blocks_controller_spec.rb b/spec/controllers/admin/domain_blocks_controller_spec.rb
index f432060d9..ef13f7676 100644
--- a/spec/controllers/admin/domain_blocks_controller_spec.rb
+++ b/spec/controllers/admin/domain_blocks_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::DomainBlocksController, type: :controller do
@@ -26,9 +28,9 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do
           domain_blocks_attributes: {
             '0' => { enabled: '1', domain: 'example.com', severity: 'silence' },
             '1' => { enabled: '0', domain: 'mastodon.social', severity: 'suspend' },
-            '2' => { enabled: '1', domain: 'mastodon.online', severity: 'suspend' }
-          }
-        }
+            '2' => { enabled: '1', domain: 'mastodon.online', severity: 'suspend' },
+          },
+        },
       }
 
       expect(DomainBlockWorker).to have_received(:perform_async).exactly(2).times
@@ -54,7 +56,7 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do
 
       post :create, params: { domain_block: { domain: 'example.com', severity: 'silence' } }
 
-      expect(DomainBlockWorker).not_to have_received(:perform_async)
+      expect(DomainBlockWorker).to_not have_received(:perform_async)
       expect(response).to render_template :new
     end
 
@@ -72,16 +74,15 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do
 
   describe 'PUT #update' do
     let!(:remote_account) { Fabricate(:account, domain: 'example.com') }
-    let(:domain_block)    { Fabricate(:domain_block, domain: 'example.com', severity: original_severity) }
+    let(:subject) do
+      post :update, params: { id: domain_block.id, domain_block: { domain: 'example.com', severity: new_severity } }
+    end
+    let(:domain_block) { Fabricate(:domain_block, domain: 'example.com', severity: original_severity) }
 
     before do
       BlockDomainService.new.call(domain_block)
     end
 
-    let(:subject) do
-      post :update, params: { id: domain_block.id, domain_block: { domain: 'example.com', severity: new_severity } }
-    end
-
     context 'downgrading a domain suspension to silence' do
       let(:original_severity) { 'suspend' }
       let(:new_severity)      { 'silence' }
diff --git a/spec/controllers/admin/export_domain_allows_controller_spec.rb b/spec/controllers/admin/export_domain_allows_controller_spec.rb
index 1e1a5ae7d..f12bd1344 100644
--- a/spec/controllers/admin/export_domain_allows_controller_spec.rb
+++ b/spec/controllers/admin/export_domain_allows_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::ExportDomainAllowsController, type: :controller do
@@ -14,7 +16,7 @@ RSpec.describe Admin::ExportDomainAllowsController, type: :controller do
 
       get :export, params: { format: :csv }
       expect(response).to have_http_status(200)
-      expect(response.body).to eq(IO.read(File.join(file_fixture_path, 'domain_allows.csv')))
+      expect(response.body).to eq(File.read(File.join(file_fixture_path, 'domain_allows.csv')))
     end
   end
 
@@ -25,12 +27,12 @@ RSpec.describe Admin::ExportDomainAllowsController, type: :controller do
       expect(response).to redirect_to(admin_instances_path)
 
       # Header should not be imported
-      expect(DomainAllow.where(domain: '#domain').present?).to eq(false)
+      expect(DomainAllow.where(domain: '#domain').present?).to be(false)
 
       # Domains should now be added
       get :export, params: { format: :csv }
       expect(response).to have_http_status(200)
-      expect(response.body).to eq(IO.read(File.join(file_fixture_path, 'domain_allows.csv')))
+      expect(response.body).to eq(File.read(File.join(file_fixture_path, 'domain_allows.csv')))
     end
 
     it 'displays error on no file selected' do
diff --git a/spec/controllers/admin/export_domain_blocks_controller_spec.rb b/spec/controllers/admin/export_domain_blocks_controller_spec.rb
index 8697e0c21..4da9f90e4 100644
--- a/spec/controllers/admin/export_domain_blocks_controller_spec.rb
+++ b/spec/controllers/admin/export_domain_blocks_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::ExportDomainBlocksController, type: :controller do
@@ -9,22 +11,44 @@ RSpec.describe Admin::ExportDomainBlocksController, type: :controller do
 
   describe 'GET #export' do
     it 'renders instances' do
-      Fabricate(:domain_block, domain: 'bad.domain', severity: 'silence', public_comment: 'bad')
-      Fabricate(:domain_block, domain: 'worse.domain', severity: 'suspend', reject_media: true, reject_reports: true, public_comment: 'worse', obfuscate: true)
-      Fabricate(:domain_block, domain: 'reject.media', severity: 'noop', reject_media: true, public_comment: 'reject media')
+      Fabricate(:domain_block, domain: 'bad.domain', severity: 'silence', public_comment: 'bad server')
+      Fabricate(:domain_block, domain: 'worse.domain', severity: 'suspend', reject_media: true, reject_reports: true, public_comment: 'worse server', obfuscate: true)
+      Fabricate(:domain_block, domain: 'reject.media', severity: 'noop', reject_media: true, public_comment: 'reject media and test unicode characters ♥')
       Fabricate(:domain_block, domain: 'no.op', severity: 'noop', public_comment: 'noop')
 
       get :export, params: { format: :csv }
       expect(response).to have_http_status(200)
-      expect(response.body).to eq(IO.read(File.join(file_fixture_path, 'domain_blocks.csv')))
+      expect(response.body).to eq(File.read(File.join(file_fixture_path, 'domain_blocks.csv')))
     end
   end
 
   describe 'POST #import' do
-    it 'blocks imported domains' do
-      post :import, params: { admin_import: { data: fixture_file_upload('domain_blocks.csv') } }
+    context 'with complete domain blocks CSV' do
+      before do
+        post :import, params: { admin_import: { data: fixture_file_upload('domain_blocks.csv') } }
+      end
+
+      it 'renders page with expected domain blocks' do
+        expect(assigns(:domain_blocks).map { |block| [block.domain, block.severity.to_sym] }).to match_array [['bad.domain', :silence], ['worse.domain', :suspend], ['reject.media', :noop]]
+      end
+
+      it 'returns http success' do
+        expect(response).to have_http_status(200)
+      end
+    end
+
+    context 'with a list of only domains' do
+      before do
+        post :import, params: { admin_import: { data: fixture_file_upload('domain_blocks_list.txt') } }
+      end
+
+      it 'renders page with expected domain blocks' do
+        expect(assigns(:domain_blocks).map { |block| [block.domain, block.severity.to_sym] }).to match_array [['bad.domain', :suspend], ['worse.domain', :suspend], ['reject.media', :suspend]]
+      end
 
-      expect(assigns(:domain_blocks).map(&:domain)).to match_array ['bad.domain', 'worse.domain', 'reject.media']
+      it 'returns http success' do
+        expect(response).to have_http_status(200)
+      end
     end
   end
 
diff --git a/spec/controllers/admin/follow_recommendations_controller_spec.rb b/spec/controllers/admin/follow_recommendations_controller_spec.rb
new file mode 100644
index 000000000..f62aa6e4b
--- /dev/null
+++ b/spec/controllers/admin/follow_recommendations_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::FollowRecommendationsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/instances_controller_spec.rb b/spec/controllers/admin/instances_controller_spec.rb
index 337f7a80c..33174b992 100644
--- a/spec/controllers/admin/instances_controller_spec.rb
+++ b/spec/controllers/admin/instances_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::InstancesController, type: :controller do
@@ -42,7 +44,7 @@ RSpec.describe Admin::InstancesController, type: :controller do
       let(:role) { UserRole.find_by(name: 'Admin') }
 
       it 'succeeds in purging instance' do
-        is_expected.to redirect_to admin_instances_path
+        expect(subject).to redirect_to admin_instances_path
       end
     end
 
@@ -50,7 +52,7 @@ RSpec.describe Admin::InstancesController, type: :controller do
       let(:role) { nil }
 
       it 'fails to purge instance' do
-        is_expected.to have_http_status :forbidden
+        expect(subject).to have_http_status 403
       end
     end
   end
diff --git a/spec/controllers/admin/invites_controller_spec.rb b/spec/controllers/admin/invites_controller_spec.rb
index 1fb488742..92ec4e449 100644
--- a/spec/controllers/admin/invites_controller_spec.rb
+++ b/spec/controllers/admin/invites_controller_spec.rb
@@ -33,10 +33,10 @@ describe Admin::InvitesController do
   end
 
   describe 'DELETE #destroy' do
-    let!(:invite) { Fabricate(:invite, expires_at: nil) }
-
     subject { delete :destroy, params: { id: invite.id } }
 
+    let!(:invite) { Fabricate(:invite, expires_at: nil) }
+
     it 'expires invite' do
       expect(subject).to redirect_to admin_invites_path
       expect(invite.reload).to be_expired
diff --git a/spec/controllers/admin/ip_blocks_controller_spec.rb b/spec/controllers/admin/ip_blocks_controller_spec.rb
new file mode 100644
index 000000000..873888afc
--- /dev/null
+++ b/spec/controllers/admin/ip_blocks_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::IpBlocksController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/relationships_controller_spec.rb b/spec/controllers/admin/relationships_controller_spec.rb
new file mode 100644
index 000000000..1099a37a3
--- /dev/null
+++ b/spec/controllers/admin/relationships_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::RelationshipsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    let(:account) { Fabricate(:account) }
+
+    it 'returns http success' do
+      get :index, params: { account_id: account.id }
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/relays_controller_spec.rb b/spec/controllers/admin/relays_controller_spec.rb
new file mode 100644
index 000000000..dfb9f3c04
--- /dev/null
+++ b/spec/controllers/admin/relays_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::RelaysController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/report_notes_controller_spec.rb b/spec/controllers/admin/report_notes_controller_spec.rb
index fa7572d18..fb2fbd058 100644
--- a/spec/controllers/admin/report_notes_controller_spec.rb
+++ b/spec/controllers/admin/report_notes_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Admin::ReportNotesController do
@@ -34,7 +36,7 @@ describe Admin::ReportNotesController do
 
           it 'creates a report note and does not resolve report' do
             expect { subject }.to change { ReportNote.count }.by(1)
-            expect(report.reload).not_to be_action_taken
+            expect(report.reload).to_not be_action_taken
             expect(subject).to redirect_to admin_report_path(report)
           end
         end
@@ -49,7 +51,7 @@ describe Admin::ReportNotesController do
 
           it 'creates a report note and unresolves report' do
             expect { subject }.to change { ReportNote.count }.by(1)
-            expect(report.reload).not_to be_action_taken
+            expect(report.reload).to_not be_action_taken
             expect(subject).to redirect_to admin_report_path(report)
           end
         end
diff --git a/spec/controllers/admin/reports/actions_controller_spec.rb b/spec/controllers/admin/reports/actions_controller_spec.rb
index 6609798dc..4c2624a40 100644
--- a/spec/controllers/admin/reports/actions_controller_spec.rb
+++ b/spec/controllers/admin/reports/actions_controller_spec.rb
@@ -1,42 +1,161 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Admin::Reports::ActionsController do
   render_views
 
   let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
-  let(:account) { Fabricate(:account) }
-  let!(:status) { Fabricate(:status, account: account) }
-  let(:media_attached_status) { Fabricate(:status, account: account) }
-  let!(:media_attachment) { Fabricate(:media_attachment, account: account, status: media_attached_status) }
-  let(:media_attached_deleted_status) { Fabricate(:status, account: account, deleted_at: 1.day.ago) }
-  let!(:media_attachment2) { Fabricate(:media_attachment, account: account, status: media_attached_deleted_status) }
-  let(:last_media_attached_status) { Fabricate(:status, account: account) }
-  let!(:last_media_attachment) { Fabricate(:media_attachment, account: account, status: last_media_attached_status) }
-  let!(:last_status) { Fabricate(:status, account: account) }
 
   before do
     sign_in user, scope: :user
   end
 
-  describe 'POST #create' do
-    let(:report) { Fabricate(:report, status_ids: status_ids, account: user.account, target_account: account) }
-    let(:status_ids) { [media_attached_status.id, media_attached_deleted_status.id] }
+  describe 'POST #preview' do
+    let(:report) { Fabricate(:report) }
 
     before do
-      post :create, params: { report_id: report.id, action => '' }
+      post :preview, params: { report_id: report.id, action => '' }
     end
 
-    context 'when action is mark_as_sensitive' do
+    context 'when the action is "suspend"' do
+      let(:action) { 'suspend' }
+
+      it 'returns http success' do
+        expect(response).to have_http_status(200)
+      end
+    end
+
+    context 'when the action is "silence"' do
+      let(:action) { 'silence' }
+
+      it 'returns http success' do
+        expect(response).to have_http_status(200)
+      end
+    end
+
+    context 'when the action is "delete"' do
+      let(:action) { 'delete' }
+
+      it 'returns http success' do
+        expect(response).to have_http_status(200)
+      end
+    end
 
+    context 'when the action is "mark_as_sensitive"' do
       let(:action) { 'mark_as_sensitive' }
 
-      it 'resolves the report' do
-        expect(report.reload.action_taken_at).to_not be_nil
+      it 'returns http success' do
+        expect(response).to have_http_status(200)
+      end
+    end
+  end
+
+  describe 'POST #create' do
+    let(:target_account) { Fabricate(:account) }
+    let(:statuses)       { [Fabricate(:status, account: target_account), Fabricate(:status, account: target_account)] }
+    let!(:media)         { Fabricate(:media_attachment, account: target_account, status: statuses[0]) }
+    let(:report)         { Fabricate(:report, target_account: target_account, status_ids: statuses.map(&:id)) }
+    let(:text)           { 'hello' }
+    let(:common_params) do
+      { report_id: report.id, text: text }
+    end
+
+    shared_examples 'common behavior' do
+      it 'closes the report' do
+        expect { subject }.to change { report.reload.action_taken? }.from(false).to(true)
+      end
+
+      it 'creates a strike with the expected text' do
+        expect { subject }.to change { report.target_account.strikes.count }.by(1)
+        expect(report.target_account.strikes.last.text).to eq text
+      end
+
+      it 'redirects' do
+        subject
+        expect(response).to redirect_to(admin_reports_path)
+      end
+
+      context 'when text is unset' do
+        let(:common_params) do
+          { report_id: report.id }
+        end
+
+        it 'closes the report' do
+          expect { subject }.to change { report.reload.action_taken? }.from(false).to(true)
+        end
+
+        it 'creates a strike with the expected text' do
+          expect { subject }.to change { report.target_account.strikes.count }.by(1)
+          expect(report.target_account.strikes.last.text).to eq ''
+        end
+
+        it 'redirects' do
+          subject
+          expect(response).to redirect_to(admin_reports_path)
+        end
       end
+    end
+
+    shared_examples 'all action types' do
+      context 'when the action is "suspend"' do
+        let(:action) { 'suspend' }
+
+        it_behaves_like 'common behavior'
 
-      it 'marks the non-deleted as sensitive' do
-        expect(media_attached_status.reload.sensitive).to eq true
+        it 'suspends the target account' do
+          expect { subject }.to change { report.target_account.reload.suspended? }.from(false).to(true)
+        end
       end
+
+      context 'when the action is "silence"' do
+        let(:action) { 'silence' }
+
+        it_behaves_like 'common behavior'
+
+        it 'suspends the target account' do
+          expect { subject }.to change { report.target_account.reload.silenced? }.from(false).to(true)
+        end
+      end
+
+      context 'when the action is "delete"' do
+        let(:action) { 'delete' }
+
+        it_behaves_like 'common behavior'
+      end
+
+      context 'when the action is "mark_as_sensitive"' do
+        let(:action) { 'mark_as_sensitive' }
+        let(:statuses) { [media_attached_status, media_attached_deleted_status] }
+
+        let!(:status) { Fabricate(:status, account: target_account) }
+        let(:media_attached_status) { Fabricate(:status, account: target_account) }
+        let!(:media_attachment) { Fabricate(:media_attachment, account: target_account, status: media_attached_status) }
+        let(:media_attached_deleted_status) { Fabricate(:status, account: target_account, deleted_at: 1.day.ago) }
+        let!(:media_attachment2) { Fabricate(:media_attachment, account: target_account, status: media_attached_deleted_status) }
+        let(:last_media_attached_status) { Fabricate(:status, account: target_account) }
+        let!(:last_media_attachment) { Fabricate(:media_attachment, account: target_account, status: last_media_attached_status) }
+        let!(:last_status) { Fabricate(:status, account: target_account) }
+
+        it_behaves_like 'common behavior'
+
+        it 'marks the non-deleted as sensitive' do
+          subject
+          expect(media_attached_status.reload.sensitive).to be true
+        end
+      end
+    end
+
+    context 'action as submit button' do
+      subject { post :create, params: common_params.merge({ action => '' }) }
+
+      it_behaves_like 'all action types'
+    end
+
+    context 'action as submit button' do
+      subject { post :create, params: common_params.merge({ moderation_action: action }) }
+
+      it_behaves_like 'all action types'
     end
   end
 end
diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb
index 4cd1524bf..97daaf8da 100644
--- a/spec/controllers/admin/reports_controller_spec.rb
+++ b/spec/controllers/admin/reports_controller_spec.rb
@@ -1,9 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Admin::ReportsController do
   render_views
 
   let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
   before do
     sign_in user, scope: :user
   end
@@ -54,7 +57,7 @@ describe Admin::ReportsController do
       expect(response).to redirect_to(admin_reports_path)
       report.reload
       expect(report.action_taken_by_account).to eq user.account
-      expect(report.action_taken?).to eq true
+      expect(report.action_taken?).to be true
     end
   end
 
@@ -65,8 +68,8 @@ describe Admin::ReportsController do
       put :reopen, params: { id: report }
       expect(response).to redirect_to(admin_report_path(report))
       report.reload
-      expect(report.action_taken_by_account).to eq nil
-      expect(report.action_taken?).to eq false
+      expect(report.action_taken_by_account).to be_nil
+      expect(report.action_taken?).to be false
     end
   end
 
@@ -88,7 +91,7 @@ describe Admin::ReportsController do
       put :unassign, params: { id: report }
       expect(response).to redirect_to(admin_report_path(report))
       report.reload
-      expect(report.assigned_account).to eq nil
+      expect(report.assigned_account).to be_nil
     end
   end
 end
diff --git a/spec/controllers/admin/resets_controller_spec.rb b/spec/controllers/admin/resets_controller_spec.rb
index aeb172318..16adb8a12 100644
--- a/spec/controllers/admin/resets_controller_spec.rb
+++ b/spec/controllers/admin/resets_controller_spec.rb
@@ -1,9 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Admin::ResetsController do
   render_views
 
   let(:account) { Fabricate(:account) }
+
   before do
     sign_in Fabricate(:user, role: UserRole.find_by(name: 'Admin')), scope: :user
   end
diff --git a/spec/controllers/admin/roles_controller_spec.rb b/spec/controllers/admin/roles_controller_spec.rb
index 8ff891205..223d0a472 100644
--- a/spec/controllers/admin/roles_controller_spec.rb
+++ b/spec/controllers/admin/roles_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Admin::RolesController do
@@ -18,7 +20,7 @@ describe Admin::RolesController do
 
     context 'when user does not have permission to manage roles' do
       it 'returns http forbidden' do
-        expect(response).to have_http_status(:forbidden)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -38,7 +40,7 @@ describe Admin::RolesController do
 
     context 'when user does not have permission to manage roles' do
       it 'returns http forbidden' do
-        expect(response).to have_http_status(:forbidden)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -128,7 +130,7 @@ describe Admin::RolesController do
 
     context 'when user does not have permission to manage roles' do
       it 'returns http forbidden' do
-        expect(response).to have_http_status(:forbidden)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -145,7 +147,7 @@ describe Admin::RolesController do
         let(:role_position) { current_role.position + 1 }
 
         it 'returns http forbidden' do
-          expect(response).to have_http_status(:forbidden)
+          expect(response).to have_http_status(403)
         end
       end
     end
@@ -165,7 +167,7 @@ describe Admin::RolesController do
 
     context 'when user does not have permission to manage roles' do
       it 'returns http forbidden' do
-        expect(response).to have_http_status(:forbidden)
+        expect(response).to have_http_status(403)
       end
 
       it 'does not update the role' do
@@ -203,7 +205,7 @@ describe Admin::RolesController do
           let(:role_position) { current_role.position + 1 }
 
           it 'returns http forbidden' do
-            expect(response).to have_http_status(:forbidden)
+            expect(response).to have_http_status(403)
           end
 
           it 'does not update the role' do
@@ -224,7 +226,7 @@ describe Admin::RolesController do
 
     context 'when user does not have permission to manage roles' do
       it 'returns http forbidden' do
-        expect(response).to have_http_status(:forbidden)
+        expect(response).to have_http_status(403)
       end
     end
 
@@ -241,7 +243,7 @@ describe Admin::RolesController do
         let(:role_position) { current_role.position + 1 }
 
         it 'returns http forbidden' do
-          expect(response).to have_http_status(:forbidden)
+          expect(response).to have_http_status(403)
         end
       end
     end
diff --git a/spec/controllers/admin/rules_controller_spec.rb b/spec/controllers/admin/rules_controller_spec.rb
new file mode 100644
index 000000000..d7b633c04
--- /dev/null
+++ b/spec/controllers/admin/rules_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::RulesController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/settings/about_controller_spec.rb b/spec/controllers/admin/settings/about_controller_spec.rb
new file mode 100644
index 000000000..2ae26090b
--- /dev/null
+++ b/spec/controllers/admin/settings/about_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Settings::AboutController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/settings/appearance_controller_spec.rb b/spec/controllers/admin/settings/appearance_controller_spec.rb
new file mode 100644
index 000000000..65b29acc3
--- /dev/null
+++ b/spec/controllers/admin/settings/appearance_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Settings::AppearanceController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/settings/content_retention_controller_spec.rb b/spec/controllers/admin/settings/content_retention_controller_spec.rb
new file mode 100644
index 000000000..53ce84d18
--- /dev/null
+++ b/spec/controllers/admin/settings/content_retention_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Settings::ContentRetentionController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/settings/discovery_controller_spec.rb b/spec/controllers/admin/settings/discovery_controller_spec.rb
new file mode 100644
index 000000000..c7307ffc8
--- /dev/null
+++ b/spec/controllers/admin/settings/discovery_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Settings::DiscoveryController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/settings/registrations_controller_spec.rb b/spec/controllers/admin/settings/registrations_controller_spec.rb
new file mode 100644
index 000000000..3fc1f9d13
--- /dev/null
+++ b/spec/controllers/admin/settings/registrations_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Settings::RegistrationsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/site_uploads_controller_spec.rb b/spec/controllers/admin/site_uploads_controller_spec.rb
new file mode 100644
index 000000000..4ea37f396
--- /dev/null
+++ b/spec/controllers/admin/site_uploads_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::SiteUploadsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'DELETE #destroy' do
+    let(:site_upload) { Fabricate(:site_upload, var: 'thumbnail') }
+
+    it 'returns http success' do
+      delete :destroy, params: { id: site_upload.id }
+
+      expect(response).to redirect_to(admin_settings_path)
+    end
+  end
+end
diff --git a/spec/controllers/admin/statuses_controller_spec.rb b/spec/controllers/admin/statuses_controller_spec.rb
index 7f912c1c0..79d83db97 100644
--- a/spec/controllers/admin/statuses_controller_spec.rb
+++ b/spec/controllers/admin/statuses_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Admin::StatusesController do
diff --git a/spec/controllers/admin/trends/links/preview_card_providers_controller_spec.rb b/spec/controllers/admin/trends/links/preview_card_providers_controller_spec.rb
new file mode 100644
index 000000000..95ed38d6b
--- /dev/null
+++ b/spec/controllers/admin/trends/links/preview_card_providers_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Trends::Links::PreviewCardProvidersController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/trends/links_controller_spec.rb b/spec/controllers/admin/trends/links_controller_spec.rb
new file mode 100644
index 000000000..7c67f5e5a
--- /dev/null
+++ b/spec/controllers/admin/trends/links_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Trends::LinksController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/trends/statuses_controller_spec.rb b/spec/controllers/admin/trends/statuses_controller_spec.rb
new file mode 100644
index 000000000..b752234d3
--- /dev/null
+++ b/spec/controllers/admin/trends/statuses_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Trends::StatusesController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/trends/tags_controller_spec.rb b/spec/controllers/admin/trends/tags_controller_spec.rb
new file mode 100644
index 000000000..4f74a5545
--- /dev/null
+++ b/spec/controllers/admin/trends/tags_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Trends::TagsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/users/roles_controller.rb b/spec/controllers/admin/users/roles_controller_spec.rb
index bd6a3fa67..fe2cee01b 100644
--- a/spec/controllers/admin/users/roles_controller.rb
+++ b/spec/controllers/admin/users/roles_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Admin::Users::RolesController do
@@ -26,7 +28,7 @@ describe Admin::Users::RolesController do
       let(:previous_role) { UserRole.create(name: 'Baz', permissions: UserRole::FLAGS[:administrator], position: 100) }
 
       it 'returns http forbidden' do
-        expect(response).to have_http_status(:forbidden)
+        expect(response).to have_http_status(403)
       end
     end
   end
@@ -74,7 +76,7 @@ describe Admin::Users::RolesController do
       end
 
       it 'returns http forbidden' do
-        expect(response).to have_http_status(:forbidden)
+        expect(response).to have_http_status(403)
       end
     end
   end
diff --git a/spec/controllers/admin/users/two_factor_authentications_controller_spec.rb b/spec/controllers/admin/users/two_factor_authentications_controller_spec.rb
index e56264ef6..eb10d4796 100644
--- a/spec/controllers/admin/users/two_factor_authentications_controller_spec.rb
+++ b/spec/controllers/admin/users/two_factor_authentications_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 require 'webauthn/fake_client'
 
@@ -20,7 +22,7 @@ describe Admin::Users::TwoFactorAuthenticationsController do
         delete :destroy, params: { user_id: user.id }
 
         user.reload
-        expect(user.otp_enabled?).to eq false
+        expect(user.otp_enabled?).to be false
         expect(response).to redirect_to(admin_account_path(user.account_id))
       end
     end
@@ -43,8 +45,8 @@ describe Admin::Users::TwoFactorAuthenticationsController do
         delete :destroy, params: { user_id: user.id }
 
         user.reload
-        expect(user.otp_enabled?).to eq false
-        expect(user.webauthn_enabled?).to eq false
+        expect(user.otp_enabled?).to be false
+        expect(user.webauthn_enabled?).to be false
         expect(response).to redirect_to(admin_account_path(user.account_id))
       end
     end
diff --git a/spec/controllers/admin/warning_presets_controller_spec.rb b/spec/controllers/admin/warning_presets_controller_spec.rb
new file mode 100644
index 000000000..6b48fc28b
--- /dev/null
+++ b/spec/controllers/admin/warning_presets_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::WarningPresetsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/admin/webhooks/secrets_controller_spec.rb b/spec/controllers/admin/webhooks/secrets_controller_spec.rb
new file mode 100644
index 000000000..291a10fba
--- /dev/null
+++ b/spec/controllers/admin/webhooks/secrets_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Webhooks::SecretsController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'POST #rotate' do
+    let(:webhook) { Fabricate(:webhook) }
+
+    it 'returns http success' do
+      post :rotate, params: { webhook_id: webhook.id }
+
+      expect(response).to redirect_to(admin_webhook_path(webhook))
+    end
+  end
+end
diff --git a/spec/controllers/admin/webhooks_controller_spec.rb b/spec/controllers/admin/webhooks_controller_spec.rb
new file mode 100644
index 000000000..12727e142
--- /dev/null
+++ b/spec/controllers/admin/webhooks_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::WebhooksController do
+  render_views
+
+  let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(:success)
+    end
+  end
+end
diff --git a/spec/controllers/api/oembed_controller_spec.rb b/spec/controllers/api/oembed_controller_spec.rb
index b9082bde1..930f36250 100644
--- a/spec/controllers/api/oembed_controller_spec.rb
+++ b/spec/controllers/api/oembed_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::OEmbedController, type: :controller do
diff --git a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb
index aae35ce38..a677aaad0 100644
--- a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Api::V1::Accounts::CredentialsController do
@@ -35,7 +37,7 @@ describe Api::V1::Accounts::CredentialsController do
             source: {
               privacy: 'unlisted',
               sensitive: true,
-            }
+            },
           }
         end
 
@@ -44,6 +46,7 @@ describe Api::V1::Accounts::CredentialsController do
         end
 
         it 'updates account info' do
+          user.reload
           user.account.reload
 
           expect(user.account.display_name).to eq("Alice Isn't Dead")
@@ -51,7 +54,7 @@ describe Api::V1::Accounts::CredentialsController do
           expect(user.account.avatar).to exist
           expect(user.account.header).to exist
           expect(user.setting_default_privacy).to eq('unlisted')
-          expect(user.setting_default_sensitive).to eq(true)
+          expect(user.setting_default_sensitive).to be(true)
         end
 
         it 'queues up an account update distribution' do
@@ -70,17 +73,17 @@ describe Api::V1::Accounts::CredentialsController do
         it 'returns http success' do
           expect(response).to have_http_status(200)
         end
-     end
+      end
 
-      describe 'with invalid data' do
+      describe 'with a too long profile bio' do
         before do
           note = 'This is too long. '
-          note = note + 'a' * (Account::MAX_NOTE_LENGTH - note.length + 1)
+          note += 'a' * (Account::MAX_NOTE_LENGTH - note.length + 1)
           patch :update, params: { note: note }
         end
 
         it 'returns http unprocessable entity' do
-          expect(response).to have_http_status(:unprocessable_entity)
+          expect(response).to have_http_status(422)
         end
       end
     end
@@ -88,20 +91,20 @@ describe Api::V1::Accounts::CredentialsController do
 
   context 'without an oauth token' do
     before do
-      allow(controller).to receive(:doorkeeper_token) { nil }
+      allow(controller).to receive(:doorkeeper_token).and_return(nil)
     end
 
     describe 'GET #show' do
       it 'returns http unauthorized' do
         get :show
-        expect(response).to have_http_status(:unauthorized)
+        expect(response).to have_http_status(401)
       end
     end
 
     describe 'PATCH #update' do
       it 'returns http unauthorized' do
         patch :update, params: { note: 'Foo' }
-        expect(response).to have_http_status(:unauthorized)
+        expect(response).to have_http_status(401)
       end
     end
   end
diff --git a/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb b/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb
new file mode 100644
index 000000000..bb075261f
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/familiar_followers_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Accounts::FamiliarFollowersController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:follows') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb b/spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb
new file mode 100644
index 000000000..53ac1e2a7
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/featured_tags_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Accounts::FeaturedTagsController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb
index 1e6e1d8e0..53298a2e4 100644
--- a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Api::V1::Accounts::FollowerAccountsController do
diff --git a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb
index cc962c6ee..7390b25b5 100644
--- a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Api::V1::Accounts::FollowingAccountsController do
diff --git a/spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb b/spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb
new file mode 100644
index 000000000..6351de761
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/identity_proofs_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Accounts::IdentityProofsController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/accounts/lists_controller_spec.rb b/spec/controllers/api/v1/accounts/lists_controller_spec.rb
index d71485633..418839cfa 100644
--- a/spec/controllers/api/v1/accounts/lists_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/lists_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Api::V1::Accounts::ListsController do
diff --git a/spec/controllers/api/v1/accounts/lookup_controller_spec.rb b/spec/controllers/api/v1/accounts/lookup_controller_spec.rb
new file mode 100644
index 000000000..37407766f
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/lookup_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Accounts::LookupController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show, params: { account_id: account.id, acct: account.acct }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/accounts/notes_controller_spec.rb b/spec/controllers/api/v1/accounts/notes_controller_spec.rb
index 42c2d8a86..fd4d34f69 100644
--- a/spec/controllers/api/v1/accounts/notes_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/notes_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Api::V1::Accounts::NotesController do
diff --git a/spec/controllers/api/v1/accounts/relationships_controller_spec.rb b/spec/controllers/api/v1/accounts/relationships_controller_spec.rb
index 69ad0d061..da8d7fe3f 100644
--- a/spec/controllers/api/v1/accounts/relationships_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/relationships_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Api::V1::Accounts::RelationshipsController do
diff --git a/spec/controllers/api/v1/accounts/search_controller_spec.rb b/spec/controllers/api/v1/accounts/search_controller_spec.rb
index 5b23bff68..d2b675a3c 100644
--- a/spec/controllers/api/v1/accounts/search_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/search_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::Accounts::SearchController, type: :controller do
diff --git a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
index b962b3398..e57c37179 100644
--- a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Api::V1::Accounts::StatusesController do
@@ -16,6 +18,11 @@ describe Api::V1::Accounts::StatusesController do
       get :index, params: { account_id: user.account.id, limit: 1 }
 
       expect(response).to have_http_status(200)
+    end
+
+    it 'returns expected headers' do
+      get :index, params: { account_id: user.account.id, limit: 1 }
+
       expect(response.headers['Link'].links.size).to eq(2)
     end
 
@@ -28,15 +35,25 @@ describe Api::V1::Accounts::StatusesController do
     end
 
     context 'with exclude replies' do
+      let!(:older_statuses) { user.account.statuses.destroy_all }
+      let!(:status) { Fabricate(:status, account: user.account) }
+      let!(:status_self_reply) { Fabricate(:status, account: user.account, thread: status) }
+
       before do
-        Fabricate(:status, account: user.account, thread: Fabricate(:status))
+        Fabricate(:status, account: user.account, thread: Fabricate(:status)) # Reply to another user
+        get :index, params: { account_id: user.account.id, exclude_replies: true }
       end
 
       it 'returns http success' do
-        get :index, params: { account_id: user.account.id, exclude_replies: true }
-
         expect(response).to have_http_status(200)
       end
+
+      it 'returns posts along with self replies' do
+        json = body_as_json
+        post_ids = json.map { |item| item[:id].to_i }.sort
+
+        expect(post_ids).to eq [status.id, status_self_reply.id]
+      end
     end
 
     context 'with only own pinned' do
@@ -55,8 +72,11 @@ describe Api::V1::Accounts::StatusesController do
       let(:account)        { Fabricate(:account, username: 'bob', domain: 'example.com') }
       let(:status)         { Fabricate(:status, account: account) }
       let(:private_status) { Fabricate(:status, account: account, visibility: :private) }
-      let!(:pin)           { Fabricate(:status_pin, account: account, status: status) }
-      let!(:private_pin)   { Fabricate(:status_pin, account: account, status: private_status) }
+
+      before do
+        Fabricate(:status_pin, account: account, status: status)
+        Fabricate(:status_pin, account: account, status: private_status)
+      end
 
       it 'returns http success' do
         get :index, params: { account_id: account.id, pinned: true }
diff --git a/spec/controllers/api/v1/accounts_controller_spec.rb b/spec/controllers/api/v1/accounts_controller_spec.rb
index d6bbcefd7..5fbb65021 100644
--- a/spec/controllers/api/v1/accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::AccountsController, type: :controller do
diff --git a/spec/controllers/api/v1/admin/account_actions_controller_spec.rb b/spec/controllers/api/v1/admin/account_actions_controller_spec.rb
index 462c2cfa9..cafbee212 100644
--- a/spec/controllers/api/v1/admin/account_actions_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/account_actions_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::Admin::AccountActionsController, type: :controller do
diff --git a/spec/controllers/api/v1/admin/accounts_controller_spec.rb b/spec/controllers/api/v1/admin/accounts_controller_spec.rb
index 8d35b86cb..9ffcdb34f 100644
--- a/spec/controllers/api/v1/admin/accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
@@ -65,7 +67,7 @@ RSpec.describe Api::V1::Admin::AccountsController, type: :controller do
         it "returns the correct accounts (#{expected_results.inspect})" do
           json = body_as_json
 
-          expect(json.map { |a| a[:id].to_i }).to eq (expected_results.map { |symbol| send(symbol).id })
+          expect(json.map { |a| a[:id].to_i }).to eq(expected_results.map { |symbol| send(symbol).id })
         end
       end
     end
diff --git a/spec/controllers/api/v1/admin/canonical_email_blocks_controller_spec.rb b/spec/controllers/api/v1/admin/canonical_email_blocks_controller_spec.rb
new file mode 100644
index 000000000..3acae843a
--- /dev/null
+++ b/spec/controllers/api/v1/admin/canonical_email_blocks_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::CanonicalEmailBlocksController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/dimensions_controller_spec.rb b/spec/controllers/api/v1/admin/dimensions_controller_spec.rb
new file mode 100644
index 000000000..ea18efe38
--- /dev/null
+++ b/spec/controllers/api/v1/admin/dimensions_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::DimensionsController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'POST #create' do
+    it 'returns http success' do
+      post :create, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/domain_allows_controller_spec.rb b/spec/controllers/api/v1/admin/domain_allows_controller_spec.rb
index 8100363f6..15567907e 100644
--- a/spec/controllers/api/v1/admin/domain_allows_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/domain_allows_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::Admin::DomainAllowsController, type: :controller do
diff --git a/spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb b/spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb
index 606def602..0460c701a 100644
--- a/spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/domain_blocks_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::Admin::DomainBlocksController, type: :controller do
@@ -73,16 +75,15 @@ RSpec.describe Api::V1::Admin::DomainBlocksController, type: :controller do
 
   describe 'PUT #update' do
     let!(:remote_account) { Fabricate(:account, domain: 'example.com') }
-    let(:domain_block)    { Fabricate(:domain_block, domain: 'example.com', severity: original_severity) }
+    let(:subject) do
+      post :update, params: { id: domain_block.id, domain: 'example.com', severity: new_severity }
+    end
+    let(:domain_block) { Fabricate(:domain_block, domain: 'example.com', severity: original_severity) }
 
     before do
       BlockDomainService.new.call(domain_block)
     end
 
-    let(:subject) do
-      post :update, params: { id: domain_block.id, domain: 'example.com', severity: new_severity }
-    end
-
     context 'downgrading a domain suspension to silence' do
       let(:original_severity) { 'suspend' }
       let(:new_severity)      { 'silence' }
diff --git a/spec/controllers/api/v1/admin/email_domain_blocks_controller_spec.rb b/spec/controllers/api/v1/admin/email_domain_blocks_controller_spec.rb
new file mode 100644
index 000000000..a92a29869
--- /dev/null
+++ b/spec/controllers/api/v1/admin/email_domain_blocks_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::EmailDomainBlocksController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/ip_blocks_controller_spec.rb b/spec/controllers/api/v1/admin/ip_blocks_controller_spec.rb
new file mode 100644
index 000000000..50e2ae968
--- /dev/null
+++ b/spec/controllers/api/v1/admin/ip_blocks_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::IpBlocksController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/measures_controller_spec.rb b/spec/controllers/api/v1/admin/measures_controller_spec.rb
new file mode 100644
index 000000000..03727a632
--- /dev/null
+++ b/spec/controllers/api/v1/admin/measures_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::MeasuresController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'POST #create' do
+    it 'returns http success' do
+      post :create, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/reports_controller_spec.rb b/spec/controllers/api/v1/admin/reports_controller_spec.rb
index 880e72030..3d61fe5c3 100644
--- a/spec/controllers/api/v1/admin/reports_controller_spec.rb
+++ b/spec/controllers/api/v1/admin/reports_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::Admin::ReportsController, type: :controller do
diff --git a/spec/controllers/api/v1/admin/retention_controller_spec.rb b/spec/controllers/api/v1/admin/retention_controller_spec.rb
new file mode 100644
index 000000000..2381dbcb4
--- /dev/null
+++ b/spec/controllers/api/v1/admin/retention_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::RetentionController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'POST #create' do
+    it 'returns http success' do
+      post :create, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/trends/links_controller_spec.rb b/spec/controllers/api/v1/admin/trends/links_controller_spec.rb
new file mode 100644
index 000000000..a64292f06
--- /dev/null
+++ b/spec/controllers/api/v1/admin/trends/links_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::Trends::LinksController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb b/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb
new file mode 100644
index 000000000..821cc499f
--- /dev/null
+++ b/spec/controllers/api/v1/admin/trends/statuses_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::Trends::StatusesController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb b/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb
new file mode 100644
index 000000000..480306ce7
--- /dev/null
+++ b/spec/controllers/api/v1/admin/trends/tags_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Admin::Trends::TagsController do
+  render_views
+
+  let(:user)    { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/announcements/reactions_controller_spec.rb b/spec/controllers/api/v1/announcements/reactions_controller_spec.rb
index 72620e242..25c52aa1d 100644
--- a/spec/controllers/api/v1/announcements/reactions_controller_spec.rb
+++ b/spec/controllers/api/v1/announcements/reactions_controller_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe Api::V1::Announcements::ReactionsController, type: :controller do
     context 'without token' do
       it 'returns http unauthorized' do
         put :update, params: { announcement_id: announcement.id, id: '😂' }
-        expect(response).to have_http_status :unauthorized
+        expect(response).to have_http_status 401
       end
     end
 
@@ -43,7 +43,7 @@ RSpec.describe Api::V1::Announcements::ReactionsController, type: :controller do
     context 'without token' do
       it 'returns http unauthorized' do
         delete :destroy, params: { announcement_id: announcement.id, id: '😂' }
-        expect(response).to have_http_status :unauthorized
+        expect(response).to have_http_status 401
       end
     end
 
diff --git a/spec/controllers/api/v1/announcements_controller_spec.rb b/spec/controllers/api/v1/announcements_controller_spec.rb
index 6ee46b60e..eaab2abd8 100644
--- a/spec/controllers/api/v1/announcements_controller_spec.rb
+++ b/spec/controllers/api/v1/announcements_controller_spec.rb
@@ -15,7 +15,7 @@ RSpec.describe Api::V1::AnnouncementsController, type: :controller do
     context 'without token' do
       it 'returns http unprocessable entity' do
         get :index
-        expect(response).to have_http_status :unprocessable_entity
+        expect(response).to have_http_status 422
       end
     end
 
@@ -35,7 +35,7 @@ RSpec.describe Api::V1::AnnouncementsController, type: :controller do
     context 'without token' do
       it 'returns http unauthorized' do
         post :dismiss, params: { id: announcement.id }
-        expect(response).to have_http_status :unauthorized
+        expect(response).to have_http_status 401
       end
     end
 
diff --git a/spec/controllers/api/v1/apps/credentials_controller_spec.rb b/spec/controllers/api/v1/apps/credentials_controller_spec.rb
index 0f811d5f3..350e0c7a0 100644
--- a/spec/controllers/api/v1/apps/credentials_controller_spec.rb
+++ b/spec/controllers/api/v1/apps/credentials_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Api::V1::Apps::CredentialsController do
@@ -30,13 +32,13 @@ describe Api::V1::Apps::CredentialsController do
 
   context 'without an oauth token' do
     before do
-      allow(controller).to receive(:doorkeeper_token) { nil }
+      allow(controller).to receive(:doorkeeper_token).and_return(nil)
     end
 
     describe 'GET #show' do
       it 'returns http unauthorized' do
         get :show
-        expect(response).to have_http_status(:unauthorized)
+        expect(response).to have_http_status(401)
       end
     end
   end
diff --git a/spec/controllers/api/v1/apps_controller_spec.rb b/spec/controllers/api/v1/apps_controller_spec.rb
index 70cd62d48..bde132c52 100644
--- a/spec/controllers/api/v1/apps_controller_spec.rb
+++ b/spec/controllers/api/v1/apps_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::AppsController, type: :controller do
@@ -28,7 +30,7 @@ RSpec.describe Api::V1::AppsController, type: :controller do
       end
 
       it 'creates an OAuth app' do
-        expect(Doorkeeper::Application.find_by(name: client_name)).to_not be nil
+        expect(Doorkeeper::Application.find_by(name: client_name)).to_not be_nil
       end
 
       it 'returns client ID and client secret' do
@@ -68,7 +70,7 @@ RSpec.describe Api::V1::AppsController, type: :controller do
     end
 
     context 'with a too-long website' do
-      let(:website) { 'https://foo.bar/' + ('hoge' * 2_000) }
+      let(:website) { "https://foo.bar/#{'hoge' * 2_000}" }
 
       it 'returns http unprocessable entity' do
         expect(response).to have_http_status(422)
@@ -76,7 +78,7 @@ RSpec.describe Api::V1::AppsController, type: :controller do
     end
 
     context 'with a too-long redirect_uris' do
-      let(:redirect_uris) { 'https://foo.bar/' + ('hoge' * 2_000) }
+      let(:redirect_uris) { "https://foo.bar/#{'hoge' * 2_000}" }
 
       it 'returns http unprocessable entity' do
         expect(response).to have_http_status(422)
diff --git a/spec/controllers/api/v1/blocks_controller_spec.rb b/spec/controllers/api/v1/blocks_controller_spec.rb
index 0e5c8296d..a746389ca 100644
--- a/spec/controllers/api/v1/blocks_controller_spec.rb
+++ b/spec/controllers/api/v1/blocks_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::BlocksController, type: :controller do
@@ -37,13 +39,13 @@ RSpec.describe Api::V1::BlocksController, type: :controller do
     it 'sets pagination header for next path' do
       blocks = 2.times.map { Fabricate(:block, account: user.account) }
       get :index, params: { limit: 1, since_id: blocks[0] }
-      expect(response.headers['Link'].find_link(['rel', 'next']).href).to eq api_v1_blocks_url(limit: 1, max_id: blocks[1])
+      expect(response.headers['Link'].find_link(%w(rel next)).href).to eq api_v1_blocks_url(limit: 1, max_id: blocks[1])
     end
 
     it 'sets pagination header for previous path' do
       block = Fabricate(:block, account: user.account)
       get :index
-      expect(response.headers['Link'].find_link(['rel', 'prev']).href).to eq api_v1_blocks_url(since_id: block)
+      expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq api_v1_blocks_url(since_id: block)
     end
 
     it 'returns http success' do
diff --git a/spec/controllers/api/v1/bookmarks_controller_spec.rb b/spec/controllers/api/v1/bookmarks_controller_spec.rb
index d7c5847b0..352d2ca02 100644
--- a/spec/controllers/api/v1/bookmarks_controller_spec.rb
+++ b/spec/controllers/api/v1/bookmarks_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::BookmarksController, type: :controller do
@@ -10,7 +12,7 @@ RSpec.describe Api::V1::BookmarksController, type: :controller do
     context 'without token' do
       it 'returns http unauthorized' do
         get :index
-        expect(response).to have_http_status :unauthorized
+        expect(response).to have_http_status 401
       end
     end
 
@@ -24,7 +26,7 @@ RSpec.describe Api::V1::BookmarksController, type: :controller do
 
         it 'returns http forbidden' do
           get :index
-          expect(response).to have_http_status :forbidden
+          expect(response).to have_http_status 403
         end
       end
 
@@ -38,7 +40,7 @@ RSpec.describe Api::V1::BookmarksController, type: :controller do
 
         it 'returns http unprocessable entity' do
           get :index
-          expect(response).to have_http_status :unprocessable_entity
+          expect(response).to have_http_status 422
         end
       end
 
@@ -63,14 +65,14 @@ RSpec.describe Api::V1::BookmarksController, type: :controller do
 
           get :index, params: { limit: 1 }
 
-          expect(response.headers['Link'].find_link(['rel', 'next']).href).to eq "http://test.host/api/v1/bookmarks?limit=1&max_id=#{bookmark.id}"
-          expect(response.headers['Link'].find_link(['rel', 'prev']).href).to eq "http://test.host/api/v1/bookmarks?limit=1&min_id=#{bookmark.id}"
+          expect(response.headers['Link'].find_link(%w(rel next)).href).to eq "http://test.host/api/v1/bookmarks?limit=1&max_id=#{bookmark.id}"
+          expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq "http://test.host/api/v1/bookmarks?limit=1&min_id=#{bookmark.id}"
         end
 
         it 'does not add pagination headers if not necessary' do
           get :index
 
-          expect(response.headers['Link']).to eq nil
+          expect(response.headers['Link']).to be_nil
         end
       end
     end
diff --git a/spec/controllers/api/v1/conversations_controller_spec.rb b/spec/controllers/api/v1/conversations_controller_spec.rb
index 5add7cf1d..36c4cb56f 100644
--- a/spec/controllers/api/v1/conversations_controller_spec.rb
+++ b/spec/controllers/api/v1/conversations_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::ConversationsController, type: :controller do
diff --git a/spec/controllers/api/v1/directories_controller_spec.rb b/spec/controllers/api/v1/directories_controller_spec.rb
new file mode 100644
index 000000000..b18aedc4d
--- /dev/null
+++ b/spec/controllers/api/v1/directories_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::DirectoriesController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:follows') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/domain_blocks_controller_spec.rb b/spec/controllers/api/v1/domain_blocks_controller_spec.rb
index d9dc1bdbf..467ddbccc 100644
--- a/spec/controllers/api/v1/domain_blocks_controller_spec.rb
+++ b/spec/controllers/api/v1/domain_blocks_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::DomainBlocksController, type: :controller do
diff --git a/spec/controllers/api/v1/emails/confirmations_controller_spec.rb b/spec/controllers/api/v1/emails/confirmations_controller_spec.rb
index 15ac31cbc..fc9843fef 100644
--- a/spec/controllers/api/v1/emails/confirmations_controller_spec.rb
+++ b/spec/controllers/api/v1/emails/confirmations_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::Emails::ConfirmationsController, type: :controller do
@@ -16,7 +18,7 @@ RSpec.describe Api::V1::Emails::ConfirmationsController, type: :controller do
       context 'from a random app' do
         it 'returns http forbidden' do
           post :create
-          expect(response).to have_http_status(:forbidden)
+          expect(response).to have_http_status(403)
         end
       end
 
@@ -30,7 +32,7 @@ RSpec.describe Api::V1::Emails::ConfirmationsController, type: :controller do
 
           it 'returns http forbidden' do
             post :create
-            expect(response).to have_http_status(:forbidden)
+            expect(response).to have_http_status(403)
           end
 
           context 'but user changed e-mail and has not confirmed it' do
@@ -57,7 +59,7 @@ RSpec.describe Api::V1::Emails::ConfirmationsController, type: :controller do
     context 'without an oauth token' do
       it 'returns http unauthorized' do
         post :create
-        expect(response).to have_http_status(:unauthorized)
+        expect(response).to have_http_status(401)
       end
     end
   end
diff --git a/spec/controllers/api/v1/favourites_controller_spec.rb b/spec/controllers/api/v1/favourites_controller_spec.rb
index 231f76500..6ae0fdc49 100644
--- a/spec/controllers/api/v1/favourites_controller_spec.rb
+++ b/spec/controllers/api/v1/favourites_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::FavouritesController, type: :controller do
@@ -10,7 +12,7 @@ RSpec.describe Api::V1::FavouritesController, type: :controller do
     context 'without token' do
       it 'returns http unauthorized' do
         get :index
-        expect(response).to have_http_status :unauthorized
+        expect(response).to have_http_status 401
       end
     end
 
@@ -24,7 +26,7 @@ RSpec.describe Api::V1::FavouritesController, type: :controller do
 
         it 'returns http forbidden' do
           get :index
-          expect(response).to have_http_status :forbidden
+          expect(response).to have_http_status 403
         end
       end
 
@@ -38,7 +40,7 @@ RSpec.describe Api::V1::FavouritesController, type: :controller do
 
         it 'returns http unprocessable entity' do
           get :index
-          expect(response).to have_http_status :unprocessable_entity
+          expect(response).to have_http_status 422
         end
       end
 
@@ -63,14 +65,14 @@ RSpec.describe Api::V1::FavouritesController, type: :controller do
 
           get :index, params: { limit: 1 }
 
-          expect(response.headers['Link'].find_link(['rel', 'next']).href).to eq "http://test.host/api/v1/favourites?limit=1&max_id=#{favourite.id}"
-          expect(response.headers['Link'].find_link(['rel', 'prev']).href).to eq "http://test.host/api/v1/favourites?limit=1&min_id=#{favourite.id}"
+          expect(response.headers['Link'].find_link(%w(rel next)).href).to eq "http://test.host/api/v1/favourites?limit=1&max_id=#{favourite.id}"
+          expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq "http://test.host/api/v1/favourites?limit=1&min_id=#{favourite.id}"
         end
 
         it 'does not add pagination headers if not necessary' do
           get :index
 
-          expect(response.headers['Link']).to eq nil
+          expect(response.headers['Link']).to be_nil
         end
       end
     end
diff --git a/spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb b/spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb
new file mode 100644
index 000000000..54c63dcc6
--- /dev/null
+++ b/spec/controllers/api/v1/featured_tags/suggestions_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::FeaturedTags::SuggestionsController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/featured_tags_controller_spec.rb b/spec/controllers/api/v1/featured_tags_controller_spec.rb
new file mode 100644
index 000000000..aac942901
--- /dev/null
+++ b/spec/controllers/api/v1/featured_tags_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::FeaturedTagsController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index, params: { account_id: account.id, limit: 2 }
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/filters_controller_spec.rb b/spec/controllers/api/v1/filters_controller_spec.rb
index 8acb46a00..d583365cc 100644
--- a/spec/controllers/api/v1/filters_controller_spec.rb
+++ b/spec/controllers/api/v1/filters_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::FiltersController, type: :controller do
diff --git a/spec/controllers/api/v1/follow_requests_controller_spec.rb b/spec/controllers/api/v1/follow_requests_controller_spec.rb
index 856ba2a1c..0220e0277 100644
--- a/spec/controllers/api/v1/follow_requests_controller_spec.rb
+++ b/spec/controllers/api/v1/follow_requests_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::FollowRequestsController, type: :controller do
diff --git a/spec/controllers/api/v1/followed_tags_controller_spec.rb b/spec/controllers/api/v1/followed_tags_controller_spec.rb
index 2191350ef..e990065a9 100644
--- a/spec/controllers/api/v1/followed_tags_controller_spec.rb
+++ b/spec/controllers/api/v1/followed_tags_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::FollowedTagsController, type: :controller do
diff --git a/spec/controllers/api/v1/instances/domain_blocks_controller_spec.rb b/spec/controllers/api/v1/instances/domain_blocks_controller_spec.rb
new file mode 100644
index 000000000..08f505c3d
--- /dev/null
+++ b/spec/controllers/api/v1/instances/domain_blocks_controller_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Instances::DomainBlocksController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      Setting.show_domain_blocks = 'all'
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/instances/extended_descriptions_controller_spec.rb b/spec/controllers/api/v1/instances/extended_descriptions_controller_spec.rb
new file mode 100644
index 000000000..58c0d4b8f
--- /dev/null
+++ b/spec/controllers/api/v1/instances/extended_descriptions_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Instances::ExtendedDescriptionsController do
+  render_views
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/instances/privacy_policies_controller_spec.rb b/spec/controllers/api/v1/instances/privacy_policies_controller_spec.rb
new file mode 100644
index 000000000..ac0bed9dc
--- /dev/null
+++ b/spec/controllers/api/v1/instances/privacy_policies_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Instances::PrivacyPoliciesController do
+  render_views
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/instances/rules_controller_spec.rb b/spec/controllers/api/v1/instances/rules_controller_spec.rb
new file mode 100644
index 000000000..5af50239b
--- /dev/null
+++ b/spec/controllers/api/v1/instances/rules_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Instances::RulesController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/instances/translation_languages_controller_spec.rb b/spec/controllers/api/v1/instances/translation_languages_controller_spec.rb
new file mode 100644
index 000000000..5b7e4abb6
--- /dev/null
+++ b/spec/controllers/api/v1/instances/translation_languages_controller_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Instances::TranslationLanguagesController do
+  describe 'GET #show' do
+    context 'when no translation service is configured' do
+      it 'returns empty language matrix' do
+        get :show
+
+        expect(response).to have_http_status(200)
+        expect(body_as_json).to eq({})
+      end
+    end
+
+    context 'when a translation service is configured' do
+      before do
+        service = instance_double(TranslationService::DeepL, languages: { nil => %w(en de), 'en' => ['de'] })
+        allow(TranslationService).to receive(:configured?).and_return(true)
+        allow(TranslationService).to receive(:configured).and_return(service)
+      end
+
+      it 'returns language matrix' do
+        get :show
+
+        expect(response).to have_http_status(200)
+        expect(body_as_json).to eq({ und: %w(en de), en: ['de'] })
+      end
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/lists/accounts_controller_spec.rb b/spec/controllers/api/v1/lists/accounts_controller_spec.rb
index 526d8b561..337a5645c 100644
--- a/spec/controllers/api/v1/lists/accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/lists/accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Api::V1::Lists::AccountsController do
diff --git a/spec/controllers/api/v1/lists_controller_spec.rb b/spec/controllers/api/v1/lists_controller_spec.rb
index 71a8094e6..f54d27e42 100644
--- a/spec/controllers/api/v1/lists_controller_spec.rb
+++ b/spec/controllers/api/v1/lists_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::ListsController, type: :controller do
diff --git a/spec/controllers/api/v1/markers_controller_spec.rb b/spec/controllers/api/v1/markers_controller_spec.rb
index ba0f3c322..fb5f59a7c 100644
--- a/spec/controllers/api/v1/markers_controller_spec.rb
+++ b/spec/controllers/api/v1/markers_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::MarkersController, type: :controller do
@@ -42,7 +44,7 @@ RSpec.describe Api::V1::MarkersController, type: :controller do
 
       it 'creates a marker' do
         expect(user.markers.first.timeline).to eq 'home'
-        expect(user.markers.first.last_read_id).to eq 69420
+        expect(user.markers.first.last_read_id).to eq 69_420
       end
     end
 
@@ -58,7 +60,7 @@ RSpec.describe Api::V1::MarkersController, type: :controller do
 
       it 'updates a marker' do
         expect(user.markers.first.timeline).to eq 'home'
-        expect(user.markers.first.last_read_id).to eq 70120
+        expect(user.markers.first.last_read_id).to eq 70_120
       end
     end
   end
diff --git a/spec/controllers/api/v1/media_controller_spec.rb b/spec/controllers/api/v1/media_controller_spec.rb
index a1f6ddb24..90379dd92 100644
--- a/spec/controllers/api/v1/media_controller_spec.rb
+++ b/spec/controllers/api/v1/media_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::MediaController, type: :controller do
@@ -19,7 +21,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do
         end
 
         it 'returns http 422' do
-          expect(response).to have_http_status(:unprocessable_entity)
+          expect(response).to have_http_status(422)
         end
       end
 
@@ -106,7 +108,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do
 
       it 'returns http not found' do
         put :update, params: { id: media.id, description: 'Lorem ipsum!!!' }
-        expect(response).to have_http_status(:not_found)
+        expect(response).to have_http_status(404)
       end
     end
 
@@ -126,7 +128,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do
         let(:status) { Fabricate(:status, account: user.account) }
 
         it 'returns http not found' do
-          expect(response).to have_http_status(:not_found)
+          expect(response).to have_http_status(404)
         end
       end
     end
diff --git a/spec/controllers/api/v1/mutes_controller_spec.rb b/spec/controllers/api/v1/mutes_controller_spec.rb
index 8176815d4..122d9d1c5 100644
--- a/spec/controllers/api/v1/mutes_controller_spec.rb
+++ b/spec/controllers/api/v1/mutes_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::MutesController, type: :controller do
@@ -37,13 +39,13 @@ RSpec.describe Api::V1::MutesController, type: :controller do
     it 'sets pagination header for next path' do
       mutes = 2.times.map { Fabricate(:mute, account: user.account) }
       get :index, params: { limit: 1, since_id: mutes[0] }
-      expect(response.headers['Link'].find_link(['rel', 'next']).href).to eq api_v1_mutes_url(limit: 1, max_id: mutes[1])
+      expect(response.headers['Link'].find_link(%w(rel next)).href).to eq api_v1_mutes_url(limit: 1, max_id: mutes[1])
     end
 
     it 'sets pagination header for previous path' do
       mute = Fabricate(:mute, account: user.account)
       get :index
-      expect(response.headers['Link'].find_link(['rel', 'prev']).href).to eq api_v1_mutes_url(since_id: mute)
+      expect(response.headers['Link'].find_link(%w(rel prev)).href).to eq api_v1_mutes_url(since_id: mute)
     end
 
     it 'returns http success' do
diff --git a/spec/controllers/api/v1/notifications_controller_spec.rb b/spec/controllers/api/v1/notifications_controller_spec.rb
index 46e177c0e..f6cbd105e 100644
--- a/spec/controllers/api/v1/notifications_controller_spec.rb
+++ b/spec/controllers/api/v1/notifications_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::NotificationsController, type: :controller do
@@ -70,19 +72,19 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do
       end
 
       it 'includes reblog' do
-        expect(body_as_json.map { |x| x[:type] }).to include 'reblog'
+        expect(body_as_json.pluck(:type)).to include 'reblog'
       end
 
       it 'includes mention' do
-        expect(body_as_json.map { |x| x[:type] }).to include 'mention'
+        expect(body_as_json.pluck(:type)).to include 'mention'
       end
 
       it 'includes favourite' do
-        expect(body_as_json.map { |x| x[:type] }).to include 'favourite'
+        expect(body_as_json.pluck(:type)).to include 'favourite'
       end
 
       it 'includes follow' do
-        expect(body_as_json.map { |x| x[:type] }).to include 'follow'
+        expect(body_as_json.pluck(:type)).to include 'follow'
       end
     end
 
@@ -125,7 +127,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do
 
       it 'returns everything but excluded type' do
         expect(body_as_json.size).to_not eq 0
-        expect(body_as_json.map { |x| x[:type] }.uniq).to_not include 'mention'
+        expect(body_as_json.pluck(:type).uniq).to_not include 'mention'
       end
     end
 
@@ -139,7 +141,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do
       end
 
       it 'returns only requested type' do
-        expect(body_as_json.map { |x| x[:type] }.uniq).to eq ['mention']
+        expect(body_as_json.pluck(:type).uniq).to eq ['mention']
       end
     end
   end
diff --git a/spec/controllers/api/v1/polls/votes_controller_spec.rb b/spec/controllers/api/v1/polls/votes_controller_spec.rb
index d7a9c1970..9d9b14e81 100644
--- a/spec/controllers/api/v1/polls/votes_controller_spec.rb
+++ b/spec/controllers/api/v1/polls/votes_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::Polls::VotesController, type: :controller do
diff --git a/spec/controllers/api/v1/polls_controller_spec.rb b/spec/controllers/api/v1/polls_controller_spec.rb
index f0d9eaf92..0602e44ee 100644
--- a/spec/controllers/api/v1/polls_controller_spec.rb
+++ b/spec/controllers/api/v1/polls_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::PollsController, type: :controller do
diff --git a/spec/controllers/api/v1/preferences_controller_spec.rb b/spec/controllers/api/v1/preferences_controller_spec.rb
new file mode 100644
index 000000000..79cc3066e
--- /dev/null
+++ b/spec/controllers/api/v1/preferences_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::PreferencesController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:accounts') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/push/subscriptions_controller_spec.rb b/spec/controllers/api/v1/push/subscriptions_controller_spec.rb
index 534d02879..168191468 100644
--- a/spec/controllers/api/v1/push/subscriptions_controller_spec.rb
+++ b/spec/controllers/api/v1/push/subscriptions_controller_spec.rb
@@ -5,13 +5,7 @@ require 'rails_helper'
 describe Api::V1::Push::SubscriptionsController do
   render_views
 
-  let(:user)  { Fabricate(:user) }
-  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'push') }
-
-  before do
-    allow(controller).to receive(:doorkeeper_token) { token }
-  end
-
+  let(:user) { Fabricate(:user) }
   let(:create_payload) do
     {
       subscription: {
@@ -20,10 +14,9 @@ describe Api::V1::Push::SubscriptionsController do
           p256dh: 'BEm_a0bdPDhf0SOsrnB2-ategf1hHoCnpXgQsFj5JCkcoMrMt2WHoPfEYOYPzOIs9mZE8ZUaD7VA5vouy0kEkr8=',
           auth: 'eH_C8rq2raXqlcBVDa1gLg==',
         },
-      }
+      },
     }.with_indifferent_access
   end
-
   let(:alerts_payload) do
     {
       data: {
@@ -37,10 +30,15 @@ describe Api::V1::Push::SubscriptionsController do
           mention: false,
           poll: true,
           status: false,
-        }
-      }
+        },
+      },
     }.with_indifferent_access
   end
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'push') }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
 
   describe 'POST #create' do
     before do
@@ -61,6 +59,10 @@ describe Api::V1::Push::SubscriptionsController do
       post :create, params: create_payload
       expect(Web::PushSubscription.where(endpoint: create_payload[:subscription][:endpoint]).count).to eq 1
     end
+
+    it 'returns the expected JSON' do
+      expect(body_as_json.with_indifferent_access).to include({ endpoint: create_payload[:subscription][:endpoint], alerts: {}, policy: 'all' })
+    end
   end
 
   describe 'PUT #update' do
@@ -78,6 +80,10 @@ describe Api::V1::Push::SubscriptionsController do
         expect(push_subscription.data['alerts'][type]).to eq(alerts_payload[:data][:alerts][type.to_sym].to_s)
       end
     end
+
+    it 'returns the expected JSON' do
+      expect(body_as_json.with_indifferent_access).to include({ endpoint: create_payload[:subscription][:endpoint], alerts: alerts_payload[:data][:alerts], policy: alerts_payload[:data][:policy] })
+    end
   end
 
   describe 'DELETE #destroy' do
diff --git a/spec/controllers/api/v1/reports_controller_spec.rb b/spec/controllers/api/v1/reports_controller_spec.rb
index dbc64e704..78a72b95b 100644
--- a/spec/controllers/api/v1/reports_controller_spec.rb
+++ b/spec/controllers/api/v1/reports_controller_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Api::V1::ReportsController, type: :controller do
     let(:target_account) { status.account }
     let(:category) { nil }
     let(:forward) { nil }
-    let(:rule_ids){ nil }
+    let(:rule_ids) { nil }
 
     before do
       allow(AdminMailer).to receive(:new_report).and_return(double('email', deliver_later: nil))
diff --git a/spec/controllers/api/v1/scheduled_statuses_controller_spec.rb b/spec/controllers/api/v1/scheduled_statuses_controller_spec.rb
new file mode 100644
index 000000000..256c4b272
--- /dev/null
+++ b/spec/controllers/api/v1/scheduled_statuses_controller_spec.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::ScheduledStatusesController do
+  render_views
+
+  let(:user)    { Fabricate(:user) }
+  let(:token)   { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') }
+  let(:account) { Fabricate(:account) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb b/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb
index 7cc77f430..c7e1b73c7 100644
--- a/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :controller do
@@ -31,7 +33,7 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control
       it 'returns accounts who favorited the status' do
         get :index, params: { status_id: status.id, limit: 2 }
         expect(body_as_json.size).to eq 2
-      expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
+        expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
       end
 
       it 'does not return blocked users' do
@@ -45,7 +47,7 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control
 
   context 'without an oauth token' do
     before do
-      allow(controller).to receive(:doorkeeper_token) { nil }
+      allow(controller).to receive(:doorkeeper_token).and_return(nil)
     end
 
     context 'with a private status' do
diff --git a/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb b/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb
index 8d4a6f91c..1aab502ef 100644
--- a/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb
+++ b/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controller do
@@ -31,7 +33,7 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll
       it 'returns accounts who reblogged the status' do
         get :index, params: { status_id: status.id, limit: 2 }
         expect(body_as_json.size).to eq 2
-      expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
+        expect([body_as_json[0][:id], body_as_json[1][:id]]).to match_array([alice.id.to_s, bob.id.to_s])
       end
 
       it 'does not return blocked users' do
@@ -45,7 +47,7 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll
 
   context 'without an oauth token' do
     before do
-      allow(controller).to receive(:doorkeeper_token) { nil }
+      allow(controller).to receive(:doorkeeper_token).and_return(nil)
     end
 
     context 'with a private status' do
diff --git a/spec/controllers/api/v1/statuses/translations_controller_spec.rb b/spec/controllers/api/v1/statuses/translations_controller_spec.rb
new file mode 100644
index 000000000..8495779bf
--- /dev/null
+++ b/spec/controllers/api/v1/statuses/translations_controller_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Statuses::TranslationsController do
+  render_views
+
+  let(:user)  { Fabricate(:user) }
+  let(:app)   { Fabricate(:application, name: 'Test app', website: 'http://testapp.com') }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses', application: app) }
+
+  context 'with an oauth token' do
+    before do
+      allow(controller).to receive(:doorkeeper_token) { token }
+    end
+
+    describe 'POST #create' do
+      let(:status) { Fabricate(:status, account: user.account, text: 'Hola', language: 'es') }
+
+      before do
+        translation = TranslationService::Translation.new(text: 'Hello')
+        service = instance_double(TranslationService::DeepL, translate: translation)
+        allow(TranslationService).to receive(:configured?).and_return(true)
+        allow(TranslationService).to receive(:configured).and_return(service)
+        Rails.cache.write('translation_service/languages', { 'es' => ['en'] })
+        post :create, params: { status_id: status.id }
+      end
+
+      it 'returns http success' do
+        expect(response).to have_http_status(200)
+      end
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/statuses_controller_spec.rb b/spec/controllers/api/v1/statuses_controller_spec.rb
index 24810a5d2..f011bfd47 100644
--- a/spec/controllers/api/v1/statuses_controller_spec.rb
+++ b/spec/controllers/api/v1/statuses_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::StatusesController, type: :controller do
@@ -133,6 +135,23 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
         end
       end
 
+      context 'with a safeguard' do
+        let!(:alice) { Fabricate(:account, username: 'alice') }
+        let!(:bob)   { Fabricate(:account, username: 'bob') }
+
+        before do
+          post :create, params: { status: '@alice hm, @bob is really annoying lately', allowed_mentions: [alice.id] }
+        end
+
+        it 'returns http unprocessable entity' do
+          expect(response).to have_http_status(422)
+        end
+
+        it 'returns serialized extra accounts in body' do
+          expect(body_as_json[:unexpected_accounts].map { |a| a.slice(:id, :acct) }).to eq [{ id: bob.id.to_s, acct: bob.acct }]
+        end
+      end
+
       context 'with missing parameters' do
         before do
           post :create, params: {}
@@ -178,7 +197,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
       end
 
       it 'removes the status' do
-        expect(Status.find_by(id: status.id)).to be nil
+        expect(Status.find_by(id: status.id)).to be_nil
       end
     end
 
@@ -202,7 +221,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do
 
   context 'without an oauth token' do
     before do
-      allow(controller).to receive(:doorkeeper_token) { nil }
+      allow(controller).to receive(:doorkeeper_token).and_return(nil)
     end
 
     context 'with a private status' do
diff --git a/spec/controllers/api/v1/streaming_controller_spec.rb b/spec/controllers/api/v1/streaming_controller_spec.rb
index 4ab409a54..7014ed9b2 100644
--- a/spec/controllers/api/v1/streaming_controller_spec.rb
+++ b/spec/controllers/api/v1/streaming_controller_spec.rb
@@ -25,7 +25,7 @@ describe Api::V1::StreamingController do
 
   context 'with streaming api on different host' do
     before(:each) do
-      Rails.configuration.x.streaming_api_base_url = 'wss://streaming-' + Rails.configuration.x.web_domain
+      Rails.configuration.x.streaming_api_base_url = "wss://streaming-#{Rails.configuration.x.web_domain}"
       @streaming_host = URI.parse(Rails.configuration.x.streaming_api_base_url).host
     end
 
@@ -38,7 +38,7 @@ describe Api::V1::StreamingController do
         [:scheme, :path, :query, :fragment].each do |part|
           expect(redirect_to_uri.send(part)).to eq(request_uri.send(part)), "redirect target #{part}"
         end
-        expect(redirect_to_uri.host).to eq(@streaming_host), "redirect target host"
+        expect(redirect_to_uri.host).to eq(@streaming_host), 'redirect target host'
       end
     end
   end
diff --git a/spec/controllers/api/v1/suggestions_controller_spec.rb b/spec/controllers/api/v1/suggestions_controller_spec.rb
index 17f10b04f..c99380c58 100644
--- a/spec/controllers/api/v1/suggestions_controller_spec.rb
+++ b/spec/controllers/api/v1/suggestions_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::SuggestionsController, type: :controller do
@@ -29,7 +31,7 @@ RSpec.describe Api::V1::SuggestionsController, type: :controller do
       json = body_as_json
 
       expect(json.size).to be >= 1
-      expect(json.map { |i| i[:id] }).to include *[bob, jeff].map { |i| i.id.to_s }
+      expect(json.pluck(:id)).to include(*[bob, jeff].map { |i| i.id.to_s })
     end
   end
 end
diff --git a/spec/controllers/api/v1/tags_controller_spec.rb b/spec/controllers/api/v1/tags_controller_spec.rb
index 216faad87..ed17a4fbf 100644
--- a/spec/controllers/api/v1/tags_controller_spec.rb
+++ b/spec/controllers/api/v1/tags_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V1::TagsController, type: :controller do
diff --git a/spec/controllers/api/v1/timelines/direct_controller_spec.rb b/spec/controllers/api/v1/timelines/direct_controller_spec.rb
index a22c2cbea..def67a0fe 100644
--- a/spec/controllers/api/v1/timelines/direct_controller_spec.rb
+++ b/spec/controllers/api/v1/timelines/direct_controller_spec.rb
@@ -2,7 +2,7 @@
 
 require 'rails_helper'
 
-RSpec.describe Api::V1::Timelines::DirectController, type: :controller do
+RSpec.describe Api::V1::Timelines::DirectController do
   let(:user)  { Fabricate(:user) }
   let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read:statuses') }
 
diff --git a/spec/controllers/api/v1/timelines/home_controller_spec.rb b/spec/controllers/api/v1/timelines/home_controller_spec.rb
index 131c2d92f..bb46d0aba 100644
--- a/spec/controllers/api/v1/timelines/home_controller_spec.rb
+++ b/spec/controllers/api/v1/timelines/home_controller_spec.rb
@@ -36,7 +36,7 @@ describe Api::V1::Timelines::HomeController do
       it 'returns http unprocessable entity' do
         get :show
 
-        expect(response).to have_http_status(:unprocessable_entity)
+        expect(response).to have_http_status(422)
         expect(response.headers['Link']).to be_nil
       end
     end
diff --git a/spec/controllers/api/v1/timelines/list_controller_spec.rb b/spec/controllers/api/v1/timelines/list_controller_spec.rb
index 526c66a05..4ef5d41af 100644
--- a/spec/controllers/api/v1/timelines/list_controller_spec.rb
+++ b/spec/controllers/api/v1/timelines/list_controller_spec.rb
@@ -36,7 +36,7 @@ describe Api::V1::Timelines::ListController do
     describe 'GET #show' do
       it 'returns http not found' do
         get :show, params: { id: list.id }
-        expect(response).to have_http_status(:not_found)
+        expect(response).to have_http_status(404)
       end
     end
   end
@@ -48,7 +48,7 @@ describe Api::V1::Timelines::ListController do
       it 'returns http unprocessable entity' do
         get :show, params: { id: list.id }
 
-        expect(response).to have_http_status(:unprocessable_entity)
+        expect(response).to have_http_status(422)
         expect(response.headers['Link']).to be_nil
       end
     end
diff --git a/spec/controllers/api/v1/trends/links_controller_spec.rb b/spec/controllers/api/v1/trends/links_controller_spec.rb
new file mode 100644
index 000000000..71a7e2e47
--- /dev/null
+++ b/spec/controllers/api/v1/trends/links_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Trends::LinksController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v1/trends/statuses_controller_spec.rb b/spec/controllers/api/v1/trends/statuses_controller_spec.rb
new file mode 100644
index 000000000..e9892bb14
--- /dev/null
+++ b/spec/controllers/api/v1/trends/statuses_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V1::Trends::StatusesController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v2/admin/accounts_controller_spec.rb b/spec/controllers/api/v2/admin/accounts_controller_spec.rb
index 2508a9e05..5766fd549 100644
--- a/spec/controllers/api/v2/admin/accounts_controller_spec.rb
+++ b/spec/controllers/api/v2/admin/accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V2::Admin::AccountsController, type: :controller do
@@ -65,7 +67,7 @@ RSpec.describe Api::V2::Admin::AccountsController, type: :controller do
         it "returns the correct accounts (#{expected_results.inspect})" do
           json = body_as_json
 
-          expect(json.map { |a| a[:id].to_i }).to eq (expected_results.map { |symbol| send(symbol).id })
+          expect(json.map { |a| a[:id].to_i }).to eq(expected_results.map { |symbol| send(symbol).id })
         end
       end
     end
diff --git a/spec/controllers/api/v2/filters/keywords_controller_spec.rb b/spec/controllers/api/v2/filters/keywords_controller_spec.rb
index 1201a4ca2..8c61059c6 100644
--- a/spec/controllers/api/v2/filters/keywords_controller_spec.rb
+++ b/spec/controllers/api/v2/filters/keywords_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V2::Filters::KeywordsController, type: :controller do
@@ -45,7 +47,7 @@ RSpec.describe Api::V2::Filters::KeywordsController, type: :controller do
     it 'returns a keyword' do
       json = body_as_json
       expect(json[:keyword]).to eq 'magic'
-      expect(json[:whole_word]).to eq false
+      expect(json[:whole_word]).to be false
     end
 
     it 'creates a keyword' do
@@ -78,7 +80,7 @@ RSpec.describe Api::V2::Filters::KeywordsController, type: :controller do
     it 'returns expected data' do
       json = body_as_json
       expect(json[:keyword]).to eq 'foo'
-      expect(json[:whole_word]).to eq false
+      expect(json[:whole_word]).to be false
     end
 
     context "when trying to access another user's filter keyword" do
diff --git a/spec/controllers/api/v2/filters/statuses_controller_spec.rb b/spec/controllers/api/v2/filters/statuses_controller_spec.rb
index 9740c1eb3..330cf45a6 100644
--- a/spec/controllers/api/v2/filters/statuses_controller_spec.rb
+++ b/spec/controllers/api/v2/filters/statuses_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V2::Filters::StatusesController, type: :controller do
@@ -64,7 +66,7 @@ RSpec.describe Api::V2::Filters::StatusesController, type: :controller do
   end
 
   describe 'GET #show' do
-    let(:scopes)  { 'read:filters' }
+    let(:scopes) { 'read:filters' }
     let!(:status_filter) { Fabricate(:custom_filter_status, custom_filter: filter) }
 
     before do
@@ -90,7 +92,7 @@ RSpec.describe Api::V2::Filters::StatusesController, type: :controller do
   end
 
   describe 'DELETE #destroy' do
-    let(:scopes)  { 'write:filters' }
+    let(:scopes) { 'write:filters' }
     let(:status_filter) { Fabricate(:custom_filter_status, custom_filter: filter) }
 
     before do
diff --git a/spec/controllers/api/v2/filters_controller_spec.rb b/spec/controllers/api/v2/filters_controller_spec.rb
index cc0070d57..2b5610a4d 100644
--- a/spec/controllers/api/v2/filters_controller_spec.rb
+++ b/spec/controllers/api/v2/filters_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Api::V2::FiltersController, type: :controller do
diff --git a/spec/controllers/api/v2/instances_controller_spec.rb b/spec/controllers/api/v2/instances_controller_spec.rb
new file mode 100644
index 000000000..b7206da0a
--- /dev/null
+++ b/spec/controllers/api/v2/instances_controller_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V2::InstancesController do
+  render_views
+
+  let(:user)  { Fabricate(:user) }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id) }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/v2/suggestions_controller_spec.rb b/spec/controllers/api/v2/suggestions_controller_spec.rb
new file mode 100644
index 000000000..5e6508bfd
--- /dev/null
+++ b/spec/controllers/api/v2/suggestions_controller_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Api::V2::SuggestionsController do
+  render_views
+
+  let(:user)  { Fabricate(:user) }
+  let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
+
+  before do
+    allow(controller).to receive(:doorkeeper_token) { token }
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/api/web/embeds_controller_spec.rb b/spec/controllers/api/web/embeds_controller_spec.rb
index a8fc1718f..e03f5a371 100644
--- a/spec/controllers/api/web/embeds_controller_spec.rb
+++ b/spec/controllers/api/web/embeds_controller_spec.rb
@@ -6,10 +6,12 @@ describe Api::Web::EmbedsController do
   render_views
 
   let(:user) { Fabricate(:user) }
+
   before { sign_in user }
 
   describe 'POST #create' do
     subject(:response) { post :create, params: { url: url } }
+
     subject(:body) { JSON.parse(response.body, symbolize_names: true) }
 
     context 'when successfully finds status' do
@@ -17,7 +19,7 @@ describe Api::Web::EmbedsController do
       let(:url) { "http://#{Rails.configuration.x.web_domain}/@#{status.account.username}/#{status.id}" }
 
       it 'returns a right response' do
-        expect(response).to have_http_status :ok
+        expect(response).to have_http_status 200
         expect(body[:author_name]).to eq status.account.username
       end
     end
@@ -35,7 +37,7 @@ describe Api::Web::EmbedsController do
         let(:call_result) { { result: :ok } }
 
         it 'returns a right response' do
-          expect(response).to have_http_status :ok
+          expect(response).to have_http_status 200
           expect(body[:result]).to eq 'ok'
         end
       end
@@ -44,7 +46,7 @@ describe Api::Web::EmbedsController do
         let(:call_result) { nil }
 
         it 'returns a right response' do
-          expect(response).to have_http_status :not_found
+          expect(response).to have_http_status 404
         end
       end
     end
diff --git a/spec/controllers/api/web/push_subscriptions_controller_spec.rb b/spec/controllers/api/web/push_subscriptions_controller_spec.rb
index bda4a7661..9f027ede9 100644
--- a/spec/controllers/api/web/push_subscriptions_controller_spec.rb
+++ b/spec/controllers/api/web/push_subscriptions_controller_spec.rb
@@ -15,7 +15,7 @@ describe Api::Web::PushSubscriptionsController do
           p256dh: 'BEm_a0bdPDhf0SOsrnB2-ategf1hHoCnpXgQsFj5JCkcoMrMt2WHoPfEYOYPzOIs9mZE8ZUaD7VA5vouy0kEkr8=',
           auth: 'eH_C8rq2raXqlcBVDa1gLg==',
         },
-      }
+      },
     }
   end
 
@@ -32,8 +32,8 @@ describe Api::Web::PushSubscriptionsController do
           mention: false,
           poll: true,
           status: false,
-        }
-      }
+        },
+      },
     }
   end
 
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 2af12376d..82455d874 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -27,8 +27,8 @@ describe ApplicationController, type: :controller do
       expect(response).to have_http_status(code)
     end
 
-    it "renders template for http" do
-      is_expected.to render_template("errors/#{code}", layout: 'error')
+    it 'renders template for http' do
+      expect(subject).to render_template("errors/#{code}", layout: 'error')
     end
   end
 
@@ -57,26 +57,26 @@ describe ApplicationController, type: :controller do
   describe 'helper_method :single_user_mode?' do
     it 'returns false if it is in single_user_mode but there is no account' do
       allow(Rails.configuration.x).to receive(:single_user_mode).and_return(true)
-      expect(controller.view_context.single_user_mode?).to eq false
+      expect(controller.view_context.single_user_mode?).to be false
     end
 
     it 'returns false if there is an account but it is not in single_user_mode' do
       allow(Rails.configuration.x).to receive(:single_user_mode).and_return(false)
       Fabricate(:account)
-      expect(controller.view_context.single_user_mode?).to eq false
+      expect(controller.view_context.single_user_mode?).to be false
     end
 
     it 'returns true if it is in single_user_mode and there is an account' do
       allow(Rails.configuration.x).to receive(:single_user_mode).and_return(true)
       Fabricate(:account)
-      expect(controller.view_context.single_user_mode?).to eq true
+      expect(controller.view_context.single_user_mode?).to be true
     end
   end
 
   describe 'helper_method :current_flavour' do
     it 'returns "glitch" when theme wasn\'t changed in admin settings' do
-      allow(Setting).to receive(:default_settings).and_return({'skin' => 'default'})
-      allow(Setting).to receive(:default_settings).and_return({'flavour' => 'glitch'})
+      allow(Setting).to receive(:default_settings).and_return({ 'skin' => 'default' })
+      allow(Setting).to receive(:default_settings).and_return({ 'flavour' => 'glitch' })
 
       expect(controller.view_context.current_flavour).to eq 'glitch'
     end
@@ -101,7 +101,8 @@ describe ApplicationController, type: :controller do
 
     it 'returns user\'s flavour when it is set' do
       current_user = Fabricate(:user)
-      current_user.settings['flavour'] = 'glitch'
+      current_user.settings.update(flavour: 'glitch')
+      current_user.save
       sign_in current_user
 
       allow(Setting).to receive(:[]).with('skin').and_return 'default'
@@ -150,7 +151,7 @@ describe ApplicationController, type: :controller do
       end
 
       it 'does not store location for user if it is devise controller' do
-        @request.env["devise.mapping"] = Devise.mappings[:user]
+        @request.env['devise.mapping'] = Devise.mappings[:user]
         get 'create'
         expect(controller.stored_location_for(:user)).to be_nil
       end
diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb
index 0ebf6641f..e3a00fa39 100644
--- a/spec/controllers/auth/registrations_controller_spec.rb
+++ b/spec/controllers/auth/registrations_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Auth::RegistrationsController, type: :controller do
@@ -32,7 +34,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
 
   describe 'GET #edit' do
     it 'returns http success' do
-      request.env["devise.mapping"] = Devise.mappings[:user]
+      request.env['devise.mapping'] = Devise.mappings[:user]
       sign_in(Fabricate(:user))
       get :edit
       expect(response).to have_http_status(200)
@@ -41,7 +43,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
 
   describe 'GET #update' do
     it 'returns http success' do
-      request.env["devise.mapping"] = Devise.mappings[:user]
+      request.env['devise.mapping'] = Devise.mappings[:user]
       sign_in(Fabricate(:user), scope: :user)
       post :update
       expect(response).to have_http_status(200)
@@ -49,7 +51,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
 
     context 'when suspended' do
       it 'returns http forbidden' do
-        request.env["devise.mapping"] = Devise.mappings[:user]
+        request.env['devise.mapping'] = Devise.mappings[:user]
         sign_in(Fabricate(:user, account_attributes: { username: 'test', suspended_at: Time.now.utc }), scope: :user)
         post :update
         expect(response).to have_http_status(403)
@@ -59,7 +61,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
 
   describe 'GET #new' do
     before do
-      request.env["devise.mapping"] = Devise.mappings[:user]
+      request.env['devise.mapping'] = Devise.mappings[:user]
     end
 
     context do
@@ -92,21 +94,21 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
       I18n.locale = current_locale
     end
 
-    before { request.env["devise.mapping"] = Devise.mappings[:user] }
+    before { request.env['devise.mapping'] = Devise.mappings[:user] }
 
     context do
+      subject do
+        Setting.registrations_mode = 'open'
+        request.headers['Accept-Language'] = accept_language
+        post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' } }
+      end
+
       around do |example|
         registrations_mode = Setting.registrations_mode
         example.run
         Setting.registrations_mode = registrations_mode
       end
 
-      subject do
-        Setting.registrations_mode = 'open'
-        request.headers["Accept-Language"] = accept_language
-        post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' } }
-      end
-
       it 'redirects to setup' do
         subject
         expect(response).to redirect_to auth_setup_path
@@ -121,18 +123,18 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
     end
 
     context 'when user has not agreed to terms of service' do
+      subject do
+        Setting.registrations_mode = 'open'
+        request.headers['Accept-Language'] = accept_language
+        post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'false' } }
+      end
+
       around do |example|
         registrations_mode = Setting.registrations_mode
         example.run
         Setting.registrations_mode = registrations_mode
       end
 
-      subject do
-        Setting.registrations_mode = 'open'
-        request.headers["Accept-Language"] = accept_language
-        post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'false' } }
-      end
-
       it 'does not create user' do
         subject
         user = User.find_by(email: 'test@example.com')
@@ -141,18 +143,18 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
     end
 
     context 'approval-based registrations without invite' do
+      subject do
+        Setting.registrations_mode = 'approved'
+        request.headers['Accept-Language'] = accept_language
+        post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' } }
+      end
+
       around do |example|
         registrations_mode = Setting.registrations_mode
         example.run
         Setting.registrations_mode = registrations_mode
       end
 
-      subject do
-        Setting.registrations_mode = 'approved'
-        request.headers["Accept-Language"] = accept_language
-        post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', agreement: 'true' } }
-      end
-
       it 'redirects to setup' do
         subject
         expect(response).to redirect_to auth_setup_path
@@ -163,24 +165,24 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
         user = User.find_by(email: 'test@example.com')
         expect(user).to_not be_nil
         expect(user.locale).to eq(accept_language)
-        expect(user.approved).to eq(false)
+        expect(user.approved).to be(false)
       end
     end
 
     context 'approval-based registrations with expired invite' do
+      subject do
+        Setting.registrations_mode = 'approved'
+        request.headers['Accept-Language'] = accept_language
+        invite = Fabricate(:invite, max_uses: nil, expires_at: 1.hour.ago)
+        post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', invite_code: invite.code, agreement: 'true' } }
+      end
+
       around do |example|
         registrations_mode = Setting.registrations_mode
         example.run
         Setting.registrations_mode = registrations_mode
       end
 
-      subject do
-        Setting.registrations_mode = 'approved'
-        request.headers["Accept-Language"] = accept_language
-        invite = Fabricate(:invite, max_uses: nil, expires_at: 1.hour.ago)
-        post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', 'invite_code': invite.code, agreement: 'true' } }
-      end
-
       it 'redirects to setup' do
         subject
         expect(response).to redirect_to auth_setup_path
@@ -191,11 +193,20 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
         user = User.find_by(email: 'test@example.com')
         expect(user).to_not be_nil
         expect(user.locale).to eq(accept_language)
-        expect(user.approved).to eq(false)
+        expect(user.approved).to be(false)
       end
     end
 
     context 'approval-based registrations with valid invite and required invite text' do
+      subject do
+        inviter = Fabricate(:user, confirmed_at: 2.days.ago)
+        Setting.registrations_mode = 'approved'
+        Setting.require_invite_text = true
+        request.headers['Accept-Language'] = accept_language
+        invite = Fabricate(:invite, user: inviter, max_uses: nil, expires_at: 1.hour.from_now)
+        post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', invite_code: invite.code, agreement: 'true' } }
+      end
+
       around do |example|
         registrations_mode = Setting.registrations_mode
         require_invite_text = Setting.require_invite_text
@@ -204,15 +215,6 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
         Setting.registrations_mode = registrations_mode
       end
 
-      subject do
-        inviter = Fabricate(:user, confirmed_at: 2.days.ago)
-        Setting.registrations_mode = 'approved'
-        Setting.require_invite_text = true
-        request.headers["Accept-Language"] = accept_language
-        invite = Fabricate(:invite, user: inviter, max_uses: nil, expires_at: 1.hour.from_now)
-        post :create, params: { user: { account_attributes: { username: 'test' }, email: 'test@example.com', password: '12345678', password_confirmation: '12345678', 'invite_code': invite.code, agreement: 'true' } }
-      end
-
       it 'redirects to setup' do
         subject
         expect(response).to redirect_to auth_setup_path
@@ -223,7 +225,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
         user = User.find_by(email: 'test@example.com')
         expect(user).to_not be_nil
         expect(user.locale).to eq(accept_language)
-        expect(user.approved).to eq(true)
+        expect(user.approved).to be(true)
       end
     end
 
@@ -245,7 +247,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do
     end
 
     it 'returns http not found' do
-      expect(response).to have_http_status(:not_found)
+      expect(response).to have_http_status(404)
     end
 
     it 'does not delete user' do
diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb
index d3db7aa1a..58befa124 100644
--- a/spec/controllers/auth/sessions_controller_spec.rb
+++ b/spec/controllers/auth/sessions_controller_spec.rb
@@ -54,7 +54,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
     context 'using PAM authentication', if: ENV['PAM_ENABLED'] == 'true' do
       context 'using a valid password' do
         before do
-          post :create, params: { user: { email: "pam_user1", password: '123456' } }
+          post :create, params: { user: { email: 'pam_user1', password: '123456' } }
         end
 
         it 'redirects to home' do
@@ -68,7 +68,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
 
       context 'using an invalid password' do
         before do
-          post :create, params: { user: { email: "pam_user1", password: 'WRONGPW' } }
+          post :create, params: { user: { email: 'pam_user1', password: 'WRONGPW' } }
         end
 
         it 'shows a login error' do
@@ -127,7 +127,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
 
         before do
           allow_any_instance_of(ActionDispatch::Request).to receive(:remote_ip).and_return(current_ip)
-          allow(UserMailer).to receive(:suspicious_sign_in).and_return(double('email', 'deliver_later!': nil))
+          allow(UserMailer).to receive(:suspicious_sign_in).and_return(double('email', deliver_later!: nil))
           user.update(current_sign_in_at: 1.month.ago)
           post :create, params: { user: { email: user.email, password: user.password } }
         end
@@ -194,7 +194,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
           post :create, params: { user: { email: user.email, password: user.password } }
         end
 
-        context "in single user mode" do
+        context 'in single user mode' do
           let(:single_user_mode) { true }
 
           it 'redirects to home' do
@@ -202,7 +202,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
           end
         end
 
-        context "in non-single user mode" do
+        context 'in non-single user mode' do
           let(:single_user_mode) { false }
 
           it "redirects back to the user's page" do
@@ -230,8 +230,8 @@ RSpec.describe Auth::SessionsController, type: :controller do
           end
 
           it 'renders two factor authentication page' do
-            expect(controller).to render_template("two_factor")
-            expect(controller).to render_template(partial: "_otp_authentication_form")
+            expect(controller).to render_template('two_factor')
+            expect(controller).to render_template(partial: '_otp_authentication_form')
           end
         end
 
@@ -246,8 +246,8 @@ RSpec.describe Auth::SessionsController, type: :controller do
           end
 
           it 'renders two factor authentication page' do
-            expect(controller).to render_template("two_factor")
-            expect(controller).to render_template(partial: "_otp_authentication_form")
+            expect(controller).to render_template('two_factor')
+            expect(controller).to render_template(partial: '_otp_authentication_form')
           end
         end
 
@@ -257,8 +257,8 @@ RSpec.describe Auth::SessionsController, type: :controller do
           end
 
           it 'renders two factor authentication page' do
-            expect(controller).to render_template("two_factor")
-            expect(controller).to render_template(partial: "_otp_authentication_form")
+            expect(controller).to render_template('two_factor')
+            expect(controller).to render_template(partial: '_otp_authentication_form')
           end
         end
 
@@ -339,11 +339,11 @@ RSpec.describe Auth::SessionsController, type: :controller do
             external_id: public_key_credential.id,
             public_key: public_key_credential.public_key,
             sign_count: '1000'
-           )
+          )
           user.webauthn_credentials.take
         end
 
-        let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http' }://#{Rails.configuration.x.web_domain}" }
+        let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http'}://#{Rails.configuration.x.web_domain}" }
 
         let(:fake_client) { WebAuthn::FakeClient.new(domain) }
 
@@ -359,8 +359,8 @@ RSpec.describe Auth::SessionsController, type: :controller do
           end
 
           it 'renders webauthn authentication page' do
-            expect(controller).to render_template("two_factor")
-            expect(controller).to render_template(partial: "_webauthn_form")
+            expect(controller).to render_template('two_factor')
+            expect(controller).to render_template(partial: '_webauthn_form')
           end
         end
 
@@ -370,8 +370,8 @@ RSpec.describe Auth::SessionsController, type: :controller do
           end
 
           it 'renders webauthn authentication page' do
-            expect(controller).to render_template("two_factor")
-            expect(controller).to render_template(partial: "_webauthn_form")
+            expect(controller).to render_template('two_factor')
+            expect(controller).to render_template(partial: '_webauthn_form')
           end
         end
 
@@ -400,7 +400,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
 
   describe 'GET #webauthn_options' do
     context 'with WebAuthn and OTP enabled as second factor' do
-      let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http' }://#{Rails.configuration.x.web_domain}" }
+      let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http'}://#{Rails.configuration.x.web_domain}" }
 
       let(:fake_client) { WebAuthn::FakeClient.new(domain) }
 
@@ -422,7 +422,7 @@ RSpec.describe Auth::SessionsController, type: :controller do
 
       it 'returns http success' do
         get :webauthn_options
-        expect(response).to have_http_status :ok
+        expect(response).to have_http_status 200
       end
     end
   end
diff --git a/spec/controllers/auth/setup_controller_spec.rb b/spec/controllers/auth/setup_controller_spec.rb
new file mode 100644
index 000000000..75e42aaf9
--- /dev/null
+++ b/spec/controllers/auth/setup_controller_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Auth::SetupController do
+  render_views
+
+  describe 'GET #show' do
+    context 'with a signed out request' do
+      it 'returns http redirect' do
+        get :show
+        expect(response).to be_redirect
+      end
+    end
+
+    context 'with an unconfirmed signed in user' do
+      before { sign_in Fabricate(:user, confirmed_at: nil) }
+
+      it 'returns http success' do
+        get :show
+        expect(response).to have_http_status(200)
+      end
+    end
+  end
+end
diff --git a/spec/controllers/authorize_interactions_controller_spec.rb b/spec/controllers/authorize_interactions_controller_spec.rb
index 44f52df69..e52103941 100644
--- a/spec/controllers/authorize_interactions_controller_spec.rb
+++ b/spec/controllers/authorize_interactions_controller_spec.rb
@@ -99,7 +99,6 @@ describe AuthorizeInteractionsController do
         allow(ResolveAccountService).to receive(:new).and_return(service)
         allow(service).to receive(:call).with('user@hostname').and_return(target_account)
 
-
         post :create, params: { acct: 'acct:user@hostname' }
 
         expect(account.following?(target_account)).to be true
diff --git a/spec/controllers/concerns/export_controller_concern_spec.rb b/spec/controllers/concerns/export_controller_concern_spec.rb
index 1a5e46f8e..003fd17f6 100644
--- a/spec/controllers/concerns/export_controller_concern_spec.rb
+++ b/spec/controllers/concerns/export_controller_concern_spec.rb
@@ -29,7 +29,7 @@ describe ApplicationController, type: :controller do
 
     it 'returns unauthorized when not signed in' do
       get :index, format: :csv
-      expect(response).to have_http_status(:unauthorized)
+      expect(response).to have_http_status(401)
     end
   end
 end
diff --git a/spec/controllers/concerns/signature_verification_spec.rb b/spec/controllers/concerns/signature_verification_spec.rb
index 6e73643b4..13655f313 100644
--- a/spec/controllers/concerns/signature_verification_spec.rb
+++ b/spec/controllers/concerns/signature_verification_spec.rb
@@ -16,6 +16,8 @@ describe ApplicationController, type: :controller do
   controller do
     include SignatureVerification
 
+    before_action :require_actor_signature!, only: [:signature_required]
+
     def success
       head 200
     end
@@ -23,10 +25,17 @@ describe ApplicationController, type: :controller do
     def alternative_success
       head 200
     end
+
+    def signature_required
+      head 200
+    end
   end
 
   before do
-    routes.draw { match via: [:get, :post], 'success' => 'anonymous#success' }
+    routes.draw do
+      match via: [:get, :post], 'success' => 'anonymous#success'
+      match via: [:get, :post], 'signature_required' => 'anonymous#signature_required'
+    end
   end
 
   context 'without signature header' do
@@ -118,6 +127,37 @@ describe ApplicationController, type: :controller do
       end
     end
 
+    context 'with request with unparseable Date header' do
+      before do
+        get :success
+
+        fake_request = Request.new(:get, request.url)
+        fake_request.add_headers({ 'Date' => 'wrong date' })
+        fake_request.on_behalf_of(author)
+
+        request.headers.merge!(fake_request.headers)
+      end
+
+      describe '#signed_request?' do
+        it 'returns true' do
+          expect(controller.signed_request?).to be true
+        end
+      end
+
+      describe '#signed_request_account' do
+        it 'returns nil' do
+          expect(controller.signed_request_account).to be_nil
+        end
+      end
+
+      describe '#signature_verification_failure_reason' do
+        it 'contains an error description' do
+          controller.signed_request_account
+          expect(controller.signature_verification_failure_reason[:error]).to eq 'Invalid Date header: not RFC 2616 compliant date: "wrong date"'
+        end
+      end
+    end
+
     context 'with request older than a day' do
       before do
         get :success
@@ -140,6 +180,13 @@ describe ApplicationController, type: :controller do
           expect(controller.signed_request_account).to be_nil
         end
       end
+
+      describe '#signature_verification_failure_reason' do
+        it 'contains an error description' do
+          controller.signed_request_account
+          expect(controller.signature_verification_failure_reason[:error]).to eq 'Signed request date outside acceptable time window'
+        end
+      end
     end
 
     context 'with inaccessible key' do
@@ -171,6 +218,7 @@ describe ApplicationController, type: :controller do
 
     context 'with body' do
       before do
+        allow(controller).to receive(:actor_refresh_key!).and_return(author)
         post :success, body: 'Hello world'
 
         fake_request = Request.new(:post, request.url, body: 'Hello world')
@@ -189,21 +237,66 @@ describe ApplicationController, type: :controller do
         it 'returns an account' do
           expect(controller.signed_request_account).to eq author
         end
+      end
 
-        it 'returns nil when path does not match' do
+      context 'when path does not match' do
+        before do
           request.path = '/alternative-path'
-          expect(controller.signed_request_account).to be_nil
         end
 
-        it 'returns nil when method does not match' do
+        describe '#signed_request_account' do
+          it 'returns nil' do
+            expect(controller.signed_request_account).to be_nil
+          end
+        end
+
+        describe '#signature_verification_failure_reason' do
+          it 'contains an error description' do
+            controller.signed_request_account
+            expect(controller.signature_verification_failure_reason[:error]).to include('using rsa-sha256 (RSASSA-PKCS1-v1_5 with SHA-256)')
+            expect(controller.signature_verification_failure_reason[:signed_string]).to include("(request-target): post /alternative-path\n")
+          end
+        end
+      end
+
+      context 'when method does not match' do
+        before do
           get :success
-          expect(controller.signed_request_account).to be_nil
         end
 
-        it 'returns nil when body has been tampered' do
+        describe '#signed_request_account' do
+          it 'returns nil' do
+            expect(controller.signed_request_account).to be_nil
+          end
+        end
+      end
+
+      context 'when body has been tampered' do
+        before do
           post :success, body: 'doo doo doo'
-          expect(controller.signed_request_account).to be_nil
         end
+
+        describe '#signed_request_account' do
+          it 'returns nil when body has been tampered' do
+            expect(controller.signed_request_account).to be_nil
+          end
+        end
+      end
+    end
+  end
+
+  context 'when a signature is required' do
+    before do
+      get :signature_required
+    end
+
+    context 'without signature header' do
+      it 'returns HTTP 401' do
+        expect(response).to have_http_status(401)
+      end
+
+      it 'returns an error' do
+        expect(Oj.load(response.body)['error']).to eq 'Request not signed'
       end
     end
   end
diff --git a/spec/controllers/custom_css_controller_spec.rb b/spec/controllers/custom_css_controller_spec.rb
new file mode 100644
index 000000000..47fe6031f
--- /dev/null
+++ b/spec/controllers/custom_css_controller_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe CustomCssController do
+  render_views
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/disputes/appeals_controller_spec.rb b/spec/controllers/disputes/appeals_controller_spec.rb
index 90f222f49..affe63c59 100644
--- a/spec/controllers/disputes/appeals_controller_spec.rb
+++ b/spec/controllers/disputes/appeals_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Disputes::AppealsController, type: :controller do
diff --git a/spec/controllers/disputes/strikes_controller_spec.rb b/spec/controllers/disputes/strikes_controller_spec.rb
index 157f9ec3c..1d678875c 100644
--- a/spec/controllers/disputes/strikes_controller_spec.rb
+++ b/spec/controllers/disputes/strikes_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Disputes::StrikesController, type: :controller do
@@ -23,7 +25,7 @@ RSpec.describe Disputes::StrikesController, type: :controller do
       let(:strike) { Fabricate(:account_warning) }
 
       it 'returns http forbidden' do
-        expect(response).to have_http_status(:forbidden)
+        expect(response).to have_http_status(403)
       end
     end
   end
diff --git a/spec/controllers/emojis_controller_spec.rb b/spec/controllers/emojis_controller_spec.rb
index fbbd11f64..710d23d92 100644
--- a/spec/controllers/emojis_controller_spec.rb
+++ b/spec/controllers/emojis_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe EmojisController do
@@ -7,6 +9,7 @@ describe EmojisController do
 
   describe 'GET #show' do
     subject(:response) { get :show, params: { id: emoji.id, format: :json } }
+
     subject(:body) { JSON.parse(response.body, symbolize_names: true) }
 
     it 'returns the right response' do
diff --git a/spec/controllers/filters/statuses_controller_spec.rb b/spec/controllers/filters/statuses_controller_spec.rb
new file mode 100644
index 000000000..492361188
--- /dev/null
+++ b/spec/controllers/filters/statuses_controller_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Filters::StatusesController do
+  render_views
+
+  describe 'GET #index' do
+    let(:filter) { Fabricate(:custom_filter) }
+
+    context 'with signed out user' do
+      it 'redirects' do
+        get :index, params: { filter_id: filter }
+
+        expect(response).to be_redirect
+      end
+    end
+
+    context 'with a signed in user' do
+      context 'with the filter user signed in' do
+        before { sign_in(filter.account.user) }
+
+        it 'returns http success' do
+          get :index, params: { filter_id: filter }
+
+          expect(response).to have_http_status(200)
+        end
+      end
+
+      context 'with another user signed in' do
+        before { sign_in(Fabricate(:user)) }
+
+        it 'returns http not found' do
+          get :index, params: { filter_id: filter }
+
+          expect(response).to have_http_status(404)
+        end
+      end
+    end
+  end
+end
diff --git a/spec/controllers/filters_controller_spec.rb b/spec/controllers/filters_controller_spec.rb
new file mode 100644
index 000000000..f68f87ba7
--- /dev/null
+++ b/spec/controllers/filters_controller_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe FiltersController do
+  render_views
+
+  describe 'GET #index' do
+    context 'with signed out user' do
+      it 'redirects' do
+        get :index
+
+        expect(response).to be_redirect
+      end
+    end
+
+    context 'with a signed in user' do
+      before { sign_in(Fabricate(:user)) }
+
+      it 'returns http success' do
+        get :index
+
+        expect(response).to have_http_status(200)
+      end
+    end
+  end
+end
diff --git a/spec/controllers/follower_accounts_controller_spec.rb b/spec/controllers/follower_accounts_controller_spec.rb
index ab2e82e85..7c53e5b47 100644
--- a/spec/controllers/follower_accounts_controller_spec.rb
+++ b/spec/controllers/follower_accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe FollowerAccountsController do
@@ -38,6 +40,7 @@ describe FollowerAccountsController do
 
     context 'when format is json' do
       subject(:response) { get :index, params: { account_username: alice.username, page: page, format: :json } }
+
       subject(:body) { JSON.parse(response.body) }
 
       context 'with page' do
diff --git a/spec/controllers/following_accounts_controller_spec.rb b/spec/controllers/following_accounts_controller_spec.rb
index e43dbf882..122f72e2d 100644
--- a/spec/controllers/following_accounts_controller_spec.rb
+++ b/spec/controllers/following_accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe FollowingAccountsController do
@@ -38,6 +40,7 @@ describe FollowingAccountsController do
 
     context 'when format is json' do
       subject(:response) { get :index, params: { account_username: alice.username, page: page, format: :json } }
+
       subject(:body) { JSON.parse(response.body) }
 
       context 'with page' do
diff --git a/spec/controllers/health_controller_spec.rb b/spec/controllers/health_controller_spec.rb
index 1e41f6ed7..282b66419 100644
--- a/spec/controllers/health_controller_spec.rb
+++ b/spec/controllers/health_controller_spec.rb
@@ -1,13 +1,14 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe HealthController do
   render_views
 
   describe 'GET #show' do
-    subject(:response) { get :show, params: { format: :json } }
-
-    it 'returns the right response' do
-      expect(response).to have_http_status 200
+    it 'returns http success' do
+      get :show
+      expect(response).to have_http_status(200)
     end
   end
 end
diff --git a/spec/controllers/home_controller_spec.rb b/spec/controllers/home_controller_spec.rb
index d845ae01d..0d3722920 100644
--- a/spec/controllers/home_controller_spec.rb
+++ b/spec/controllers/home_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe HomeController, type: :controller do
@@ -9,7 +11,7 @@ RSpec.describe HomeController, type: :controller do
     context 'when not signed in' do
       it 'returns http success' do
         @request.path = '/'
-        is_expected.to have_http_status(:success)
+        expect(subject).to have_http_status(:success)
       end
     end
 
@@ -21,7 +23,7 @@ RSpec.describe HomeController, type: :controller do
       end
 
       it 'returns http success' do
-        is_expected.to have_http_status(:success)
+        expect(subject).to have_http_status(:success)
       end
     end
   end
diff --git a/spec/controllers/instance_actors_controller_spec.rb b/spec/controllers/instance_actors_controller_spec.rb
index f64a7d2ca..84a07d497 100644
--- a/spec/controllers/instance_actors_controller_spec.rb
+++ b/spec/controllers/instance_actors_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe InstanceActorsController, type: :controller do
@@ -20,7 +22,7 @@ RSpec.describe InstanceActorsController, type: :controller do
 
         it 'does not set cookies' do
           expect(response.cookies).to be_empty
-          expect(response.headers['Set-Cookies']).to be nil
+          expect(response.headers['Set-Cookies']).to be_nil
         end
 
         it 'does not set sessions' do
@@ -43,11 +45,13 @@ RSpec.describe InstanceActorsController, type: :controller do
 
       context 'without authorized fetch mode' do
         let(:authorized_fetch_mode) { false }
+
         it_behaves_like 'shared behavior'
       end
 
       context 'with authorized fetch mode' do
         let(:authorized_fetch_mode) { true }
+
         it_behaves_like 'shared behavior'
       end
     end
diff --git a/spec/controllers/intents_controller_spec.rb b/spec/controllers/intents_controller_spec.rb
index ddfd5ea36..02b46ddc7 100644
--- a/spec/controllers/intents_controller_spec.rb
+++ b/spec/controllers/intents_controller_spec.rb
@@ -1,9 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe IntentsController, type: :controller do
   render_views
 
   let(:user) { Fabricate(:user) }
+
   before { sign_in user, scope: :user }
 
   describe 'GET #show' do
diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb
index 23b98fb12..408c5e1b5 100644
--- a/spec/controllers/invites_controller_spec.rb
+++ b/spec/controllers/invites_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe InvitesController do
diff --git a/spec/controllers/manifests_controller_spec.rb b/spec/controllers/manifests_controller_spec.rb
index a549adef3..ecd6957fc 100644
--- a/spec/controllers/manifests_controller_spec.rb
+++ b/spec/controllers/manifests_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe ManifestsController do
diff --git a/spec/controllers/oauth/authorized_applications_controller_spec.rb b/spec/controllers/oauth/authorized_applications_controller_spec.rb
index 901e538e9..885bfa35b 100644
--- a/spec/controllers/oauth/authorized_applications_controller_spec.rb
+++ b/spec/controllers/oauth/authorized_applications_controller_spec.rb
@@ -13,7 +13,7 @@ describe Oauth::AuthorizedApplicationsController do
     shared_examples 'stores location for user' do
       it 'stores location for user' do
         subject
-        expect(controller.stored_location_for(:user)).to eq "/oauth/authorized_applications"
+        expect(controller.stored_location_for(:user)).to eq '/oauth/authorized_applications'
       end
     end
 
diff --git a/spec/controllers/privacy_controller_spec.rb b/spec/controllers/privacy_controller_spec.rb
new file mode 100644
index 000000000..c92c71ea6
--- /dev/null
+++ b/spec/controllers/privacy_controller_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PrivacyController do
+  render_views
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/relationships_controller_spec.rb b/spec/controllers/relationships_controller_spec.rb
index 2056a2ac2..53a5daa51 100644
--- a/spec/controllers/relationships_controller_spec.rb
+++ b/spec/controllers/relationships_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe RelationshipsController do
@@ -7,7 +9,7 @@ describe RelationshipsController do
 
   shared_examples 'authenticate user' do
     it 'redirects when not signed in' do
-      is_expected.to redirect_to '/auth/sign_in'
+      expect(subject).to redirect_to '/auth/sign_in'
     end
   end
 
@@ -51,11 +53,12 @@ describe RelationshipsController do
 
     context 'when select parameter is not provided' do
       subject { patch :update }
+
       include_examples 'redirects back to followers page'
     end
 
     context 'when select parameter is provided' do
-      subject { patch :update, params: { form_account_batch: { account_ids: [poopfeast.id] }, block_domains: '' } }
+      subject { patch :update, params: { form_account_batch: { account_ids: [poopfeast.id] }, remove_domains_from_followers: '' } }
 
       it 'soft-blocks followers from selected domains' do
         poopfeast.follow!(user.account)
@@ -66,6 +69,15 @@ describe RelationshipsController do
         expect(poopfeast.following?(user.account)).to be false
       end
 
+      it 'does not unfollow users from selected domains' do
+        user.account.follow!(poopfeast)
+
+        sign_in user, scope: :user
+        subject
+
+        expect(user.account.following?(poopfeast)).to be true
+      end
+
       include_examples 'authenticate user'
       include_examples 'redirects back to followers page'
     end
diff --git a/spec/controllers/settings/aliases_controller_spec.rb b/spec/controllers/settings/aliases_controller_spec.rb
new file mode 100644
index 000000000..805f65988
--- /dev/null
+++ b/spec/controllers/settings/aliases_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::AliasesController do
+  render_views
+
+  let!(:user) { Fabricate(:user) }
+  let(:account) { user.account }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/settings/applications_controller_spec.rb b/spec/controllers/settings/applications_controller_spec.rb
index 29c278148..5c6b04a15 100644
--- a/spec/controllers/settings/applications_controller_spec.rb
+++ b/spec/controllers/settings/applications_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Settings::ApplicationsController do
@@ -32,12 +34,12 @@ describe Settings::ApplicationsController do
       app.update!(owner: nil)
 
       get :show, params: { id: app.id }
-      expect(response.status).to eq 404
+      expect(response).to have_http_status 404
     end
   end
 
   describe 'GET #new' do
-    it 'works' do
+    it 'returns http success' do
       get :new
       expect(response).to have_http_status(200)
     end
@@ -51,8 +53,8 @@ describe Settings::ApplicationsController do
             name: 'My New App',
             redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
             website: 'http://google.com',
-            scopes: 'read write follow'
-          }
+            scopes: 'read write follow',
+          },
         }
         response
       end
@@ -73,8 +75,8 @@ describe Settings::ApplicationsController do
             name: 'My New App',
             redirect_uri: 'urn:ietf:wg:oauth:2.0:oob',
             website: 'http://google.com',
-            scopes: [ 'read', 'write', 'follow' ]
-          }
+            scopes: %w(read write follow),
+          },
         }
         response
       end
@@ -95,8 +97,8 @@ describe Settings::ApplicationsController do
             name: '',
             redirect_uri: '',
             website: '',
-            scopes: []
-          }
+            scopes: [],
+          },
         }
       end
 
@@ -112,16 +114,16 @@ describe Settings::ApplicationsController do
 
   describe 'PATCH #update' do
     context 'success' do
-      let(:opts) {
+      let(:opts) do
         {
-          website: 'https://foo.bar/'
+          website: 'https://foo.bar/',
         }
-      }
+      end
 
       def call_update
         patch :update, params: {
           id: app.id,
-          doorkeeper_application: opts
+          doorkeeper_application: opts,
         }
         response
       end
@@ -132,7 +134,7 @@ describe Settings::ApplicationsController do
       end
 
       it 'redirects back to applications page' do
-        expect(call_update).to redirect_to(settings_applications_path)
+        expect(call_update).to redirect_to(settings_application_path(app))
       end
     end
 
@@ -144,8 +146,8 @@ describe Settings::ApplicationsController do
             name: '',
             redirect_uri: '',
             website: '',
-            scopes: []
-          }
+            scopes: [],
+          },
         }
       end
 
@@ -175,12 +177,13 @@ describe Settings::ApplicationsController do
 
   describe 'regenerate' do
     let(:token) { user.token_for_app(app) }
+
     before do
       expect(token).to_not be_nil
       post :regenerate, params: { id: app.id }
     end
 
-    it 'should create new token' do
+    it 'creates new token' do
       expect(user.token_for_app(app)).to_not eql(token)
     end
   end
diff --git a/spec/controllers/settings/deletes_controller_spec.rb b/spec/controllers/settings/deletes_controller_spec.rb
index a94dc042a..a7edac6a9 100644
--- a/spec/controllers/settings/deletes_controller_spec.rb
+++ b/spec/controllers/settings/deletes_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Settings::DeletesController do
diff --git a/spec/controllers/settings/exports/blocked_accounts_controller_spec.rb b/spec/controllers/settings/exports/blocked_accounts_controller_spec.rb
index 5ff41b7fc..459b278d6 100644
--- a/spec/controllers/settings/exports/blocked_accounts_controller_spec.rb
+++ b/spec/controllers/settings/exports/blocked_accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Settings::Exports::BlockedAccountsController do
diff --git a/spec/controllers/settings/exports/blocked_domains_controller_spec.rb b/spec/controllers/settings/exports/blocked_domains_controller_spec.rb
new file mode 100644
index 000000000..ac72fd9dd
--- /dev/null
+++ b/spec/controllers/settings/exports/blocked_domains_controller_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::Exports::BlockedDomainsController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns a csv of the domains' do
+      account = Fabricate(:account, domain: 'example.com')
+      user = Fabricate(:user, account: account)
+      Fabricate(:account_domain_block, domain: 'example.com', account: account)
+
+      sign_in user, scope: :user
+      get :index, format: :csv
+
+      expect(response.body).to eq "example.com\n"
+    end
+  end
+end
diff --git a/spec/controllers/settings/exports/bookmarks_controller_spec.rb b/spec/controllers/settings/exports/bookmarks_controller_spec.rb
index a06c02e0c..9982eff16 100644
--- a/spec/controllers/settings/exports/bookmarks_controller_spec.rb
+++ b/spec/controllers/settings/exports/bookmarks_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Settings::Exports::BookmarksController do
diff --git a/spec/controllers/settings/exports/following_accounts_controller_spec.rb b/spec/controllers/settings/exports/following_accounts_controller_spec.rb
index bfe010555..72b0b94e1 100644
--- a/spec/controllers/settings/exports/following_accounts_controller_spec.rb
+++ b/spec/controllers/settings/exports/following_accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Settings::Exports::FollowingAccountsController do
diff --git a/spec/controllers/settings/exports/lists_controller_spec.rb b/spec/controllers/settings/exports/lists_controller_spec.rb
new file mode 100644
index 000000000..29623ba49
--- /dev/null
+++ b/spec/controllers/settings/exports/lists_controller_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::Exports::ListsController do
+  render_views
+
+  describe 'GET #index' do
+    it 'returns a csv of the domains' do
+      account = Fabricate(:account)
+      user = Fabricate(:user, account: account)
+      list = Fabricate(:list, account: account, title: 'The List')
+      Fabricate(:list_account, list: list, account: account)
+
+      sign_in user, scope: :user
+      get :index, format: :csv
+
+      expect(response.body).to match 'The List'
+    end
+  end
+end
diff --git a/spec/controllers/settings/exports/muted_accounts_controller_spec.rb b/spec/controllers/settings/exports/muted_accounts_controller_spec.rb
index 642f0a9b8..b4170cb16 100644
--- a/spec/controllers/settings/exports/muted_accounts_controller_spec.rb
+++ b/spec/controllers/settings/exports/muted_accounts_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Settings::Exports::MutedAccountsController do
diff --git a/spec/controllers/settings/featured_tags_controller_spec.rb b/spec/controllers/settings/featured_tags_controller_spec.rb
index 33b87f9f6..5c61351af 100644
--- a/spec/controllers/settings/featured_tags_controller_spec.rb
+++ b/spec/controllers/settings/featured_tags_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Settings::FeaturedTagsController do
@@ -5,7 +7,7 @@ describe Settings::FeaturedTagsController do
 
   shared_examples 'authenticate user' do
     it 'redirects to sign_in page' do
-      is_expected.to redirect_to new_user_session_path
+      expect(subject).to redirect_to new_user_session_path
     end
   end
 
diff --git a/spec/controllers/settings/flavours_controller_spec.rb b/spec/controllers/settings/flavours_controller_spec.rb
index f89bde1f9..8c7d4a768 100644
--- a/spec/controllers/settings/flavours_controller_spec.rb
+++ b/spec/controllers/settings/flavours_controller_spec.rb
@@ -1,7 +1,8 @@
 # frozen_string_literal: true
+
 require 'rails_helper'
 
-RSpec.describe Settings::FlavoursController, type: :controller do
+RSpec.describe Settings::FlavoursController do
   let(:user) { Fabricate(:user) }
 
   before do
diff --git a/spec/controllers/settings/imports_controller_spec.rb b/spec/controllers/settings/imports_controller_spec.rb
index b8caf5941..78973df2b 100644
--- a/spec/controllers/settings/imports_controller_spec.rb
+++ b/spec/controllers/settings/imports_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Settings::ImportsController, type: :controller do
@@ -7,8 +9,8 @@ RSpec.describe Settings::ImportsController, type: :controller do
     sign_in Fabricate(:user), scope: :user
   end
 
-  describe "GET #show" do
-    it "returns http success" do
+  describe 'GET #show' do
+    it 'returns http success' do
       get :show
       expect(response).to have_http_status(200)
     end
@@ -21,8 +23,8 @@ RSpec.describe Settings::ImportsController, type: :controller do
       post :create, params: {
         import: {
           type: 'following',
-          data: fixture_file_upload('imports.txt')
-        }
+          data: fixture_file_upload('imports.txt'),
+        },
       }
 
       expect(response).to redirect_to(settings_import_path)
@@ -34,8 +36,8 @@ RSpec.describe Settings::ImportsController, type: :controller do
       post :create, params: {
         import: {
           type: 'blocking',
-          data: fixture_file_upload('imports.txt')
-        }
+          data: fixture_file_upload('imports.txt'),
+        },
       }
 
       expect(response).to redirect_to(settings_import_path)
diff --git a/spec/controllers/settings/login_activities_controller_spec.rb b/spec/controllers/settings/login_activities_controller_spec.rb
new file mode 100644
index 000000000..6f1f3de31
--- /dev/null
+++ b/spec/controllers/settings/login_activities_controller_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::LoginActivitiesController do
+  render_views
+
+  let!(:user) { Fabricate(:user) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #index' do
+    it 'returns http success' do
+      get :index
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/settings/migration/redirects_controller_spec.rb b/spec/controllers/settings/migration/redirects_controller_spec.rb
new file mode 100644
index 000000000..50d9e1927
--- /dev/null
+++ b/spec/controllers/settings/migration/redirects_controller_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::Migration::RedirectsController do
+  render_views
+
+  let!(:user) { Fabricate(:user) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #new' do
+    it 'returns http success' do
+      get :new
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/settings/migrations_controller_spec.rb b/spec/controllers/settings/migrations_controller_spec.rb
index 35c5747a0..9b12bc40f 100644
--- a/spec/controllers/settings/migrations_controller_spec.rb
+++ b/spec/controllers/settings/migrations_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Settings::MigrationsController do
@@ -5,7 +7,7 @@ describe Settings::MigrationsController do
 
   shared_examples 'authenticate user' do
     it 'redirects to sign_in page' do
-      is_expected.to redirect_to new_user_session_path
+      expect(subject).to redirect_to new_user_session_path
     end
   end
 
@@ -27,8 +29,8 @@ describe Settings::MigrationsController do
         let(:moved_to_account) { nil }
 
         it 'renders show page' do
-          is_expected.to have_http_status 200
-          is_expected.to render_template :show
+          expect(subject).to have_http_status 200
+          expect(subject).to render_template :show
         end
       end
 
@@ -36,8 +38,8 @@ describe Settings::MigrationsController do
         let(:moved_to_account) { Fabricate(:account) }
 
         it 'renders show page' do
-          is_expected.to have_http_status 200
-          is_expected.to render_template :show
+          expect(subject).to have_http_status 200
+          expect(subject).to render_template :show
         end
       end
     end
@@ -61,7 +63,7 @@ describe Settings::MigrationsController do
         let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) }
 
         it 'updates moved to account' do
-          is_expected.to redirect_to settings_migration_path
+          expect(subject).to redirect_to settings_migration_path
           expect(user.account.reload.moved_to_account_id).to eq acct.id
         end
       end
@@ -70,7 +72,7 @@ describe Settings::MigrationsController do
         let(:acct) { user.account }
 
         it 'renders show' do
-          is_expected.to render_template :show
+          expect(subject).to render_template :show
         end
 
         it 'does not update the moved account' do
@@ -82,7 +84,7 @@ describe Settings::MigrationsController do
         let(:acct) { Fabricate(:account, also_known_as: []) }
 
         it 'renders show' do
-          is_expected.to render_template :show
+          expect(subject).to render_template :show
         end
 
         it 'does not update the moved account' do
@@ -90,7 +92,7 @@ describe Settings::MigrationsController do
         end
       end
 
-      context 'when a recent migration already exists ' do
+      context 'when a recent migration already exists' do
         let(:acct) { Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(user.account)]) }
 
         before do
@@ -99,7 +101,7 @@ describe Settings::MigrationsController do
         end
 
         it 'renders show' do
-          is_expected.to render_template :show
+          expect(subject).to render_template :show
         end
 
         it 'does not update the moved account' do
diff --git a/spec/controllers/settings/pictures_controller_spec.rb b/spec/controllers/settings/pictures_controller_spec.rb
new file mode 100644
index 000000000..2368dc55d
--- /dev/null
+++ b/spec/controllers/settings/pictures_controller_spec.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::PicturesController do
+  render_views
+
+  let!(:user) { Fabricate(:user) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'DELETE #destroy' do
+    context 'with invalid picture id' do
+      it 'returns http bad request' do
+        delete :destroy, params: { id: 'invalid' }
+        expect(response).to have_http_status(400)
+      end
+    end
+  end
+end
diff --git a/spec/controllers/settings/preferences/appearance_controller_spec.rb b/spec/controllers/settings/preferences/appearance_controller_spec.rb
new file mode 100644
index 000000000..7c7f716b7
--- /dev/null
+++ b/spec/controllers/settings/preferences/appearance_controller_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Settings::Preferences::AppearanceController do
+  render_views
+
+  let!(:user) { Fabricate(:user) }
+
+  before do
+    sign_in user, scope: :user
+  end
+
+  describe 'GET #show' do
+    it 'returns http success' do
+      get :show
+      expect(response).to have_http_status(200)
+    end
+  end
+end
diff --git a/spec/controllers/settings/preferences/notifications_controller_spec.rb b/spec/controllers/settings/preferences/notifications_controller_spec.rb
index 02180b383..29b7b6aec 100644
--- a/spec/controllers/settings/preferences/notifications_controller_spec.rb
+++ b/spec/controllers/settings/preferences/notifications_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Settings::Preferences::NotificationsController do
@@ -18,20 +20,22 @@ describe Settings::Preferences::NotificationsController do
 
   describe 'PUT #update' do
     it 'updates notifications settings' do
-      user.settings['notification_emails'] = user.settings['notification_emails'].merge('follow' => false)
-      user.settings['interactions'] = user.settings['interactions'].merge('must_be_follower' => true)
+      user.settings.update('notification_emails.follow': false, 'interactions.must_be_follower': true)
+      user.save
 
       put :update, params: {
         user: {
-          notification_emails: { follow: '1' },
-          interactions: { must_be_follower: '0' },
-        }
+          settings_attributes: {
+            'notification_emails.follow': '1',
+            'interactions.must_be_follower': '0',
+          },
+        },
       }
 
       expect(response).to redirect_to(settings_preferences_notifications_path)
       user.reload
-      expect(user.settings['notification_emails']['follow']).to be true
-      expect(user.settings['interactions']['must_be_follower']).to be false
+      expect(user.settings['notification_emails.follow']).to be true
+      expect(user.settings['interactions.must_be_follower']).to be false
     end
   end
 end
diff --git a/spec/controllers/settings/preferences/other_controller_spec.rb b/spec/controllers/settings/preferences/other_controller_spec.rb
index 960378a01..249d1b5b5 100644
--- a/spec/controllers/settings/preferences/other_controller_spec.rb
+++ b/spec/controllers/settings/preferences/other_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Settings::Preferences::OtherController do
@@ -23,24 +25,26 @@ describe Settings::Preferences::OtherController do
       expect(response).to redirect_to(settings_preferences_other_path)
       user.reload
       expect(user.locale).to eq 'en'
-      expect(user.chosen_languages).to eq ['es', 'fr']
+      expect(user.chosen_languages).to eq %w(es fr)
     end
 
     it 'updates user settings' do
-      user.settings['boost_modal'] = false
-      user.settings['delete_modal'] = true
+      user.settings.update('web.reblog_modal': false, 'web.delete_modal': true)
+      user.save
 
       put :update, params: {
         user: {
-          setting_boost_modal: '1',
-          setting_delete_modal: '0',
-        }
+          settings_attributes: {
+            'web.reblog_modal': '1',
+            'web.delete_modal': '0',
+          },
+        },
       }
 
       expect(response).to redirect_to(settings_preferences_other_path)
       user.reload
-      expect(user.settings['boost_modal']).to be true
-      expect(user.settings['delete_modal']).to be false
+      expect(user.settings['web.reblog_modal']).to be true
+      expect(user.settings['web.delete_modal']).to be false
     end
   end
 end
diff --git a/spec/controllers/settings/profiles_controller_spec.rb b/spec/controllers/settings/profiles_controller_spec.rb
index ee3aec815..563e60271 100644
--- a/spec/controllers/settings/profiles_controller_spec.rb
+++ b/spec/controllers/settings/profiles_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Settings::ProfilesController, type: :controller do
@@ -10,8 +12,8 @@ RSpec.describe Settings::ProfilesController, type: :controller do
     sign_in user, scope: :user
   end
 
-  describe "GET #show" do
-    it "returns http success" do
+  describe 'GET #show' do
+    it 'returns http success' do
       get :show
       expect(response).to have_http_status(200)
     end
@@ -38,16 +40,8 @@ RSpec.describe Settings::ProfilesController, type: :controller do
 
       put :update, params: { account: { avatar: fixture_file_upload('avatar.gif', 'image/gif') } }
       expect(response).to redirect_to(settings_profile_path)
-      expect(account.reload.avatar.instance.avatar_file_name).not_to be_nil
+      expect(account.reload.avatar.instance.avatar_file_name).to_not be_nil
       expect(ActivityPub::UpdateDistributionWorker).to have_received(:perform_async).with(account.id)
     end
   end
-
-  describe 'PUT #update with oversized image' do
-    it 'gives the user an error message' do
-      allow(ActivityPub::UpdateDistributionWorker).to receive(:perform_async)
-      put :update, params: { account: { avatar: fixture_file_upload('4096x4097.png', 'image/png') } }
-      expect(response.body).to include('images are not supported')
-    end
-  end
 end
diff --git a/spec/controllers/settings/sessions_controller_spec.rb b/spec/controllers/settings/sessions_controller_spec.rb
index 52b204a6a..a4248e1bd 100644
--- a/spec/controllers/settings/sessions_controller_spec.rb
+++ b/spec/controllers/settings/sessions_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Settings::SessionsController do
@@ -5,6 +7,7 @@ describe Settings::SessionsController do
 
   let(:user) { Fabricate(:user) }
   let(:session_activation) { Fabricate(:session_activation, user: user) }
+
   before { sign_in user, scope: :user }
 
   describe 'DELETE #destroy' do
@@ -14,7 +17,7 @@ describe Settings::SessionsController do
       let(:id) { session_activation.id }
 
       it 'destroys session activation' do
-        is_expected.to redirect_to edit_user_registration_path
+        expect(subject).to redirect_to edit_user_registration_path
         expect(SessionActivation.find_by(id: id)).to be_nil
       end
     end
@@ -23,7 +26,7 @@ describe Settings::SessionsController do
       let(:id) { session_activation.id + 1000 }
 
       it 'destroys session activation' do
-        is_expected.to have_http_status :not_found
+        expect(subject).to have_http_status 404
       end
     end
   end
diff --git a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb
index 569c8322b..0b807b280 100644
--- a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb
+++ b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb
@@ -5,7 +5,6 @@ require 'rails_helper'
 describe Settings::TwoFactorAuthentication::ConfirmationsController do
   render_views
 
-
   shared_examples 'renders :new' do
     it 'renders the new view' do
       subject
diff --git a/spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb
index fe53b4dfc..a95521c94 100644
--- a/spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb
+++ b/spec/controllers/settings/two_factor_authentication/webauthn_credentials_controller_spec.rb
@@ -7,7 +7,7 @@ describe Settings::TwoFactorAuthentication::WebauthnCredentialsController do
   render_views
 
   let(:user) { Fabricate(:user) }
-  let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http' }://#{Rails.configuration.x.web_domain}" }
+  let(:domain) { "#{Rails.configuration.x.use_https ? 'https' : 'http'}://#{Rails.configuration.x.web_domain}" }
   let(:fake_client) { WebAuthn::FakeClient.new(domain) }
 
   def add_webauthn_credential(user)
@@ -137,10 +137,10 @@ describe Settings::TwoFactorAuthentication::WebauthnCredentialsController do
             expect { get :options }.to_not change { user.webauthn_id }
           end
 
-          it "includes existing credentials in list of excluded credentials" do
+          it 'includes existing credentials in list of excluded credentials' do
             get :options
 
-            excluded_credentials_ids = JSON.parse(response.body)['excludeCredentials'].map { |credential| credential['id'] }
+            excluded_credentials_ids = JSON.parse(response.body)['excludeCredentials'].pluck('id')
             expect(excluded_credentials_ids).to match_array(user.webauthn_credentials.pluck(:external_id))
           end
         end
@@ -248,7 +248,7 @@ describe Settings::TwoFactorAuthentication::WebauthnCredentialsController do
 
               post :create, params: { credential: new_webauthn_credential, nickname: 'USB Key' }
 
-              expect(response).to have_http_status(500)
+              expect(response).to have_http_status(422)
               expect(flash[:error]).to be_present
             end
           end
@@ -268,7 +268,7 @@ describe Settings::TwoFactorAuthentication::WebauthnCredentialsController do
 
               post :create, params: { credential: new_webauthn_credential, nickname: nickname }
 
-              expect(response).to have_http_status(500)
+              expect(response).to have_http_status(422)
               expect(flash[:error]).to be_present
             end
           end
diff --git a/spec/controllers/shares_controller_spec.rb b/spec/controllers/shares_controller_spec.rb
index d6de3016a..6d5bb4f8d 100644
--- a/spec/controllers/shares_controller_spec.rb
+++ b/spec/controllers/shares_controller_spec.rb
@@ -1,9 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe SharesController do
   render_views
 
   let(:user) { Fabricate(:user) }
+
   before { sign_in user }
 
   describe 'GTE #show' do
@@ -12,7 +15,7 @@ describe SharesController do
     before { get :show, params: { title: 'test title', text: 'test text', url: 'url1 url2' } }
 
     it 'returns http success' do
-      expect(response).to have_http_status :ok
+      expect(response).to have_http_status 200
       expect(body_classes).to eq 'modal-layout compose-standalone'
     end
   end
diff --git a/spec/controllers/statuses_cleanup_controller_spec.rb b/spec/controllers/statuses_cleanup_controller_spec.rb
index 924709260..969778bbf 100644
--- a/spec/controllers/statuses_cleanup_controller_spec.rb
+++ b/spec/controllers/statuses_cleanup_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe StatusesCleanupController, type: :controller do
@@ -8,8 +10,8 @@ RSpec.describe StatusesCleanupController, type: :controller do
     sign_in @user, scope: :user
   end
 
-  describe "GET #show" do
-    it "returns http success" do
+  describe 'GET #show' do
+    it 'returns http success' do
       get :show
       expect(response).to have_http_status(200)
     end
@@ -19,9 +21,9 @@ RSpec.describe StatusesCleanupController, type: :controller do
     it 'updates the account status cleanup policy' do
       put :update, params: { account_statuses_cleanup_policy: { enabled: true, min_status_age: 2.weeks.seconds, keep_direct: false, keep_polls: true } }
       expect(response).to redirect_to(statuses_cleanup_path)
-      expect(@user.account.statuses_cleanup_policy.enabled).to eq true
-      expect(@user.account.statuses_cleanup_policy.keep_direct).to eq false
-      expect(@user.account.statuses_cleanup_policy.keep_polls).to eq true
+      expect(@user.account.statuses_cleanup_policy.enabled).to be true
+      expect(@user.account.statuses_cleanup_policy.keep_direct).to be false
+      expect(@user.account.statuses_cleanup_policy.keep_polls).to be true
     end
   end
 end
diff --git a/spec/controllers/statuses_controller_spec.rb b/spec/controllers/statuses_controller_spec.rb
index 6ed5d4bbb..c8b503d68 100644
--- a/spec/controllers/statuses_controller_spec.rb
+++ b/spec/controllers/statuses_controller_spec.rb
@@ -8,7 +8,7 @@ describe StatusesController do
   shared_examples 'cacheable response' do
     it 'does not set cookies' do
       expect(response.cookies).to be_empty
-      expect(response.headers['Set-Cookies']).to be nil
+      expect(response.headers['Set-Cookies']).to be_nil
     end
 
     it 'does not set sessions' do
diff --git a/spec/controllers/tags_controller_spec.rb b/spec/controllers/tags_controller_spec.rb
index 547bcfb39..8a3fa0bf8 100644
--- a/spec/controllers/tags_controller_spec.rb
+++ b/spec/controllers/tags_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe TagsController, type: :controller do
diff --git a/spec/controllers/well_known/host_meta_controller_spec.rb b/spec/controllers/well_known/host_meta_controller_spec.rb
index c02aa0d59..d53704370 100644
--- a/spec/controllers/well_known/host_meta_controller_spec.rb
+++ b/spec/controllers/well_known/host_meta_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe WellKnown::HostMetaController, type: :controller do
@@ -9,12 +11,12 @@ describe WellKnown::HostMetaController, type: :controller do
 
       expect(response).to have_http_status(200)
       expect(response.media_type).to eq 'application/xrd+xml'
-      expect(response.body).to eq <<XML
-<?xml version="1.0" encoding="UTF-8"?>
-<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
-  <Link rel="lrdd" template="https://cb6e6126.ngrok.io/.well-known/webfinger?resource={uri}"/>
-</XRD>
-XML
+      expect(response.body).to eq <<~XML
+        <?xml version="1.0" encoding="UTF-8"?>
+        <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
+          <Link rel="lrdd" template="https://cb6e6126.ngrok.io/.well-known/webfinger?resource={uri}"/>
+        </XRD>
+      XML
     end
   end
 end
diff --git a/spec/controllers/well_known/nodeinfo_controller_spec.rb b/spec/controllers/well_known/nodeinfo_controller_spec.rb
index 36e85f20d..f5cde150d 100644
--- a/spec/controllers/well_known/nodeinfo_controller_spec.rb
+++ b/spec/controllers/well_known/nodeinfo_controller_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe WellKnown::NodeInfoController, type: :controller do
@@ -26,9 +28,10 @@ describe WellKnown::NodeInfoController, type: :controller do
       expect(response.media_type).to eq 'application/json'
 
       json = body_as_json
+      foo = { 'foo' => 0 }
 
-      expect({ "foo" => 0 }).not_to match_json_schema("nodeinfo_2.0")
-      expect(json).to match_json_schema("nodeinfo_2.0")
+      expect(foo).to_not match_json_schema('nodeinfo_2.0')
+      expect(json).to match_json_schema('nodeinfo_2.0')
       expect(json[:version]).to eq '2.0'
       expect(json[:usage]).to be_a Hash
       expect(json[:software]).to be_a Hash
diff --git a/spec/controllers/well_known/webfinger_controller_spec.rb b/spec/controllers/well_known/webfinger_controller_spec.rb
index 8574d369d..00103df70 100644
--- a/spec/controllers/well_known/webfinger_controller_spec.rb
+++ b/spec/controllers/well_known/webfinger_controller_spec.rb
@@ -1,9 +1,15 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe WellKnown::WebfingerController, type: :controller do
   render_views
 
   describe 'GET #show' do
+    subject do
+      get :show, params: { resource: resource }, format: :json
+    end
+
     let(:alternate_domains) { [] }
     let(:alice) { Fabricate(:account, username: 'alice') }
     let(:resource) { nil }
@@ -15,10 +21,6 @@ describe WellKnown::WebfingerController, type: :controller do
       Rails.configuration.x.alternate_domains = tmp
     end
 
-    subject do
-      get :show, params: { resource: resource }, format: :json
-    end
-
     shared_examples 'a successful response' do
       it 'returns http success' do
         expect(response).to have_http_status(200)
diff --git a/spec/fabricators/access_grant_fabricator.rb b/spec/fabricators/access_grant_fabricator.rb
index ae1945f2b..adc2b8369 100644
--- a/spec/fabricators/access_grant_fabricator.rb
+++ b/spec/fabricators/access_grant_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator :access_grant, from: 'Doorkeeper::AccessGrant' do
   application
   resource_owner_id { Fabricate(:user).id }
diff --git a/spec/fabricators/access_token_fabricator.rb b/spec/fabricators/access_token_fabricator.rb
index 1856a8eb3..508c32808 100644
--- a/spec/fabricators/access_token_fabricator.rb
+++ b/spec/fabricators/access_token_fabricator.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
 Fabricator :access_token, from: 'Doorkeeper::AccessToken' do
 end
diff --git a/spec/fabricators/accessible_access_token_fabricator.rb b/spec/fabricators/accessible_access_token_fabricator.rb
index 4b7e99b20..fb3d0889b 100644
--- a/spec/fabricators/accessible_access_token_fabricator.rb
+++ b/spec/fabricators/accessible_access_token_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator :accessible_access_token, from: :access_token do
   expires_in { nil }
   revoked_at { nil }
diff --git a/spec/fabricators/account_alias_fabricator.rb b/spec/fabricators/account_alias_fabricator.rb
deleted file mode 100644
index 94dde9bb8..000000000
--- a/spec/fabricators/account_alias_fabricator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-Fabricator(:account_alias) do
-  account
-  acct 'test@example.com'
-  uri 'https://example.com/users/test'
-end
diff --git a/spec/fabricators/account_deletion_request_fabricator.rb b/spec/fabricators/account_deletion_request_fabricator.rb
deleted file mode 100644
index 08a82ba3c..000000000
--- a/spec/fabricators/account_deletion_request_fabricator.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-Fabricator(:account_deletion_request) do
-  account
-end
diff --git a/spec/fabricators/account_domain_block_fabricator.rb b/spec/fabricators/account_domain_block_fabricator.rb
index 2ad4b67a9..ff85e17f3 100644
--- a/spec/fabricators/account_domain_block_fabricator.rb
+++ b/spec/fabricators/account_domain_block_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:account_domain_block) do
   account
   domain 'example.com'
diff --git a/spec/fabricators/account_fabricator.rb b/spec/fabricators/account_fabricator.rb
index 205706532..6ffbba584 100644
--- a/spec/fabricators/account_fabricator.rb
+++ b/spec/fabricators/account_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 keypair     = OpenSSL::PKey::RSA.new(2048)
 public_key  = keypair.public_key.to_pem
 private_key = keypair.to_pem
diff --git a/spec/fabricators/account_migration_fabricator.rb b/spec/fabricators/account_migration_fabricator.rb
index 2a8e747a8..ae6143a65 100644
--- a/spec/fabricators/account_migration_fabricator.rb
+++ b/spec/fabricators/account_migration_fabricator.rb
@@ -1,6 +1,9 @@
+# frozen_string_literal: true
+
 Fabricator(:account_migration) do
   account
   target_account { |attrs| Fabricate(:account, also_known_as: [ActivityPub::TagManager.instance.uri_for(attrs[:account])]) }
   acct           { |attrs| attrs[:target_account].acct }
   followers_count 1234
+  created_at { 60.days.ago }
 end
diff --git a/spec/fabricators/account_moderation_note_fabricator.rb b/spec/fabricators/account_moderation_note_fabricator.rb
index 9277af165..341a24dea 100644
--- a/spec/fabricators/account_moderation_note_fabricator.rb
+++ b/spec/fabricators/account_moderation_note_fabricator.rb
@@ -1,4 +1,7 @@
+# frozen_string_literal: true
+
 Fabricator(:account_moderation_note) do
-  content "MyText"
-  account nil
+  content 'MyText'
+  account
+  target_account { Fabricate(:account) }
 end
diff --git a/spec/fabricators/account_note_fabricator.rb b/spec/fabricators/account_note_fabricator.rb
index 1b061745a..bb4ed8b24 100644
--- a/spec/fabricators/account_note_fabricator.rb
+++ b/spec/fabricators/account_note_fabricator.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
 Fabricator(:account_note) do
   account
   target_account { Fabricate(:account) }
-  comment        "User note text"
+  comment        'User note text'
 end
diff --git a/spec/fabricators/account_pin_fabricator.rb b/spec/fabricators/account_pin_fabricator.rb
index c0f8b8afb..32a5f3bdb 100644
--- a/spec/fabricators/account_pin_fabricator.rb
+++ b/spec/fabricators/account_pin_fabricator.rb
@@ -1,4 +1,7 @@
+# frozen_string_literal: true
+
 Fabricator(:account_pin) do
-  account        nil
-  target_account nil
+  account
+  target_account(fabricator: :account)
+  before_create { |account_pin, _| account_pin.account.follow!(account_pin.target_account) }
 end
diff --git a/spec/fabricators/account_stat_fabricator.rb b/spec/fabricators/account_stat_fabricator.rb
index 2b06b4790..e6085c5f2 100644
--- a/spec/fabricators/account_stat_fabricator.rb
+++ b/spec/fabricators/account_stat_fabricator.rb
@@ -1,6 +1,8 @@
+# frozen_string_literal: true
+
 Fabricator(:account_stat) do
-  account         nil
-  statuses_count  ""
-  following_count ""
-  followers_count ""
+  account
+  statuses_count  '123'
+  following_count '456'
+  followers_count '789'
 end
diff --git a/spec/fabricators/account_statuses_cleanup_policy_fabricator.rb b/spec/fabricators/account_statuses_cleanup_policy_fabricator.rb
index 29cf1d133..0e756ddba 100644
--- a/spec/fabricators/account_statuses_cleanup_policy_fabricator.rb
+++ b/spec/fabricators/account_statuses_cleanup_policy_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:account_statuses_cleanup_policy) do
   account
 end
diff --git a/spec/fabricators/account_tag_stat_fabricator.rb b/spec/fabricators/account_tag_stat_fabricator.rb
deleted file mode 100644
index 9edb550be..000000000
--- a/spec/fabricators/account_tag_stat_fabricator.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-Fabricator(:account_tag_stat) do
-  accounts_count ""
-end
diff --git a/spec/fabricators/account_warning_fabricator.rb b/spec/fabricators/account_warning_fabricator.rb
index 72fe835d9..e5059e37f 100644
--- a/spec/fabricators/account_warning_fabricator.rb
+++ b/spec/fabricators/account_warning_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:account_warning) do
   account
   target_account(fabricator: :account)
diff --git a/spec/fabricators/account_warning_preset_fabricator.rb b/spec/fabricators/account_warning_preset_fabricator.rb
index 6c0b87e7c..c50e08bf4 100644
--- a/spec/fabricators/account_warning_preset_fabricator.rb
+++ b/spec/fabricators/account_warning_preset_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:account_warning_preset) do
-  text "MyText"
+  text { Faker::Lorem.paragraph }
 end
diff --git a/spec/fabricators/admin_action_log_fabricator.rb b/spec/fabricators/admin_action_log_fabricator.rb
index 2f44e953d..a259644bd 100644
--- a/spec/fabricators/admin_action_log_fabricator.rb
+++ b/spec/fabricators/admin_action_log_fabricator.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
 Fabricator('Admin::ActionLog') do
-  account nil
-  action  "MyString"
+  account
+  action  'MyString'
   target  nil
 end
diff --git a/spec/fabricators/announcement_fabricator.rb b/spec/fabricators/announcement_fabricator.rb
index 5a3871d90..5d7736587 100644
--- a/spec/fabricators/announcement_fabricator.rb
+++ b/spec/fabricators/announcement_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:announcement) do
   text      { Faker::Lorem.paragraph(sentence_count: 2) }
   published true
diff --git a/spec/fabricators/announcement_mute_fabricator.rb b/spec/fabricators/announcement_mute_fabricator.rb
deleted file mode 100644
index c4eafe8f4..000000000
--- a/spec/fabricators/announcement_mute_fabricator.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-Fabricator(:announcement_mute) do
-  account
-  announcement
-end
diff --git a/spec/fabricators/announcement_reaction_fabricator.rb b/spec/fabricators/announcement_reaction_fabricator.rb
deleted file mode 100644
index f923c59c6..000000000
--- a/spec/fabricators/announcement_reaction_fabricator.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-Fabricator(:announcement_reaction) do
-  account
-  announcement
-  name '🌿'
-end
diff --git a/spec/fabricators/appeal_fabricator.rb b/spec/fabricators/appeal_fabricator.rb
index 339363822..039086c4e 100644
--- a/spec/fabricators/appeal_fabricator.rb
+++ b/spec/fabricators/appeal_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:appeal) do
   strike(fabricator: :account_warning)
   account { |attrs| attrs[:strike].target_account }
diff --git a/spec/fabricators/application_fabricator.rb b/spec/fabricators/application_fabricator.rb
index 42b7009dc..272821304 100644
--- a/spec/fabricators/application_fabricator.rb
+++ b/spec/fabricators/application_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:application, from: Doorkeeper::Application) do
   name         'Example'
   website      'http://example.com'
diff --git a/spec/fabricators/backup_fabricator.rb b/spec/fabricators/backup_fabricator.rb
index 99a5bdcda..c73ae54be 100644
--- a/spec/fabricators/backup_fabricator.rb
+++ b/spec/fabricators/backup_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:backup) do
   user
 end
diff --git a/spec/fabricators/block_fabricator.rb b/spec/fabricators/block_fabricator.rb
index 379931ba6..c2e9e9628 100644
--- a/spec/fabricators/block_fabricator.rb
+++ b/spec/fabricators/block_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:block) do
   account
   target_account { Fabricate(:account) }
diff --git a/spec/fabricators/bookmark_fabricator.rb b/spec/fabricators/bookmark_fabricator.rb
index 12cbc5bfa..e21046fc2 100644
--- a/spec/fabricators/bookmark_fabricator.rb
+++ b/spec/fabricators/bookmark_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:bookmark) do
   account
   status
diff --git a/spec/fabricators/canonical_email_block_fabricator.rb b/spec/fabricators/canonical_email_block_fabricator.rb
index a0b6e0d22..21d7c2402 100644
--- a/spec/fabricators/canonical_email_block_fabricator.rb
+++ b/spec/fabricators/canonical_email_block_fabricator.rb
@@ -1,4 +1,6 @@
+# frozen_string_literal: true
+
 Fabricator(:canonical_email_block) do
-  email "test@example.com"
+  email 'test@example.com'
   reference_account { Fabricate(:account) }
 end
diff --git a/spec/fabricators/conversation_account_fabricator.rb b/spec/fabricators/conversation_account_fabricator.rb
deleted file mode 100644
index f57ffd535..000000000
--- a/spec/fabricators/conversation_account_fabricator.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-Fabricator(:conversation_account) do
-  account                 nil
-  conversation            nil
-  participant_account_ids ""
-  last_status             nil
-end
diff --git a/spec/fabricators/conversation_fabricator.rb b/spec/fabricators/conversation_fabricator.rb
index b4fadb46b..07c6780bf 100644
--- a/spec/fabricators/conversation_fabricator.rb
+++ b/spec/fabricators/conversation_fabricator.rb
@@ -1,2 +1,4 @@
+# frozen_string_literal: true
+
 Fabricator(:conversation) do
 end
diff --git a/spec/fabricators/conversation_mute_fabricator.rb b/spec/fabricators/conversation_mute_fabricator.rb
deleted file mode 100644
index 84f131c26..000000000
--- a/spec/fabricators/conversation_mute_fabricator.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-Fabricator(:conversation_mute) do
-end
diff --git a/spec/fabricators/custom_emoji_category_fabricator.rb b/spec/fabricators/custom_emoji_category_fabricator.rb
deleted file mode 100644
index f593b95ed..000000000
--- a/spec/fabricators/custom_emoji_category_fabricator.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-Fabricator(:custom_emoji_category) do
-  name "MyString"
-end
diff --git a/spec/fabricators/custom_emoji_fabricator.rb b/spec/fabricators/custom_emoji_fabricator.rb
index 18a7d23dc..fa570eec6 100644
--- a/spec/fabricators/custom_emoji_fabricator.rb
+++ b/spec/fabricators/custom_emoji_fabricator.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
 Fabricator(:custom_emoji) do
   shortcode 'coolcat'
   domain    nil
-  image     { File.open(Rails.root.join('spec', 'fixtures', 'files', 'emojo.png')) }
+  image     { Rails.root.join('spec', 'fixtures', 'files', 'emojo.png').open }
 end
diff --git a/spec/fabricators/custom_filter_fabricator.rb b/spec/fabricators/custom_filter_fabricator.rb
index 64297a7e3..5fee4f01a 100644
--- a/spec/fabricators/custom_filter_fabricator.rb
+++ b/spec/fabricators/custom_filter_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:custom_filter) do
   account
   expires_at nil
diff --git a/spec/fabricators/custom_filter_keyword_fabricator.rb b/spec/fabricators/custom_filter_keyword_fabricator.rb
index 0f101dcd1..f1fb440dc 100644
--- a/spec/fabricators/custom_filter_keyword_fabricator.rb
+++ b/spec/fabricators/custom_filter_keyword_fabricator.rb
@@ -1,4 +1,6 @@
+# frozen_string_literal: true
+
 Fabricator(:custom_filter_keyword) do
   custom_filter
-  keyword       'discourse'
+  keyword 'discourse'
 end
diff --git a/spec/fabricators/custom_filter_status_fabricator.rb b/spec/fabricators/custom_filter_status_fabricator.rb
index d082b81c5..3ef1d0ec8 100644
--- a/spec/fabricators/custom_filter_status_fabricator.rb
+++ b/spec/fabricators/custom_filter_status_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:custom_filter_status) do
   custom_filter
   status
diff --git a/spec/fabricators/device_fabricator.rb b/spec/fabricators/device_fabricator.rb
index b15d8248f..26c71b4fd 100644
--- a/spec/fabricators/device_fabricator.rb
+++ b/spec/fabricators/device_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:device) do
   access_token
   account
diff --git a/spec/fabricators/domain_allow_fabricator.rb b/spec/fabricators/domain_allow_fabricator.rb
index 6226b1e20..b32af129b 100644
--- a/spec/fabricators/domain_allow_fabricator.rb
+++ b/spec/fabricators/domain_allow_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:domain_allow) do
-  domain "MyString"
+  domain 'MyString'
 end
diff --git a/spec/fabricators/domain_block_fabricator.rb b/spec/fabricators/domain_block_fabricator.rb
index cc1f928e5..c703a18e9 100644
--- a/spec/fabricators/domain_block_fabricator.rb
+++ b/spec/fabricators/domain_block_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:domain_block) do
   domain { sequence(:domain) { |i| "#{i}#{Faker::Internet.domain_name}" } }
 end
diff --git a/spec/fabricators/email_domain_block_fabricator.rb b/spec/fabricators/email_domain_block_fabricator.rb
index d18af6433..a74cca73d 100644
--- a/spec/fabricators/email_domain_block_fabricator.rb
+++ b/spec/fabricators/email_domain_block_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:email_domain_block) do
   domain { sequence(:domain) { |i| "#{i}#{Faker::Internet.domain_name}" } }
 end
diff --git a/spec/fabricators/encrypted_message_fabricator.rb b/spec/fabricators/encrypted_message_fabricator.rb
index e65f66302..43b310514 100644
--- a/spec/fabricators/encrypted_message_fabricator.rb
+++ b/spec/fabricators/encrypted_message_fabricator.rb
@@ -1,8 +1,7 @@
+# frozen_string_literal: true
+
 Fabricator(:encrypted_message) do
   device
-  from_account
-  from_device_id   { Faker::Number.number(digits: 5) }
-  type             0
-  body             ""
-  message_franking ""
+  from_account { Fabricate(:account) }
+  from_device_id { Faker::Number.number(digits: 5) }
 end
diff --git a/spec/fabricators/favourite_fabricator.rb b/spec/fabricators/favourite_fabricator.rb
index 464ac8d71..005947e6f 100644
--- a/spec/fabricators/favourite_fabricator.rb
+++ b/spec/fabricators/favourite_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:favourite) do
   account
   status
diff --git a/spec/fabricators/featured_tag_fabricator.rb b/spec/fabricators/featured_tag_fabricator.rb
deleted file mode 100644
index 25cbdaac0..000000000
--- a/spec/fabricators/featured_tag_fabricator.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-Fabricator(:featured_tag) do
-  account
-  tag
-  statuses_count 1_337
-  last_status_at Time.now.utc
-end
diff --git a/spec/fabricators/follow_fabricator.rb b/spec/fabricators/follow_fabricator.rb
index 9b25dc547..41b5305d5 100644
--- a/spec/fabricators/follow_fabricator.rb
+++ b/spec/fabricators/follow_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:follow) do
   account
   target_account { Fabricate(:account) }
diff --git a/spec/fabricators/follow_recommendation_suppression_fabricator.rb b/spec/fabricators/follow_recommendation_suppression_fabricator.rb
deleted file mode 100644
index 4a6a07a66..000000000
--- a/spec/fabricators/follow_recommendation_suppression_fabricator.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-Fabricator(:follow_recommendation_suppression) do
-  account
-end
diff --git a/spec/fabricators/follow_request_fabricator.rb b/spec/fabricators/follow_request_fabricator.rb
index c00ddf84d..86b82611f 100644
--- a/spec/fabricators/follow_request_fabricator.rb
+++ b/spec/fabricators/follow_request_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:follow_request) do
   account
   target_account { Fabricate(:account, locked: true) }
diff --git a/spec/fabricators/identity_fabricator.rb b/spec/fabricators/identity_fabricator.rb
index bc832df9f..58072c0d6 100644
--- a/spec/fabricators/identity_fabricator.rb
+++ b/spec/fabricators/identity_fabricator.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
 Fabricator(:identity) do
-  user     nil
-  provider "MyString"
-  uid      "MyString"
+  user
+  provider 'MyString'
+  uid      'MyString'
 end
diff --git a/spec/fabricators/import_fabricator.rb b/spec/fabricators/import_fabricator.rb
deleted file mode 100644
index e2eb1e0df..000000000
--- a/spec/fabricators/import_fabricator.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-Fabricator(:import) do
-end
diff --git a/spec/fabricators/invite_fabricator.rb b/spec/fabricators/invite_fabricator.rb
index 62b9b3904..4f47d6ce2 100644
--- a/spec/fabricators/invite_fabricator.rb
+++ b/spec/fabricators/invite_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:invite) do
   user
   expires_at nil
diff --git a/spec/fabricators/ip_block_fabricator.rb b/spec/fabricators/ip_block_fabricator.rb
deleted file mode 100644
index 31dc336e6..000000000
--- a/spec/fabricators/ip_block_fabricator.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-Fabricator(:ip_block) do
-  ip         ""
-  severity   ""
-  expires_at "2020-10-08 22:20:37"
-  comment    "MyText"
-end
\ No newline at end of file
diff --git a/spec/fabricators/list_account_fabricator.rb b/spec/fabricators/list_account_fabricator.rb
index 30e4004aa..00dde83cd 100644
--- a/spec/fabricators/list_account_fabricator.rb
+++ b/spec/fabricators/list_account_fabricator.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
 Fabricator(:list_account) do
-  list    nil
-  account nil
-  follow  nil
+  list
+  account
+  before_create { |list_account, _| list_account.list.account.follow!(account) }
 end
diff --git a/spec/fabricators/list_fabricator.rb b/spec/fabricators/list_fabricator.rb
index c3db690fa..47af752b8 100644
--- a/spec/fabricators/list_fabricator.rb
+++ b/spec/fabricators/list_fabricator.rb
@@ -1,4 +1,6 @@
+# frozen_string_literal: true
+
 Fabricator(:list) do
   account
-  title "MyString"
+  title 'MyString'
 end
diff --git a/spec/fabricators/login_activity_fabricator.rb b/spec/fabricators/login_activity_fabricator.rb
index 686fd6483..2b30658ff 100644
--- a/spec/fabricators/login_activity_fabricator.rb
+++ b/spec/fabricators/login_activity_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:login_activity) do
   user
   authentication_method 'password'
diff --git a/spec/fabricators/marker_fabricator.rb b/spec/fabricators/marker_fabricator.rb
index 0c94150e0..561c2553a 100644
--- a/spec/fabricators/marker_fabricator.rb
+++ b/spec/fabricators/marker_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:marker) do
   user
   timeline     'home'
diff --git a/spec/fabricators/media_attachment_fabricator.rb b/spec/fabricators/media_attachment_fabricator.rb
index 651927c2d..4a081dccb 100644
--- a/spec/fabricators/media_attachment_fabricator.rb
+++ b/spec/fabricators/media_attachment_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:media_attachment) do
   account
 
diff --git a/spec/fabricators/mention_fabricator.rb b/spec/fabricators/mention_fabricator.rb
index cb5fe4299..5a8392827 100644
--- a/spec/fabricators/mention_fabricator.rb
+++ b/spec/fabricators/mention_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:mention) do
   account
   status
diff --git a/spec/fabricators/mute_fabricator.rb b/spec/fabricators/mute_fabricator.rb
index 30d20e87e..242ae2b08 100644
--- a/spec/fabricators/mute_fabricator.rb
+++ b/spec/fabricators/mute_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:mute) do
   account
   target_account { Fabricate(:account) }
diff --git a/spec/fabricators/notification_fabricator.rb b/spec/fabricators/notification_fabricator.rb
index 638844e0f..959fda913 100644
--- a/spec/fabricators/notification_fabricator.rb
+++ b/spec/fabricators/notification_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:notification) do
   activity fabricator: [:mention, :status, :follow, :follow_request, :favourite].sample
   account
diff --git a/spec/fabricators/one_time_key_fabricator.rb b/spec/fabricators/one_time_key_fabricator.rb
index 8794baeb5..cfb365cab 100644
--- a/spec/fabricators/one_time_key_fabricator.rb
+++ b/spec/fabricators/one_time_key_fabricator.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
 Fabricator(:one_time_key) do
   device
   key_id { Faker::Alphanumeric.alphanumeric(number: 10) }
-  key    { Base64.strict_encode64(Ed25519::SigningKey.generate.verify_key.to_bytes) }
+  key { Base64.strict_encode64(Ed25519::SigningKey.generate.verify_key.to_bytes) }
 
   signature do |attrs|
     signing_key = Ed25519::SigningKey.generate
diff --git a/spec/fabricators/poll_fabricator.rb b/spec/fabricators/poll_fabricator.rb
index 746610f7c..19c3b1d16 100644
--- a/spec/fabricators/poll_fabricator.rb
+++ b/spec/fabricators/poll_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:poll) do
   account
   status
diff --git a/spec/fabricators/poll_vote_fabricator.rb b/spec/fabricators/poll_vote_fabricator.rb
index 51f9b006e..9099ae96f 100644
--- a/spec/fabricators/poll_vote_fabricator.rb
+++ b/spec/fabricators/poll_vote_fabricator.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
 Fabricator(:poll_vote) do
   account
   poll
-  choice  0
+  choice 0
 end
diff --git a/spec/fabricators/preview_card_fabricator.rb b/spec/fabricators/preview_card_fabricator.rb
index 99b5edc43..b8f2c5097 100644
--- a/spec/fabricators/preview_card_fabricator.rb
+++ b/spec/fabricators/preview_card_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:preview_card) do
   url { Faker::Internet.url }
   title { Faker::Lorem.sentence }
diff --git a/spec/fabricators/preview_card_provider_fabricator.rb b/spec/fabricators/preview_card_provider_fabricator.rb
new file mode 100644
index 000000000..78db71000
--- /dev/null
+++ b/spec/fabricators/preview_card_provider_fabricator.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+Fabricator(:preview_card_provider) do
+  domain { Faker::Internet.domain_name }
+end
diff --git a/spec/fabricators/relay_fabricator.rb b/spec/fabricators/relay_fabricator.rb
index 488913f77..ad8ba86fc 100644
--- a/spec/fabricators/relay_fabricator.rb
+++ b/spec/fabricators/relay_fabricator.rb
@@ -1,4 +1,6 @@
+# frozen_string_literal: true
+
 Fabricator(:relay) do
-  inbox_url "https://example.com/inbox"
+  inbox_url 'https://example.com/inbox'
   state :idle
 end
diff --git a/spec/fabricators/report_fabricator.rb b/spec/fabricators/report_fabricator.rb
index 2c7101e09..7124773ad 100644
--- a/spec/fabricators/report_fabricator.rb
+++ b/spec/fabricators/report_fabricator.rb
@@ -1,6 +1,8 @@
+# frozen_string_literal: true
+
 Fabricator(:report) do
   account
   target_account  { Fabricate(:account) }
-  comment         "You nasty"
+  comment         'You nasty'
   action_taken_at nil
 end
diff --git a/spec/fabricators/report_note_fabricator.rb b/spec/fabricators/report_note_fabricator.rb
index e139efffb..f257fe2b7 100644
--- a/spec/fabricators/report_note_fabricator.rb
+++ b/spec/fabricators/report_note_fabricator.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
 Fabricator(:report_note) do
   report
   account { Fabricate(:account) }
-  content "Test Content"
+  content 'Test Content'
 end
diff --git a/spec/fabricators/rule_fabricator.rb b/spec/fabricators/rule_fabricator.rb
index bc29bc48e..a29fd905a 100644
--- a/spec/fabricators/rule_fabricator.rb
+++ b/spec/fabricators/rule_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:rule) do
   priority   0
   deleted_at nil
diff --git a/spec/fabricators/scheduled_status_fabricator.rb b/spec/fabricators/scheduled_status_fabricator.rb
index 52384d137..e517f258a 100644
--- a/spec/fabricators/scheduled_status_fabricator.rb
+++ b/spec/fabricators/scheduled_status_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:scheduled_status) do
   account
   scheduled_at { 20.hours.from_now }
diff --git a/spec/fabricators/session_activation_fabricator.rb b/spec/fabricators/session_activation_fabricator.rb
index 526faaec2..b28d5e41d 100644
--- a/spec/fabricators/session_activation_fabricator.rb
+++ b/spec/fabricators/session_activation_fabricator.rb
@@ -1,4 +1,6 @@
+# frozen_string_literal: true
+
 Fabricator(:session_activation) do
   user
-  session_id "MyString"
+  session_id 'MyString'
 end
diff --git a/spec/fabricators/setting_fabricator.rb b/spec/fabricators/setting_fabricator.rb
index 336d7c355..ce9a48e90 100644
--- a/spec/fabricators/setting_fabricator.rb
+++ b/spec/fabricators/setting_fabricator.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
 
 Fabricator(:setting) do
+  var 'var'
 end
diff --git a/spec/fabricators/site_upload_fabricator.rb b/spec/fabricators/site_upload_fabricator.rb
index 2efc57e28..87553ccb8 100644
--- a/spec/fabricators/site_upload_fabricator.rb
+++ b/spec/fabricators/site_upload_fabricator.rb
@@ -1,3 +1,6 @@
+# frozen_string_literal: true
+
 Fabricator(:site_upload) do
-  file { File.open(File.join(Rails.root, 'spec', 'fabricators', 'assets', 'utah_teapot.png')) }
+  file { Rails.root.join('spec', 'fabricators', 'assets', 'utah_teapot.png').open }
+  var 'thumbnail'
 end
diff --git a/spec/fabricators/status_edit_fabricator.rb b/spec/fabricators/status_edit_fabricator.rb
deleted file mode 100644
index 21b793747..000000000
--- a/spec/fabricators/status_edit_fabricator.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-Fabricator(:status_edit) do
-  status                    nil
-  account                   nil
-  text                      "MyText"
-  spoiler_text              "MyText"
-  media_attachments_changed false
-end
\ No newline at end of file
diff --git a/spec/fabricators/status_fabricator.rb b/spec/fabricators/status_fabricator.rb
index 04bbbcf4b..17ac9ccd8 100644
--- a/spec/fabricators/status_fabricator.rb
+++ b/spec/fabricators/status_fabricator.rb
@@ -1,6 +1,8 @@
+# frozen_string_literal: true
+
 Fabricator(:status) do
   account
-  text "Lorem ipsum dolor sit amet"
+  text 'Lorem ipsum dolor sit amet'
 
   after_build do |status|
     status.uri = Faker::Internet.device_token if !status.account.local? && status.uri.nil?
diff --git a/spec/fabricators/status_pin_fabricator.rb b/spec/fabricators/status_pin_fabricator.rb
index f1f1c05f3..9ad0ac9de 100644
--- a/spec/fabricators/status_pin_fabricator.rb
+++ b/spec/fabricators/status_pin_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:status_pin) do
   account
   status { |attrs| Fabricate(:status, account: attrs[:account], visibility: :public) }
diff --git a/spec/fabricators/status_stat_fabricator.rb b/spec/fabricators/status_stat_fabricator.rb
deleted file mode 100644
index 9c67fd404..000000000
--- a/spec/fabricators/status_stat_fabricator.rb
+++ /dev/null
@@ -1,6 +0,0 @@
-Fabricator(:status_stat) do
-  status_id        nil
-  replies_count    ""
-  reblogs_count    ""
-  favourites_count ""
-end
diff --git a/spec/fabricators/system_key_fabricator.rb b/spec/fabricators/system_key_fabricator.rb
index f808495e0..ef6cec9c4 100644
--- a/spec/fabricators/system_key_fabricator.rb
+++ b/spec/fabricators/system_key_fabricator.rb
@@ -1,3 +1,4 @@
-Fabricator(:system_key) do
+# frozen_string_literal: true
 
+Fabricator(:system_key) do
 end
diff --git a/spec/fabricators/tag_fabricator.rb b/spec/fabricators/tag_fabricator.rb
index 33d57c928..a7b52e967 100644
--- a/spec/fabricators/tag_fabricator.rb
+++ b/spec/fabricators/tag_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:tag) do
   name { sequence(:hashtag) { |i| "#{Faker::Lorem.word}#{i}" } }
 end
diff --git a/spec/fabricators/tag_follow_fabricator.rb b/spec/fabricators/tag_follow_fabricator.rb
index a2cccb07a..cbe5b0989 100644
--- a/spec/fabricators/tag_follow_fabricator.rb
+++ b/spec/fabricators/tag_follow_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:tag_follow) do
   tag
   account
diff --git a/spec/fabricators/unavailable_domain_fabricator.rb b/spec/fabricators/unavailable_domain_fabricator.rb
index f661b87c4..cb9707020 100644
--- a/spec/fabricators/unavailable_domain_fabricator.rb
+++ b/spec/fabricators/unavailable_domain_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:unavailable_domain) do
-  domain { Faker::Internet.domain }
+  domain { Faker::Internet.domain_name }
 end
diff --git a/spec/fabricators/user_fabricator.rb b/spec/fabricators/user_fabricator.rb
index 10ad2c53a..9031d5cd0 100644
--- a/spec/fabricators/user_fabricator.rb
+++ b/spec/fabricators/user_fabricator.rb
@@ -1,7 +1,10 @@
+# frozen_string_literal: true
+
 Fabricator(:user) do
   account      { Fabricate.build(:account, user: nil) }
   email        { sequence(:email) { |i| "#{i}#{Faker::Internet.email}" } }
-  password     "123456789"
+  password     '123456789'
   confirmed_at { Time.zone.now }
-  agreement    true
+  current_sign_in_at { Time.zone.now }
+  agreement true
 end
diff --git a/spec/fabricators/user_invite_request_fabricator.rb b/spec/fabricators/user_invite_request_fabricator.rb
deleted file mode 100644
index 5cc6ae56f..000000000
--- a/spec/fabricators/user_invite_request_fabricator.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-Fabricator(:user_invite_request) do
-  user
-  text { Faker::Lorem.sentence }
-end
diff --git a/spec/fabricators/user_role_fabricator.rb b/spec/fabricators/user_role_fabricator.rb
index 28f76c8c4..d44322760 100644
--- a/spec/fabricators/user_role_fabricator.rb
+++ b/spec/fabricators/user_role_fabricator.rb
@@ -1,5 +1,7 @@
+# frozen_string_literal: true
+
 Fabricator(:user_role) do
-  name        "MyString"
-  color       "MyString"
-  permissions ""
-end
\ No newline at end of file
+  name        'MyString'
+  color       ''
+  permissions 0
+end
diff --git a/spec/fabricators/web_push_subscription_fabricator.rb b/spec/fabricators/web_push_subscription_fabricator.rb
index 97f90675d..baffdbf83 100644
--- a/spec/fabricators/web_push_subscription_fabricator.rb
+++ b/spec/fabricators/web_push_subscription_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:web_push_subscription, from: Web::PushSubscription) do
   endpoint   Faker::Internet.url
   key_p256dh Faker::Internet.password
diff --git a/spec/fabricators/web_setting_fabricator.rb b/spec/fabricators/web_setting_fabricator.rb
deleted file mode 100644
index 369b86bc1..000000000
--- a/spec/fabricators/web_setting_fabricator.rb
+++ /dev/null
@@ -1,2 +0,0 @@
-Fabricator(:web_setting, from: Web::Setting) do
-end
diff --git a/spec/fabricators/webauthn_credential_fabricator.rb b/spec/fabricators/webauthn_credential_fabricator.rb
index ba59ce967..b578d55f0 100644
--- a/spec/fabricators/webauthn_credential_fabricator.rb
+++ b/spec/fabricators/webauthn_credential_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:webauthn_credential) do
   user_id { Fabricate(:user).id }
   external_id { Base64.urlsafe_encode64(SecureRandom.random_bytes(16)) }
diff --git a/spec/fabricators/webhook_fabricator.rb b/spec/fabricators/webhook_fabricator.rb
index fa4f17b55..477e715ef 100644
--- a/spec/fabricators/webhook_fabricator.rb
+++ b/spec/fabricators/webhook_fabricator.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 Fabricator(:webhook) do
   url { Faker::Internet.url }
   secret { SecureRandom.hex }
diff --git a/spec/fabricators_spec.rb b/spec/fabricators_spec.rb
new file mode 100644
index 000000000..3b76c56ce
--- /dev/null
+++ b/spec/fabricators_spec.rb
@@ -0,0 +1,12 @@
+require 'rails_helper'
+
+Fabrication.manager.load_definitions if Fabrication.manager.empty?
+
+Fabrication.manager.schematics.map(&:first).each do |factory_name|
+  describe "The #{factory_name} factory" do
+    it 'is valid' do
+      factory = Fabricate(factory_name)
+      expect(factory).to be_valid
+    end
+  end
+end
diff --git a/spec/features/log_in_spec.rb b/spec/features/log_in_spec.rb
index de1a6de03..934575ea6 100644
--- a/spec/features/log_in_spec.rb
+++ b/spec/features/log_in_spec.rb
@@ -2,45 +2,45 @@
 
 require 'rails_helper'
 
-feature 'Log in' do
+describe 'Log in' do
   include ProfileStories
 
-  given(:email)        { "test@example.com" }
-  given(:password)     { "password" }
-  given(:confirmed_at) { Time.zone.now }
+  subject { page }
+
+  let(:email)        { 'test@example.com' }
+  let(:password)     { 'password' }
+  let(:confirmed_at) { Time.zone.now }
 
-  background do
+  before do
     as_a_registered_user
     visit new_user_session_path
   end
 
-  subject { page }
-
-  scenario 'A valid email and password user is able to log in' do
+  it 'A valid email and password user is able to log in' do
     fill_in 'user_email', with: email
     fill_in 'user_password', with: password
     click_on I18n.t('auth.login')
 
-    is_expected.to have_css('div.app-holder')
+    expect(subject).to have_css('div.app-holder')
   end
 
-  scenario 'A invalid email and password user is not able to log in' do
+  it 'A invalid email and password user is not able to log in' do
     fill_in 'user_email', with: 'invalid_email'
     fill_in 'user_password', with: 'invalid_password'
     click_on I18n.t('auth.login')
 
-    is_expected.to have_css('.flash-message', text: failure_message('invalid'))
+    expect(subject).to have_css('.flash-message', text: failure_message('invalid'))
   end
 
   context do
-    given(:confirmed_at) { nil }
+    let(:confirmed_at) { nil }
 
-    scenario 'A unconfirmed user is able to log in' do
+    it 'A unconfirmed user is able to log in' do
       fill_in 'user_email', with: email
       fill_in 'user_password', with: password
       click_on I18n.t('auth.login')
 
-      is_expected.to have_css('div.admin-wrapper')
+      expect(subject).to have_css('div.admin-wrapper')
     end
   end
 
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
index ec4f9a53f..421b68a16 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -2,25 +2,25 @@
 
 require 'rails_helper'
 
-feature 'Profile' do
+describe 'Profile' do
   include ProfileStories
 
-  given(:local_domain) { ENV['LOCAL_DOMAIN'] }
+  subject { page }
+
+  let(:local_domain) { ENV['LOCAL_DOMAIN'] }
 
-  background do
+  before do
     as_a_logged_in_user
     with_alice_as_local_user
   end
 
-  subject { page }
-
-  scenario 'I can view Annes public account' do
+  it 'I can view Annes public account' do
     visit account_path('alice')
 
-    is_expected.to have_title("alice (@alice@#{local_domain})")
+    expect(subject).to have_title("alice (@alice@#{local_domain})")
   end
 
-  scenario 'I can change my account' do
+  it 'I can change my account' do
     visit settings_profile_path
 
     fill_in 'Display name', with: 'Bob'
@@ -28,6 +28,6 @@ feature 'Profile' do
 
     first('button[type=submit]').click
 
-    is_expected.to have_content 'Changes successfully saved!'
+    expect(subject).to have_content 'Changes successfully saved!'
   end
 end
diff --git a/spec/fixtures/files/domain_blocks.csv b/spec/fixtures/files/domain_blocks.csv
index 28ffb9175..9dbfb4eaf 100644
--- a/spec/fixtures/files/domain_blocks.csv
+++ b/spec/fixtures/files/domain_blocks.csv
@@ -1,4 +1,4 @@
 #domain,#severity,#reject_media,#reject_reports,#public_comment,#obfuscate
-bad.domain,silence,false,false,bad,false
-worse.domain,suspend,true,true,worse,true
-reject.media,noop,true,false,reject media,false
+bad.domain,silence,false,false,bad server,false
+worse.domain,suspend,true,true,worse server,true
+reject.media,noop,true,false,reject media and test unicode characters ♥,false
diff --git a/spec/fixtures/files/domain_blocks_list.txt b/spec/fixtures/files/domain_blocks_list.txt
new file mode 100644
index 000000000..7b6b24253
--- /dev/null
+++ b/spec/fixtures/files/domain_blocks_list.txt
@@ -0,0 +1,3 @@
+bad.domain
+worse.domain
+reject.media
diff --git a/spec/helpers/accounts_helper_spec.rb b/spec/helpers/accounts_helper_spec.rb
index 2b35b23b7..184b47dec 100644
--- a/spec/helpers/accounts_helper_spec.rb
+++ b/spec/helpers/accounts_helper_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AccountsHelper, type: :helper do
@@ -13,15 +15,15 @@ RSpec.describe AccountsHelper, type: :helper do
 
   describe '#display_name' do
     it 'uses the display name when it exists' do
-      account = Account.new(display_name: "Display", username: "Username")
+      account = Account.new(display_name: 'Display', username: 'Username')
 
-      expect(helper.display_name(account)).to eq "Display"
+      expect(helper.display_name(account)).to eq 'Display'
     end
 
     it 'uses the username when display name is nil' do
-      account = Account.new(display_name: nil, username: "Username")
+      account = Account.new(display_name: nil, username: 'Username')
 
-      expect(helper.display_name(account)).to eq "Username"
+      expect(helper.display_name(account)).to eq 'Username'
     end
   end
 
diff --git a/spec/helpers/admin/account_moderation_notes_helper_spec.rb b/spec/helpers/admin/account_moderation_notes_helper_spec.rb
index 622ce8806..e01eba51d 100644
--- a/spec/helpers/admin/account_moderation_notes_helper_spec.rb
+++ b/spec/helpers/admin/account_moderation_notes_helper_spec.rb
@@ -42,13 +42,11 @@ RSpec.describe Admin::AccountModerationNotesHelper, type: :helper do
       let(:account) { Fabricate(:account) }
 
       it 'calls #link_to' do
-        expect(helper).to receive(:link_to).with(
-          admin_account_path(account.id),
-          class: name_tag_classes(account, true),
-          title: account.acct
-        )
+        result = helper.admin_account_inline_link_to(account)
 
-        helper.admin_account_inline_link_to(account)
+        expect(result).to match(name_tag_classes(account, true))
+        expect(result).to match(account.acct)
+        expect(result).to match(admin_account_path(account.id))
       end
     end
   end
diff --git a/spec/helpers/admin/action_log_helper_spec.rb b/spec/helpers/admin/action_logs_helper_spec.rb
index 9d7ed4ab7..9d7ed4ab7 100644
--- a/spec/helpers/admin/action_log_helper_spec.rb
+++ b/spec/helpers/admin/action_logs_helper_spec.rb
diff --git a/spec/helpers/admin/dashboard_helper_spec.rb b/spec/helpers/admin/dashboard_helper_spec.rb
new file mode 100644
index 000000000..59062e483
--- /dev/null
+++ b/spec/helpers/admin/dashboard_helper_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::DashboardHelper do
+  describe 'relevant_account_timestamp' do
+    context 'with an account with older sign in' do
+      let(:account) { Fabricate(:account) }
+      let(:stamp) { 10.days.ago }
+
+      it 'returns a time element' do
+        account.user.update(current_sign_in_at: stamp)
+        result = helper.relevant_account_timestamp(account)
+
+        expect(result).to match('time-ago')
+        expect(result).to match(I18n.l(stamp))
+      end
+    end
+
+    context 'with an account with newer sign in' do
+      let(:account) { Fabricate(:account) }
+
+      it 'returns a time element' do
+        account.user.update(current_sign_in_at: 10.hours.ago)
+        result = helper.relevant_account_timestamp(account)
+
+        expect(result).to eq(I18n.t('generic.today'))
+      end
+    end
+
+    context 'with an account where the user is pending' do
+      let(:account) { Fabricate(:account) }
+
+      it 'returns a time element' do
+        account.user.update(current_sign_in_at: nil)
+        account.user.update(approved: false)
+        result = helper.relevant_account_timestamp(account)
+
+        expect(result).to match('time-ago')
+        expect(result).to match(I18n.l(account.user.created_at))
+      end
+    end
+
+    context 'with an account with a last status value' do
+      let(:account) { Fabricate(:account) }
+      let(:stamp) { 5.minutes.ago }
+
+      it 'returns a time element' do
+        account.user.update(current_sign_in_at: nil)
+        account.account_stat.update(last_status_at: stamp)
+        result = helper.relevant_account_timestamp(account)
+
+        expect(result).to match('time-ago')
+        expect(result).to match(I18n.l(stamp))
+      end
+    end
+
+    context 'with an account without sign in or last status or pending' do
+      let(:account) { Fabricate(:account) }
+
+      it 'returns a time element' do
+        account.user.update(current_sign_in_at: nil)
+        result = helper.relevant_account_timestamp(account)
+
+        expect(result).to eq('-')
+      end
+    end
+  end
+end
diff --git a/spec/helpers/admin/filter_helper_spec.rb b/spec/helpers/admin/filter_helper_spec.rb
index 9d4ea2829..bbf90a996 100644
--- a/spec/helpers/admin/filter_helper_spec.rb
+++ b/spec/helpers/admin/filter_helper_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Admin::FilterHelper do
diff --git a/spec/helpers/admin/trends/statuses_helper_spec.rb b/spec/helpers/admin/trends/statuses_helper_spec.rb
new file mode 100644
index 000000000..92caae690
--- /dev/null
+++ b/spec/helpers/admin/trends/statuses_helper_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::Trends::StatusesHelper do
+  describe '.one_line_preview' do
+    before do
+      allow(helper).to receive(:current_user).and_return(Fabricate.build(:user))
+    end
+
+    context 'with a local status' do
+      let(:status) { Fabricate.build(:status, text: 'Test local status') }
+
+      it 'renders a correct preview text' do
+        result = helper.one_line_preview(status)
+
+        expect(result).to eq 'Test local status'
+      end
+    end
+
+    context 'with a remote status' do
+      let(:status) { Fabricate.build(:status, uri: 'https://sfd.sdf', text: '<html><body><p>Test remote status</p><p>text</p></body></html>') }
+
+      it 'renders a correct preview text' do
+        result = helper.one_line_preview(status)
+
+        expect(result).to eq 'Test remote status'
+      end
+    end
+
+    context 'with a status that has empty text' do
+      let(:status) { Fabricate.build(:status, text: '') }
+
+      it 'renders a correct preview text' do
+        result = helper.one_line_preview(status)
+
+        expect(result).to eq ''
+      end
+    end
+
+    context 'with a status that has emoji' do
+      before { Fabricate(:custom_emoji, shortcode: 'florpy') }
+
+      let(:status) { Fabricate(:status, text: 'hello there :florpy:') }
+
+      it 'renders a correct preview text' do
+        result = helper.one_line_preview(status)
+
+        expect(result).to match 'hello there'
+        expect(result).to match '<img rel="emoji"'
+      end
+    end
+  end
+end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 1dbd985bf..2db2ee288 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe ApplicationHelper do
@@ -5,8 +7,8 @@ describe ApplicationHelper do
     it 'returns active when on the current page' do
       allow(helper).to receive(:current_page?).and_return(true)
 
-      result = helper.active_nav_class("/test")
-      expect(result).to eq "active"
+      result = helper.active_nav_class('/test')
+      expect(result).to eq 'active'
     end
 
     it 'returns active when on a current page' do
@@ -14,14 +16,14 @@ describe ApplicationHelper do
       allow(helper).to receive(:current_page?).with('/test').and_return(true)
 
       result = helper.active_nav_class('/foo', '/test')
-      expect(result).to eq "active"
+      expect(result).to eq 'active'
     end
 
     it 'returns empty string when not on current page' do
       allow(helper).to receive(:current_page?).and_return(false)
 
-      result = helper.active_nav_class("/test")
-      expect(result).to eq ""
+      result = helper.active_nav_class('/test')
+      expect(result).to eq ''
     end
   end
 
@@ -65,7 +67,7 @@ describe ApplicationHelper do
         expect(Setting).to receive(:registrations_mode).and_return('open')
       end
 
-      expect(helper.open_registrations?).to eq true
+      expect(helper.open_registrations?).to be true
     end
 
     it 'returns false when closed for registrations' do
@@ -73,7 +75,7 @@ describe ApplicationHelper do
         expect(Setting).to receive(:registrations_mode).and_return('none')
       end
 
-      expect(helper.open_registrations?).to eq false
+      expect(helper.open_registrations?).to be false
     end
   end
 
@@ -82,8 +84,9 @@ describe ApplicationHelper do
       before do
         allow(helper).to receive(:user_signed_in?).and_return(true)
       end
+
       it 'does not show landing strip' do
-        expect(helper.show_landing_strip?).to eq false
+        expect(helper.show_landing_strip?).to be false
       end
     end
 
@@ -95,13 +98,13 @@ describe ApplicationHelper do
       it 'does not show landing strip on single user instance' do
         allow(helper).to receive(:single_user_mode?).and_return(true)
 
-        expect(helper.show_landing_strip?).to eq false
+        expect(helper.show_landing_strip?).to be false
       end
 
       it 'shows landing strip on multi user instance' do
         allow(helper).to receive(:single_user_mode?).and_return(false)
 
-        expect(helper.show_landing_strip?).to eq true
+        expect(helper.show_landing_strip?).to be true
       end
     end
   end
diff --git a/spec/helpers/home_helper_spec.rb b/spec/helpers/home_helper_spec.rb
index a3dc6f836..3d2c5fe24 100644
--- a/spec/helpers/home_helper_spec.rb
+++ b/spec/helpers/home_helper_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe HomeHelper, type: :helper do
@@ -6,4 +8,116 @@ RSpec.describe HomeHelper, type: :helper do
       expect(helper.default_props).to eq locale: I18n.locale
     end
   end
+
+  describe 'account_link_to' do
+    context 'with a missing account' do
+      let(:account) { nil }
+
+      it 'returns a button' do
+        result = helper.account_link_to(account)
+
+        expect(result).to match t('about.contact_missing')
+      end
+    end
+
+    context 'with a valid account' do
+      let(:account) { Fabricate(:account) }
+
+      it 'returns a link to the account' do
+        without_partial_double_verification do
+          allow(helper).to receive(:current_account).and_return(account)
+          allow(helper).to receive(:prefers_autoplay?).and_return(false)
+          result = helper.account_link_to(account)
+
+          expect(result).to match "@#{account.acct}"
+        end
+      end
+    end
+  end
+
+  describe 'obscured_counter' do
+    context 'with a value of less than zero' do
+      let(:count) { -10 }
+
+      it 'returns the correct string' do
+        expect(helper.obscured_counter(count)).to eq '0'
+      end
+    end
+
+    context 'with a value of zero' do
+      let(:count) { 0 }
+
+      it 'returns the correct string' do
+        expect(helper.obscured_counter(count)).to eq '0'
+      end
+    end
+
+    context 'with a value of one' do
+      let(:count) { 1 }
+
+      it 'returns the correct string' do
+        expect(helper.obscured_counter(count)).to eq '1'
+      end
+    end
+
+    context 'with a value of more than one' do
+      let(:count) { 10 }
+
+      it 'returns the correct string' do
+        expect(helper.obscured_counter(count)).to eq '1+'
+      end
+    end
+  end
+
+  describe 'custom_field_classes' do
+    context 'with a verified field' do
+      let(:field) { instance_double(Account::Field, verified?: true) }
+
+      it 'returns verified string' do
+        result = helper.custom_field_classes(field)
+        expect(result).to eq 'verified'
+      end
+    end
+
+    context 'with a non-verified field' do
+      let(:field) { instance_double(Account::Field, verified?: false) }
+
+      it 'returns verified string' do
+        result = helper.custom_field_classes(field)
+        expect(result).to eq 'emojify'
+      end
+    end
+  end
+
+  describe 'sign_up_messages' do
+    context 'with closed registrations' do
+      it 'returns correct sign up message' do
+        allow(helper).to receive(:closed_registrations?).and_return(true)
+        result = helper.sign_up_message
+
+        expect(result).to eq t('auth.registration_closed', instance: 'cb6e6126.ngrok.io')
+      end
+    end
+
+    context 'with open registrations' do
+      it 'returns correct sign up message' do
+        allow(helper).to receive(:closed_registrations?).and_return(false)
+        allow(helper).to receive(:open_registrations?).and_return(true)
+        result = helper.sign_up_message
+
+        expect(result).to eq t('auth.register')
+      end
+    end
+
+    context 'with approved registrations' do
+      it 'returns correct sign up message' do
+        allow(helper).to receive(:closed_registrations?).and_return(false)
+        allow(helper).to receive(:open_registrations?).and_return(false)
+        allow(helper).to receive(:approved_registrations?).and_return(true)
+        result = helper.sign_up_message
+
+        expect(result).to eq t('auth.apply_for_account')
+      end
+    end
+  end
 end
diff --git a/spec/helpers/jsonld_helper_spec.rb b/spec/helpers/jsonld_helper_spec.rb
index 744a14f26..ddd4bfe62 100644
--- a/spec/helpers/jsonld_helper_spec.rb
+++ b/spec/helpers/jsonld_helper_spec.rb
@@ -66,14 +66,14 @@ describe JsonLdHelper do
         stub_request(:get, 'https://mallory.test/').to_return body: '{"id": "https://marvin.test/"}'
         stub_request(:get, 'https://marvin.test/').to_return body: '{"id": "https://alice.test/"}'
 
-        expect(fetch_resource('https://mallory.test/', false)).to eq nil
+        expect(fetch_resource('https://mallory.test/', false)).to be_nil
       end
     end
 
     context 'when the second argument is true' do
       it 'returns nil if the retrieved ID and the given URI does not match' do
         stub_request(:get, 'https://mallory.test/').to_return body: '{"id": "https://alice.test/"}'
-        expect(fetch_resource('https://mallory.test/', true)).to eq nil
+        expect(fetch_resource('https://mallory.test/', true)).to be_nil
       end
     end
   end
@@ -81,7 +81,7 @@ describe JsonLdHelper do
   describe '#fetch_resource_without_id_validation' do
     it 'returns nil if the status code is not 200' do
       stub_request(:get, 'https://host.test/').to_return status: 400, body: '{}'
-      expect(fetch_resource_without_id_validation('https://host.test/')).to eq nil
+      expect(fetch_resource_without_id_validation('https://host.test/')).to be_nil
     end
 
     it 'returns hash' do
@@ -113,7 +113,7 @@ describe JsonLdHelper do
             {
               'type' => 'Mention',
               'href' => ['foo'],
-            }
+            },
           ],
         },
         'signature' => {
@@ -150,7 +150,7 @@ describe JsonLdHelper do
         patch_for_forwarding!(json, compacted)
         expect(compacted['to']).to eq ['https://www.w3.org/ns/activitystreams#Public']
         expect(compacted.dig('object', 'tag', 0, 'href')).to eq ['foo']
-        expect(safe_for_forwarding?(json, compacted)).to eq true
+        expect(safe_for_forwarding?(json, compacted)).to be true
       end
     end
 
@@ -160,14 +160,14 @@ describe JsonLdHelper do
         compacted = compact(json)
         deemed_compatible = patch_for_forwarding!(json, compacted)
         expect(compacted['to']).to eq ['https://www.w3.org/ns/activitystreams#Public']
-        expect(safe_for_forwarding?(json, compacted)).to eq true
+        expect(safe_for_forwarding?(json, compacted)).to be true
       end
 
       it 'deems an unsafe compacting as such' do
         compacted = compact(json)
         deemed_compatible = patch_for_forwarding!(json, compacted)
         expect(compacted['to']).to eq ['https://www.w3.org/ns/activitystreams#Public']
-        expect(safe_for_forwarding?(json, compacted)).to eq false
+        expect(safe_for_forwarding?(json, compacted)).to be false
       end
     end
   end
diff --git a/spec/helpers/languages_helper_spec.rb b/spec/helpers/languages_helper_spec.rb
index 217c9b239..98c8064a3 100644
--- a/spec/helpers/languages_helper_spec.rb
+++ b/spec/helpers/languages_helper_spec.rb
@@ -10,14 +10,54 @@ describe LanguagesHelper do
   end
 
   describe 'native_locale_name' do
-    it 'finds the human readable native name from a key' do
-      expect(helper.native_locale_name(:de)).to eq('Deutsch')
+    context 'with a blank locale' do
+      it 'defaults to a generic value' do
+        expect(helper.native_locale_name(nil)).to eq(I18n.t('generic.none'))
+      end
+    end
+
+    context 'with a locale of `und`' do
+      it 'defaults to a generic value' do
+        expect(helper.native_locale_name('und')).to eq(I18n.t('generic.none'))
+      end
+    end
+
+    context 'with a supported locale' do
+      it 'finds the human readable native name from a key' do
+        expect(helper.native_locale_name(:de)).to eq('Deutsch')
+      end
+    end
+
+    context 'with a regional locale' do
+      it 'finds the human readable regional name from a key' do
+        expect(helper.native_locale_name('en-GB')).to eq('English (British)')
+      end
+    end
+
+    context 'with a non-existent locale' do
+      it 'returns the supplied locale value' do
+        expect(helper.native_locale_name(:xxx)).to eq(:xxx)
+      end
     end
   end
 
   describe 'standard_locale_name' do
-    it 'finds the human readable standard name from a key' do
-      expect(helper.standard_locale_name(:de)).to eq('German')
+    context 'with a blank locale' do
+      it 'defaults to a generic value' do
+        expect(helper.standard_locale_name(nil)).to eq(I18n.t('generic.none'))
+      end
+    end
+
+    context 'with a non-existent locale' do
+      it 'returns the supplied locale value' do
+        expect(helper.standard_locale_name(:xxx)).to eq(:xxx)
+      end
+    end
+
+    context 'with a supported locale' do
+      it 'finds the human readable standard name from a key' do
+        expect(helper.standard_locale_name(:de)).to eq('German')
+      end
     end
   end
 end
diff --git a/spec/helpers/settings_helper_spec.rb b/spec/helpers/settings_helper_spec.rb
new file mode 100644
index 000000000..cba5c6ee8
--- /dev/null
+++ b/spec/helpers/settings_helper_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe SettingsHelper do
+  describe 'session_device_icon' do
+    context 'with a mobile device' do
+      let(:session) { SessionActivation.new(user_agent: 'Mozilla/5.0 (iPhone)') }
+
+      it 'detects the device and returns a descriptive string' do
+        result = helper.session_device_icon(session)
+
+        expect(result).to eq('mobile')
+      end
+    end
+
+    context 'with a tablet device' do
+      let(:session) { SessionActivation.new(user_agent: 'Mozilla/5.0 (iPad)') }
+
+      it 'detects the device and returns a descriptive string' do
+        result = helper.session_device_icon(session)
+
+        expect(result).to eq('tablet')
+      end
+    end
+
+    context 'with a desktop device' do
+      let(:session) { SessionActivation.new(user_agent: 'Mozilla/5.0 (Macintosh)') }
+
+      it 'detects the device and returns a descriptive string' do
+        result = helper.session_device_icon(session)
+
+        expect(result).to eq('desktop')
+      end
+    end
+  end
+end
diff --git a/spec/helpers/statuses_helper_spec.rb b/spec/helpers/statuses_helper_spec.rb
index cba659bfb..105da7e1b 100644
--- a/spec/helpers/statuses_helper_spec.rb
+++ b/spec/helpers/statuses_helper_spec.rb
@@ -1,6 +1,96 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-RSpec.describe StatusesHelper, type: :helper do
+describe StatusesHelper do
+  describe 'status_text_summary' do
+    context 'with blank text' do
+      let(:status) { Status.new(spoiler_text: '') }
+
+      it 'returns immediately with nil' do
+        result = helper.status_text_summary(status)
+        expect(result).to be_nil
+      end
+    end
+
+    context 'with present text' do
+      let(:status) { Status.new(spoiler_text: 'SPOILERS!!!') }
+
+      it 'returns the content warning' do
+        result = helper.status_text_summary(status)
+        expect(result).to eq(I18n.t('statuses.content_warning', warning: 'SPOILERS!!!'))
+      end
+    end
+  end
+
+  def status_text_summary(status)
+    return if status.spoiler_text.blank?
+
+    I18n.t('statuses.content_warning', warning: status.spoiler_text)
+  end
+
+  describe 'link_to_newer' do
+    it 'returns a link to newer content' do
+      url = 'https://example.com'
+      result = helper.link_to_newer(url)
+
+      expect(result).to match('load-more')
+      expect(result).to match(I18n.t('statuses.show_newer'))
+    end
+  end
+
+  describe 'link_to_older' do
+    it 'returns a link to older content' do
+      url = 'https://example.com'
+      result = helper.link_to_older(url)
+
+      expect(result).to match('load-more')
+      expect(result).to match(I18n.t('statuses.show_older'))
+    end
+  end
+
+  describe 'fa_visibility_icon' do
+    context 'with a status that is public' do
+      let(:status) { Status.new(visibility: 'public') }
+
+      it 'returns the correct fa icon' do
+        result = helper.fa_visibility_icon(status)
+
+        expect(result).to match('fa-globe')
+      end
+    end
+
+    context 'with a status that is unlisted' do
+      let(:status) { Status.new(visibility: 'unlisted') }
+
+      it 'returns the correct fa icon' do
+        result = helper.fa_visibility_icon(status)
+
+        expect(result).to match('fa-unlock')
+      end
+    end
+
+    context 'with a status that is private' do
+      let(:status) { Status.new(visibility: 'private') }
+
+      it 'returns the correct fa icon' do
+        result = helper.fa_visibility_icon(status)
+
+        expect(result).to match('fa-lock')
+      end
+    end
+
+    context 'with a status that is direct' do
+      let(:status) { Status.new(visibility: 'direct') }
+
+      it 'returns the correct fa icon' do
+        result = helper.fa_visibility_icon(status)
+
+        expect(result).to match('fa-at')
+      end
+    end
+  end
+
   describe '#stream_link_target' do
     it 'returns nil if it is not an embedded view' do
       set_not_embedded_view
diff --git a/spec/lib/activitypub/activity/accept_spec.rb b/spec/lib/activitypub/activity/accept_spec.rb
index 304cf2208..890a07be5 100644
--- a/spec/lib/activitypub/activity/accept_spec.rb
+++ b/spec/lib/activitypub/activity/accept_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Accept do
@@ -42,6 +44,8 @@ RSpec.describe ActivityPub::Activity::Accept do
   end
 
   context 'given a relay' do
+    subject { described_class.new(json, sender) }
+
     let!(:relay) { Fabricate(:relay, state: :pending, follow_activity_id: 'https://abc-123/456') }
 
     let(:json) do
@@ -59,8 +63,6 @@ RSpec.describe ActivityPub::Activity::Accept do
       }.with_indifferent_access
     end
 
-    subject { described_class.new(json, sender) }
-
     it 'marks the relay as accepted' do
       subject.perform
       expect(relay.reload.accepted?).to be true
diff --git a/spec/lib/activitypub/activity/add_spec.rb b/spec/lib/activitypub/activity/add_spec.rb
index e6408b610..9c45e465e 100644
--- a/spec/lib/activitypub/activity/add_spec.rb
+++ b/spec/lib/activitypub/activity/add_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Add do
@@ -48,10 +50,10 @@ RSpec.describe ActivityPub::Activity::Add do
         end
 
         it 'fetches the status and pins it' do
-          allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil|
+          allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil, request_id: nil| # rubocop:disable Lint/UnusedBlockArgument
             expect(uri).to eq 'https://example.com/unknown'
-            expect(id).to eq true
-            expect(on_behalf_of&.following?(sender)).to eq true
+            expect(id).to be true
+            expect(on_behalf_of&.following?(sender)).to be true
             status
           end
           subject.perform
@@ -62,10 +64,10 @@ RSpec.describe ActivityPub::Activity::Add do
 
       context 'when there is no local follower' do
         it 'tries to fetch the status' do
-          allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil|
+          allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil, request_id: nil| # rubocop:disable Lint/UnusedBlockArgument
             expect(uri).to eq 'https://example.com/unknown'
-            expect(id).to eq true
-            expect(on_behalf_of).to eq nil
+            expect(id).to be true
+            expect(on_behalf_of).to be_nil
             nil
           end
           subject.perform
diff --git a/spec/lib/activitypub/activity/announce_spec.rb b/spec/lib/activitypub/activity/announce_spec.rb
index e9cd6c68c..394b1d7b9 100644
--- a/spec/lib/activitypub/activity/announce_spec.rb
+++ b/spec/lib/activitypub/activity/announce_spec.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Announce do
+  subject { described_class.new(json, sender) }
+
   let(:sender)    { Fabricate(:account, followers_url: 'http://example.com/followers', uri: 'https://example.com/actor') }
   let(:recipient) { Fabricate(:account) }
   let(:status)    { Fabricate(:status, account: recipient) }
@@ -27,8 +31,6 @@ RSpec.describe ActivityPub::Activity::Announce do
     }
   end
 
-  subject { described_class.new(json, sender) }
-
   describe '#perform' do
     context 'when sender is followed by a local account' do
       before do
@@ -82,10 +84,10 @@ RSpec.describe ActivityPub::Activity::Announce do
             content: 'Lorem ipsum',
             attributedTo: 'https://example.com/actor',
             to: {
-              'type': 'OrderedCollection',
-              'id': 'http://example.com/followers',
-              'first': 'http://example.com/followers?page=true',
-            }
+              type: 'OrderedCollection',
+              id: 'http://example.com/followers',
+              first: 'http://example.com/followers?page=true',
+            },
           }
         end
 
@@ -110,13 +112,13 @@ RSpec.describe ActivityPub::Activity::Announce do
     end
 
     context 'when the sender is relayed' do
+      subject { described_class.new(json, sender, relayed_through_actor: relay_account) }
+
       let!(:relay_account) { Fabricate(:account, inbox_url: 'https://relay.example.com/inbox') }
       let!(:relay) { Fabricate(:relay, inbox_url: 'https://relay.example.com/inbox') }
 
       let(:object_json) { 'https://example.com/actor/hello-world' }
 
-      subject { described_class.new(json, sender, relayed_through_actor: relay_account) }
-
       before do
         stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: Oj.dump(unknown_object_json))
       end
@@ -139,7 +141,7 @@ RSpec.describe ActivityPub::Activity::Announce do
         end
 
         it 'does not fetch the remote status' do
-          expect(a_request(:get, 'https://example.com/actor/hello-world')).not_to have_been_made
+          expect(a_request(:get, 'https://example.com/actor/hello-world')).to_not have_been_made
           expect(Status.find_by(uri: 'https://example.com/actor/hello-world')).to be_nil
         end
 
diff --git a/spec/lib/activitypub/activity/block_spec.rb b/spec/lib/activitypub/activity/block_spec.rb
index 42bdfdc81..6f6898401 100644
--- a/spec/lib/activitypub/activity/block_spec.rb
+++ b/spec/lib/activitypub/activity/block_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Block do
diff --git a/spec/lib/activitypub/activity/create_spec.rb b/spec/lib/activitypub/activity/create_spec.rb
index 738e644c5..1226cfd8e 100644
--- a/spec/lib/activitypub/activity/create_spec.rb
+++ b/spec/lib/activitypub/activity/create_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Create do
@@ -51,7 +53,7 @@ RSpec.describe ActivityPub::Activity::Create do
           status = sender.statuses.first
 
           expect(status).to_not be_nil
-          expect(status.edited?).to eq true
+          expect(status.edited?).to be true
         end
       end
 
@@ -77,7 +79,7 @@ RSpec.describe ActivityPub::Activity::Create do
           status = sender.statuses.first
 
           expect(status).to_not be_nil
-          expect(status.edited?).to eq false
+          expect(status.edited?).to be false
         end
       end
 
@@ -252,10 +254,10 @@ RSpec.describe ActivityPub::Activity::Create do
             type: 'Note',
             content: 'Lorem ipsum',
             to: {
-              'type': 'OrderedCollection',
-              'id': 'http://example.com/followers',
-              'first': 'http://example.com/followers?page=true',
-            }
+              type: 'OrderedCollection',
+              id: 'http://example.com/followers',
+              first: 'http://example.com/followers?page=true',
+            },
           }
         end
 
@@ -454,7 +456,6 @@ RSpec.describe ActivityPub::Activity::Create do
         end
       end
 
-
       context 'with media attachments with long description' do
         let(:object_json) do
           {
@@ -733,7 +734,7 @@ RSpec.describe ActivityPub::Activity::Create do
                 replies: {
                   type: 'Collection',
                   totalItems: 3,
-                }
+                },
               },
             ],
           }
@@ -763,7 +764,7 @@ RSpec.describe ActivityPub::Activity::Create do
             id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
             type: 'Note',
             name: 'Yellow',
-            inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
+            inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
           }
         end
 
@@ -788,7 +789,7 @@ RSpec.describe ActivityPub::Activity::Create do
             id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
             type: 'Note',
             name: 'Yellow',
-            inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status)
+            inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
           }
         end
 
@@ -799,11 +800,9 @@ RSpec.describe ActivityPub::Activity::Create do
     end
 
     context 'with an encrypted message' do
-      let(:recipient) { Fabricate(:account) }
-      let(:target_device) { Fabricate(:device, account: recipient) }
-
       subject { described_class.new(json, sender, delivery: true, delivered_to_account_id: recipient.id) }
 
+      let(:recipient) { Fabricate(:account) }
       let(:object_json) do
         {
           id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
@@ -825,6 +824,7 @@ RSpec.describe ActivityPub::Activity::Create do
           },
         }
       end
+      let(:target_device) { Fabricate(:device, account: recipient) }
 
       before do
         subject.perform
@@ -879,14 +879,9 @@ RSpec.describe ActivityPub::Activity::Create do
     end
 
     context 'when sender replies to local status' do
-      let!(:local_status) { Fabricate(:status) }
-
       subject { described_class.new(json, sender, delivery: true) }
 
-      before do
-        subject.perform
-      end
-
+      let!(:local_status) { Fabricate(:status) }
       let(:object_json) do
         {
           id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
@@ -896,6 +891,10 @@ RSpec.describe ActivityPub::Activity::Create do
         }
       end
 
+      before do
+        subject.perform
+      end
+
       it 'creates status' do
         status = sender.statuses.first
 
@@ -905,14 +904,9 @@ RSpec.describe ActivityPub::Activity::Create do
     end
 
     context 'when sender targets a local user' do
-      let!(:local_account) { Fabricate(:account) }
-
       subject { described_class.new(json, sender, delivery: true) }
 
-      before do
-        subject.perform
-      end
-
+      let!(:local_account) { Fabricate(:account) }
       let(:object_json) do
         {
           id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
@@ -922,6 +916,10 @@ RSpec.describe ActivityPub::Activity::Create do
         }
       end
 
+      before do
+        subject.perform
+      end
+
       it 'creates status' do
         status = sender.statuses.first
 
@@ -931,14 +929,9 @@ RSpec.describe ActivityPub::Activity::Create do
     end
 
     context 'when sender cc\'s a local user' do
-      let!(:local_account) { Fabricate(:account) }
-
       subject { described_class.new(json, sender, delivery: true) }
 
-      before do
-        subject.perform
-      end
-
+      let!(:local_account) { Fabricate(:account) }
       let(:object_json) do
         {
           id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
@@ -948,6 +941,10 @@ RSpec.describe ActivityPub::Activity::Create do
         }
       end
 
+      before do
+        subject.perform
+      end
+
       it 'creates status' do
         status = sender.statuses.first
 
diff --git a/spec/lib/activitypub/activity/delete_spec.rb b/spec/lib/activitypub/activity/delete_spec.rb
index 9dfb8a61b..3a73b3726 100644
--- a/spec/lib/activitypub/activity/delete_spec.rb
+++ b/spec/lib/activitypub/activity/delete_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Delete do
@@ -30,6 +32,7 @@ RSpec.describe ActivityPub::Activity::Delete do
   context 'when the status has been reblogged' do
     describe '#perform' do
       subject { described_class.new(json, sender) }
+
       let!(:reblogger) { Fabricate(:account) }
       let!(:follower)  { Fabricate(:account, username: 'follower', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
       let!(:reblog)    { Fabricate(:status, account: reblogger, reblog: status) }
@@ -53,6 +56,7 @@ RSpec.describe ActivityPub::Activity::Delete do
   context 'when the status has been reported' do
     describe '#perform' do
       subject { described_class.new(json, sender) }
+
       let!(:reporter) { Fabricate(:account) }
 
       before do
diff --git a/spec/lib/activitypub/activity/flag_spec.rb b/spec/lib/activitypub/activity/flag_spec.rb
index 2f2d13876..005e185e6 100644
--- a/spec/lib/activitypub/activity/flag_spec.rb
+++ b/spec/lib/activitypub/activity/flag_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Flag do
@@ -110,7 +112,8 @@ RSpec.describe ActivityPub::Activity::Flag do
 
   describe '#perform with a defined uri' do
     subject { described_class.new(json, sender) }
-    let (:flag_id) { 'http://example.com/reports/1' }
+
+    let(:flag_id) { 'http://example.com/reports/1' }
 
     before do
       subject.perform
diff --git a/spec/lib/activitypub/activity/follow_spec.rb b/spec/lib/activitypub/activity/follow_spec.rb
index fd4ede82b..eb8b17d61 100644
--- a/spec/lib/activitypub/activity/follow_spec.rb
+++ b/spec/lib/activitypub/activity/follow_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Follow do
diff --git a/spec/lib/activitypub/activity/like_spec.rb b/spec/lib/activitypub/activity/like_spec.rb
index b69615a9d..640d61ab3 100644
--- a/spec/lib/activitypub/activity/like_spec.rb
+++ b/spec/lib/activitypub/activity/like_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Like do
diff --git a/spec/lib/activitypub/activity/move_spec.rb b/spec/lib/activitypub/activity/move_spec.rb
index c468fdeff..8bd23aa7b 100644
--- a/spec/lib/activitypub/activity/move_spec.rb
+++ b/spec/lib/activitypub/activity/move_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Move do
diff --git a/spec/lib/activitypub/activity/reject_spec.rb b/spec/lib/activitypub/activity/reject_spec.rb
index fed4cd8cd..5e0f09bfe 100644
--- a/spec/lib/activitypub/activity/reject_spec.rb
+++ b/spec/lib/activitypub/activity/reject_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Reject do
@@ -121,6 +123,8 @@ RSpec.describe ActivityPub::Activity::Reject do
   end
 
   context 'given a relay' do
+    subject { described_class.new(json, sender) }
+
     let!(:relay) { Fabricate(:relay, state: :pending, follow_activity_id: 'https://abc-123/456') }
 
     let(:json) do
@@ -138,8 +142,6 @@ RSpec.describe ActivityPub::Activity::Reject do
       }.with_indifferent_access
     end
 
-    subject { described_class.new(json, sender) }
-
     it 'marks the relay as rejected' do
       subject.perform
       expect(relay.reload.rejected?).to be true
diff --git a/spec/lib/activitypub/activity/remove_spec.rb b/spec/lib/activitypub/activity/remove_spec.rb
index 4209dfde2..fc12aec8c 100644
--- a/spec/lib/activitypub/activity/remove_spec.rb
+++ b/spec/lib/activitypub/activity/remove_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Remove do
diff --git a/spec/lib/activitypub/activity/undo_spec.rb b/spec/lib/activitypub/activity/undo_spec.rb
index c0309e49d..b4cbc7196 100644
--- a/spec/lib/activitypub/activity/undo_spec.rb
+++ b/spec/lib/activitypub/activity/undo_spec.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Undo do
+  subject { described_class.new(json, sender) }
+
   let(:sender) { Fabricate(:account, domain: 'example.com') }
 
   let(:json) do
@@ -13,8 +17,6 @@ RSpec.describe ActivityPub::Activity::Undo do
     }.with_indifferent_access
   end
 
-  subject { described_class.new(json, sender) }
-
   describe '#perform' do
     context 'with Announce' do
       let(:status) { Fabricate(:status) }
diff --git a/spec/lib/activitypub/activity/update_spec.rb b/spec/lib/activitypub/activity/update_spec.rb
index 4cd853af2..f77279c02 100644
--- a/spec/lib/activitypub/activity/update_spec.rb
+++ b/spec/lib/activitypub/activity/update_spec.rb
@@ -1,14 +1,16 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Activity::Update do
+  subject { described_class.new(json, sender) }
+
   let!(:sender) { Fabricate(:account) }
 
   before do
     sender.update!(uri: ActivityPub::TagManager.instance.uri_for(sender))
   end
 
-  subject { described_class.new(json, sender) }
-
   describe '#perform' do
     context 'with an Actor object' do
       let(:modified_sender) do
diff --git a/spec/lib/activitypub/adapter_spec.rb b/spec/lib/activitypub/adapter_spec.rb
index ea03797aa..b981ea9c6 100644
--- a/spec/lib/activitypub/adapter_spec.rb
+++ b/spec/lib/activitypub/adapter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Adapter do
@@ -41,10 +43,10 @@ RSpec.describe ActivityPub::Adapter do
   end
 
   describe '#serializable_hash' do
-    let(:serializer_class) {}
-
     subject { ActiveModelSerializers::SerializableResource.new(TestObject.new(foo: 'bar'), serializer: serializer_class, adapter: described_class).as_json }
 
+    let(:serializer_class) {}
+
     context 'when serializer defines no context' do
       let(:serializer_class) { TestWithBasicContextSerializer }
 
diff --git a/spec/lib/activitypub/dereferencer_spec.rb b/spec/lib/activitypub/dereferencer_spec.rb
index e50b497c7..11078de86 100644
--- a/spec/lib/activitypub/dereferencer_spec.rb
+++ b/spec/lib/activitypub/dereferencer_spec.rb
@@ -1,14 +1,16 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::Dereferencer do
   describe '#object' do
+    subject { described_class.new(uri, permitted_origin: permitted_origin, signature_actor: signature_actor).object }
+
     let(:object) { { '@context': 'https://www.w3.org/ns/activitystreams', id: 'https://example.com/foo', type: 'Note', content: 'Hoge' } }
     let(:permitted_origin) { 'https://example.com' }
     let(:signature_actor) { nil }
     let(:uri) { nil }
 
-    subject { described_class.new(uri, permitted_origin: permitted_origin, signature_actor: signature_actor).object }
-
     before do
       stub_request(:get, 'https://example.com/foo').to_return(body: Oj.dump(object), headers: { 'Content-Type' => 'application/activity+json' })
     end
diff --git a/spec/lib/activitypub/linked_data_signature_spec.rb b/spec/lib/activitypub/linked_data_signature_spec.rb
index d55a7c7fa..619d6df12 100644
--- a/spec/lib/activitypub/linked_data_signature_spec.rb
+++ b/spec/lib/activitypub/linked_data_signature_spec.rb
@@ -1,8 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::LinkedDataSignature do
   include JsonLdHelper
 
+  subject { described_class.new(json) }
+
   let!(:sender) { Fabricate(:account, uri: 'http://example.com/alice') }
 
   let(:raw_json) do
@@ -14,8 +18,6 @@ RSpec.describe ActivityPub::LinkedDataSignature do
 
   let(:json) { raw_json.merge('signature' => signature) }
 
-  subject { described_class.new(json) }
-
   before do
     stub_jsonld_contexts!
   end
diff --git a/spec/lib/activitypub/tag_manager_spec.rb b/spec/lib/activitypub/tag_manager_spec.rb
index 606a1de2e..596e91e95 100644
--- a/spec/lib/activitypub/tag_manager_spec.rb
+++ b/spec/lib/activitypub/tag_manager_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::TagManager do
diff --git a/spec/lib/admin/system_check/base_check_spec.rb b/spec/lib/admin/system_check/base_check_spec.rb
new file mode 100644
index 000000000..fdd9f6b6c
--- /dev/null
+++ b/spec/lib/admin/system_check/base_check_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::SystemCheck::BaseCheck do
+  subject(:check) { described_class.new(user) }
+
+  let(:user) { Fabricate(:user) }
+
+  describe 'skip?' do
+    it 'returns false' do
+      expect(check.skip?).to be false
+    end
+  end
+
+  describe 'pass?' do
+    it 'raises not implemented error' do
+      expect { check.pass? }.to raise_error(NotImplementedError)
+    end
+  end
+
+  describe 'message' do
+    it 'raises not implemented error' do
+      expect { check.message }.to raise_error(NotImplementedError)
+    end
+  end
+end
diff --git a/spec/lib/admin/system_check/database_schema_check_spec.rb b/spec/lib/admin/system_check/database_schema_check_spec.rb
new file mode 100644
index 000000000..db1dcb52f
--- /dev/null
+++ b/spec/lib/admin/system_check/database_schema_check_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::SystemCheck::DatabaseSchemaCheck do
+  subject(:check) { described_class.new(user) }
+
+  let(:user) { Fabricate(:user) }
+
+  it_behaves_like 'a check available to devops users'
+
+  describe 'pass?' do
+    context 'when database needs migration' do
+      before do
+        context = instance_double(ActiveRecord::MigrationContext, needs_migration?: true)
+        allow(ActiveRecord::Base.connection).to receive(:migration_context).and_return(context)
+      end
+
+      it 'returns false' do
+        expect(check.pass?).to be false
+      end
+    end
+
+    context 'when database does not need migration' do
+      before do
+        context = instance_double(ActiveRecord::MigrationContext, needs_migration?: false)
+        allow(ActiveRecord::Base.connection).to receive(:migration_context).and_return(context)
+      end
+
+      it 'returns true' do
+        expect(check.pass?).to be true
+      end
+    end
+  end
+
+  describe 'message' do
+    it 'sends class name symbol to message instance' do
+      allow(Admin::SystemCheck::Message).to receive(:new).with(:database_schema_check)
+
+      check.message
+
+      expect(Admin::SystemCheck::Message).to have_received(:new).with(:database_schema_check)
+    end
+  end
+end
diff --git a/spec/lib/admin/system_check/elasticsearch_check_spec.rb b/spec/lib/admin/system_check/elasticsearch_check_spec.rb
new file mode 100644
index 000000000..1ffac89ee
--- /dev/null
+++ b/spec/lib/admin/system_check/elasticsearch_check_spec.rb
@@ -0,0 +1,100 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::SystemCheck::ElasticsearchCheck do
+  subject(:check) { described_class.new(user) }
+
+  let(:user) { Fabricate(:user) }
+
+  it_behaves_like 'a check available to devops users'
+
+  describe 'pass?' do
+    context 'when chewy is enabled' do
+      before { allow(Chewy).to receive(:enabled?).and_return(true) }
+
+      context 'when running version is present and high enough' do
+        before do
+          allow(Chewy.client).to receive(:info)
+            .and_return({ 'version' => { 'number' => '999.99.9' } })
+        end
+
+        it 'returns true' do
+          expect(check.pass?).to be true
+        end
+      end
+
+      context 'when running version is present and too low' do
+        context 'when compatible version is too low' do
+          before do
+            allow(Chewy.client).to receive(:info)
+              .and_return({ 'version' => { 'number' => '1.2.3', 'minimum_wire_compatibility_version' => '1.0' } })
+          end
+
+          it 'returns false' do
+            expect(check.pass?).to be false
+          end
+        end
+
+        context 'when compatible version is high enough' do
+          before do
+            allow(Chewy.client).to receive(:info)
+              .and_return({ 'version' => { 'number' => '1.2.3', 'minimum_wire_compatibility_version' => '99.9' } })
+          end
+
+          it 'returns true' do
+            expect(check.pass?).to be true
+          end
+        end
+      end
+
+      context 'when running version is missing' do
+        before do
+          client = instance_double(Elasticsearch::Transport::Client)
+          allow(client).to receive(:info).and_raise(Elasticsearch::Transport::Transport::Error)
+          allow(Chewy).to receive(:client).and_return(client)
+        end
+
+        it 'returns false' do
+          expect(check.pass?).to be false
+        end
+      end
+    end
+
+    context 'when chewy is not enabled' do
+      before { allow(Chewy).to receive(:enabled?).and_return(false) }
+
+      it 'returns true' do
+        expect(check.pass?).to be true
+      end
+    end
+  end
+
+  describe 'message' do
+    context 'when running version is present' do
+      before { allow(Chewy.client).to receive(:info).and_return({ 'version' => { 'number' => '999.99.9' } }) }
+
+      it 'sends class name symbol to message instance' do
+        allow(Admin::SystemCheck::Message).to receive(:new)
+          .with(:elasticsearch_version_check, anything)
+
+        check.message
+
+        expect(Admin::SystemCheck::Message).to have_received(:new)
+          .with(:elasticsearch_version_check, 'Elasticsearch 999.99.9 is running while 7.x is required')
+      end
+    end
+
+    context 'when running version is missing' do
+      it 'sends class name symbol to message instance' do
+        allow(Admin::SystemCheck::Message).to receive(:new)
+          .with(:elasticsearch_running_check)
+
+        check.message
+
+        expect(Admin::SystemCheck::Message).to have_received(:new)
+          .with(:elasticsearch_running_check)
+      end
+    end
+  end
+end
diff --git a/spec/lib/admin/system_check/media_privacy_check_spec.rb b/spec/lib/admin/system_check/media_privacy_check_spec.rb
new file mode 100644
index 000000000..316bf1215
--- /dev/null
+++ b/spec/lib/admin/system_check/media_privacy_check_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::SystemCheck::MediaPrivacyCheck do
+  subject(:check) { described_class.new(user) }
+
+  let(:user) { Fabricate(:user) }
+
+  it_behaves_like 'a check available to devops users'
+
+  describe 'pass?' do
+    context 'when the media cannot be listed' do
+      before do
+        stub_request(:get, /ngrok.io/).to_return(status: 200, body: 'a list of no files')
+      end
+
+      it 'returns true' do
+        expect(check.pass?).to be true
+      end
+    end
+  end
+
+  describe 'message' do
+    it 'sends values to message instance' do
+      allow(Admin::SystemCheck::Message).to receive(:new).with(nil, nil, nil, true)
+
+      check.message
+
+      expect(Admin::SystemCheck::Message).to have_received(:new).with(nil, nil, nil, true)
+    end
+  end
+end
diff --git a/spec/lib/admin/system_check/message_spec.rb b/spec/lib/admin/system_check/message_spec.rb
new file mode 100644
index 000000000..c0671f345
--- /dev/null
+++ b/spec/lib/admin/system_check/message_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::SystemCheck::Message do
+  subject(:check) { described_class.new(:key_value, :value_value, :action_value, :critical_value) }
+
+  it 'providers readers when initialized' do
+    expect(check.key).to eq :key_value
+    expect(check.value).to eq :value_value
+    expect(check.action).to eq :action_value
+    expect(check.critical).to eq :critical_value
+  end
+end
diff --git a/spec/lib/admin/system_check/rules_check_spec.rb b/spec/lib/admin/system_check/rules_check_spec.rb
new file mode 100644
index 000000000..fb3293fb2
--- /dev/null
+++ b/spec/lib/admin/system_check/rules_check_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::SystemCheck::RulesCheck do
+  subject(:check) { described_class.new(user) }
+
+  let(:user) { Fabricate(:user) }
+
+  describe 'skip?' do
+    context 'when user can manage rules' do
+      before { allow(user).to receive(:can?).with(:manage_rules).and_return(true) }
+
+      it 'returns false' do
+        expect(check.skip?).to be false
+      end
+    end
+
+    context 'when user cannot manage rules' do
+      before { allow(user).to receive(:can?).with(:manage_rules).and_return(false) }
+
+      it 'returns true' do
+        expect(check.skip?).to be true
+      end
+    end
+  end
+
+  describe 'pass?' do
+    context 'when there is not a kept rule' do
+      it 'returns false' do
+        expect(check.pass?).to be false
+      end
+    end
+
+    context 'when there is a kept rule' do
+      before { Fabricate(:rule) }
+
+      it 'returns true' do
+        expect(check.pass?).to be true
+      end
+    end
+  end
+
+  describe 'message' do
+    it 'sends class name symbol to message instance' do
+      allow(Admin::SystemCheck::Message).to receive(:new).with(:rules_check, nil, '/admin/rules')
+
+      check.message
+
+      expect(Admin::SystemCheck::Message).to have_received(:new).with(:rules_check, nil, '/admin/rules')
+    end
+  end
+end
diff --git a/spec/lib/admin/system_check/sidekiq_process_check_spec.rb b/spec/lib/admin/system_check/sidekiq_process_check_spec.rb
new file mode 100644
index 000000000..9bd9daddf
--- /dev/null
+++ b/spec/lib/admin/system_check/sidekiq_process_check_spec.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::SystemCheck::SidekiqProcessCheck do
+  subject(:check) { described_class.new(user) }
+
+  let(:user) { Fabricate(:user) }
+
+  it_behaves_like 'a check available to devops users'
+
+  describe 'pass?' do
+    context 'when missing queues is empty' do
+      before do
+        process_set = instance_double(Sidekiq::ProcessSet, reduce: [])
+        allow(Sidekiq::ProcessSet).to receive(:new).and_return(process_set)
+      end
+
+      it 'returns true' do
+        expect(check.pass?).to be true
+      end
+    end
+
+    context 'when missing queues is not empty' do
+      before do
+        process_set = instance_double(Sidekiq::ProcessSet, reduce: [:something])
+        allow(Sidekiq::ProcessSet).to receive(:new).and_return(process_set)
+      end
+
+      it 'returns false' do
+        expect(check.pass?).to be false
+      end
+    end
+  end
+
+  describe 'message' do
+    it 'sends values to message instance' do
+      allow(Admin::SystemCheck::Message).to receive(:new).with(:sidekiq_process_check, 'default, push, mailers, pull, scheduler, ingress')
+
+      check.message
+
+      expect(Admin::SystemCheck::Message).to have_received(:new).with(:sidekiq_process_check, 'default, push, mailers, pull, scheduler, ingress')
+    end
+  end
+end
diff --git a/spec/lib/admin/system_check_spec.rb b/spec/lib/admin/system_check_spec.rb
new file mode 100644
index 000000000..30048fd3a
--- /dev/null
+++ b/spec/lib/admin/system_check_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::SystemCheck do
+  let(:user) { Fabricate(:user) }
+
+  describe 'perform' do
+    let(:result) { described_class.perform(user) }
+
+    it 'runs all the checks' do
+      expect(result).to be_an(Array)
+    end
+  end
+end
diff --git a/spec/lib/advanced_text_formatter_spec.rb b/spec/lib/advanced_text_formatter_spec.rb
index c1e469606..8b27b56a1 100644
--- a/spec/lib/advanced_text_formatter_spec.rb
+++ b/spec/lib/advanced_text_formatter_spec.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AdvancedTextFormatter do
   describe '#to_s' do
+    subject { described_class.new(text, preloaded_accounts: preloaded_accounts, content_type: content_type).to_s }
+
     let(:preloaded_accounts) { nil }
     let(:content_type) { 'text/markdown' }
 
-    subject { described_class.new(text, preloaded_accounts: preloaded_accounts, content_type: content_type).to_s }
-
     context 'given a markdown source' do
       let(:content_type) { 'text/markdown' }
 
@@ -14,7 +16,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'text' }
 
         it 'paragraphizes the text' do
-          is_expected.to eq '<p>text</p>'
+          expect(subject).to eq '<p>text</p>'
         end
       end
 
@@ -22,7 +24,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { "line\nfeed" }
 
         it 'removes line feeds' do
-          is_expected.not_to include "\n"
+          expect(subject).to_not include "\n"
         end
       end
 
@@ -30,7 +32,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'test `foo` bar' }
 
         it 'formats code using <code>' do
-          is_expected.to include 'test <code>foo</code> bar'
+          expect(subject).to include 'test <code>foo</code> bar'
         end
       end
 
@@ -38,15 +40,15 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { "test\n\n```\nint main(void) {\n  return 0; // https://joinmastodon.org/foo\n}\n```\n" }
 
         it 'formats code using <pre> and <code>' do
-          is_expected.to include '<pre><code>int main'
+          expect(subject).to include '<pre><code>int main'
         end
 
         it 'does not strip leading spaces' do
-          is_expected.to include '>  return 0'
+          expect(subject).to include '>  return 0'
         end
 
         it 'does not format links' do
-          is_expected.to include 'return 0; // https://joinmastodon.org/foo'
+          expect(subject).to include 'return 0; // https://joinmastodon.org/foo'
         end
       end
 
@@ -54,7 +56,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'test `https://foo.bar/bar` bar' }
 
         it 'does not rewrite the link' do
-          is_expected.to include 'test <code>https://foo.bar/bar</code> bar'
+          expect(subject).to include 'test <code>https://foo.bar/bar</code> bar'
         end
       end
 
@@ -62,7 +64,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'foo https://cb6e6126.ngrok.io/about/more' }
 
         it 'creates a link' do
-          is_expected.to include '<a href="https://cb6e6126.ngrok.io/about/more"'
+          expect(subject).to include '<a href="https://cb6e6126.ngrok.io/about/more"'
         end
       end
 
@@ -71,7 +73,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { '@alice' }
 
         it 'creates a mention link' do
-          is_expected.to include '<a href="https://cb6e6126.ngrok.io/@alice" class="u-url mention">@<span>alice</span></a></span>'
+          expect(subject).to include '<a href="https://cb6e6126.ngrok.io/@alice" class="u-url mention">@<span>alice</span></a></span>'
         end
       end
 
@@ -80,7 +82,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { '@alice' }
 
         it 'does not create a mention link' do
-          is_expected.to include '@alice'
+          expect(subject).to include '@alice'
         end
       end
 
@@ -88,7 +90,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4' }
 
         it 'matches the full URL' do
-          is_expected.to include 'href="https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4"'
+          expect(subject).to include 'href="https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4"'
         end
       end
 
@@ -96,7 +98,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'http://google.com' }
 
         it 'matches the full URL' do
-          is_expected.to include 'href="http://google.com"'
+          expect(subject).to include 'href="http://google.com"'
         end
       end
 
@@ -104,7 +106,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'http://example.gay' }
 
         it 'matches the full URL' do
-          is_expected.to include 'href="http://example.gay"'
+          expect(subject).to include 'href="http://example.gay"'
         end
       end
 
@@ -112,11 +114,11 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'https://nic.みんな/' }
 
         it 'matches the full URL' do
-          is_expected.to include 'href="https://nic.みんな/"'
+          expect(subject).to include 'href="https://nic.みんな/"'
         end
 
         it 'has display URL' do
-          is_expected.to include '<span class="">nic.みんな/</span>'
+          expect(subject).to include '<span class="">nic.みんな/</span>'
         end
       end
 
@@ -124,7 +126,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona. ' }
 
         it 'matches the full URL but not the period' do
-          is_expected.to include 'href="http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona"'
+          expect(subject).to include 'href="http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona"'
         end
       end
 
@@ -132,7 +134,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { '(http://google.com/)' }
 
         it 'matches the full URL but not the parentheses' do
-          is_expected.to include 'href="http://google.com/"'
+          expect(subject).to include 'href="http://google.com/"'
         end
       end
 
@@ -140,7 +142,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'http://www.google.com!' }
 
         it 'matches the full URL but not the exclamation point' do
-          is_expected.to include 'href="http://www.google.com"'
+          expect(subject).to include 'href="http://www.google.com"'
         end
       end
 
@@ -148,7 +150,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { "http://www.google.com'" }
 
         it 'matches the full URL but not the single quote' do
-          is_expected.to include 'href="http://www.google.com"'
+          expect(subject).to include 'href="http://www.google.com"'
         end
       end
     end
@@ -157,7 +159,7 @@ RSpec.describe AdvancedTextFormatter do
       let(:text) { 'http://www.google.com>' }
 
       it 'matches the full URL but not the angle bracket' do
-        is_expected.to include 'href="http://www.google.com"'
+        expect(subject).to include 'href="http://www.google.com"'
       end
     end
 
@@ -166,7 +168,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' }
 
         it 'matches the full URL' do
-          is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&amp;q=autolink"'
+          expect(subject).to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&amp;q=autolink"'
         end
       end
 
@@ -174,7 +176,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓&q=autolink' }
 
         it 'matches the full URL' do
-          is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓&amp;q=autolink"'
+          expect(subject).to include 'href="https://www.ruby-toolbox.com/search?utf8=✓&amp;q=autolink"'
         end
       end
 
@@ -182,7 +184,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓' }
 
         it 'matches the full URL' do
-          is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓"'
+          expect(subject).to include 'href="https://www.ruby-toolbox.com/search?utf8=✓"'
         end
       end
 
@@ -190,7 +192,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink' }
 
         it 'preserves escaped unicode characters' do
-          is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&amp;utf81=✓&amp;q=autolink"'
+          expect(subject).to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&amp;utf81=✓&amp;q=autolink"'
         end
       end
 
@@ -198,7 +200,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'https://en.wikipedia.org/wiki/Diaspora_(software)' }
 
         it 'matches the full URL' do
-          is_expected.to include 'href="https://en.wikipedia.org/wiki/Diaspora_(software)"'
+          expect(subject).to include 'href="https://en.wikipedia.org/wiki/Diaspora_(software)"'
         end
       end
 
@@ -206,7 +208,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { '"https://example.com/"' }
 
         it 'does not match the quotation marks' do
-          is_expected.to include 'href="https://example.com/"'
+          expect(subject).to include 'href="https://example.com/"'
         end
       end
 
@@ -214,19 +216,19 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { '<https://example.com/>' }
 
         it 'does not match the angle brackets' do
-          is_expected.to include 'href="https://example.com/"'
+          expect(subject).to include 'href="https://example.com/"'
         end
       end
 
       context 'given a URL containing unsafe code (XSS attack, invisible part)' do
-        let(:text) { %q{http://example.com/blahblahblahblah/a<script>alert("Hello")</script>} }
+        let(:text) { 'http://example.com/blahblahblahblah/a<script>alert("Hello")</script>' }
 
         it 'does not include the HTML in the URL' do
-          is_expected.to include '"http://example.com/blahblahblahblah/a"'
+          expect(subject).to include '"http://example.com/blahblahblahblah/a"'
         end
 
         it 'does not include a script tag' do
-          is_expected.to_not include '<script>'
+          expect(subject).to_not include '<script>'
         end
       end
 
@@ -234,7 +236,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { '<script>alert("Hello")</script>' }
 
         it 'does not include a script tag' do
-          is_expected.to_not include '<script>'
+          expect(subject).to_not include '<script>'
         end
       end
 
@@ -242,7 +244,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { %q{<img src="javascript:alert('XSS');">} }
 
         it 'does not include the javascript' do
-          is_expected.to_not include 'href="javascript:'
+          expect(subject).to_not include 'href="javascript:'
         end
       end
 
@@ -250,7 +252,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'http://www\.google\.com' }
 
         it 'outputs the raw URL' do
-          is_expected.to eq '<p>http://www\.google\.com</p>'
+          expect(subject).to eq '<p>http://www\.google\.com</p>'
         end
       end
 
@@ -258,7 +260,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text)  { '#hashtag' }
 
         it 'creates a hashtag link' do
-          is_expected.to include '/tags/hashtag" class="mention hashtag" rel="tag">#<span>hashtag</span></a>'
+          expect(subject).to include '/tags/hashtag" class="mention hashtag" rel="tag">#<span>hashtag</span></a>'
         end
       end
 
@@ -266,7 +268,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text)  { '#hashtagタグ' }
 
         it 'creates a hashtag link' do
-          is_expected.to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>'
+          expect(subject).to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>'
         end
       end
 
@@ -274,7 +276,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'xmpp:user@instance.com' }
 
         it 'matches the full URI' do
-          is_expected.to include 'href="xmpp:user@instance.com"'
+          expect(subject).to include 'href="xmpp:user@instance.com"'
         end
       end
 
@@ -282,7 +284,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'please join xmpp:muc@instance.com?join right now' }
 
         it 'matches the full URI' do
-          is_expected.to include 'href="xmpp:muc@instance.com?join"'
+          expect(subject).to include 'href="xmpp:muc@instance.com?join"'
         end
       end
 
@@ -290,7 +292,7 @@ RSpec.describe AdvancedTextFormatter do
         let(:text) { 'wikipedia gives this example of a magnet uri: magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a' }
 
         it 'matches the full URI' do
-          is_expected.to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"'
+          expect(subject).to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"'
         end
       end
     end
diff --git a/spec/lib/emoji_formatter_spec.rb b/spec/lib/emoji_formatter_spec.rb
index e1747bdd9..b73d5be4b 100644
--- a/spec/lib/emoji_formatter_spec.rb
+++ b/spec/lib/emoji_formatter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe EmojiFormatter do
@@ -24,7 +26,7 @@ RSpec.describe EmojiFormatter do
       let(:text) { preformat_text(':coolcat: Beep boop') }
 
       it 'converts the shortcode to an image tag' do
-        is_expected.to match(/<img rel="emoji" draggable="false" width="16" height="16" class="emojione custom-emoji" alt=":coolcat:"/)
+        expect(subject).to match(/<img rel="emoji" draggable="false" width="16" height="16" class="emojione custom-emoji" alt=":coolcat:"/)
       end
     end
 
@@ -32,7 +34,7 @@ RSpec.describe EmojiFormatter do
       let(:text) { preformat_text('Beep :coolcat: boop') }
 
       it 'converts the shortcode to an image tag' do
-        is_expected.to match(/Beep <img rel="emoji" draggable="false" width="16" height="16" class="emojione custom-emoji" alt=":coolcat:"/)
+        expect(subject).to match(/Beep <img rel="emoji" draggable="false" width="16" height="16" class="emojione custom-emoji" alt=":coolcat:"/)
       end
     end
 
@@ -40,7 +42,7 @@ RSpec.describe EmojiFormatter do
       let(:text) { preformat_text(':coolcat::coolcat:') }
 
       it 'does not touch the shortcodes' do
-        is_expected.to match(/:coolcat::coolcat:/)
+        expect(subject).to match(/:coolcat::coolcat:/)
       end
     end
 
@@ -48,7 +50,7 @@ RSpec.describe EmojiFormatter do
       let(:text) { preformat_text('Beep boop :coolcat:') }
 
       it 'converts the shortcode to an image tag' do
-        is_expected.to match(/boop <img rel="emoji" draggable="false" width="16" height="16" class="emojione custom-emoji" alt=":coolcat:"/)
+        expect(subject).to match(/boop <img rel="emoji" draggable="false" width="16" height="16" class="emojione custom-emoji" alt=":coolcat:"/)
       end
     end
   end
diff --git a/spec/lib/entity_cache_spec.rb b/spec/lib/entity_cache_spec.rb
index 43494bd92..c750cddf3 100644
--- a/spec/lib/entity_cache_spec.rb
+++ b/spec/lib/entity_cache_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe EntityCache do
@@ -12,7 +14,7 @@ RSpec.describe EntityCache do
       let(:domain)     { 'example.org' }
 
       it 'returns an empty array' do
-        is_expected.to eq []
+        expect(subject).to eq []
       end
     end
   end
diff --git a/spec/lib/extractor_spec.rb b/spec/lib/extractor_spec.rb
index dba4bd0bb..560617ed7 100644
--- a/spec/lib/extractor_spec.rb
+++ b/spec/lib/extractor_spec.rb
@@ -20,7 +20,7 @@ describe Extractor do
       text = '@screen_name'
       extracted = Extractor.extract_mentions_or_lists_with_indices(text)
       expect(extracted).to eq [
-        { screen_name: 'screen_name', indices: [ 0, 12 ] }
+        { screen_name: 'screen_name', indices: [0, 12] },
       ]
     end
 
@@ -44,19 +44,19 @@ describe Extractor do
     it 'does not exclude normal hash text before ://' do
       text = '#hashtag://'
       extracted = Extractor.extract_hashtags_with_indices(text)
-      expect(extracted).to eq [ { hashtag: 'hashtag', indices: [ 0, 8 ] } ]
+      expect(extracted).to eq [{ hashtag: 'hashtag', indices: [0, 8] }]
     end
 
     it 'excludes http://' do
       text = '#hashtaghttp://'
       extracted = Extractor.extract_hashtags_with_indices(text)
-      expect(extracted).to eq [ { hashtag: 'hashtag', indices: [ 0, 8 ] } ]
+      expect(extracted).to eq [{ hashtag: 'hashtag', indices: [0, 8] }]
     end
 
     it 'excludes https://' do
       text = '#hashtaghttps://'
       extracted = Extractor.extract_hashtags_with_indices(text)
-      expect(extracted).to eq [ { hashtag: 'hashtag', indices: [ 0, 8 ] } ]
+      expect(extracted).to eq [{ hashtag: 'hashtag', indices: [0, 8] }]
     end
 
     it 'yields hashtags if a block is given' do
diff --git a/spec/lib/fast_ip_map_spec.rb b/spec/lib/fast_ip_map_spec.rb
index c66f64828..78b3ddb05 100644
--- a/spec/lib/fast_ip_map_spec.rb
+++ b/spec/lib/fast_ip_map_spec.rb
@@ -4,7 +4,7 @@ require 'rails_helper'
 
 describe FastIpMap do
   describe '#include?' do
-    subject { described_class.new([IPAddr.new('20.4.0.0/16'), IPAddr.new('145.22.30.0/24'), IPAddr.new('189.45.86.3')])}
+    subject { described_class.new([IPAddr.new('20.4.0.0/16'), IPAddr.new('145.22.30.0/24'), IPAddr.new('189.45.86.3')]) }
 
     it 'returns true for an exact match' do
       expect(subject.include?(IPAddr.new('189.45.86.3'))).to be true
diff --git a/spec/lib/feed_manager_spec.rb b/spec/lib/feed_manager_spec.rb
index f2ab2570d..d1e0d60e0 100644
--- a/spec/lib/feed_manager_spec.rb
+++ b/spec/lib/feed_manager_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe FeedManager do
@@ -304,7 +306,7 @@ RSpec.describe FeedManager do
       status = Fabricate(:status, reblog: reblog)
       FeedManager.instance.push_to_home(account, status)
 
-      expect(FeedManager.instance.push_to_home(account, reblog)).to eq false
+      expect(FeedManager.instance.push_to_home(account, reblog)).to be false
     end
   end
 
@@ -329,7 +331,7 @@ RSpec.describe FeedManager do
       status = Fabricate(:status, reblog: reblog)
       FeedManager.instance.push_to_list(list, status)
 
-      expect(FeedManager.instance.push_to_list(list, reblog)).to eq false
+      expect(FeedManager.instance.push_to_list(list, reblog)).to be false
     end
 
     context 'when replies policy is set to no replies' do
@@ -339,19 +341,19 @@ RSpec.describe FeedManager do
 
       it 'pushes statuses that are not replies' do
         status = Fabricate(:status, text: 'Hello world', account: bob)
-        expect(FeedManager.instance.push_to_list(list, status)).to eq true
+        expect(FeedManager.instance.push_to_list(list, status)).to be true
       end
 
       it 'pushes statuses that are replies to list owner' do
         status = Fabricate(:status, text: 'Hello world', account: owner)
         reply  = Fabricate(:status, text: 'Nay', thread: status, account: bob)
-        expect(FeedManager.instance.push_to_list(list, reply)).to eq true
+        expect(FeedManager.instance.push_to_list(list, reply)).to be true
       end
 
       it 'does not push replies to another member of the list' do
         status = Fabricate(:status, text: 'Hello world', account: alice)
         reply  = Fabricate(:status, text: 'Nay', thread: status, account: bob)
-        expect(FeedManager.instance.push_to_list(list, reply)).to eq false
+        expect(FeedManager.instance.push_to_list(list, reply)).to be false
       end
     end
 
@@ -362,25 +364,25 @@ RSpec.describe FeedManager do
 
       it 'pushes statuses that are not replies' do
         status = Fabricate(:status, text: 'Hello world', account: bob)
-        expect(FeedManager.instance.push_to_list(list, status)).to eq true
+        expect(FeedManager.instance.push_to_list(list, status)).to be true
       end
 
       it 'pushes statuses that are replies to list owner' do
         status = Fabricate(:status, text: 'Hello world', account: owner)
         reply  = Fabricate(:status, text: 'Nay', thread: status, account: bob)
-        expect(FeedManager.instance.push_to_list(list, reply)).to eq true
+        expect(FeedManager.instance.push_to_list(list, reply)).to be true
       end
 
       it 'pushes replies to another member of the list' do
         status = Fabricate(:status, text: 'Hello world', account: alice)
         reply  = Fabricate(:status, text: 'Nay', thread: status, account: bob)
-        expect(FeedManager.instance.push_to_list(list, reply)).to eq true
+        expect(FeedManager.instance.push_to_list(list, reply)).to be true
       end
 
       it 'does not push replies to someone not a member of the list' do
         status = Fabricate(:status, text: 'Hello world', account: eve)
         reply  = Fabricate(:status, text: 'Nay', thread: status, account: bob)
-        expect(FeedManager.instance.push_to_list(list, reply)).to eq false
+        expect(FeedManager.instance.push_to_list(list, reply)).to be false
       end
     end
 
@@ -391,25 +393,25 @@ RSpec.describe FeedManager do
 
       it 'pushes statuses that are not replies' do
         status = Fabricate(:status, text: 'Hello world', account: bob)
-        expect(FeedManager.instance.push_to_list(list, status)).to eq true
+        expect(FeedManager.instance.push_to_list(list, status)).to be true
       end
 
       it 'pushes statuses that are replies to list owner' do
         status = Fabricate(:status, text: 'Hello world', account: owner)
         reply  = Fabricate(:status, text: 'Nay', thread: status, account: bob)
-        expect(FeedManager.instance.push_to_list(list, reply)).to eq true
+        expect(FeedManager.instance.push_to_list(list, reply)).to be true
       end
 
       it 'pushes replies to another member of the list' do
         status = Fabricate(:status, text: 'Hello world', account: alice)
         reply  = Fabricate(:status, text: 'Nay', thread: status, account: bob)
-        expect(FeedManager.instance.push_to_list(list, reply)).to eq true
+        expect(FeedManager.instance.push_to_list(list, reply)).to be true
       end
 
       it 'pushes replies to someone not a member of the list' do
         status = Fabricate(:status, text: 'Hello world', account: eve)
         reply  = Fabricate(:status, text: 'Nay', thread: status, account: bob)
-        expect(FeedManager.instance.push_to_list(list, reply)).to eq true
+        expect(FeedManager.instance.push_to_list(list, reply)).to be true
       end
     end
   end
@@ -423,7 +425,7 @@ RSpec.describe FeedManager do
 
       FeedManager.instance.merge_into_home(account, reblog.account)
 
-      expect(redis.zscore("feed:home:0", reblog.id)).to eq nil
+      expect(redis.zscore('feed:home:0', reblog.id)).to be_nil
     end
   end
 
diff --git a/spec/lib/html_aware_formatter_spec.rb b/spec/lib/html_aware_formatter_spec.rb
index 18d23abf5..315035957 100644
--- a/spec/lib/html_aware_formatter_spec.rb
+++ b/spec/lib/html_aware_formatter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe HtmlAwareFormatter do
@@ -9,7 +11,7 @@ RSpec.describe HtmlAwareFormatter do
       let(:text) { 'Foo bar' }
 
       it 'returns formatted text' do
-        is_expected.to eq '<p>Foo bar</p>'
+        expect(subject).to eq '<p>Foo bar</p>'
       end
     end
 
@@ -20,7 +22,7 @@ RSpec.describe HtmlAwareFormatter do
         let(:text) { 'Beep boop' }
 
         it 'keeps the plain text' do
-          is_expected.to include 'Beep boop'
+          expect(subject).to include 'Beep boop'
         end
       end
 
@@ -28,7 +30,7 @@ RSpec.describe HtmlAwareFormatter do
         let(:text) { '<script>alert("Hello")</script>' }
 
         it 'strips the scripts' do
-          is_expected.to_not include '<script>alert("Hello")</script>'
+          expect(subject).to_not include '<script>alert("Hello")</script>'
         end
       end
 
@@ -36,7 +38,7 @@ RSpec.describe HtmlAwareFormatter do
         let(:text) { '<span class="mention  status__content__spoiler-link">Show more</span>' }
 
         it 'strips the malicious classes' do
-          is_expected.to_not include 'status__content__spoiler-link'
+          expect(subject).to_not include 'status__content__spoiler-link'
         end
       end
     end
diff --git a/spec/lib/importer/accounts_index_importer_spec.rb b/spec/lib/importer/accounts_index_importer_spec.rb
new file mode 100644
index 000000000..73f9bce39
--- /dev/null
+++ b/spec/lib/importer/accounts_index_importer_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Importer::AccountsIndexImporter do
+  describe 'import!' do
+    let(:pool) { Concurrent::FixedThreadPool.new(5) }
+    let(:importer) { described_class.new(batch_size: 123, executor: pool) }
+
+    before { Fabricate(:account) }
+
+    it 'indexes relevant accounts' do
+      expect { importer.import! }.to update_index(AccountsIndex)
+    end
+  end
+end
diff --git a/spec/lib/importer/base_importer_spec.rb b/spec/lib/importer/base_importer_spec.rb
new file mode 100644
index 000000000..78e9a869b
--- /dev/null
+++ b/spec/lib/importer/base_importer_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Importer::BaseImporter do
+  describe 'import!' do
+    let(:pool) { Concurrent::FixedThreadPool.new(5) }
+    let(:importer) { described_class.new(batch_size: 123, executor: pool) }
+
+    it 'raises an error' do
+      expect { importer.import! }.to raise_error(NotImplementedError)
+    end
+  end
+end
diff --git a/spec/lib/importer/statuses_index_importer_spec.rb b/spec/lib/importer/statuses_index_importer_spec.rb
new file mode 100644
index 000000000..d5e1c9f2c
--- /dev/null
+++ b/spec/lib/importer/statuses_index_importer_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Importer::StatusesIndexImporter do
+  describe 'import!' do
+    let(:pool) { Concurrent::FixedThreadPool.new(5) }
+    let(:importer) { described_class.new(batch_size: 123, executor: pool) }
+
+    before { Fabricate(:status) }
+
+    it 'indexes relevant statuses' do
+      expect { importer.import! }.to update_index(StatusesIndex)
+    end
+  end
+end
diff --git a/spec/lib/importer/tags_index_importer_spec.rb b/spec/lib/importer/tags_index_importer_spec.rb
new file mode 100644
index 000000000..348990c01
--- /dev/null
+++ b/spec/lib/importer/tags_index_importer_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Importer::TagsIndexImporter do
+  describe 'import!' do
+    let(:pool) { Concurrent::FixedThreadPool.new(5) }
+    let(:importer) { described_class.new(batch_size: 123, executor: pool) }
+
+    before { Fabricate(:tag) }
+
+    it 'indexes relevant tags' do
+      expect { importer.import! }.to update_index(TagsIndex)
+    end
+  end
+end
diff --git a/spec/lib/link_details_extractor_spec.rb b/spec/lib/link_details_extractor_spec.rb
index 7ea867c61..a46dd743a 100644
--- a/spec/lib/link_details_extractor_spec.rb
+++ b/spec/lib/link_details_extractor_spec.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe LinkDetailsExtractor do
+  subject { described_class.new(original_url, html, html_charset) }
+
   let(:original_url) { '' }
   let(:html) { '' }
   let(:html_charset) { nil }
 
-  subject { described_class.new(original_url, html, html_charset) }
-
   describe '#canonical_url' do
     let(:original_url) { 'https://foo.com/article?bar=baz123' }
 
@@ -39,17 +41,17 @@ RSpec.describe LinkDetailsExtractor do
     let(:original_url) { 'https://example.com/page.html' }
 
     context 'and is wrapped in CDATA tags' do
-      let(:html) { <<-HTML }
-<!doctype html>
-<html>
-<head>
-  <script type="application/ld+json">
-  //<![CDATA[
-  {"@context":"http://schema.org","@type":"NewsArticle","mainEntityOfPage":"https://example.com/page.html","headline":"Foo","datePublished":"2022-01-31T19:53:00+00:00","url":"https://example.com/page.html","description":"Bar","author":{"@type":"Person","name":"Hoge"},"publisher":{"@type":"Organization","name":"Baz"}}
-  //]]>
-  </script>
-</head>
-</html>
+      let(:html) { <<~HTML }
+        <!doctype html>
+        <html>
+        <head>
+          <script type="application/ld+json">
+          //<![CDATA[
+          {"@context":"http://schema.org","@type":"NewsArticle","mainEntityOfPage":"https://example.com/page.html","headline":"Foo","datePublished":"2022-01-31T19:53:00+00:00","url":"https://example.com/page.html","description":"Bar","author":{"@type":"Person","name":"Hoge"},"publisher":{"@type":"Organization","name":"Baz"}}
+          //]]>
+          </script>
+        </head>
+        </html>
       HTML
 
       describe '#title' do
@@ -78,57 +80,57 @@ RSpec.describe LinkDetailsExtractor do
     end
 
     context 'but the first tag is invalid JSON' do
-      let(:html) { <<-HTML }
-<!doctype html>
-<html>
-<body>
-  <script type="application/ld+json">
-    {
-      "@context":"https://schema.org",
-      "@type":"ItemList",
-      "url":"https://example.com/page.html",
-      "name":"Foo",
-      "description":"Bar"
-    },
-    {
-      "@context": "https://schema.org",
-      "@type": "BreadcrumbList",
-      "itemListElement":[
-        {
-          "@type":"ListItem",
-          "position":1,
-          "item":{
-            "@id":"https://www.example.com",
-            "name":"Baz"
-          }
-        }
-      ]
-    }
-  </script>
-  <script type="application/ld+json">
-    {
-      "@context":"https://schema.org",
-      "@type":"NewsArticle",
-      "mainEntityOfPage": {
-        "@type":"WebPage",
-        "@id": "http://example.com/page.html"
-      },
-      "headline": "Foo",
-      "description": "Bar",
-      "datePublished": "2022-01-31T19:46:00+00:00",
-      "author": {
-        "@type": "Organization",
-        "name": "Hoge"
-      },
-      "publisher": {
-        "@type": "NewsMediaOrganization",
-        "name":"Baz",
-        "url":"https://example.com/"
-      }
-    }
-  </script>
-</body>
-</html>
+      let(:html) { <<~HTML }
+        <!doctype html>
+        <html>
+        <body>
+          <script type="application/ld+json">
+            {
+              "@context":"https://schema.org",
+              "@type":"ItemList",
+              "url":"https://example.com/page.html",
+              "name":"Foo",
+              "description":"Bar"
+            },
+            {
+              "@context": "https://schema.org",
+              "@type": "BreadcrumbList",
+              "itemListElement":[
+                {
+                  "@type":"ListItem",
+                  "position":1,
+                  "item":{
+                    "@id":"https://www.example.com",
+                    "name":"Baz"
+                  }
+                }
+              ]
+            }
+          </script>
+          <script type="application/ld+json">
+            {
+              "@context":"https://schema.org",
+              "@type":"NewsArticle",
+              "mainEntityOfPage": {
+                "@type":"WebPage",
+                "@id": "http://example.com/page.html"
+              },
+              "headline": "Foo",
+              "description": "Bar",
+              "datePublished": "2022-01-31T19:46:00+00:00",
+              "author": {
+                "@type": "Organization",
+                "name": "Hoge"
+              },
+              "publisher": {
+                "@type": "NewsMediaOrganization",
+                "name":"Baz",
+                "url":"https://example.com/"
+              }
+            }
+          </script>
+        </body>
+        </html>
       HTML
 
       describe '#title' do
diff --git a/spec/lib/mastodon/cli_spec.rb b/spec/lib/mastodon/cli_spec.rb
new file mode 100644
index 000000000..419f8b864
--- /dev/null
+++ b/spec/lib/mastodon/cli_spec.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'cli'
+
+describe Mastodon::CLI do
+  describe 'version' do
+    it 'returns the Mastodon version' do
+      expect { described_class.new.invoke(:version) }.to output(
+        a_string_including(Mastodon::Version.to_s)
+      ).to_stdout
+    end
+  end
+end
diff --git a/spec/lib/ostatus/tag_manager_spec.rb b/spec/lib/ostatus/tag_manager_spec.rb
index 31195bae2..8104a7e79 100644
--- a/spec/lib/ostatus/tag_manager_spec.rb
+++ b/spec/lib/ostatus/tag_manager_spec.rb
@@ -15,15 +15,15 @@ describe OStatus::TagManager do
     end
 
     it 'returns nil if it is not local id' do
-      expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:remote,2000-01-01:objectId=12:objectType=Status', 'Status')).to eq nil
+      expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:remote,2000-01-01:objectId=12:objectType=Status', 'Status')).to be_nil
     end
 
     it 'returns nil if it is not expected type' do
-      expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Block', 'Status')).to eq nil
+      expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectId=12:objectType=Block', 'Status')).to be_nil
     end
 
     it 'returns nil if it does not have object ID' do
-      expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectType=Status', 'Status')).to eq nil
+      expect(OStatus::TagManager.instance.unique_tag_to_local_id('tag:cb6e6126.ngrok.io,2000-01-01:objectType=Status', 'Status')).to be_nil
     end
   end
 
@@ -45,7 +45,7 @@ describe OStatus::TagManager do
 
       it 'returns the unique tag for status' do
         expect(target.object_type).to eq :comment
-        is_expected.to eq target.uri
+        expect(subject).to eq target.uri
       end
     end
 
@@ -54,7 +54,7 @@ describe OStatus::TagManager do
 
       it 'returns the unique tag for status' do
         expect(target.object_type).to eq :note
-        is_expected.to eq target.uri
+        expect(subject).to eq target.uri
       end
     end
 
@@ -63,7 +63,7 @@ describe OStatus::TagManager do
 
       it 'returns the URL for account' do
         expect(target.object_type).to eq :person
-        is_expected.to eq 'https://cb6e6126.ngrok.io/users/alice'
+        expect(subject).to eq 'https://cb6e6126.ngrok.io/users/alice'
       end
     end
   end
diff --git a/spec/lib/plain_text_formatter_spec.rb b/spec/lib/plain_text_formatter_spec.rb
index c3d0ee630..80b3c331a 100644
--- a/spec/lib/plain_text_formatter_spec.rb
+++ b/spec/lib/plain_text_formatter_spec.rb
@@ -1,23 +1,76 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe PlainTextFormatter do
   describe '#to_s' do
     subject { described_class.new(status.text, status.local?).to_s }
 
-    context 'given a post with local status' do
+    context 'when status is local' do
       let(:status) { Fabricate(:status, text: '<p>a text by a nerd who uses an HTML tag in text</p>', uri: nil) }
 
       it 'returns the raw text' do
-        is_expected.to eq '<p>a text by a nerd who uses an HTML tag in text</p>'
+        expect(subject).to eq '<p>a text by a nerd who uses an HTML tag in text</p>'
       end
     end
 
-    context 'given a post with remote status' do
+    context 'when status is remote' do
       let(:remote_account) { Fabricate(:account, domain: 'remote.test', username: 'bob', url: 'https://remote.test/') }
-      let(:status) { Fabricate(:status, account: remote_account, text: '<p>Hello</p><script>alert("Hello")</script>') }
 
-      it 'returns tag-stripped text' do
-        is_expected.to eq 'Hello'
+      context 'when text contains inline HTML tags' do
+        let(:status) { Fabricate(:status, account: remote_account, text: '<b>Lorem</b> <em>ipsum</em>') }
+
+        it 'strips the tags' do
+          expect(subject).to eq 'Lorem ipsum'
+        end
+      end
+
+      context 'when text contains <p> tags' do
+        let(:status) { Fabricate(:status, account: remote_account, text: '<p>Lorem</p><p>ipsum</p>') }
+
+        it 'inserts a newline' do
+          expect(subject).to eq "Lorem\nipsum"
+        end
+      end
+
+      context 'when text contains a single <br> tag' do
+        let(:status) { Fabricate(:status, account: remote_account, text: 'Lorem<br>ipsum') }
+
+        it 'inserts a newline' do
+          expect(subject).to eq "Lorem\nipsum"
+        end
+      end
+
+      context 'when text contains consecutive <br> tag' do
+        let(:status) { Fabricate(:status, account: remote_account, text: 'Lorem<br><br><br>ipsum') }
+
+        it 'inserts a single newline' do
+          expect(subject).to eq "Lorem\nipsum"
+        end
+      end
+
+      context 'when text contains HTML entity' do
+        let(:status) { Fabricate(:status, account: remote_account, text: 'Lorem &amp; ipsum &#x2764;') }
+
+        it 'unescapes the entity' do
+          expect(subject).to eq 'Lorem & ipsum ❤'
+        end
+      end
+
+      context 'when text contains <script> tag' do
+        let(:status) { Fabricate(:status, account: remote_account, text: 'Lorem <script> alert("Booh!") </script>ipsum') }
+
+        it 'strips the tag and its contents' do
+          expect(subject).to eq 'Lorem ipsum'
+        end
+      end
+
+      context 'when text contains an HTML comment tags' do
+        let(:status) { Fabricate(:status, account: remote_account, text: 'Lorem <!-- Booh! -->ipsum') }
+
+        it 'strips the comment' do
+          expect(subject).to eq 'Lorem ipsum'
+        end
       end
     end
   end
diff --git a/spec/lib/request_pool_spec.rb b/spec/lib/request_pool_spec.rb
index 4a144d7c7..63dc9c5dd 100644
--- a/spec/lib/request_pool_spec.rb
+++ b/spec/lib/request_pool_spec.rb
@@ -33,7 +33,7 @@ describe RequestPool do
 
       subject
 
-      threads = 20.times.map do |i|
+      threads = 20.times.map do |_i|
         Thread.new do
           20.times do
             subject.with('http://example.com') do |http_client|
diff --git a/spec/lib/request_spec.rb b/spec/lib/request_spec.rb
index 8539944e2..25fe9ed37 100644
--- a/spec/lib/request_spec.rb
+++ b/spec/lib/request_spec.rb
@@ -43,7 +43,7 @@ describe Request do
       before { stub_request(:get, 'http://example.com') }
 
       it 'executes a HTTP request' do
-        expect { |block| subject.perform &block }.to yield_control
+        expect { |block| subject.perform(&block) }.to yield_control
         expect(a_request(:get, 'http://example.com')).to have_been_made.once
       end
 
@@ -54,18 +54,18 @@ describe Request do
         allow(resolver).to receive(:timeouts=).and_return(nil)
         allow(Resolv::DNS).to receive(:open).and_yield(resolver)
 
-        expect { |block| subject.perform &block }.to yield_control
+        expect { |block| subject.perform(&block) }.to yield_control
         expect(a_request(:get, 'http://example.com')).to have_been_made.once
       end
 
       it 'sets headers' do
-        expect { |block| subject.perform &block }.to yield_control
+        expect { |block| subject.perform(&block) }.to yield_control
         expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made
       end
 
       it 'closes underlying connection' do
         expect_any_instance_of(HTTP::Client).to receive(:close)
-        expect { |block| subject.perform &block }.to yield_control
+        expect { |block| subject.perform(&block) }.to yield_control
       end
 
       it 'returns response which implements body_with_limit' do
@@ -97,12 +97,12 @@ describe Request do
   describe "response's body_with_limit method" do
     it 'rejects body more than 1 megabyte by default' do
       stub_request(:any, 'http://example.com').to_return(body: SecureRandom.random_bytes(2.megabytes))
-      expect { subject.perform { |response| response.body_with_limit } }.to raise_error Mastodon::LengthValidationError
+      expect { subject.perform(&:body_with_limit) }.to raise_error Mastodon::LengthValidationError
     end
 
     it 'accepts body less than 1 megabyte by default' do
       stub_request(:any, 'http://example.com').to_return(body: SecureRandom.random_bytes(2.kilobytes))
-      expect { subject.perform { |response| response.body_with_limit } }.not_to raise_error
+      expect { subject.perform(&:body_with_limit) }.to_not raise_error
     end
 
     it 'rejects body by given size' do
@@ -112,12 +112,12 @@ describe Request do
 
     it 'rejects too large chunked body' do
       stub_request(:any, 'http://example.com').to_return(body: SecureRandom.random_bytes(2.megabytes), headers: { 'Transfer-Encoding' => 'chunked' })
-      expect { subject.perform { |response| response.body_with_limit } }.to raise_error Mastodon::LengthValidationError
+      expect { subject.perform(&:body_with_limit) }.to raise_error Mastodon::LengthValidationError
     end
 
     it 'rejects too large monolithic body' do
       stub_request(:any, 'http://example.com').to_return(body: SecureRandom.random_bytes(2.megabytes), headers: { 'Content-Length' => 2.megabytes })
-      expect { subject.perform { |response| response.body_with_limit } }.to raise_error Mastodon::LengthValidationError
+      expect { subject.perform(&:body_with_limit) }.to raise_error Mastodon::LengthValidationError
     end
 
     it 'truncates large monolithic body' do
diff --git a/spec/lib/search_query_transformer_spec.rb b/spec/lib/search_query_transformer_spec.rb
new file mode 100644
index 000000000..109533469
--- /dev/null
+++ b/spec/lib/search_query_transformer_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe SearchQueryTransformer do
+  describe 'initialization' do
+    let(:parser) { SearchQueryParser.new.parse('query') }
+
+    it 'sets attributes' do
+      transformer = described_class.new.apply(parser)
+
+      expect(transformer.should_clauses.first).to be_a(SearchQueryTransformer::TermClause)
+      expect(transformer.must_clauses.first).to be_nil
+      expect(transformer.must_not_clauses.first).to be_nil
+      expect(transformer.filter_clauses.first).to be_nil
+    end
+  end
+end
diff --git a/spec/lib/settings/extend_spec.rb b/spec/lib/settings/extend_spec.rb
deleted file mode 100644
index 83ced4230..000000000
--- a/spec/lib/settings/extend_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-RSpec.describe Settings::Extend do
-  class User
-    include Settings::Extend
-  end
-
-  describe '#settings' do
-    it 'sets @settings as an instance of Settings::ScopedSettings' do
-      user = Fabricate(:user)
-      expect(user.settings).to be_kind_of Settings::ScopedSettings
-    end
-  end
-end
diff --git a/spec/lib/settings/scoped_settings_spec.rb b/spec/lib/settings/scoped_settings_spec.rb
deleted file mode 100644
index 7566685b4..000000000
--- a/spec/lib/settings/scoped_settings_spec.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-RSpec.describe Settings::ScopedSettings do
-  let(:object)         { Fabricate(:user) }
-  let(:scoped_setting) { described_class.new(object) }
-  let(:val)            { 'whatever' }
-  let(:methods)        { %i(auto_play_gif default_sensitive unfollow_modal boost_modal delete_modal reduce_motion system_font_ui noindex theme) }
-
-  describe '.initialize' do
-    it 'sets @object' do
-      scoped_setting = described_class.new(object)
-      expect(scoped_setting.instance_variable_get(:@object)).to be object
-    end
-  end
-
-  describe '#method_missing' do
-    it 'sets scoped_setting.method_name = val' do
-      methods.each do |key|
-        scoped_setting.send("#{key}=", val)
-        expect(scoped_setting.send(key)).to eq val
-      end
-    end
-  end
-
-  describe '#[]= and #[]' do
-    it 'sets [key] = val' do
-      methods.each do |key|
-        scoped_setting[key] = val
-        expect(scoped_setting[key]).to eq val
-      end
-    end
-  end
-end
diff --git a/spec/lib/status_filter_spec.rb b/spec/lib/status_filter_spec.rb
index a851014d9..08519bc59 100644
--- a/spec/lib/status_filter_spec.rb
+++ b/spec/lib/status_filter_spec.rb
@@ -10,7 +10,7 @@ describe StatusFilter do
       subject { described_class.new(status, nil) }
 
       context 'when there are no connections' do
-        it { is_expected.not_to be_filtered }
+        it { is_expected.to_not be_filtered }
       end
 
       context 'when status account is silenced' do
@@ -31,11 +31,12 @@ describe StatusFilter do
     end
 
     context 'with real account' do
-      let(:account) { Fabricate(:account) }
       subject { described_class.new(status, account) }
 
+      let(:account) { Fabricate(:account) }
+
       context 'when there are no connections' do
-        it { is_expected.not_to be_filtered }
+        it { is_expected.to_not be_filtered }
       end
 
       context 'when status account is blocked' do
diff --git a/spec/lib/status_reach_finder_spec.rb b/spec/lib/status_reach_finder_spec.rb
index f0c22b165..785ce28a0 100644
--- a/spec/lib/status_reach_finder_spec.rb
+++ b/spec/lib/status_reach_finder_spec.rb
@@ -5,13 +5,13 @@ require 'rails_helper'
 describe StatusReachFinder do
   describe '#inboxes' do
     context 'for a local status' do
+      subject { described_class.new(status) }
+
       let(:parent_status) { nil }
       let(:visibility) { :public }
       let(:alice) { Fabricate(:account, username: 'alice') }
       let(:status) { Fabricate(:status, account: alice, thread: parent_status, visibility: visibility) }
 
-      subject { described_class.new(status) }
-
       context 'when it contains mentions of remote accounts' do
         let(:bob) { Fabricate(:account, username: 'bob', domain: 'foo.bar', protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') }
 
diff --git a/spec/lib/suspicious_sign_in_detector_spec.rb b/spec/lib/suspicious_sign_in_detector_spec.rb
index 101a18aa0..c61b1ef1e 100644
--- a/spec/lib/suspicious_sign_in_detector_spec.rb
+++ b/spec/lib/suspicious_sign_in_detector_spec.rb
@@ -1,13 +1,15 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe SuspiciousSignInDetector do
   describe '#suspicious?' do
+    subject { described_class.new(user).suspicious?(request) }
+
     let(:user) { Fabricate(:user, current_sign_in_at: 1.day.ago) }
     let(:request) { double(remote_ip: remote_ip) }
     let(:remote_ip) { nil }
 
-    subject { described_class.new(user).suspicious?(request) }
-
     context 'when user has 2FA enabled' do
       before do
         user.update!(otp_required_for_login: true)
diff --git a/spec/lib/tag_manager_spec.rb b/spec/lib/tag_manager_spec.rb
index cd9fb936c..8de290541 100644
--- a/spec/lib/tag_manager_spec.rb
+++ b/spec/lib/tag_manager_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe TagManager do
@@ -14,15 +16,15 @@ RSpec.describe TagManager do
     end
 
     it 'returns true for nil' do
-      expect(TagManager.instance.local_domain?(nil)).to eq true
+      expect(TagManager.instance.local_domain?(nil)).to be true
     end
 
     it 'returns true if the slash-stripped string equals to local domain' do
-      expect(TagManager.instance.local_domain?('DoMaIn.Example.com/')).to eq true
+      expect(TagManager.instance.local_domain?('DoMaIn.Example.com/')).to be true
     end
 
     it 'returns false for irrelevant string' do
-      expect(TagManager.instance.local_domain?('DoMaIn.Example.com!')).to eq false
+      expect(TagManager.instance.local_domain?('DoMaIn.Example.com!')).to be false
     end
   end
 
@@ -39,21 +41,21 @@ RSpec.describe TagManager do
     end
 
     it 'returns true for nil' do
-      expect(TagManager.instance.web_domain?(nil)).to eq true
+      expect(TagManager.instance.web_domain?(nil)).to be true
     end
 
     it 'returns true if the slash-stripped string equals to web domain' do
-      expect(TagManager.instance.web_domain?('DoMaIn.Example.com/')).to eq true
+      expect(TagManager.instance.web_domain?('DoMaIn.Example.com/')).to be true
     end
 
     it 'returns false for string with irrelevant characters' do
-      expect(TagManager.instance.web_domain?('DoMaIn.Example.com!')).to eq false
+      expect(TagManager.instance.web_domain?('DoMaIn.Example.com!')).to be false
     end
   end
 
   describe '#normalize_domain' do
     it 'returns nil if the given parameter is nil' do
-      expect(TagManager.instance.normalize_domain(nil)).to eq nil
+      expect(TagManager.instance.normalize_domain(nil)).to be_nil
     end
 
     it 'returns normalized domain' do
@@ -70,17 +72,17 @@ RSpec.describe TagManager do
 
     it 'returns true if the normalized string with port is local URL' do
       Rails.configuration.x.web_domain = 'domain.example.com:42'
-      expect(TagManager.instance.local_url?('https://DoMaIn.Example.com:42/')).to eq true
+      expect(TagManager.instance.local_url?('https://DoMaIn.Example.com:42/')).to be true
     end
 
     it 'returns true if the normalized string without port is local URL' do
       Rails.configuration.x.web_domain = 'domain.example.com'
-      expect(TagManager.instance.local_url?('https://DoMaIn.Example.com/')).to eq true
+      expect(TagManager.instance.local_url?('https://DoMaIn.Example.com/')).to be true
     end
 
     it 'returns false for string with irrelevant characters' do
       Rails.configuration.x.web_domain = 'domain.example.com'
-      expect(TagManager.instance.local_url?('https://domain.example.net/')).to eq false
+      expect(TagManager.instance.local_url?('https://domain.example.net/')).to be false
     end
   end
 end
diff --git a/spec/lib/text_formatter_spec.rb b/spec/lib/text_formatter_spec.rb
index 52a9d2498..3417b450c 100644
--- a/spec/lib/text_formatter_spec.rb
+++ b/spec/lib/text_formatter_spec.rb
@@ -1,16 +1,18 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe TextFormatter do
   describe '#to_s' do
-    let(:preloaded_accounts) { nil }
-
     subject { described_class.new(text, preloaded_accounts: preloaded_accounts).to_s }
 
+    let(:preloaded_accounts) { nil }
+
     context 'given text containing plain text' do
       let(:text) { 'text' }
 
       it 'paragraphizes the text' do
-        is_expected.to eq '<p>text</p>'
+        expect(subject).to eq '<p>text</p>'
       end
     end
 
@@ -18,7 +20,7 @@ RSpec.describe TextFormatter do
       let(:text) { "line\nfeed" }
 
       it 'removes line feeds' do
-        is_expected.not_to include "\n"
+        expect(subject).to_not include "\n"
       end
     end
 
@@ -27,7 +29,7 @@ RSpec.describe TextFormatter do
       let(:text) { '@alice' }
 
       it 'creates a mention link' do
-        is_expected.to include '<a href="https://cb6e6126.ngrok.io/@alice" class="u-url mention">@<span>alice</span></a></span>'
+        expect(subject).to include '<a href="https://cb6e6126.ngrok.io/@alice" class="u-url mention">@<span>alice</span></a></span>'
       end
     end
 
@@ -36,7 +38,7 @@ RSpec.describe TextFormatter do
       let(:text) { '@alice' }
 
       it 'does not create a mention link' do
-        is_expected.to include '@alice'
+        expect(subject).to include '@alice'
       end
     end
 
@@ -44,7 +46,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4' }
 
       it 'matches the full URL' do
-        is_expected.to include 'href="https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4"'
+        expect(subject).to include 'href="https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4"'
       end
     end
 
@@ -52,7 +54,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'http://google.com' }
 
       it 'matches the full URL' do
-        is_expected.to include 'href="http://google.com"'
+        expect(subject).to include 'href="http://google.com"'
       end
     end
 
@@ -60,7 +62,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'http://example.gay' }
 
       it 'matches the full URL' do
-        is_expected.to include 'href="http://example.gay"'
+        expect(subject).to include 'href="http://example.gay"'
       end
     end
 
@@ -68,11 +70,11 @@ RSpec.describe TextFormatter do
       let(:text) { 'https://nic.みんな/' }
 
       it 'matches the full URL' do
-        is_expected.to include 'href="https://nic.みんな/"'
+        expect(subject).to include 'href="https://nic.みんな/"'
       end
 
       it 'has display URL' do
-        is_expected.to include '<span class="">nic.みんな/</span>'
+        expect(subject).to include '<span class="">nic.みんな/</span>'
       end
     end
 
@@ -80,7 +82,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona. ' }
 
       it 'matches the full URL but not the period' do
-        is_expected.to include 'href="http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona"'
+        expect(subject).to include 'href="http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona"'
       end
     end
 
@@ -88,7 +90,7 @@ RSpec.describe TextFormatter do
       let(:text) { '(http://google.com/)' }
 
       it 'matches the full URL but not the parentheses' do
-        is_expected.to include 'href="http://google.com/"'
+        expect(subject).to include 'href="http://google.com/"'
       end
     end
 
@@ -96,7 +98,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'http://www.google.com!' }
 
       it 'matches the full URL but not the exclamation point' do
-        is_expected.to include 'href="http://www.google.com"'
+        expect(subject).to include 'href="http://www.google.com"'
       end
     end
 
@@ -104,7 +106,7 @@ RSpec.describe TextFormatter do
       let(:text) { "http://www.google.com'" }
 
       it 'matches the full URL but not the single quote' do
-        is_expected.to include 'href="http://www.google.com"'
+        expect(subject).to include 'href="http://www.google.com"'
       end
     end
 
@@ -112,7 +114,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'http://www.google.com>' }
 
       it 'matches the full URL but not the angle bracket' do
-        is_expected.to include 'href="http://www.google.com"'
+        expect(subject).to include 'href="http://www.google.com"'
       end
     end
 
@@ -121,7 +123,7 @@ RSpec.describe TextFormatter do
         let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' }
 
         it 'matches the full URL' do
-          is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&amp;q=autolink"'
+          expect(subject).to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&amp;q=autolink"'
         end
       end
 
@@ -129,7 +131,7 @@ RSpec.describe TextFormatter do
         let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓&q=autolink' }
 
         it 'matches the full URL' do
-          is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓&amp;q=autolink"'
+          expect(subject).to include 'href="https://www.ruby-toolbox.com/search?utf8=✓&amp;q=autolink"'
         end
       end
 
@@ -137,7 +139,7 @@ RSpec.describe TextFormatter do
         let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓' }
 
         it 'matches the full URL' do
-          is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓"'
+          expect(subject).to include 'href="https://www.ruby-toolbox.com/search?utf8=✓"'
         end
       end
 
@@ -145,7 +147,7 @@ RSpec.describe TextFormatter do
         let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink' }
 
         it 'preserves escaped unicode characters' do
-          is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&amp;utf81=✓&amp;q=autolink"'
+          expect(subject).to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&amp;utf81=✓&amp;q=autolink"'
         end
       end
     end
@@ -154,7 +156,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'https://en.wikipedia.org/wiki/Diaspora_(software)' }
 
       it 'matches the full URL' do
-        is_expected.to include 'href="https://en.wikipedia.org/wiki/Diaspora_(software)"'
+        expect(subject).to include 'href="https://en.wikipedia.org/wiki/Diaspora_(software)"'
       end
     end
 
@@ -162,7 +164,7 @@ RSpec.describe TextFormatter do
       let(:text) { '"https://example.com/"' }
 
       it 'does not match the quotation marks' do
-        is_expected.to include 'href="https://example.com/"'
+        expect(subject).to include 'href="https://example.com/"'
       end
     end
 
@@ -170,7 +172,7 @@ RSpec.describe TextFormatter do
       let(:text) { '<https://example.com/>' }
 
       it 'does not match the angle brackets' do
-        is_expected.to include 'href="https://example.com/"'
+        expect(subject).to include 'href="https://example.com/"'
       end
     end
 
@@ -178,7 +180,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'https://ja.wikipedia.org/wiki/日本' }
 
       it 'matches the full URL' do
-        is_expected.to include 'href="https://ja.wikipedia.org/wiki/日本"'
+        expect(subject).to include 'href="https://ja.wikipedia.org/wiki/日本"'
       end
     end
 
@@ -186,7 +188,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'https://ko.wikipedia.org/wiki/대한민국' }
 
       it 'matches the full URL' do
-        is_expected.to include 'href="https://ko.wikipedia.org/wiki/대한민국"'
+        expect(subject).to include 'href="https://ko.wikipedia.org/wiki/대한민국"'
       end
     end
 
@@ -194,7 +196,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'https://example.com/ abc123' }
 
       it 'does not match the full-width space' do
-        is_expected.to include 'href="https://example.com/"'
+        expect(subject).to include 'href="https://example.com/"'
       end
     end
 
@@ -202,7 +204,7 @@ RSpec.describe TextFormatter do
       let(:text) { '「[https://example.org/」' }
 
       it 'does not match the quotation marks' do
-        is_expected.to include 'href="https://example.org/"'
+        expect(subject).to include 'href="https://example.org/"'
       end
     end
 
@@ -210,7 +212,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'https://baike.baidu.com/item/中华人民共和国' }
 
       it 'matches the full URL' do
-        is_expected.to include 'href="https://baike.baidu.com/item/中华人民共和国"'
+        expect(subject).to include 'href="https://baike.baidu.com/item/中华人民共和国"'
       end
     end
 
@@ -218,31 +220,31 @@ RSpec.describe TextFormatter do
       let(:text) { 'https://zh.wikipedia.org/wiki/臺灣' }
 
       it 'matches the full URL' do
-        is_expected.to include 'href="https://zh.wikipedia.org/wiki/臺灣"'
+        expect(subject).to include 'href="https://zh.wikipedia.org/wiki/臺灣"'
       end
     end
 
     context 'given a URL containing unsafe code (XSS attack, visible part)' do
-      let(:text) { %q{http://example.com/b<del>b</del>} }
+      let(:text) { 'http://example.com/b<del>b</del>' }
 
       it 'does not include the HTML in the URL' do
-        is_expected.to include '"http://example.com/b"'
+        expect(subject).to include '"http://example.com/b"'
       end
 
       it 'escapes the HTML' do
-        is_expected.to include '&lt;del&gt;b&lt;/del&gt;'
+        expect(subject).to include '&lt;del&gt;b&lt;/del&gt;'
       end
     end
 
     context 'given a URL containing unsafe code (XSS attack, invisible part)' do
-      let(:text) { %q{http://example.com/blahblahblahblah/a<script>alert("Hello")</script>} }
+      let(:text) { 'http://example.com/blahblahblahblah/a<script>alert("Hello")</script>' }
 
       it 'does not include the HTML in the URL' do
-        is_expected.to include '"http://example.com/blahblahblahblah/a"'
+        expect(subject).to include '"http://example.com/blahblahblahblah/a"'
       end
 
       it 'escapes the HTML' do
-        is_expected.to include '&lt;script&gt;alert(&quot;Hello&quot;)&lt;/script&gt;'
+        expect(subject).to include '&lt;script&gt;alert(&quot;Hello&quot;)&lt;/script&gt;'
       end
     end
 
@@ -250,7 +252,7 @@ RSpec.describe TextFormatter do
       let(:text) { '<script>alert("Hello")</script>' }
 
       it 'escapes the HTML' do
-        is_expected.to include '<p>&lt;script&gt;alert(&quot;Hello&quot;)&lt;/script&gt;</p>'
+        expect(subject).to include '<p>&lt;script&gt;alert(&quot;Hello&quot;)&lt;/script&gt;</p>'
       end
     end
 
@@ -258,7 +260,7 @@ RSpec.describe TextFormatter do
       let(:text) { %q{<img src="javascript:alert('XSS');">} }
 
       it 'escapes the HTML' do
-        is_expected.to include '<p>&lt;img src=&quot;javascript:alert(&#39;XSS&#39;);&quot;&gt;</p>'
+        expect(subject).to include '<p>&lt;img src=&quot;javascript:alert(&#39;XSS&#39;);&quot;&gt;</p>'
       end
     end
 
@@ -266,7 +268,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'http://www\.google\.com' }
 
       it 'outputs the raw URL' do
-        is_expected.to eq '<p>http://www\.google\.com</p>'
+        expect(subject).to eq '<p>http://www\.google\.com</p>'
       end
     end
 
@@ -274,7 +276,7 @@ RSpec.describe TextFormatter do
       let(:text)  { '#hashtag' }
 
       it 'creates a hashtag link' do
-        is_expected.to include '/tags/hashtag" class="mention hashtag" rel="tag">#<span>hashtag</span></a>'
+        expect(subject).to include '/tags/hashtag" class="mention hashtag" rel="tag">#<span>hashtag</span></a>'
       end
     end
 
@@ -282,7 +284,7 @@ RSpec.describe TextFormatter do
       let(:text)  { '#hashtagタグ' }
 
       it 'creates a hashtag link' do
-        is_expected.to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>'
+        expect(subject).to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>'
       end
     end
 
@@ -290,7 +292,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'xmpp:user@instance.com' }
 
       it 'matches the full URI' do
-        is_expected.to include 'href="xmpp:user@instance.com"'
+        expect(subject).to include 'href="xmpp:user@instance.com"'
       end
     end
 
@@ -298,7 +300,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'please join xmpp:muc@instance.com?join right now' }
 
       it 'matches the full URI' do
-        is_expected.to include 'href="xmpp:muc@instance.com?join"'
+        expect(subject).to include 'href="xmpp:muc@instance.com?join"'
       end
     end
 
@@ -306,7 +308,7 @@ RSpec.describe TextFormatter do
       let(:text) { 'wikipedia gives this example of a magnet uri: magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a' }
 
       it 'matches the full URI' do
-        is_expected.to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"'
+        expect(subject).to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"'
       end
     end
   end
diff --git a/spec/lib/translation_service/deepl_spec.rb b/spec/lib/translation_service/deepl_spec.rb
new file mode 100644
index 000000000..2363f8f13
--- /dev/null
+++ b/spec/lib/translation_service/deepl_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe TranslationService::DeepL do
+  subject(:service) { described_class.new(plan, 'my-api-key') }
+
+  let(:plan) { 'advanced' }
+
+  before do
+    stub_request(:get, 'https://api.deepl.com/v2/languages?type=source').to_return(
+      body: '[{"language":"EN","name":"English"},{"language":"UK","name":"Ukrainian"}]'
+    )
+    stub_request(:get, 'https://api.deepl.com/v2/languages?type=target').to_return(
+      body: '[{"language":"EN-GB","name":"English (British)"},{"language":"ZH","name":"Chinese"}]'
+    )
+  end
+
+  describe '#translate' do
+    it 'returns translation with specified source language' do
+      stub_request(:post, 'https://api.deepl.com/v2/translate')
+        .with(body: 'text=Hasta+la+vista&source_lang=ES&target_lang=en&tag_handling=html')
+        .to_return(body: '{"translations":[{"detected_source_language":"ES","text":"See you soon"}]}')
+
+      translation = service.translate('Hasta la vista', 'es', 'en')
+      expect(translation.detected_source_language).to eq 'es'
+      expect(translation.provider).to eq 'DeepL.com'
+      expect(translation.text).to eq 'See you soon'
+    end
+
+    it 'returns translation with auto-detected source language' do
+      stub_request(:post, 'https://api.deepl.com/v2/translate')
+        .with(body: 'text=Guten+Tag&source_lang&target_lang=en&tag_handling=html')
+        .to_return(body: '{"translations":[{"detected_source_language":"DE","text":"Good Morning"}]}')
+
+      translation = service.translate('Guten Tag', nil, 'en')
+      expect(translation.detected_source_language).to eq 'de'
+      expect(translation.provider).to eq 'DeepL.com'
+      expect(translation.text).to eq 'Good Morning'
+    end
+  end
+
+  describe '#languages' do
+    it 'returns source languages' do
+      expect(service.languages.keys).to eq [nil, 'en', 'uk']
+    end
+
+    it 'returns target languages for each source language' do
+      expect(service.languages['en']).to eq %w(pt en-GB zh)
+      expect(service.languages['uk']).to eq %w(en pt en-GB zh)
+    end
+
+    it 'returns target languages for auto-detection' do
+      expect(service.languages[nil]).to eq %w(en pt en-GB zh)
+    end
+  end
+
+  describe '#request' do
+    before do
+      stub_request(:any, //)
+      # rubocop:disable Lint/EmptyBlock
+      service.send(:request, :get, '/v2/languages') { |res| }
+      # rubocop:enable Lint/EmptyBlock
+    end
+
+    it 'uses paid plan base URL' do
+      expect(a_request(:get, 'https://api.deepl.com/v2/languages')).to have_been_made.once
+    end
+
+    context 'with free plan' do
+      let(:plan) { 'free' }
+
+      it 'uses free plan base URL' do
+        expect(a_request(:get, 'https://api-free.deepl.com/v2/languages')).to have_been_made.once
+      end
+    end
+
+    it 'sends API key' do
+      expect(a_request(:get, 'https://api.deepl.com/v2/languages').with(headers: { Authorization: 'DeepL-Auth-Key my-api-key' })).to have_been_made.once
+    end
+  end
+end
diff --git a/spec/lib/translation_service/libre_translate_spec.rb b/spec/lib/translation_service/libre_translate_spec.rb
new file mode 100644
index 000000000..fbd726a7e
--- /dev/null
+++ b/spec/lib/translation_service/libre_translate_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe TranslationService::LibreTranslate do
+  subject(:service) { described_class.new('https://libretranslate.example.com', 'my-api-key') }
+
+  before do
+    stub_request(:get, 'https://libretranslate.example.com/languages').to_return(
+      body: '[{"code": "en","name": "English","targets": ["de","en","es"]},{"code": "da","name": "Danish","targets": ["en","pt"]}]'
+    )
+  end
+
+  describe '#languages' do
+    subject(:languages) { service.languages }
+
+    it 'returns source languages' do
+      expect(languages.keys).to eq ['en', 'da', nil]
+    end
+
+    it 'returns target languages for each source language' do
+      expect(languages['en']).to eq %w(de es)
+      expect(languages['da']).to eq %w(en pt)
+    end
+
+    it 'returns target languages for auto-detected language' do
+      expect(languages[nil]).to eq %w(de en es pt)
+    end
+  end
+
+  describe '#translate' do
+    it 'returns translation with specified source language' do
+      stub_request(:post, 'https://libretranslate.example.com/translate')
+        .with(body: '{"q":"Hasta la vista","source":"es","target":"en","format":"html","api_key":"my-api-key"}')
+        .to_return(body: '{"translatedText": "See you"}')
+
+      translation = service.translate('Hasta la vista', 'es', 'en')
+      expect(translation.detected_source_language).to eq 'es'
+      expect(translation.provider).to eq 'LibreTranslate'
+      expect(translation.text).to eq 'See you'
+    end
+
+    it 'returns translation with auto-detected source language' do
+      stub_request(:post, 'https://libretranslate.example.com/translate')
+        .with(body: '{"q":"Guten Morgen","source":"auto","target":"en","format":"html","api_key":"my-api-key"}')
+        .to_return(body: '{"detectedLanguage":{"confidence":92,"language":"de"},"translatedText":"Good morning"}')
+
+      translation = service.translate('Guten Morgen', nil, 'en')
+      expect(translation.detected_source_language).to be_nil
+      expect(translation.provider).to eq 'LibreTranslate'
+      expect(translation.text).to eq 'Good morning'
+    end
+  end
+end
diff --git a/spec/lib/user_settings_decorator_spec.rb b/spec/lib/user_settings_decorator_spec.rb
deleted file mode 100644
index 462c5b124..000000000
--- a/spec/lib/user_settings_decorator_spec.rb
+++ /dev/null
@@ -1,84 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-describe UserSettingsDecorator do
-  describe 'update' do
-    let(:user) { Fabricate(:user) }
-    let(:settings) { described_class.new(user) }
-
-    it 'updates the user settings value for email notifications' do
-      values = { 'notification_emails' => { 'follow' => '1' } }
-
-      settings.update(values)
-      expect(user.settings['notification_emails']['follow']).to eq true
-    end
-
-    it 'updates the user settings value for interactions' do
-      values = { 'interactions' => { 'must_be_follower' => '0' } }
-
-      settings.update(values)
-      expect(user.settings['interactions']['must_be_follower']).to eq false
-    end
-
-    it 'updates the user settings value for privacy' do
-      values = { 'setting_default_privacy' => 'public' }
-
-      settings.update(values)
-      expect(user.settings['default_privacy']).to eq 'public'
-    end
-
-    it 'updates the user settings value for sensitive' do
-      values = { 'setting_default_sensitive' => '1' }
-
-      settings.update(values)
-      expect(user.settings['default_sensitive']).to eq true
-    end
-
-    it 'updates the user settings value for unfollow modal' do
-      values = { 'setting_unfollow_modal' => '0' }
-
-      settings.update(values)
-      expect(user.settings['unfollow_modal']).to eq false
-    end
-
-    it 'updates the user settings value for boost modal' do
-      values = { 'setting_boost_modal' => '1' }
-
-      settings.update(values)
-      expect(user.settings['boost_modal']).to eq true
-    end
-
-    it 'updates the user settings value for delete toot modal' do
-      values = { 'setting_delete_modal' => '0' }
-
-      settings.update(values)
-      expect(user.settings['delete_modal']).to eq false
-    end
-
-    it 'updates the user settings value for gif auto play' do
-      values = { 'setting_auto_play_gif' => '0' }
-
-      settings.update(values)
-      expect(user.settings['auto_play_gif']).to eq false
-    end
-
-    it 'updates the user settings value for system font in UI' do
-      values = { 'setting_system_font_ui' => '0' }
-
-      settings.update(values)
-      expect(user.settings['system_font_ui']).to eq false
-    end
-
-    it 'decoerces setting values before applying' do
-      values = {
-        'setting_delete_modal' => 'false',
-        'setting_boost_modal' => 'true',
-      }
-
-      settings.update(values)
-      expect(user.settings['delete_modal']).to eq false
-      expect(user.settings['boost_modal']).to eq true
-    end
-  end
-end
diff --git a/spec/lib/vacuum/access_tokens_vacuum_spec.rb b/spec/lib/vacuum/access_tokens_vacuum_spec.rb
index 0244c3449..6b7234065 100644
--- a/spec/lib/vacuum/access_tokens_vacuum_spec.rb
+++ b/spec/lib/vacuum/access_tokens_vacuum_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Vacuum::AccessTokensVacuum do
diff --git a/spec/lib/vacuum/backups_vacuum_spec.rb b/spec/lib/vacuum/backups_vacuum_spec.rb
index 4e2de083f..867dbe402 100644
--- a/spec/lib/vacuum/backups_vacuum_spec.rb
+++ b/spec/lib/vacuum/backups_vacuum_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Vacuum::BackupsVacuum do
-  let(:retention_period) { 7.days }
-
   subject { described_class.new(retention_period) }
 
+  let(:retention_period) { 7.days }
+
   describe '#perform' do
     let!(:expired_backup) { Fabricate(:backup, created_at: (retention_period + 1.day).ago) }
     let!(:current_backup) { Fabricate(:backup) }
diff --git a/spec/lib/vacuum/feeds_vacuum_spec.rb b/spec/lib/vacuum/feeds_vacuum_spec.rb
index 0aec26740..ede1e3c36 100644
--- a/spec/lib/vacuum/feeds_vacuum_spec.rb
+++ b/spec/lib/vacuum/feeds_vacuum_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Vacuum::FeedsVacuum do
diff --git a/spec/lib/vacuum/media_attachments_vacuum_spec.rb b/spec/lib/vacuum/media_attachments_vacuum_spec.rb
index be8458d9b..3c17ecb00 100644
--- a/spec/lib/vacuum/media_attachments_vacuum_spec.rb
+++ b/spec/lib/vacuum/media_attachments_vacuum_spec.rb
@@ -1,10 +1,11 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Vacuum::MediaAttachmentsVacuum do
-  let(:retention_period) { 7.days }
-
   subject { described_class.new(retention_period) }
 
+  let(:retention_period) { 7.days }
   let(:remote_status) { Fabricate(:status, account: Fabricate(:account, domain: 'example.com')) }
   let(:local_status) { Fabricate(:status) }
 
diff --git a/spec/lib/vacuum/preview_cards_vacuum_spec.rb b/spec/lib/vacuum/preview_cards_vacuum_spec.rb
index 275f9ba92..c1b7f7e9c 100644
--- a/spec/lib/vacuum/preview_cards_vacuum_spec.rb
+++ b/spec/lib/vacuum/preview_cards_vacuum_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Vacuum::PreviewCardsVacuum do
-  let(:retention_period) { 7.days }
-
   subject { described_class.new(retention_period) }
 
+  let(:retention_period) { 7.days }
+
   describe '#perform' do
     let!(:orphaned_preview_card) { Fabricate(:preview_card, created_at: 2.days.ago) }
     let!(:old_preview_card) { Fabricate(:preview_card, updated_at: (retention_period + 1.day).ago) }
diff --git a/spec/lib/vacuum/statuses_vacuum_spec.rb b/spec/lib/vacuum/statuses_vacuum_spec.rb
index 83f3c5c9f..d5c013950 100644
--- a/spec/lib/vacuum/statuses_vacuum_spec.rb
+++ b/spec/lib/vacuum/statuses_vacuum_spec.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Vacuum::StatusesVacuum do
+  subject { described_class.new(retention_period) }
+
   let(:retention_period) { 7.days }
 
   let(:remote_account) { Fabricate(:account, domain: 'example.com') }
 
-  subject { described_class.new(retention_period) }
-
   describe '#perform' do
     let!(:remote_status_old) { Fabricate(:status, account: remote_account, created_at: (retention_period + 2.days).ago) }
     let!(:remote_status_recent) { Fabricate(:status, account: remote_account, created_at: (retention_period - 2.days).ago) }
diff --git a/spec/lib/vacuum/system_keys_vacuum_spec.rb b/spec/lib/vacuum/system_keys_vacuum_spec.rb
index 565892f02..84cae3041 100644
--- a/spec/lib/vacuum/system_keys_vacuum_spec.rb
+++ b/spec/lib/vacuum/system_keys_vacuum_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Vacuum::SystemKeysVacuum do
diff --git a/spec/lib/webfinger_resource_spec.rb b/spec/lib/webfinger_resource_spec.rb
index 5c7f475d6..8ec6dd205 100644
--- a/spec/lib/webfinger_resource_spec.rb
+++ b/spec/lib/webfinger_resource_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe WebfingerResource do
@@ -14,9 +16,9 @@ describe WebfingerResource do
       it 'raises with a route whose controller is not AccountsController' do
         resource = 'https://example.com/users/alice/other'
 
-        expect {
+        expect do
           WebfingerResource.new(resource).username
-        }.to raise_error(ActiveRecord::RecordNotFound)
+        end.to raise_error(ActiveRecord::RecordNotFound)
       end
 
       it 'raises with a route whose action is not show' do
@@ -29,17 +31,17 @@ describe WebfingerResource do
 
         expect(Rails.application.routes).to receive(:recognize_path).with(resource).and_return(recognized).at_least(:once)
 
-        expect {
+        expect do
           WebfingerResource.new(resource).username
-        }.to raise_error(ActiveRecord::RecordNotFound)
+        end.to raise_error(ActiveRecord::RecordNotFound)
       end
 
       it 'raises with a string that doesnt start with URL' do
         resource = 'website for http://example.com/users/alice/other'
 
-        expect {
+        expect do
           WebfingerResource.new(resource).username
-        }.to raise_error(WebfingerResource::InvalidRequest)
+        end.to raise_error(WebfingerResource::InvalidRequest)
       end
 
       it 'finds the username in a valid https route' do
@@ -68,9 +70,9 @@ describe WebfingerResource do
       it 'raises on a non-local domain' do
         resource = 'user@remote-host.com'
 
-        expect {
+        expect do
           WebfingerResource.new(resource).username
-        }.to raise_error(ActiveRecord::RecordNotFound)
+        end.to raise_error(ActiveRecord::RecordNotFound)
       end
 
       it 'finds username for a local domain' do
@@ -94,17 +96,17 @@ describe WebfingerResource do
       it 'raises on a non-local domain' do
         resource = 'acct:user@remote-host.com'
 
-        expect {
+        expect do
           WebfingerResource.new(resource).username
-        }.to raise_error(ActiveRecord::RecordNotFound)
+        end.to raise_error(ActiveRecord::RecordNotFound)
       end
 
       it 'raises on a nonsense domain' do
         resource = 'acct:user@remote-host@remote-hostess.remote.local@remote'
 
-        expect {
+        expect do
           WebfingerResource.new(resource).username
-        }.to raise_error(ActiveRecord::RecordNotFound)
+        end.to raise_error(ActiveRecord::RecordNotFound)
       end
 
       it 'finds the username for a local account if the domain is the local one' do
@@ -128,9 +130,9 @@ describe WebfingerResource do
       it 'raises InvalidRequest' do
         resource = 'df/:dfkj'
 
-        expect {
+        expect do
           WebfingerResource.new(resource).username
-        }.to raise_error(WebfingerResource::InvalidRequest)
+        end.to raise_error(WebfingerResource::InvalidRequest)
       end
     end
   end
diff --git a/spec/mailers/admin_mailer_spec.rb b/spec/mailers/admin_mailer_spec.rb
index 29fb586a3..132c6c758 100644
--- a/spec/mailers/admin_mailer_spec.rb
+++ b/spec/mailers/admin_mailer_spec.rb
@@ -23,4 +23,66 @@ RSpec.describe AdminMailer, type: :mailer do
       expect(mail.body.encoded).to eq("Mike,\r\n\r\nJohn has reported Mike\r\n\r\nView: https://cb6e6126.ngrok.io/admin/reports/#{report.id}\r\n")
     end
   end
+
+  describe '.new_appeal' do
+    let(:appeal) { Fabricate(:appeal) }
+    let(:recipient) { Fabricate(:account, username: 'Kurt') }
+    let(:mail)      { described_class.new_appeal(recipient, appeal) }
+
+    before do
+      recipient.user.update(locale: :en)
+    end
+
+    it 'renders the headers' do
+      expect(mail.subject).to eq("#{appeal.account.username} is appealing a moderation decision on cb6e6126.ngrok.io")
+      expect(mail.to).to eq [recipient.user_email]
+      expect(mail.from).to eq ['notifications@localhost']
+    end
+
+    it 'renders the body' do
+      expect(mail.body.encoded).to match "#{appeal.account.username} is appealing a moderation decision by #{appeal.strike.account.username}"
+    end
+  end
+
+  describe '.new_pending_account' do
+    let(:recipient) { Fabricate(:account, username: 'Barklums') }
+    let(:user) { Fabricate(:user) }
+    let(:mail) { described_class.new_pending_account(recipient, user) }
+
+    before do
+      recipient.user.update(locale: :en)
+    end
+
+    it 'renders the headers' do
+      expect(mail.subject).to eq("New account up for review on cb6e6126.ngrok.io (#{user.account.username})")
+      expect(mail.to).to eq [recipient.user_email]
+      expect(mail.from).to eq ['notifications@localhost']
+    end
+
+    it 'renders the body' do
+      expect(mail.body.encoded).to match 'The details of the new account are below. You can approve or reject this application.'
+    end
+  end
+
+  describe '.new_trends' do
+    let(:recipient) { Fabricate(:account, username: 'Snurf') }
+    let(:links) { [] }
+    let(:statuses) { [] }
+    let(:tags) { [] }
+    let(:mail) { described_class.new_trends(recipient, links, tags, statuses) }
+
+    before do
+      recipient.user.update(locale: :en)
+    end
+
+    it 'renders the headers' do
+      expect(mail.subject).to eq('New trends up for review on cb6e6126.ngrok.io')
+      expect(mail.to).to eq [recipient.user_email]
+      expect(mail.from).to eq ['notifications@localhost']
+    end
+
+    it 'renders the body' do
+      expect(mail.body.encoded).to match 'The following items need a review before they can be displayed publicly'
+    end
+  end
 end
diff --git a/spec/mailers/notification_mailer_spec.rb b/spec/mailers/notification_mailer_spec.rb
index 29bdc349b..a6db08d85 100644
--- a/spec/mailers/notification_mailer_spec.rb
+++ b/spec/mailers/notification_mailer_spec.rb
@@ -1,4 +1,6 @@
-require "rails_helper"
+# frozen_string_literal: true
+
+require 'rails_helper'
 
 RSpec.describe NotificationMailer, type: :mailer do
   let(:receiver)       { Fabricate(:user) }
@@ -19,69 +21,69 @@ RSpec.describe NotificationMailer, type: :mailer do
     end
   end
 
-  describe "mention" do
+  describe 'mention' do
     let(:mention) { Mention.create!(account: receiver.account, status: foreign_status) }
     let(:mail) { NotificationMailer.mention(receiver.account, Notification.create!(account: receiver.account, activity: mention)) }
 
     include_examples 'localized subject', 'notification_mailer.mention.subject', name: 'bob'
 
-    it "renders the headers" do
-      expect(mail.subject).to eq("You were mentioned by bob")
+    it 'renders the headers' do
+      expect(mail.subject).to eq('You were mentioned by bob')
       expect(mail.to).to eq([receiver.email])
     end
 
-    it "renders the body" do
-      expect(mail.body.encoded).to match("You were mentioned by bob")
+    it 'renders the body' do
+      expect(mail.body.encoded).to match('You were mentioned by bob')
       expect(mail.body.encoded).to include 'The body of the foreign status'
     end
   end
 
-  describe "follow" do
+  describe 'follow' do
     let(:follow) { sender.follow!(receiver.account) }
     let(:mail) { NotificationMailer.follow(receiver.account, Notification.create!(account: receiver.account, activity: follow)) }
 
     include_examples 'localized subject', 'notification_mailer.follow.subject', name: 'bob'
 
-    it "renders the headers" do
-      expect(mail.subject).to eq("bob is now following you")
+    it 'renders the headers' do
+      expect(mail.subject).to eq('bob is now following you')
       expect(mail.to).to eq([receiver.email])
     end
 
-    it "renders the body" do
-      expect(mail.body.encoded).to match("bob is now following you")
+    it 'renders the body' do
+      expect(mail.body.encoded).to match('bob is now following you')
     end
   end
 
-  describe "favourite" do
+  describe 'favourite' do
     let(:favourite) { Favourite.create!(account: sender, status: own_status) }
     let(:mail) { NotificationMailer.favourite(own_status.account, Notification.create!(account: receiver.account, activity: favourite)) }
 
     include_examples 'localized subject', 'notification_mailer.favourite.subject', name: 'bob'
 
-    it "renders the headers" do
-      expect(mail.subject).to eq("bob favourited your post")
+    it 'renders the headers' do
+      expect(mail.subject).to eq('bob favourited your post')
       expect(mail.to).to eq([receiver.email])
     end
 
-    it "renders the body" do
-      expect(mail.body.encoded).to match("Your post was favourited by bob")
+    it 'renders the body' do
+      expect(mail.body.encoded).to match('Your post was favourited by bob')
       expect(mail.body.encoded).to include 'The body of the own status'
     end
   end
 
-  describe "reblog" do
+  describe 'reblog' do
     let(:reblog) { Status.create!(account: sender, reblog: own_status) }
     let(:mail) { NotificationMailer.reblog(own_status.account, Notification.create!(account: receiver.account, activity: reblog)) }
 
     include_examples 'localized subject', 'notification_mailer.reblog.subject', name: 'bob'
 
-    it "renders the headers" do
-      expect(mail.subject).to eq("bob boosted your post")
+    it 'renders the headers' do
+      expect(mail.subject).to eq('bob boosted your post')
       expect(mail.to).to eq([receiver.email])
     end
 
-    it "renders the body" do
-      expect(mail.body.encoded).to match("Your post was boosted by bob")
+    it 'renders the body' do
+      expect(mail.body.encoded).to match('Your post was boosted by bob')
       expect(mail.body.encoded).to include 'The body of the own status'
     end
   end
@@ -98,7 +100,7 @@ RSpec.describe NotificationMailer, type: :mailer do
     end
 
     it 'renders the body' do
-      expect(mail.body.encoded).to match("bob has requested to follow you")
+      expect(mail.body.encoded).to match('bob has requested to follow you')
     end
   end
 end
diff --git a/spec/mailers/previews/admin_mailer_preview.rb b/spec/mailers/previews/admin_mailer_preview.rb
index 0ec9e9882..9572768cd 100644
--- a/spec/mailers/previews/admin_mailer_preview.rb
+++ b/spec/mailers/previews/admin_mailer_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # Preview all emails at http://localhost:3000/rails/mailers/admin_mailer
 
 class AdminMailerPreview < ActionMailer::Preview
diff --git a/spec/mailers/previews/notification_mailer_preview.rb b/spec/mailers/previews/notification_mailer_preview.rb
index e31445c36..bc41662a1 100644
--- a/spec/mailers/previews/notification_mailer_preview.rb
+++ b/spec/mailers/previews/notification_mailer_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # Preview all emails at http://localhost:3000/rails/mailers/notification_mailer
 
 class NotificationMailerPreview < ActionMailer::Preview
diff --git a/spec/mailers/previews/user_mailer_preview.rb b/spec/mailers/previews/user_mailer_preview.rb
index 95712e6cf..098c9cd90 100644
--- a/spec/mailers/previews/user_mailer_preview.rb
+++ b/spec/mailers/previews/user_mailer_preview.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 # Preview all emails at http://localhost:3000/rails/mailers/user_mailer
 
 class UserMailerPreview < ActionMailer::Preview
diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb
index 2ed33c1e4..30824e7b4 100644
--- a/spec/mailers/user_mailer_spec.rb
+++ b/spec/mailers/user_mailer_spec.rb
@@ -90,8 +90,56 @@ describe UserMailer, type: :mailer do
 
     it 'renders warning notification' do
       receiver.update!(locale: nil)
-      expect(mail.body.encoded).to include I18n.t("user_mailer.warning.title.suspend", acct: receiver.account.acct)
+      expect(mail.body.encoded).to include I18n.t('user_mailer.warning.title.suspend', acct: receiver.account.acct)
       expect(mail.body.encoded).to include strike.text
     end
   end
+
+  describe 'webauthn_credential_deleted' do
+    let(:credential) { Fabricate(:webauthn_credential, user_id: receiver.id) }
+    let(:mail) { UserMailer.webauthn_credential_deleted(receiver, credential) }
+
+    it 'renders webauthn credential deleted notification' do
+      receiver.update!(locale: nil)
+      expect(mail.body.encoded).to include I18n.t('devise.mailer.webauthn_credential.deleted.title')
+    end
+
+    include_examples 'localized subject',
+                     'devise.mailer.webauthn_credential.deleted.subject'
+  end
+
+  describe 'suspicious_sign_in' do
+    let(:ip) { '192.168.0.1' }
+    let(:agent) { 'NCSA_Mosaic/2.0 (Windows 3.1)' }
+    let(:timestamp) { Time.now.utc }
+    let(:mail) { UserMailer.suspicious_sign_in(receiver, ip, agent, timestamp) }
+
+    it 'renders suspicious sign in notification' do
+      receiver.update!(locale: nil)
+      expect(mail.body.encoded).to include I18n.t('user_mailer.suspicious_sign_in.explanation')
+    end
+
+    include_examples 'localized subject',
+                     'user_mailer.suspicious_sign_in.subject'
+  end
+
+  describe 'appeal_approved' do
+    let(:appeal) { Fabricate(:appeal, account: receiver.account, approved_at: Time.now.utc) }
+    let(:mail) { UserMailer.appeal_approved(receiver, appeal) }
+
+    it 'renders appeal_approved notification' do
+      expect(mail.subject).to eq I18n.t('user_mailer.appeal_approved.subject', date: I18n.l(appeal.created_at))
+      expect(mail.body.encoded).to include I18n.t('user_mailer.appeal_approved.title')
+    end
+  end
+
+  describe 'appeal_rejected' do
+    let(:appeal) { Fabricate(:appeal, account: receiver.account, rejected_at: Time.now.utc) }
+    let(:mail) { UserMailer.appeal_rejected(receiver, appeal) }
+
+    it 'renders appeal_rejected notification' do
+      expect(mail.subject).to eq I18n.t('user_mailer.appeal_rejected.subject', date: I18n.l(appeal.created_at))
+      expect(mail.body.encoded).to include I18n.t('user_mailer.appeal_rejected.title')
+    end
+  end
 end
diff --git a/spec/models/account/field_spec.rb b/spec/models/account/field_spec.rb
index 0ac9769bc..6745fbb26 100644
--- a/spec/models/account/field_spec.rb
+++ b/spec/models/account/field_spec.rb
@@ -1,11 +1,13 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Account::Field, type: :model do
   describe '#verified?' do
-    let(:account) { double('Account', local?: true) }
-
     subject { described_class.new(account, 'name' => 'Foo', 'value' => 'Bar', 'verified_at' => verified_at) }
 
+    let(:account) { double('Account', local?: true) }
+
     context 'when verified_at is set' do
       let(:verified_at) { Time.now.utc.iso8601 }
 
@@ -24,11 +26,11 @@ RSpec.describe Account::Field, type: :model do
   end
 
   describe '#mark_verified!' do
+    subject { described_class.new(account, original_hash) }
+
     let(:account) { double('Account', local?: true) }
     let(:original_hash) { { 'name' => 'Foo', 'value' => 'Bar' } }
 
-    subject { described_class.new(account, original_hash) }
-
     before do
       subject.mark_verified!
     end
@@ -43,10 +45,10 @@ RSpec.describe Account::Field, type: :model do
   end
 
   describe '#verifiable?' do
-    let(:account) { double('Account', local?: local) }
-
     subject { described_class.new(account, 'name' => 'Foo', 'value' => value) }
 
+    let(:account) { double('Account', local?: local) }
+
     context 'for local accounts' do
       let(:local) { true }
 
@@ -97,7 +99,7 @@ RSpec.describe Account::Field, type: :model do
           expect(subject.verifiable?).to be false
         end
       end
-      
+
       context 'for text which is blank' do
         let(:value) { '' }
 
@@ -149,7 +151,7 @@ RSpec.describe Account::Field, type: :model do
           expect(subject.verifiable?).to be false
         end
       end
-      
+
       context 'for text which is blank' do
         let(:value) { '' }
 
diff --git a/spec/models/account_alias_spec.rb b/spec/models/account_alias_spec.rb
index 27ec215aa..08c3eaff4 100644
--- a/spec/models/account_alias_spec.rb
+++ b/spec/models/account_alias_spec.rb
@@ -1,5 +1,6 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AccountAlias, type: :model do
-
 end
diff --git a/spec/models/account_conversation_spec.rb b/spec/models/account_conversation_spec.rb
index 70a76281e..c4e8918ad 100644
--- a/spec/models/account_conversation_spec.rb
+++ b/spec/models/account_conversation_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AccountConversation, type: :model do
diff --git a/spec/models/account_deletion_request_spec.rb b/spec/models/account_deletion_request_spec.rb
index afaecbe22..db332f14c 100644
--- a/spec/models/account_deletion_request_spec.rb
+++ b/spec/models/account_deletion_request_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AccountDeletionRequest, type: :model do
diff --git a/spec/models/account_domain_block_spec.rb b/spec/models/account_domain_block_spec.rb
index 469bc05cb..bc46f44ba 100644
--- a/spec/models/account_domain_block_spec.rb
+++ b/spec/models/account_domain_block_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AccountDomainBlock, type: :model do
@@ -7,7 +9,7 @@ RSpec.describe AccountDomainBlock, type: :model do
 
     AccountDomainBlock.create!(account: account, domain: 'a.domain.blocked.later')
 
-    expect(Rails.cache.exist?("exclude_domains_for:#{account.id}")).to eq false
+    expect(Rails.cache.exist?("exclude_domains_for:#{account.id}")).to be false
   end
 
   it 'removes blocking cache after destruction' do
@@ -17,6 +19,6 @@ RSpec.describe AccountDomainBlock, type: :model do
 
     block.destroy!
 
-    expect(Rails.cache.exist?("exclude_domains_for:#{account.id}")).to eq false
+    expect(Rails.cache.exist?("exclude_domains_for:#{account.id}")).to be false
   end
 end
diff --git a/spec/models/account_filter_spec.rb b/spec/models/account_filter_spec.rb
index c2bd8c220..3032260fe 100644
--- a/spec/models/account_filter_spec.rb
+++ b/spec/models/account_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe AccountFilter do
@@ -16,4 +18,30 @@ describe AccountFilter do
       expect { filter.results }.to raise_error(/wrong/)
     end
   end
+
+  describe 'with origin and by_domain interacting' do
+    let!(:local_account) { Fabricate(:account, domain: nil) }
+    let!(:remote_account_one) { Fabricate(:account, domain: 'example.org') }
+    let(:remote_account_two) { Fabricate(:account, domain: 'other.domain') }
+
+    it 'works with domain first and origin remote' do
+      filter = described_class.new(by_domain: 'example.org', origin: 'remote')
+      expect(filter.results).to match_array [remote_account_one]
+    end
+
+    it 'works with domain last and origin remote' do
+      filter = described_class.new(origin: 'remote', by_domain: 'example.org')
+      expect(filter.results).to match_array [remote_account_one]
+    end
+
+    it 'works with domain first and origin local' do
+      filter = described_class.new(by_domain: 'example.org', origin: 'local')
+      expect(filter.results).to match_array [local_account]
+    end
+
+    it 'works with domain last and origin local' do
+      filter = described_class.new(origin: 'local', by_domain: 'example.org')
+      expect(filter.results).to match_array [remote_account_one]
+    end
+  end
 end
diff --git a/spec/models/account_migration_spec.rb b/spec/models/account_migration_spec.rb
index 5f66fe8da..a91ba5dc5 100644
--- a/spec/models/account_migration_spec.rb
+++ b/spec/models/account_migration_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AccountMigration, type: :model do
diff --git a/spec/models/account_moderation_note_spec.rb b/spec/models/account_moderation_note_spec.rb
index 69bd5500a..b7f5701e6 100644
--- a/spec/models/account_moderation_note_spec.rb
+++ b/spec/models/account_moderation_note_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AccountModerationNote, type: :model do
diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb
index 6cd769dc8..ae4e5ee32 100644
--- a/spec/models/account_spec.rb
+++ b/spec/models/account_spec.rb
@@ -1,10 +1,13 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Account, type: :model do
   context do
-    let(:bob) { Fabricate(:account, username: 'bob') }
     subject { Fabricate(:account) }
 
+    let(:bob) { Fabricate(:account, username: 'bob') }
+
     describe '#suspend!' do
       it 'marks the account as suspended' do
         subject.suspend!
@@ -30,7 +33,7 @@ RSpec.describe Account, type: :model do
           end
 
           it 'does not raise an error' do
-            expect { subject.suspend! }.not_to raise_error
+            expect { subject.suspend! }.to_not raise_error
           end
         end
       end
@@ -86,14 +89,14 @@ RSpec.describe Account, type: :model do
   end
 
   describe 'Local domain user methods' do
+    subject { Fabricate(:account, domain: nil, username: 'alice') }
+
     around do |example|
       before = Rails.configuration.x.local_domain
       example.run
       Rails.configuration.x.local_domain = before
     end
 
-    subject { Fabricate(:account, domain: nil, username: 'alice') }
-
     describe '#to_webfinger_s' do
       it 'returns a webfinger string for the account' do
         Rails.configuration.x.local_domain = 'example.com'
@@ -159,7 +162,7 @@ RSpec.describe Account, type: :model do
       it 'sets default avatar, header, avatar_remote_url, and header_remote_url' do
         expect(account.avatar_remote_url).to eq 'https://remote.test/invalid_avatar'
         expect(account.header_remote_url).to eq expectation.header_remote_url
-        expect(account.avatar_file_name).to  eq nil
+        expect(account.avatar_file_name).to  be_nil
         expect(account.header_file_name).to  eq expectation.header_file_name
       end
     end
@@ -205,7 +208,7 @@ RSpec.describe Account, type: :model do
       end
 
       it 'calls not ResolveAccountService#call' do
-        expect_any_instance_of(ResolveAccountService).not_to receive(:call).with(acct)
+        expect_any_instance_of(ResolveAccountService).to_not receive(:call).with(acct)
         account.refresh!
       end
     end
@@ -242,13 +245,13 @@ RSpec.describe Account, type: :model do
   end
 
   describe '#favourited?' do
+    subject { Fabricate(:account) }
+
     let(:original_status) do
       author = Fabricate(:account, username: 'original')
       Fabricate(:status, account: author)
     end
 
-    subject { Fabricate(:account) }
-
     context 'when the status is a reblog of another status' do
       let(:original_reblog) do
         author = Fabricate(:account, username: 'original_reblogger')
@@ -258,11 +261,11 @@ RSpec.describe Account, type: :model do
       it 'is true when this account has favourited it' do
         Fabricate(:favourite, status: original_reblog, account: subject)
 
-        expect(subject.favourited?(original_status)).to eq true
+        expect(subject.favourited?(original_status)).to be true
       end
 
       it 'is false when this account has not favourited it' do
-        expect(subject.favourited?(original_status)).to eq false
+        expect(subject.favourited?(original_status)).to be false
       end
     end
 
@@ -270,23 +273,23 @@ RSpec.describe Account, type: :model do
       it 'is true when this account has favourited it' do
         Fabricate(:favourite, status: original_status, account: subject)
 
-        expect(subject.favourited?(original_status)).to eq true
+        expect(subject.favourited?(original_status)).to be true
       end
 
       it 'is false when this account has not favourited it' do
-        expect(subject.favourited?(original_status)).to eq false
+        expect(subject.favourited?(original_status)).to be false
       end
     end
   end
 
   describe '#reblogged?' do
+    subject { Fabricate(:account) }
+
     let(:original_status) do
       author = Fabricate(:account, username: 'original')
       Fabricate(:status, account: author)
     end
 
-    subject { Fabricate(:account) }
-
     context 'when the status is a reblog of another status' do
       let(:original_reblog) do
         author = Fabricate(:account, username: 'original_reblogger')
@@ -296,11 +299,11 @@ RSpec.describe Account, type: :model do
       it 'is true when this account has reblogged it' do
         Fabricate(:status, reblog: original_reblog, account: subject)
 
-        expect(subject.reblogged?(original_reblog)).to eq true
+        expect(subject.reblogged?(original_reblog)).to be true
       end
 
       it 'is false when this account has not reblogged it' do
-        expect(subject.reblogged?(original_reblog)).to eq false
+        expect(subject.reblogged?(original_reblog)).to be false
       end
     end
 
@@ -308,11 +311,11 @@ RSpec.describe Account, type: :model do
       it 'is true when this account has reblogged it' do
         Fabricate(:status, reblog: original_status, account: subject)
 
-        expect(subject.reblogged?(original_status)).to eq true
+        expect(subject.reblogged?(original_status)).to be true
       end
 
       it 'is false when this account has not reblogged it' do
-        expect(subject.reblogged?(original_status)).to eq false
+        expect(subject.reblogged?(original_status)).to be false
       end
     end
   end
@@ -344,9 +347,9 @@ RSpec.describe Account, type: :model do
     before do
       _missing = Fabricate(
         :account,
-        display_name: "Missing",
-        username: "missing",
-        domain: "missing.com"
+        display_name: 'Missing',
+        username: 'missing',
+        domain: 'missing.com'
       )
     end
 
@@ -404,58 +407,58 @@ RSpec.describe Account, type: :model do
     it 'finds accounts with matching display_name' do
       match = Fabricate(
         :account,
-        display_name: "Display Name",
-        username: "username",
-        domain: "example.com"
+        display_name: 'Display Name',
+        username: 'username',
+        domain: 'example.com'
       )
 
-      results = Account.search_for("display")
+      results = Account.search_for('display')
       expect(results).to eq [match]
     end
 
     it 'finds accounts with matching username' do
       match = Fabricate(
         :account,
-        display_name: "Display Name",
-        username: "username",
-        domain: "example.com"
+        display_name: 'Display Name',
+        username: 'username',
+        domain: 'example.com'
       )
 
-      results = Account.search_for("username")
+      results = Account.search_for('username')
       expect(results).to eq [match]
     end
 
     it 'finds accounts with matching domain' do
       match = Fabricate(
         :account,
-        display_name: "Display Name",
-        username: "username",
-        domain: "example.com"
+        display_name: 'Display Name',
+        username: 'username',
+        domain: 'example.com'
       )
 
-      results = Account.search_for("example")
+      results = Account.search_for('example')
       expect(results).to eq [match]
     end
 
     it 'limits by 10 by default' do
-      11.times.each { Fabricate(:account, display_name: "Display Name") }
-      results = Account.search_for("display")
+      11.times.each { Fabricate(:account, display_name: 'Display Name') }
+      results = Account.search_for('display')
       expect(results.size).to eq 10
     end
 
     it 'accepts arbitrary limits' do
-      2.times.each { Fabricate(:account, display_name: "Display Name") }
-      results = Account.search_for("display", limit: 1)
+      2.times.each { Fabricate(:account, display_name: 'Display Name') }
+      results = Account.search_for('display', limit: 1)
       expect(results.size).to eq 1
     end
 
     it 'ranks multiple matches higher' do
       matches = [
-        { username: "username", display_name: "username" },
-        { display_name: "Display Name", username: "username", domain: "example.com" },
+        { username: 'username', display_name: 'username' },
+        { display_name: 'Display Name', username: 'username', domain: 'example.com' },
       ].map(&method(:Fabricate).curry(2).call(:account))
 
-      results = Account.search_for("username")
+      results = Account.search_for('username')
       expect(results).to eq matches
     end
   end
@@ -581,23 +584,23 @@ RSpec.describe Account, type: :model do
     end
 
     it 'limits by 10 by default' do
-      11.times { Fabricate(:account, display_name: "Display Name") }
-      results = Account.advanced_search_for("display", account)
+      11.times { Fabricate(:account, display_name: 'Display Name') }
+      results = Account.advanced_search_for('display', account)
       expect(results.size).to eq 10
     end
 
     it 'accepts arbitrary limits' do
-      2.times { Fabricate(:account, display_name: "Display Name") }
-      results = Account.advanced_search_for("display", account, limit: 1)
+      2.times { Fabricate(:account, display_name: 'Display Name') }
+      results = Account.advanced_search_for('display', account, limit: 1)
       expect(results.size).to eq 1
     end
 
     it 'ranks followed accounts higher' do
-      match = Fabricate(:account, username: "Matching")
-      followed_match = Fabricate(:account, username: "Matcher")
+      match = Fabricate(:account, username: 'Matching')
+      followed_match = Fabricate(:account, username: 'Matcher')
       Fabricate(:follow, account: account, target_account: followed_match)
 
-      results = Account.advanced_search_for("match", account)
+      results = Account.advanced_search_for('match', account)
       expect(results).to eq [followed_match, match]
       expect(results.first.rank).to be > results.last.rank
     end
@@ -701,12 +704,6 @@ RSpec.describe Account, type: :model do
   end
 
   describe 'validations' do
-    it 'has a valid fabricator' do
-      account = Fabricate.build(:account)
-      account.valid?
-      expect(account).to be_valid
-    end
-
     it 'is invalid without a username' do
       account = Fabricate.build(:account, username: nil)
       account.valid?
@@ -810,19 +807,19 @@ RSpec.describe Account, type: :model do
       it 'is valid even if the username is longer than 30 characters' do
         account = Fabricate.build(:account, domain: 'domain', username: Faker::Lorem.characters(number: 31))
         account.valid?
-        expect(account).not_to model_have_error_on_field(:username)
+        expect(account).to_not model_have_error_on_field(:username)
       end
 
       it 'is valid even if the display name is longer than 30 characters' do
         account = Fabricate.build(:account, domain: 'domain', display_name: Faker::Lorem.characters(number: 31))
         account.valid?
-        expect(account).not_to model_have_error_on_field(:display_name)
+        expect(account).to_not model_have_error_on_field(:display_name)
       end
 
       it 'is valid even if the note is longer than 500 characters' do
         account = Fabricate.build(:account, domain: 'domain', note: Faker::Lorem.characters(number: 501))
         account.valid?
-        expect(account).not_to model_have_error_on_field(:note)
+        expect(account).to_not model_have_error_on_field(:note)
       end
     end
   end
@@ -894,7 +891,7 @@ RSpec.describe Account, type: :model do
 
     describe 'partitioned' do
       it 'returns a relation of accounts partitioned by domain' do
-        matches = ['a', 'b', 'a', 'b']
+        matches = %w(a b a b)
         matches.size.times.to_a.shuffle.each do |index|
           matches[index] = Fabricate(:account, domain: matches[index])
         end
@@ -957,7 +954,7 @@ RSpec.describe Account, type: :model do
     # Test disabled because test environment omits autogenerating keys for performance
     xit 'generates keys' do
       account = Account.create!(domain: nil, username: Faker::Internet.user_name(separators: ['_']))
-      expect(account.keypair.private?).to eq true
+      expect(account.keypair.private?).to be true
     end
   end
 
diff --git a/spec/models/account_statuses_cleanup_policy_spec.rb b/spec/models/account_statuses_cleanup_policy_spec.rb
index b01321a20..1b7857547 100644
--- a/spec/models/account_statuses_cleanup_policy_spec.rb
+++ b/spec/models/account_statuses_cleanup_policy_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AccountStatusesCleanupPolicy, type: :model do
@@ -16,16 +18,15 @@ RSpec.describe AccountStatusesCleanupPolicy, type: :model do
     context 'when widening a policy' do
       let!(:account_statuses_cleanup_policy) do
         Fabricate(:account_statuses_cleanup_policy,
-          account: account,
-          keep_direct: true,
-          keep_pinned: true,
-          keep_polls: true,
-          keep_media: true,
-          keep_self_fav: true,
-          keep_self_bookmark: true,
-          min_favs: 1,
-          min_reblogs: 1
-        )
+                  account: account,
+                  keep_direct: true,
+                  keep_pinned: true,
+                  keep_polls: true,
+                  keep_media: true,
+                  keep_self_fav: true,
+                  keep_self_bookmark: true,
+                  min_favs: 1,
+                  min_reblogs: 1)
       end
 
       before do
@@ -35,77 +36,76 @@ RSpec.describe AccountStatusesCleanupPolicy, type: :model do
       it 'invalidates last_inspected when widened because of keep_direct' do
         account_statuses_cleanup_policy.keep_direct = false
         account_statuses_cleanup_policy.save
-        expect(account_statuses_cleanup_policy.last_inspected).to be nil
+        expect(account_statuses_cleanup_policy.last_inspected).to be_nil
       end
 
       it 'invalidates last_inspected when widened because of keep_pinned' do
         account_statuses_cleanup_policy.keep_pinned = false
         account_statuses_cleanup_policy.save
-        expect(account_statuses_cleanup_policy.last_inspected).to be nil
+        expect(account_statuses_cleanup_policy.last_inspected).to be_nil
       end
 
       it 'invalidates last_inspected when widened because of keep_polls' do
         account_statuses_cleanup_policy.keep_polls = false
         account_statuses_cleanup_policy.save
-        expect(account_statuses_cleanup_policy.last_inspected).to be nil
+        expect(account_statuses_cleanup_policy.last_inspected).to be_nil
       end
 
       it 'invalidates last_inspected when widened because of keep_media' do
         account_statuses_cleanup_policy.keep_media = false
         account_statuses_cleanup_policy.save
-        expect(account_statuses_cleanup_policy.last_inspected).to be nil
+        expect(account_statuses_cleanup_policy.last_inspected).to be_nil
       end
 
       it 'invalidates last_inspected when widened because of keep_self_fav' do
         account_statuses_cleanup_policy.keep_self_fav = false
         account_statuses_cleanup_policy.save
-        expect(account_statuses_cleanup_policy.last_inspected).to be nil
+        expect(account_statuses_cleanup_policy.last_inspected).to be_nil
       end
 
       it 'invalidates last_inspected when widened because of keep_self_bookmark' do
         account_statuses_cleanup_policy.keep_self_bookmark = false
         account_statuses_cleanup_policy.save
-        expect(account_statuses_cleanup_policy.last_inspected).to be nil
+        expect(account_statuses_cleanup_policy.last_inspected).to be_nil
       end
 
       it 'invalidates last_inspected when widened because of higher min_favs' do
         account_statuses_cleanup_policy.min_favs = 5
         account_statuses_cleanup_policy.save
-        expect(account_statuses_cleanup_policy.last_inspected).to be nil
+        expect(account_statuses_cleanup_policy.last_inspected).to be_nil
       end
 
       it 'invalidates last_inspected when widened because of disabled min_favs' do
         account_statuses_cleanup_policy.min_favs = nil
         account_statuses_cleanup_policy.save
-        expect(account_statuses_cleanup_policy.last_inspected).to be nil
+        expect(account_statuses_cleanup_policy.last_inspected).to be_nil
       end
 
       it 'invalidates last_inspected when widened because of higher min_reblogs' do
         account_statuses_cleanup_policy.min_reblogs = 5
         account_statuses_cleanup_policy.save
-        expect(account_statuses_cleanup_policy.last_inspected).to be nil
+        expect(account_statuses_cleanup_policy.last_inspected).to be_nil
       end
 
       it 'invalidates last_inspected when widened because of disable min_reblogs' do
         account_statuses_cleanup_policy.min_reblogs = nil
         account_statuses_cleanup_policy.save
-        expect(account_statuses_cleanup_policy.last_inspected).to be nil
+        expect(account_statuses_cleanup_policy.last_inspected).to be_nil
       end
     end
 
     context 'when narrowing a policy' do
       let!(:account_statuses_cleanup_policy) do
         Fabricate(:account_statuses_cleanup_policy,
-          account: account,
-          keep_direct: false,
-          keep_pinned: false,
-          keep_polls: false,
-          keep_media: false,
-          keep_self_fav: false,
-          keep_self_bookmark: false,
-          min_favs: nil,
-          min_reblogs: nil
-        )
+                  account: account,
+                  keep_direct: false,
+                  keep_pinned: false,
+                  keep_polls: false,
+                  keep_media: false,
+                  keep_self_fav: false,
+                  keep_self_bookmark: false,
+                  min_favs: nil,
+                  min_reblogs: nil)
       end
 
       it 'does not unnecessarily invalidate last_inspected' do
@@ -134,9 +134,10 @@ RSpec.describe AccountStatusesCleanupPolicy, type: :model do
   end
 
   describe '#invalidate_last_inspected' do
+    subject { account_statuses_cleanup_policy.invalidate_last_inspected(status, action) }
+
     let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
     let(:status) { Fabricate(:status, id: 10, account: account) }
-    subject { account_statuses_cleanup_policy.invalidate_last_inspected(status, action) }
 
     before do
       account_statuses_cleanup_policy.record_last_inspected(42)
@@ -232,11 +233,11 @@ RSpec.describe AccountStatusesCleanupPolicy, type: :model do
   end
 
   describe '#compute_cutoff_id' do
-    let!(:unrelated_status)  { Fabricate(:status, created_at: 3.years.ago) }
-    let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
-
     subject { account_statuses_cleanup_policy.compute_cutoff_id }
 
+    let!(:unrelated_status) { Fabricate(:status, created_at: 3.years.ago) }
+    let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
+
     context 'when the account has posted multiple toots' do
       let!(:very_old_status)   { Fabricate(:status, created_at: 3.years.ago, account: account) }
       let!(:old_status)        { Fabricate(:status, created_at: 3.weeks.ago, account: account) }
@@ -255,13 +256,15 @@ RSpec.describe AccountStatusesCleanupPolicy, type: :model do
   end
 
   describe '#statuses_to_delete' do
+    subject { account_statuses_cleanup_policy.statuses_to_delete }
+
     let!(:unrelated_status)  { Fabricate(:status, created_at: 3.years.ago) }
     let!(:very_old_status)   { Fabricate(:status, created_at: 3.years.ago, account: account) }
     let!(:pinned_status)     { Fabricate(:status, created_at: 1.year.ago, account: account) }
     let!(:direct_message)    { Fabricate(:status, created_at: 1.year.ago, account: account, visibility: :direct) }
     let!(:self_faved)        { Fabricate(:status, created_at: 1.year.ago, account: account) }
     let!(:self_bookmarked)   { Fabricate(:status, created_at: 1.year.ago, account: account) }
-    let!(:status_with_poll)  { Fabricate(:status, created_at: 1.year.ago, account: account, poll_attributes: { account: account, voters_count: 0, options: ['a', 'b'], expires_in: 2.days }) }
+    let!(:status_with_poll)  { Fabricate(:status, created_at: 1.year.ago, account: account, poll_attributes: { account: account, voters_count: 0, options: %w(a b), expires_in: 2.days }) }
     let!(:status_with_media) { Fabricate(:status, created_at: 1.year.ago, account: account) }
     let!(:faved4)            { Fabricate(:status, created_at: 1.year.ago, account: account) }
     let!(:faved5)            { Fabricate(:status, created_at: 1.year.ago, account: account) }
@@ -276,8 +279,6 @@ RSpec.describe AccountStatusesCleanupPolicy, type: :model do
 
     let(:account_statuses_cleanup_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
 
-    subject { account_statuses_cleanup_policy.statuses_to_delete }
-
     before do
       4.times { faved4.increment_count!(:favourites_count) }
       5.times { faved5.increment_count!(:favourites_count) }
@@ -286,11 +287,11 @@ RSpec.describe AccountStatusesCleanupPolicy, type: :model do
     end
 
     context 'when passed a max_id' do
+      subject { account_statuses_cleanup_policy.statuses_to_delete(50, old_status.id).pluck(:id) }
+
       let!(:old_status)               { Fabricate(:status, created_at: 1.year.ago, account: account) }
       let!(:slightly_less_old_status) { Fabricate(:status, created_at: 6.months.ago, account: account) }
 
-      subject { account_statuses_cleanup_policy.statuses_to_delete(50, old_status.id).pluck(:id) }
-
       it 'returns statuses including max_id' do
         expect(subject).to include(old_status.id)
       end
@@ -305,11 +306,11 @@ RSpec.describe AccountStatusesCleanupPolicy, type: :model do
     end
 
     context 'when passed a min_id' do
+      subject { account_statuses_cleanup_policy.statuses_to_delete(50, recent_status.id, old_status.id).pluck(:id) }
+
       let!(:old_status)               { Fabricate(:status, created_at: 1.year.ago, account: account) }
       let!(:slightly_less_old_status) { Fabricate(:status, created_at: 6.months.ago, account: account) }
 
-      subject { account_statuses_cleanup_policy.statuses_to_delete(50, recent_status.id, old_status.id).pluck(:id) }
-
       it 'returns statuses including min_id' do
         expect(subject).to include(old_status.id)
       end
diff --git a/spec/models/account_statuses_filter_spec.rb b/spec/models/account_statuses_filter_spec.rb
index 03f0ffeb0..fa7664d92 100644
--- a/spec/models/account_statuses_filter_spec.rb
+++ b/spec/models/account_statuses_filter_spec.rb
@@ -3,12 +3,12 @@
 require 'rails_helper'
 
 RSpec.describe AccountStatusesFilter do
+  subject { described_class.new(account, current_account, params) }
+
   let(:account) { Fabricate(:account) }
   let(:current_account) { nil }
   let(:params) { {} }
 
-  subject { described_class.new(account, current_account, params) }
-
   def status!(visibility)
     Fabricate(:status, account: account, visibility: visibility)
   end
diff --git a/spec/models/account_warning_preset_spec.rb b/spec/models/account_warning_preset_spec.rb
new file mode 100644
index 000000000..f171df7c9
--- /dev/null
+++ b/spec/models/account_warning_preset_spec.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe AccountWarningPreset do
+  describe 'alphabetical' do
+    let(:first) { Fabricate(:account_warning_preset, title: 'aaa', text: 'aaa') }
+    let(:second) { Fabricate(:account_warning_preset, title: 'bbb', text: 'aaa') }
+    let(:third) { Fabricate(:account_warning_preset, title: 'bbb', text: 'bbb') }
+
+    it 'returns records in order of title and text' do
+      results = described_class.alphabetic
+
+      expect(results).to eq([first, second, third])
+    end
+  end
+end
diff --git a/spec/models/admin/account_action_spec.rb b/spec/models/admin/account_action_spec.rb
index b6a052b76..9f41b7c8e 100644
--- a/spec/models/admin/account_action_spec.rb
+++ b/spec/models/admin/account_action_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Admin::AccountAction, type: :model do
@@ -5,15 +7,16 @@ RSpec.describe Admin::AccountAction, type: :model do
 
   describe '#save!' do
     subject              { account_action.save! }
+
     let(:account)        { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
     let(:target_account) { Fabricate(:account) }
     let(:type)           { 'disable' }
 
     before do
       account_action.assign_attributes(
-        type:            type,
+        type: type,
         current_account: account,
-        target_account:  target_account
+        target_account: target_account
       )
     end
 
diff --git a/spec/models/admin/appeal_filter_spec.rb b/spec/models/admin/appeal_filter_spec.rb
new file mode 100644
index 000000000..e840bc3bc
--- /dev/null
+++ b/spec/models/admin/appeal_filter_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::AppealFilter do
+  describe '#results' do
+    let(:approved_appeal) { Fabricate(:appeal, approved_at: 10.days.ago) }
+    let(:not_approved_appeal) { Fabricate(:appeal, approved_at: nil) }
+
+    it 'returns filtered appeals' do
+      filter = described_class.new(status: 'approved')
+
+      expect(filter.results).to eq([approved_appeal])
+    end
+  end
+end
diff --git a/spec/models/announcement_mute_spec.rb b/spec/models/announcement_mute_spec.rb
index 9d0e4c903..f4a7a5dc9 100644
--- a/spec/models/announcement_mute_spec.rb
+++ b/spec/models/announcement_mute_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AnnouncementMute, type: :model do
diff --git a/spec/models/announcement_reaction_spec.rb b/spec/models/announcement_reaction_spec.rb
index f6e151584..38095b015 100644
--- a/spec/models/announcement_reaction_spec.rb
+++ b/spec/models/announcement_reaction_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AnnouncementReaction, type: :model do
diff --git a/spec/models/announcement_spec.rb b/spec/models/announcement_spec.rb
index 7f7b647a9..024fa2888 100644
--- a/spec/models/announcement_spec.rb
+++ b/spec/models/announcement_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Announcement, type: :model do
diff --git a/spec/models/appeal_spec.rb b/spec/models/appeal_spec.rb
index 14062dc4f..12373a949 100644
--- a/spec/models/appeal_spec.rb
+++ b/spec/models/appeal_spec.rb
@@ -1,5 +1,38 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-RSpec.describe Appeal, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe Appeal do
+  describe 'scopes' do
+    describe 'approved' do
+      let(:approved_appeal) { Fabricate(:appeal, approved_at: 10.days.ago) }
+      let(:not_approved_appeal) { Fabricate(:appeal, approved_at: nil) }
+
+      it 'finds the correct records' do
+        results = described_class.approved
+        expect(results).to eq([approved_appeal])
+      end
+    end
+
+    describe 'rejected' do
+      let(:rejected_appeal) { Fabricate(:appeal, rejected_at: 10.days.ago) }
+      let(:not_rejected_appeal) { Fabricate(:appeal, rejected_at: nil) }
+
+      it 'finds the correct records' do
+        results = described_class.rejected
+        expect(results).to eq([rejected_appeal])
+      end
+    end
+
+    describe 'pending' do
+      let(:approved_appeal) { Fabricate(:appeal, approved_at: 10.days.ago) }
+      let(:rejected_appeal) { Fabricate(:appeal, rejected_at: 10.days.ago) }
+      let(:pending_appeal) { Fabricate(:appeal, rejected_at: nil, approved_at: nil) }
+
+      it 'finds the correct records' do
+        results = described_class.pending
+        expect(results).to eq([pending_appeal])
+      end
+    end
+  end
 end
diff --git a/spec/models/backup_spec.rb b/spec/models/backup_spec.rb
index 45230986d..239e7aef7 100644
--- a/spec/models/backup_spec.rb
+++ b/spec/models/backup_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Backup, type: :model do
diff --git a/spec/models/block_spec.rb b/spec/models/block_spec.rb
index acbdc77f5..6e31786d0 100644
--- a/spec/models/block_spec.rb
+++ b/spec/models/block_spec.rb
@@ -1,12 +1,9 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Block, type: :model do
   describe 'validations' do
-    it 'has a valid fabricator' do
-      block = Fabricate.build(:block)
-      expect(block).to be_valid
-    end
-
     it 'is invalid without an account' do
       block = Fabricate.build(:block, account: nil)
       block.valid?
@@ -28,8 +25,8 @@ RSpec.describe Block, type: :model do
 
     Block.create!(account: account, target_account: target_account)
 
-    expect(Rails.cache.exist?("exclude_account_ids_for:#{account.id}")).to eq false
-    expect(Rails.cache.exist?("exclude_account_ids_for:#{target_account.id}")).to eq false
+    expect(Rails.cache.exist?("exclude_account_ids_for:#{account.id}")).to be false
+    expect(Rails.cache.exist?("exclude_account_ids_for:#{target_account.id}")).to be false
   end
 
   it 'removes blocking cache after destruction' do
@@ -41,7 +38,7 @@ RSpec.describe Block, type: :model do
 
     block.destroy!
 
-    expect(Rails.cache.exist?("exclude_account_ids_for:#{account.id}")).to eq false
-    expect(Rails.cache.exist?("exclude_account_ids_for:#{target_account.id}")).to eq false
+    expect(Rails.cache.exist?("exclude_account_ids_for:#{account.id}")).to be false
+    expect(Rails.cache.exist?("exclude_account_ids_for:#{target_account.id}")).to be false
   end
 end
diff --git a/spec/models/canonical_email_block_spec.rb b/spec/models/canonical_email_block_spec.rb
index 8e0050d65..2b3fd6d6a 100644
--- a/spec/models/canonical_email_block_spec.rb
+++ b/spec/models/canonical_email_block_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe CanonicalEmailBlock, type: :model do
diff --git a/spec/models/concerns/account_counters_spec.rb b/spec/models/concerns/account_counters_spec.rb
index 4350496e7..fb02d79f1 100644
--- a/spec/models/concerns/account_counters_spec.rb
+++ b/spec/models/concerns/account_counters_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe AccountCounters do
diff --git a/spec/models/concerns/account_interactions_spec.rb b/spec/models/concerns/account_interactions_spec.rb
index b5aecf6be..32e08d5f7 100644
--- a/spec/models/concerns/account_interactions_spec.rb
+++ b/spec/models/concerns/account_interactions_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe AccountInteractions do
@@ -14,20 +16,20 @@ describe AccountInteractions do
     context 'account with Follow' do
       it 'returns { target_account_id => { reblogs: true } }' do
         Fabricate(:follow, account: account, target_account: target_account)
-        is_expected.to eq(target_account_id => { reblogs: true, notify: false, languages: nil })
+        expect(subject).to eq(target_account_id => { reblogs: true, notify: false, languages: nil })
       end
     end
 
     context 'account with Follow but with reblogs disabled' do
       it 'returns { target_account_id => { reblogs: false } }' do
         Fabricate(:follow, account: account, target_account: target_account, show_reblogs: false)
-        is_expected.to eq(target_account_id => { reblogs: false, notify: false, languages: nil })
+        expect(subject).to eq(target_account_id => { reblogs: false, notify: false, languages: nil })
       end
     end
 
     context 'account without Follow' do
       it 'returns {}' do
-        is_expected.to eq({})
+        expect(subject).to eq({})
       end
     end
   end
@@ -38,13 +40,13 @@ describe AccountInteractions do
     context 'account with Follow' do
       it 'returns { target_account_id => true }' do
         Fabricate(:follow, account: target_account, target_account: account)
-        is_expected.to eq(target_account_id => true)
+        expect(subject).to eq(target_account_id => true)
       end
     end
 
     context 'account without Follow' do
       it 'returns {}' do
-        is_expected.to eq({})
+        expect(subject).to eq({})
       end
     end
   end
@@ -55,13 +57,13 @@ describe AccountInteractions do
     context 'account with Block' do
       it 'returns { target_account_id => true }' do
         Fabricate(:block, account: account, target_account: target_account)
-        is_expected.to eq(target_account_id => true)
+        expect(subject).to eq(target_account_id => true)
       end
     end
 
     context 'account without Block' do
       it 'returns {}' do
-        is_expected.to eq({})
+        expect(subject).to eq({})
       end
     end
   end
@@ -78,7 +80,7 @@ describe AccountInteractions do
         let(:hide) { true }
 
         it 'returns { target_account_id => { notifications: true } }' do
-          is_expected.to eq(target_account_id => { notifications: true })
+          expect(subject).to eq(target_account_id => { notifications: true })
         end
       end
 
@@ -86,14 +88,14 @@ describe AccountInteractions do
         let(:hide) { false }
 
         it 'returns { target_account_id => { notifications: false } }' do
-          is_expected.to eq(target_account_id => { notifications: false })
+          expect(subject).to eq(target_account_id => { notifications: false })
         end
       end
     end
 
     context 'account without Mute' do
       it 'returns {}' do
-        is_expected.to eq({})
+        expect(subject).to eq({})
       end
     end
   end
@@ -101,7 +103,7 @@ describe AccountInteractions do
   describe '#follow!' do
     it 'creates and returns Follow' do
       expect do
-        expect(account.follow!(target_account)).to be_kind_of Follow
+        expect(account.follow!(target_account)).to be_a Follow
       end.to change { account.following.count }.by 1
     end
   end
@@ -109,7 +111,7 @@ describe AccountInteractions do
   describe '#block' do
     it 'creates and returns Block' do
       expect do
-        expect(account.block!(target_account)).to be_kind_of Block
+        expect(account.block!(target_account)).to be_a Block
       end.to change { account.block_relationships.count }.by 1
     end
   end
@@ -123,7 +125,7 @@ describe AccountInteractions do
 
         it 'creates Mute, and returns Mute' do
           expect do
-            expect(subject).to be_kind_of Mute
+            expect(subject).to be_a Mute
           end.to change { account.mute_relationships.count }.by 1
         end
       end
@@ -133,7 +135,7 @@ describe AccountInteractions do
 
         it 'creates Mute, and returns Mute' do
           expect do
-            expect(subject).to be_kind_of Mute
+            expect(subject).to be_a Mute
           end.to change { account.mute_relationships.count }.by 1
         end
       end
@@ -143,7 +145,7 @@ describe AccountInteractions do
 
         it 'creates Mute, and returns Mute' do
           expect do
-            expect(subject).to be_kind_of Mute
+            expect(subject).to be_a Mute
           end.to change { account.mute_relationships.count }.by 1
         end
       end
@@ -156,8 +158,8 @@ describe AccountInteractions do
 
       let(:mute) do
         Fabricate(:mute,
-                  account:            account,
-                  target_account:     target_account,
+                  account: account,
+                  target_account: target_account,
                   hide_notifications: hide_notifications)
       end
 
@@ -169,8 +171,8 @@ describe AccountInteractions do
 
           it 'returns Mute without updating mute.hide_notifications' do
             expect do
-              expect(subject).to be_kind_of Mute
-            end.not_to change { mute.reload.hide_notifications? }.from(true)
+              expect(subject).to be_a Mute
+            end.to_not change { mute.reload.hide_notifications? }.from(true)
           end
         end
 
@@ -179,7 +181,7 @@ describe AccountInteractions do
 
           it 'returns Mute, and updates mute.hide_notifications false' do
             expect do
-              expect(subject).to be_kind_of Mute
+              expect(subject).to be_a Mute
             end.to change { mute.reload.hide_notifications? }.from(true).to(false)
           end
         end
@@ -189,8 +191,8 @@ describe AccountInteractions do
 
           it 'returns Mute without updating mute.hide_notifications' do
             expect do
-              expect(subject).to be_kind_of Mute
-            end.not_to change { mute.reload.hide_notifications? }.from(true)
+              expect(subject).to be_a Mute
+            end.to_not change { mute.reload.hide_notifications? }.from(true)
           end
         end
       end
@@ -203,7 +205,7 @@ describe AccountInteractions do
 
           it 'returns Mute, and updates mute.hide_notifications true' do
             expect do
-              expect(subject).to be_kind_of Mute
+              expect(subject).to be_a Mute
             end.to change { mute.reload.hide_notifications? }.from(false).to(true)
           end
         end
@@ -213,8 +215,8 @@ describe AccountInteractions do
 
           it 'returns Mute without updating mute.hide_notifications' do
             expect do
-              expect(subject).to be_kind_of Mute
-            end.not_to change { mute.reload.hide_notifications? }.from(false)
+              expect(subject).to be_a Mute
+            end.to_not change { mute.reload.hide_notifications? }.from(false)
           end
         end
 
@@ -223,7 +225,7 @@ describe AccountInteractions do
 
           it 'returns Mute, and updates mute.hide_notifications true' do
             expect do
-              expect(subject).to be_kind_of Mute
+              expect(subject).to be_a Mute
             end.to change { mute.reload.hide_notifications? }.from(false).to(true)
           end
         end
@@ -232,25 +234,25 @@ describe AccountInteractions do
   end
 
   describe '#mute_conversation!' do
-    let(:conversation) { Fabricate(:conversation) }
-
     subject { account.mute_conversation!(conversation) }
 
+    let(:conversation) { Fabricate(:conversation) }
+
     it 'creates and returns ConversationMute' do
       expect do
-        is_expected.to be_kind_of ConversationMute
+        expect(subject).to be_a ConversationMute
       end.to change { account.conversation_mutes.count }.by 1
     end
   end
 
   describe '#block_domain!' do
-    let(:domain) { 'example.com' }
-
     subject { account.block_domain!(domain) }
 
+    let(:domain) { 'example.com' }
+
     it 'creates and returns AccountDomainBlock' do
       expect do
-        is_expected.to be_kind_of AccountDomainBlock
+        expect(subject).to be_a AccountDomainBlock
       end.to change { account.domain_blocks.count }.by 1
     end
   end
@@ -261,14 +263,14 @@ describe AccountInteractions do
     context 'following target_account' do
       it 'returns destroyed Follow' do
         account.active_relationships.create(target_account: target_account)
-        is_expected.to be_kind_of Follow
+        expect(subject).to be_a Follow
         expect(subject).to be_destroyed
       end
     end
 
     context 'not following target_account' do
       it 'returns nil' do
-        is_expected.to be_nil
+        expect(subject).to be_nil
       end
     end
   end
@@ -279,14 +281,14 @@ describe AccountInteractions do
     context 'blocking target_account' do
       it 'returns destroyed Block' do
         account.block_relationships.create(target_account: target_account)
-        is_expected.to be_kind_of Block
+        expect(subject).to be_a Block
         expect(subject).to be_destroyed
       end
     end
 
     context 'not blocking target_account' do
       it 'returns nil' do
-        is_expected.to be_nil
+        expect(subject).to be_nil
       end
     end
   end
@@ -297,55 +299,55 @@ describe AccountInteractions do
     context 'muting target_account' do
       it 'returns destroyed Mute' do
         account.mute_relationships.create(target_account: target_account)
-        is_expected.to be_kind_of Mute
+        expect(subject).to be_a Mute
         expect(subject).to be_destroyed
       end
     end
 
     context 'not muting target_account' do
       it 'returns nil' do
-        is_expected.to be_nil
+        expect(subject).to be_nil
       end
     end
   end
 
   describe '#unmute_conversation!' do
-    let(:conversation) { Fabricate(:conversation) }
-
     subject { account.unmute_conversation!(conversation) }
 
+    let(:conversation) { Fabricate(:conversation) }
+
     context 'muting the conversation' do
       it 'returns destroyed ConversationMute' do
         account.conversation_mutes.create(conversation: conversation)
-        is_expected.to be_kind_of ConversationMute
+        expect(subject).to be_a ConversationMute
         expect(subject).to be_destroyed
       end
     end
 
     context 'not muting the conversation' do
       it 'returns nil' do
-        is_expected.to be nil
+        expect(subject).to be_nil
       end
     end
   end
 
   describe '#unblock_domain!' do
-    let(:domain) { 'example.com' }
-
     subject { account.unblock_domain!(domain) }
 
+    let(:domain) { 'example.com' }
+
     context 'blocking the domain' do
       it 'returns destroyed AccountDomainBlock' do
         account_domain_block = Fabricate(:account_domain_block, domain: domain)
         account.domain_blocks << account_domain_block
-        is_expected.to be_kind_of AccountDomainBlock
+        expect(subject).to be_a AccountDomainBlock
         expect(subject).to be_destroyed
       end
     end
 
     context 'unblocking the domain' do
       it 'returns nil' do
-        is_expected.to be_nil
+        expect(subject).to be_nil
       end
     end
   end
@@ -356,13 +358,13 @@ describe AccountInteractions do
     context 'following target_account' do
       it 'returns true' do
         account.active_relationships.create(target_account: target_account)
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
     context 'not following target_account' do
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
@@ -373,13 +375,13 @@ describe AccountInteractions do
     context 'followed by target_account' do
       it 'returns true' do
         account.passive_relationships.create(account: target_account)
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
     context 'not followed by target_account' do
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
@@ -390,33 +392,33 @@ describe AccountInteractions do
     context 'blocking target_account' do
       it 'returns true' do
         account.block_relationships.create(target_account: target_account)
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
     context 'not blocking target_account' do
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
 
   describe '#domain_blocking?' do
-    let(:domain)               { 'example.com' }
-
     subject { account.domain_blocking?(domain) }
 
+    let(:domain) { 'example.com' }
+
     context 'blocking the domain' do
-      it' returns true' do
+      it 'returns true' do
         account_domain_block = Fabricate(:account_domain_block, domain: domain)
         account.domain_blocks << account_domain_block
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
     context 'not blocking the domain' do
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
@@ -428,49 +430,49 @@ describe AccountInteractions do
       it 'returns true' do
         mute = Fabricate(:mute, account: account, target_account: target_account)
         account.mute_relationships << mute
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
     context 'not muting target_account' do
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
 
   describe '#muting_conversation?' do
-    let(:conversation) { Fabricate(:conversation) }
-
     subject { account.muting_conversation?(conversation) }
 
+    let(:conversation) { Fabricate(:conversation) }
+
     context 'muting the conversation' do
       it 'returns true' do
         account.conversation_mutes.create(conversation: conversation)
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
     context 'not muting the conversation' do
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
 
   describe '#muting_notifications?' do
+    subject { account.muting_notifications?(target_account) }
+
     before do
       mute = Fabricate(:mute, target_account: target_account, account: account, hide_notifications: hide)
       account.mute_relationships << mute
     end
 
-    subject { account.muting_notifications?(target_account) }
-
     context 'muting notifications of target_account' do
       let(:hide) { true }
 
       it 'returns true' do
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
@@ -478,7 +480,7 @@ describe AccountInteractions do
       let(:hide) { false }
 
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
@@ -489,27 +491,27 @@ describe AccountInteractions do
     context 'requested by target_account' do
       it 'returns true' do
         Fabricate(:follow_request, account: account, target_account: target_account)
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
     context 'not requested by target_account' do
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
 
   describe '#favourited?' do
-    let(:status) { Fabricate(:status, account: account, favourites: favourites) }
-
     subject { account.favourited?(status) }
 
+    let(:status) { Fabricate(:status, account: account, favourites: favourites) }
+
     context 'favorited' do
       let(:favourites) { [Fabricate(:favourite, account: account)] }
 
       it 'returns true' do
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
@@ -517,21 +519,21 @@ describe AccountInteractions do
       let(:favourites) { [] }
 
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
 
   describe '#reblogged?' do
-    let(:status) { Fabricate(:status, account: account, reblogs: reblogs) }
-
     subject { account.reblogged?(status) }
 
+    let(:status) { Fabricate(:status, account: account, reblogs: reblogs) }
+
     context 'reblogged' do
       let(:reblogs) { [Fabricate(:status, account: account)] }
 
       it 'returns true' do
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
@@ -539,26 +541,26 @@ describe AccountInteractions do
       let(:reblogs) { [] }
 
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
 
   describe '#pinned?' do
-    let(:status) { Fabricate(:status, account: account) }
-
     subject { account.pinned?(status) }
 
+    let(:status) { Fabricate(:status, account: account) }
+
     context 'pinned' do
       it 'returns true' do
         Fabricate(:status_pin, account: account, status: status)
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
     context 'not pinned' do
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
@@ -688,41 +690,4 @@ describe AccountInteractions do
       end
     end
   end
-
-  describe 'ignoring reblogs from an account' do
-    before do
-      @me = Fabricate(:account, username: 'Me')
-      @you = Fabricate(:account, username: 'You')
-    end
-
-    context 'with the reblogs option unspecified' do
-      before do
-        @me.follow!(@you)
-      end
-
-      it 'defaults to showing reblogs' do
-        expect(@me.muting_reblogs?(@you)).to be(false)
-      end
-    end
-
-    context 'with the reblogs option set to false' do
-      before do
-        @me.follow!(@you, reblogs: false)
-      end
-
-      it 'does mute reblogs' do
-        expect(@me.muting_reblogs?(@you)).to be(true)
-      end
-    end
-
-    context 'with the reblogs option set to true' do
-      before do
-        @me.follow!(@you, reblogs: true)
-      end
-
-      it 'does not mute reblogs' do
-        expect(@me.muting_reblogs?(@you)).to be(false)
-      end
-    end
-  end
 end
diff --git a/spec/models/concerns/remotable_spec.rb b/spec/models/concerns/remotable_spec.rb
index 9cc849ded..964520427 100644
--- a/spec/models/concerns/remotable_spec.rb
+++ b/spec/models/concerns/remotable_spec.rb
@@ -147,8 +147,8 @@ RSpec.describe Remotable do
         let(:code) { 500 }
 
         it 'does not assign file' do
-          expect(foo).not_to receive(:public_send).with("#{hoge}=", any_args)
-          expect(foo).not_to receive(:public_send).with("#{hoge}_file_name=", any_args)
+          expect(foo).to_not receive(:public_send).with("#{hoge}=", any_args)
+          expect(foo).to_not receive(:public_send).with("#{hoge}_file_name=", any_args)
 
           foo.hoge_remote_url = url
         end
@@ -194,7 +194,9 @@ RSpec.describe Remotable do
           let(:error_class) { error_class }
 
           it 'calls Rails.logger.debug' do
-            expect(Rails.logger).to receive(:debug).with(/^Error fetching remote #{hoge}: /)
+            expect(Rails.logger).to receive(:debug) do |&block|
+              expect(block.call).to match(/^Error fetching remote #{hoge}: /)
+            end
             foo.hoge_remote_url = url
           end
         end
diff --git a/spec/models/conversation_mute_spec.rb b/spec/models/conversation_mute_spec.rb
index 3fc2915d4..6439b0ecd 100644
--- a/spec/models/conversation_mute_spec.rb
+++ b/spec/models/conversation_mute_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ConversationMute, type: :model do
diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb
index 8b5e4fdaf..9d58ad0ac 100644
--- a/spec/models/conversation_spec.rb
+++ b/spec/models/conversation_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Conversation, type: :model do
diff --git a/spec/models/custom_emoji_category_spec.rb b/spec/models/custom_emoji_category_spec.rb
index 160033f4d..30de07bd8 100644
--- a/spec/models/custom_emoji_category_spec.rb
+++ b/spec/models/custom_emoji_category_spec.rb
@@ -1,5 +1,14 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-RSpec.describe CustomEmojiCategory, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe CustomEmojiCategory do
+  describe 'validations' do
+    it 'validates name presence' do
+      record = described_class.new(name: nil)
+
+      expect(record).to_not be_valid
+      expect(record).to model_have_error_on_field(:name)
+    end
+  end
 end
diff --git a/spec/models/custom_emoji_filter_spec.rb b/spec/models/custom_emoji_filter_spec.rb
index 2b1b5dc54..30f0ec2b2 100644
--- a/spec/models/custom_emoji_filter_spec.rb
+++ b/spec/models/custom_emoji_filter_spec.rb
@@ -4,18 +4,18 @@ require 'rails_helper'
 
 RSpec.describe CustomEmojiFilter do
   describe '#results' do
+    subject { described_class.new(params).results }
+
     let!(:custom_emoji_0) { Fabricate(:custom_emoji, domain: 'a') }
     let!(:custom_emoji_1) { Fabricate(:custom_emoji, domain: 'b') }
     let!(:custom_emoji_2) { Fabricate(:custom_emoji, domain: nil, shortcode: 'hoge') }
 
-    subject { described_class.new(params).results }
-
     context 'params have values' do
       context 'local' do
         let(:params) { { local: true } }
 
         it 'returns ActiveRecord::Relation' do
-          expect(subject).to be_kind_of(ActiveRecord::Relation)
+          expect(subject).to be_a(ActiveRecord::Relation)
           expect(subject).to match_array([custom_emoji_2])
         end
       end
@@ -24,7 +24,7 @@ RSpec.describe CustomEmojiFilter do
         let(:params) { { remote: true } }
 
         it 'returns ActiveRecord::Relation' do
-          expect(subject).to be_kind_of(ActiveRecord::Relation)
+          expect(subject).to be_a(ActiveRecord::Relation)
           expect(subject).to match_array([custom_emoji_0, custom_emoji_1])
         end
       end
@@ -33,7 +33,7 @@ RSpec.describe CustomEmojiFilter do
         let(:params) { { by_domain: 'a' } }
 
         it 'returns ActiveRecord::Relation' do
-          expect(subject).to be_kind_of(ActiveRecord::Relation)
+          expect(subject).to be_a(ActiveRecord::Relation)
           expect(subject).to match_array([custom_emoji_0])
         end
       end
@@ -42,7 +42,7 @@ RSpec.describe CustomEmojiFilter do
         let(:params) { { shortcode: 'hoge' } }
 
         it 'returns ActiveRecord::Relation' do
-          expect(subject).to be_kind_of(ActiveRecord::Relation)
+          expect(subject).to be_a(ActiveRecord::Relation)
           expect(subject).to match_array([custom_emoji_2])
         end
       end
@@ -62,7 +62,7 @@ RSpec.describe CustomEmojiFilter do
       let(:params) { { hoge: nil } }
 
       it 'returns ActiveRecord::Relation' do
-        expect(subject).to be_kind_of(ActiveRecord::Relation)
+        expect(subject).to be_a(ActiveRecord::Relation)
         expect(subject).to match_array([custom_emoji_0, custom_emoji_1, custom_emoji_2])
       end
     end
diff --git a/spec/models/custom_emoji_spec.rb b/spec/models/custom_emoji_spec.rb
index 9de218b4f..ef5f39aca 100644
--- a/spec/models/custom_emoji_spec.rb
+++ b/spec/models/custom_emoji_spec.rb
@@ -1,17 +1,19 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe CustomEmoji, type: :model do
   describe '#search' do
-    let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: shortcode) }
-
     subject { described_class.search(search_term) }
 
+    let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: shortcode) }
+
     context 'shortcode is exact' do
       let(:shortcode) { 'blobpats' }
       let(:search_term) { 'blobpats' }
 
       it 'finds emoji' do
-        is_expected.to include(custom_emoji)
+        expect(subject).to include(custom_emoji)
       end
     end
 
@@ -20,21 +22,21 @@ RSpec.describe CustomEmoji, type: :model do
       let(:search_term) { 'blob' }
 
       it 'finds emoji' do
-        is_expected.to include(custom_emoji)
+        expect(subject).to include(custom_emoji)
       end
     end
   end
 
   describe '#local?' do
-    let(:custom_emoji) { Fabricate(:custom_emoji, domain: domain) }
-
     subject { custom_emoji.local? }
 
+    let(:custom_emoji) { Fabricate(:custom_emoji, domain: domain) }
+
     context 'domain is nil' do
       let(:domain) { nil }
 
       it 'returns true' do
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
@@ -42,7 +44,7 @@ RSpec.describe CustomEmoji, type: :model do
       let(:domain) { 'example.com' }
 
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
@@ -55,15 +57,15 @@ RSpec.describe CustomEmoji, type: :model do
   end
 
   describe '.from_text' do
-    let!(:emojo) { Fabricate(:custom_emoji) }
-
     subject { described_class.from_text(text, nil) }
 
+    let!(:emojo) { Fabricate(:custom_emoji) }
+
     context 'with plain text' do
       let(:text) { 'Hello :coolcat:' }
 
       it 'returns records used via shortcodes in text' do
-        is_expected.to include(emojo)
+        expect(subject).to include(emojo)
       end
     end
 
@@ -71,7 +73,7 @@ RSpec.describe CustomEmoji, type: :model do
       let(:text) { '<p>Hello :coolcat:</p>' }
 
       it 'returns records used via shortcodes in text' do
-        is_expected.to include(emojo)
+        expect(subject).to include(emojo)
       end
     end
   end
@@ -79,7 +81,7 @@ RSpec.describe CustomEmoji, type: :model do
   describe 'pre_validation' do
     let(:custom_emoji) { Fabricate(:custom_emoji, domain: 'wWw.MaStOdOn.CoM') }
 
-    it 'should downcase' do
+    it 'downcases' do
       custom_emoji.valid?
       expect(custom_emoji.domain).to eq('www.mastodon.com')
     end
diff --git a/spec/models/custom_filter_keyword_spec.rb b/spec/models/custom_filter_keyword_spec.rb
index e15b9dad5..bbc4b9c2e 100644
--- a/spec/models/custom_filter_keyword_spec.rb
+++ b/spec/models/custom_filter_keyword_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe CustomFilterKeyword, type: :model do
diff --git a/spec/models/custom_filter_spec.rb b/spec/models/custom_filter_spec.rb
index 3943dd5f1..d2bc090ab 100644
--- a/spec/models/custom_filter_spec.rb
+++ b/spec/models/custom_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe CustomFilter, type: :model do
diff --git a/spec/models/device_spec.rb b/spec/models/device_spec.rb
index f56fbf978..cb214b9cb 100644
--- a/spec/models/device_spec.rb
+++ b/spec/models/device_spec.rb
@@ -1,5 +1,6 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Device, type: :model do
-
 end
diff --git a/spec/models/domain_allow_spec.rb b/spec/models/domain_allow_spec.rb
index e65435127..49e16376e 100644
--- a/spec/models/domain_allow_spec.rb
+++ b/spec/models/domain_allow_spec.rb
@@ -1,5 +1,18 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-RSpec.describe DomainAllow, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe DomainAllow do
+  describe 'scopes' do
+    describe 'matches_domain' do
+      let(:domain) { Fabricate(:domain_allow, domain: 'example.com') }
+      let(:other_domain) { Fabricate(:domain_allow, domain: 'example.biz') }
+
+      it 'returns the correct records' do
+        results = described_class.matches_domain('example.com')
+
+        expect(results).to eq([domain])
+      end
+    end
+  end
 end
diff --git a/spec/models/domain_block_spec.rb b/spec/models/domain_block_spec.rb
index 28647dc89..9839ee9d4 100644
--- a/spec/models/domain_block_spec.rb
+++ b/spec/models/domain_block_spec.rb
@@ -1,12 +1,9 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe DomainBlock, type: :model do
   describe 'validations' do
-    it 'has a valid fabricator' do
-      domain_block = Fabricate.build(:domain_block)
-      expect(domain_block).to be_valid
-    end
-
     it 'is invalid without a domain' do
       domain_block = Fabricate.build(:domain_block, domain: nil)
       domain_block.valid?
@@ -24,16 +21,16 @@ RSpec.describe DomainBlock, type: :model do
   describe '.blocked?' do
     it 'returns true if the domain is suspended' do
       Fabricate(:domain_block, domain: 'example.com', severity: :suspend)
-      expect(DomainBlock.blocked?('example.com')).to eq true
+      expect(DomainBlock.blocked?('example.com')).to be true
     end
 
     it 'returns false even if the domain is silenced' do
       Fabricate(:domain_block, domain: 'example.com', severity: :silence)
-      expect(DomainBlock.blocked?('example.com')).to eq false
+      expect(DomainBlock.blocked?('example.com')).to be false
     end
 
     it 'returns false if the domain is not suspended nor silenced' do
-      expect(DomainBlock.blocked?('example.com')).to eq false
+      expect(DomainBlock.blocked?('example.com')).to be false
     end
   end
 
diff --git a/spec/models/email_domain_block_spec.rb b/spec/models/email_domain_block_spec.rb
index e23116888..3321ffc81 100644
--- a/spec/models/email_domain_block_spec.rb
+++ b/spec/models/email_domain_block_spec.rb
@@ -1,13 +1,8 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe EmailDomainBlock, type: :model do
-  describe 'validations' do
-    it 'has a valid fabricator' do
-      email_domain_block = Fabricate.build(:email_domain_block)
-      expect(email_domain_block).to be_valid
-    end
-  end
-
   describe 'block?' do
     let(:input) { nil }
 
diff --git a/spec/models/encrypted_message_spec.rb b/spec/models/encrypted_message_spec.rb
index 1238d57b6..bf7a406ff 100644
--- a/spec/models/encrypted_message_spec.rb
+++ b/spec/models/encrypted_message_spec.rb
@@ -1,5 +1,6 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe EncryptedMessage, type: :model do
-
 end
diff --git a/spec/models/export_spec.rb b/spec/models/export_spec.rb
index 135d7a36b..3fb5fc3a5 100644
--- a/spec/models/export_spec.rb
+++ b/spec/models/export_spec.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Export do
   let(:account) { Fabricate(:account) }
   let(:target_accounts) do
-    [ {}, { username: 'one', domain: 'local.host' } ].map(&method(:Fabricate).curry(2).call(:account))
+    [{}, { username: 'one', domain: 'local.host' }].map(&method(:Fabricate).curry(2).call(:account))
   end
 
   describe 'to_csv' do
diff --git a/spec/models/extended_description_spec.rb b/spec/models/extended_description_spec.rb
new file mode 100644
index 000000000..ecc27c0f6
--- /dev/null
+++ b/spec/models/extended_description_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe ExtendedDescription do
+  describe '.current' do
+    context 'with the default values' do
+      it 'makes a new instance' do
+        record = described_class.current
+
+        expect(record.text).to be_nil
+        expect(record.updated_at).to be_nil
+      end
+    end
+
+    context 'with a custom setting value' do
+      before do
+        setting = instance_double(Setting, value: 'Extended text', updated_at: 10.days.ago)
+        allow(Setting).to receive(:find_by).with(var: 'site_extended_description').and_return(setting)
+      end
+
+      it 'has the privacy text' do
+        record = described_class.current
+
+        expect(record.text).to eq('Extended text')
+      end
+    end
+  end
+end
diff --git a/spec/models/favourite_spec.rb b/spec/models/favourite_spec.rb
index ba1410a45..f7e2812a6 100644
--- a/spec/models/favourite_spec.rb
+++ b/spec/models/favourite_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Favourite, type: :model do
@@ -9,7 +11,7 @@ RSpec.describe Favourite, type: :model do
 
     it 'invalidates if the reblogged status is already a favourite' do
       Favourite.create!(account: account, status: reblog)
-      expect(Favourite.new(account: account, status: status).valid?).to eq false
+      expect(Favourite.new(account: account, status: status).valid?).to be false
     end
 
     it 'replaces status with the reblogged one if it is a reblog' do
diff --git a/spec/models/featured_tag_spec.rb b/spec/models/featured_tag_spec.rb
index 07533e0b9..4bf087c82 100644
--- a/spec/models/featured_tag_spec.rb
+++ b/spec/models/featured_tag_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe FeaturedTag, type: :model do
diff --git a/spec/models/follow_recommendation_suppression_spec.rb b/spec/models/follow_recommendation_suppression_spec.rb
index 39107a2b0..4c1d8281b 100644
--- a/spec/models/follow_recommendation_suppression_spec.rb
+++ b/spec/models/follow_recommendation_suppression_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe FollowRecommendationSuppression, type: :model do
diff --git a/spec/models/follow_request_spec.rb b/spec/models/follow_request_spec.rb
index c456c285f..ff81cd78d 100644
--- a/spec/models/follow_request_spec.rb
+++ b/spec/models/follow_request_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe FollowRequest, type: :model do
diff --git a/spec/models/follow_spec.rb b/spec/models/follow_spec.rb
index e723a1ef2..a9a9af88a 100644
--- a/spec/models/follow_spec.rb
+++ b/spec/models/follow_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Follow, type: :model do
@@ -7,11 +9,6 @@ RSpec.describe Follow, type: :model do
   describe 'validations' do
     subject { Follow.new(account: alice, target_account: bob, rate_limit: true) }
 
-    it 'has a valid fabricator' do
-      follow = Fabricate.build(:follow)
-      expect(follow).to be_valid
-    end
-
     it 'is invalid without an account' do
       follow = Fabricate.build(:follow, account: nil)
       follow.valid?
diff --git a/spec/models/form/admin_settings_spec.rb b/spec/models/form/admin_settings_spec.rb
new file mode 100644
index 000000000..0dc2d881a
--- /dev/null
+++ b/spec/models/form/admin_settings_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Form::AdminSettings do
+  describe 'validations' do
+    describe 'site_contact_username' do
+      context 'with no accounts' do
+        it 'is not valid' do
+          setting = described_class.new(site_contact_username: 'Test')
+          setting.valid?
+
+          expect(setting).to model_have_error_on_field(:site_contact_username)
+        end
+      end
+
+      context 'with an account' do
+        before { Fabricate(:account, username: 'Glorp') }
+
+        it 'is not valid when account doesnt match' do
+          setting = described_class.new(site_contact_username: 'Test')
+          setting.valid?
+
+          expect(setting).to model_have_error_on_field(:site_contact_username)
+        end
+
+        it 'is valid when account matches' do
+          setting = described_class.new(site_contact_username: 'Glorp')
+          setting.valid?
+
+          expect(setting).to_not model_have_error_on_field(:site_contact_username)
+        end
+      end
+    end
+  end
+end
diff --git a/spec/models/form/status_filter_batch_action_spec.rb b/spec/models/form/status_filter_batch_action_spec.rb
new file mode 100644
index 000000000..f06a11cc8
--- /dev/null
+++ b/spec/models/form/status_filter_batch_action_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Form::StatusFilterBatchAction do
+  describe '#save!' do
+    it 'does nothing if status_filter_ids is empty' do
+      batch_action = described_class.new(status_filter_ids: [])
+
+      expect(batch_action.save!).to be_nil
+    end
+  end
+end
diff --git a/spec/models/home_feed_spec.rb b/spec/models/home_feed_spec.rb
index 80f6edbff..d7034f3f0 100644
--- a/spec/models/home_feed_spec.rb
+++ b/spec/models/home_feed_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe HomeFeed, type: :model do
-  let(:account) { Fabricate(:account) }
-
   subject { described_class.new(account) }
 
+  let(:account) { Fabricate(:account) }
+
   describe '#get' do
     before do
       Fabricate(:status, account: account, id: 1)
diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb
index 689c9b797..6eab5a2e1 100644
--- a/spec/models/identity_spec.rb
+++ b/spec/models/identity_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Identity, type: :model do
diff --git a/spec/models/import_spec.rb b/spec/models/import_spec.rb
index a5eec1722..1c8474413 100644
--- a/spec/models/import_spec.rb
+++ b/spec/models/import_spec.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Import, type: :model do
-  let (:account) { Fabricate(:account) }
-  let (:type) { 'following' }
-  let (:data) { attachment_fixture('imports.txt') }
+  let(:account) { Fabricate(:account) }
+  let(:type) { 'following' }
+  let(:data) { attachment_fixture('imports.txt') }
 
   describe 'validations' do
     it 'has a valid parameters' do
@@ -21,6 +23,11 @@ RSpec.describe Import, type: :model do
       expect(import).to model_have_error_on_field(:data)
     end
 
+    it 'is invalid with malformed data' do
+      import = Import.create(account: account, type: type, data: StringIO.new('\"test'))
+      expect(import).to model_have_error_on_field(:data)
+    end
+
     it 'is invalid with too many rows in data' do
       import = Import.create(account: account, type: type, data: StringIO.new("foo@bar.com\n" * (ImportService::ROWS_PROCESSING_LIMIT + 10)))
       expect(import).to model_have_error_on_field(:data)
diff --git a/spec/models/invite_spec.rb b/spec/models/invite_spec.rb
index b0596c561..dac4b6431 100644
--- a/spec/models/invite_spec.rb
+++ b/spec/models/invite_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Invite, type: :model do
diff --git a/spec/models/ip_block_spec.rb b/spec/models/ip_block_spec.rb
index 6603c6417..ed5882667 100644
--- a/spec/models/ip_block_spec.rb
+++ b/spec/models/ip_block_spec.rb
@@ -1,5 +1,15 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-RSpec.describe IpBlock, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe IpBlock do
+  describe 'to_log_human_identifier' do
+    let(:ip_block) { described_class.new(ip: '192.168.0.1') }
+
+    it 'combines the IP and prefix into a string' do
+      result = ip_block.to_log_human_identifier
+
+      expect(result).to eq('192.168.0.1/32')
+    end
+  end
 end
diff --git a/spec/models/list_account_spec.rb b/spec/models/list_account_spec.rb
index a0cf02efe..8312defac 100644
--- a/spec/models/list_account_spec.rb
+++ b/spec/models/list_account_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ListAccount, type: :model do
diff --git a/spec/models/list_spec.rb b/spec/models/list_spec.rb
index b780bb1de..8167f8a7e 100644
--- a/spec/models/list_spec.rb
+++ b/spec/models/list_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe List, type: :model do
diff --git a/spec/models/login_activity_spec.rb b/spec/models/login_activity_spec.rb
index ba2d207c9..1c3111a20 100644
--- a/spec/models/login_activity_spec.rb
+++ b/spec/models/login_activity_spec.rb
@@ -1,5 +1,6 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe LoginActivity, type: :model do
-
 end
diff --git a/spec/models/marker_spec.rb b/spec/models/marker_spec.rb
index d716aa75c..51dd58438 100644
--- a/spec/models/marker_spec.rb
+++ b/spec/models/marker_spec.rb
@@ -1,5 +1,16 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-RSpec.describe Marker, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe Marker do
+  describe 'validations' do
+    describe 'timeline' do
+      it 'must be included in valid list' do
+        record = described_class.new(timeline: 'not real timeline')
+
+        expect(record).to_not be_valid
+        expect(record).to model_have_error_on_field(:timeline)
+      end
+    end
+  end
 end
diff --git a/spec/models/media_attachment_spec.rb b/spec/models/media_attachment_spec.rb
index 29fd313ae..63edfc152 100644
--- a/spec/models/media_attachment_spec.rb
+++ b/spec/models/media_attachment_spec.rb
@@ -1,16 +1,18 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe MediaAttachment, type: :model do
   describe 'local?' do
-    let(:media_attachment) { Fabricate(:media_attachment, remote_url: remote_url) }
-
     subject { media_attachment.local? }
 
+    let(:media_attachment) { Fabricate(:media_attachment, remote_url: remote_url) }
+
     context 'remote_url is blank' do
       let(:remote_url) { '' }
 
       it 'returns true' do
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
@@ -18,16 +20,16 @@ RSpec.describe MediaAttachment, type: :model do
       let(:remote_url) { 'remote_url' }
 
       it 'returns false' do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
 
   describe 'needs_redownload?' do
-    let(:media_attachment) { Fabricate(:media_attachment, remote_url: remote_url, file: file) }
-
     subject { media_attachment.needs_redownload? }
 
+    let(:media_attachment) { Fabricate(:media_attachment, remote_url: remote_url, file: file) }
+
     context 'file is blank' do
       let(:file) { nil }
 
@@ -35,7 +37,7 @@ RSpec.describe MediaAttachment, type: :model do
         let(:remote_url) { 'remote_url' }
 
         it 'returns true' do
-          is_expected.to be true
+          expect(subject).to be true
         end
       end
     end
@@ -47,7 +49,7 @@ RSpec.describe MediaAttachment, type: :model do
         let(:remote_url) { '' }
 
         it 'returns false' do
-          is_expected.to be false
+          expect(subject).to be false
         end
       end
 
@@ -55,7 +57,7 @@ RSpec.describe MediaAttachment, type: :model do
         let(:remote_url) { 'remote_url' }
 
         it 'returns true' do
-          is_expected.to be false
+          expect(subject).to be false
         end
       end
     end
@@ -94,8 +96,8 @@ RSpec.describe MediaAttachment, type: :model do
     end
 
     it 'sets meta' do
-      expect(media.file.meta["original"]["width"]).to eq 128
-      expect(media.file.meta["original"]["height"]).to eq 128
+      expect(media.file.meta['original']['width']).to eq 128
+      expect(media.file.meta['original']['height']).to eq 128
     end
   end
 
@@ -118,9 +120,9 @@ RSpec.describe MediaAttachment, type: :model do
         end
 
         it 'sets meta' do
-          expect(media.file.meta["original"]["width"]).to eq fixture[:width]
-          expect(media.file.meta["original"]["height"]).to eq fixture[:height]
-          expect(media.file.meta["original"]["aspect"]).to eq fixture[:aspect]
+          expect(media.file.meta['original']['width']).to eq fixture[:width]
+          expect(media.file.meta['original']['height']).to eq fixture[:height]
+          expect(media.file.meta['original']['aspect']).to eq fixture[:aspect]
         end
       end
     end
@@ -138,7 +140,7 @@ RSpec.describe MediaAttachment, type: :model do
     end
 
     it 'extracts thumbnail' do
-      expect(media.thumbnail.present?).to eq true
+      expect(media.thumbnail.present?).to be true
     end
 
     it 'extracts colors from thumbnail' do
@@ -154,12 +156,12 @@ RSpec.describe MediaAttachment, type: :model do
     let(:media) { MediaAttachment.create(account: Fabricate(:account), file: attachment_fixture('attachment.jpg')) }
 
     it 'sets meta for different style' do
-      expect(media.file.meta["original"]["width"]).to eq 600
-      expect(media.file.meta["original"]["height"]).to eq 400
-      expect(media.file.meta["original"]["aspect"]).to eq 1.5
-      expect(media.file.meta["small"]["width"]).to eq 588
-      expect(media.file.meta["small"]["height"]).to eq 392
-      expect(media.file.meta["small"]["aspect"]).to eq 1.5
+      expect(media.file.meta['original']['width']).to eq 600
+      expect(media.file.meta['original']['height']).to eq 400
+      expect(media.file.meta['original']['aspect']).to eq 1.5
+      expect(media.file.meta['small']['width']).to eq 588
+      expect(media.file.meta['small']['height']).to eq 392
+      expect(media.file.meta['small']['aspect']).to eq 1.5
     end
 
     it 'gives the file a random name' do
diff --git a/spec/models/mention_spec.rb b/spec/models/mention_spec.rb
index dbcf6a32c..044bb80cf 100644
--- a/spec/models/mention_spec.rb
+++ b/spec/models/mention_spec.rb
@@ -1,12 +1,9 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Mention, type: :model do
   describe 'validations' do
-    it 'has a valid fabricator' do
-      mention = Fabricate.build(:mention)
-      expect(mention).to be_valid
-    end
-
     it 'is invalid without an account' do
       mention = Fabricate.build(:mention, account: nil)
       mention.valid?
diff --git a/spec/models/mute_spec.rb b/spec/models/mute_spec.rb
index 38a87bdf4..48b5a37ab 100644
--- a/spec/models/mute_spec.rb
+++ b/spec/models/mute_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Mute, type: :model do
diff --git a/spec/models/notification_spec.rb b/spec/models/notification_spec.rb
index 1e9e45d8d..64527e3d7 100644
--- a/spec/models/notification_spec.rb
+++ b/spec/models/notification_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Notification, type: :model do
@@ -68,7 +70,7 @@ RSpec.describe Notification, type: :model do
       let(:notifications) { [] }
 
       it 'returns []' do
-        is_expected.to eq []
+        expect(subject).to eq []
       end
     end
 
diff --git a/spec/models/one_time_key_spec.rb b/spec/models/one_time_key_spec.rb
index 34598334c..6ff7ffc5c 100644
--- a/spec/models/one_time_key_spec.rb
+++ b/spec/models/one_time_key_spec.rb
@@ -1,5 +1,23 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-RSpec.describe OneTimeKey, type: :model do
+describe OneTimeKey do
+  describe 'validations' do
+    context 'with an invalid signature' do
+      let(:one_time_key) { Fabricate.build(:one_time_key, signature: 'wrong!') }
+
+      it 'is invalid' do
+        expect(one_time_key).to_not be_valid
+      end
+    end
+
+    context 'with an invalid key' do
+      let(:one_time_key) { Fabricate.build(:one_time_key, key: 'wrong!') }
 
+      it 'is invalid' do
+        expect(one_time_key).to_not be_valid
+      end
+    end
+  end
 end
diff --git a/spec/models/poll_spec.rb b/spec/models/poll_spec.rb
index 666f8ca68..8ae04ca41 100644
--- a/spec/models/poll_spec.rb
+++ b/spec/models/poll_spec.rb
@@ -1,5 +1,32 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-RSpec.describe Poll, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe Poll do
+  describe 'scopes' do
+    let(:status) { Fabricate(:status) }
+    let(:attached_poll) { Fabricate(:poll, status: status) }
+    let(:not_attached_poll) do
+      Fabricate(:poll).tap do |poll|
+        poll.status = nil
+        poll.save(validate: false)
+      end
+    end
+
+    describe 'attached' do
+      it 'finds the correct records' do
+        results = described_class.attached
+
+        expect(results).to eq([attached_poll])
+      end
+    end
+
+    describe 'unattached' do
+      it 'finds the correct records' do
+        results = described_class.unattached
+
+        expect(results).to eq([not_attached_poll])
+      end
+    end
+  end
 end
diff --git a/spec/models/poll_vote_spec.rb b/spec/models/poll_vote_spec.rb
index 563f34699..6886a82aa 100644
--- a/spec/models/poll_vote_spec.rb
+++ b/spec/models/poll_vote_spec.rb
@@ -10,4 +10,53 @@ RSpec.describe PollVote, type: :model do
       expect(poll_vote.object_type).to eq :vote
     end
   end
+
+  describe 'validations' do
+    context 'with a vote on an expired poll' do
+      it 'marks the vote invalid' do
+        poll = Fabricate.build(:poll, expires_at: 30.days.ago)
+
+        vote = Fabricate.build(:poll_vote, poll: poll)
+        expect(vote).to_not be_valid
+      end
+    end
+
+    context 'with invalid choices' do
+      it 'marks vote invalid with negative choice' do
+        poll = Fabricate.build(:poll)
+
+        vote = Fabricate.build(:poll_vote, poll: poll, choice: -100)
+        expect(vote).to_not be_valid
+      end
+
+      it 'marks vote invalid with choice in excess of options' do
+        poll = Fabricate.build(:poll, options: %w(a b c))
+
+        vote = Fabricate.build(:poll_vote, poll: poll, choice: 10)
+        expect(vote).to_not be_valid
+      end
+    end
+
+    context 'with a poll where multiple is true' do
+      it 'does not allow a second vote on same choice from same account' do
+        poll = Fabricate(:poll, multiple: true, options: %w(a b c))
+        first_vote = Fabricate(:poll_vote, poll: poll, choice: 1)
+        expect(first_vote).to be_valid
+
+        second_vote = Fabricate.build(:poll_vote, account: first_vote.account, poll: poll, choice: 1)
+        expect(second_vote).to_not be_valid
+      end
+    end
+
+    context 'with a poll where multiple is false' do
+      it 'does not allow a second vote from same account' do
+        poll = Fabricate(:poll, multiple: false, options: %w(a b c))
+        first_vote = Fabricate(:poll_vote, poll: poll)
+        expect(first_vote).to be_valid
+
+        second_vote = Fabricate.build(:poll_vote, account: first_vote.account, poll: poll)
+        expect(second_vote).to_not be_valid
+      end
+    end
+  end
 end
diff --git a/spec/models/preview_card_provider_spec.rb b/spec/models/preview_card_provider_spec.rb
new file mode 100644
index 000000000..7425b9394
--- /dev/null
+++ b/spec/models/preview_card_provider_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PreviewCardProvider do
+  describe 'scopes' do
+    let(:trendable_and_reviewed) { Fabricate(:preview_card_provider, trendable: true, reviewed_at: 5.days.ago) }
+    let(:not_trendable_and_not_reviewed) { Fabricate(:preview_card_provider, trendable: false, reviewed_at: nil) }
+
+    describe 'trendable' do
+      it 'returns the relevant records' do
+        results = described_class.trendable
+
+        expect(results).to eq([trendable_and_reviewed])
+      end
+    end
+
+    describe 'not_trendable' do
+      it 'returns the relevant records' do
+        results = described_class.not_trendable
+
+        expect(results).to eq([not_trendable_and_not_reviewed])
+      end
+    end
+
+    describe 'reviewed' do
+      it 'returns the relevant records' do
+        results = described_class.reviewed
+
+        expect(results).to eq([trendable_and_reviewed])
+      end
+    end
+
+    describe 'pending_review' do
+      it 'returns the relevant records' do
+        results = described_class.pending_review
+
+        expect(results).to eq([not_trendable_and_not_reviewed])
+      end
+    end
+  end
+end
diff --git a/spec/models/preview_card_spec.rb b/spec/models/preview_card_spec.rb
index 45233d1d4..1858644c9 100644
--- a/spec/models/preview_card_spec.rb
+++ b/spec/models/preview_card_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe PreviewCard, type: :model do
diff --git a/spec/models/preview_card_trend_spec.rb b/spec/models/preview_card_trend_spec.rb
index c7ab6ed14..97ad05e75 100644
--- a/spec/models/preview_card_trend_spec.rb
+++ b/spec/models/preview_card_trend_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe PreviewCardTrend, type: :model do
diff --git a/spec/models/privacy_policy_spec.rb b/spec/models/privacy_policy_spec.rb
new file mode 100644
index 000000000..0d7471375
--- /dev/null
+++ b/spec/models/privacy_policy_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PrivacyPolicy do
+  describe '.current' do
+    context 'with the default values' do
+      it 'has the privacy text' do
+        policy = described_class.current
+
+        expect(policy.text).to eq(PrivacyPolicy::DEFAULT_PRIVACY_POLICY)
+      end
+    end
+
+    context 'with a custom setting value' do
+      before do
+        terms_setting = instance_double(Setting, value: 'Terms text', updated_at: 10.days.ago)
+        allow(Setting).to receive(:find_by).with(var: 'site_terms').and_return(terms_setting)
+      end
+
+      it 'has the privacy text' do
+        policy = described_class.current
+
+        expect(policy.text).to eq('Terms text')
+      end
+    end
+  end
+end
diff --git a/spec/models/public_feed_spec.rb b/spec/models/public_feed_spec.rb
index 23cc3ceea..d31aba084 100644
--- a/spec/models/public_feed_spec.rb
+++ b/spec/models/public_feed_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe PublicFeed, type: :model do
@@ -11,7 +13,7 @@ RSpec.describe PublicFeed, type: :model do
       private_status = Fabricate(:status, visibility: :private)
 
       expect(subject).to include(public_status.id)
-      expect(subject).not_to include(private_status.id)
+      expect(subject).to_not include(private_status.id)
     end
 
     it 'does not include replies' do
@@ -19,7 +21,7 @@ RSpec.describe PublicFeed, type: :model do
       reply = Fabricate(:status, in_reply_to_id: status.id)
 
       expect(subject).to include(status.id)
-      expect(subject).not_to include(reply.id)
+      expect(subject).to_not include(reply.id)
     end
 
     it 'does not include boosts' do
@@ -27,7 +29,7 @@ RSpec.describe PublicFeed, type: :model do
       boost = Fabricate(:status, reblog_of_id: status.id)
 
       expect(subject).to include(status.id)
-      expect(subject).not_to include(boost.id)
+      expect(subject).to_not include(boost.id)
     end
 
     it 'filters out silenced accounts' do
@@ -36,10 +38,12 @@ RSpec.describe PublicFeed, type: :model do
       silenced_status = Fabricate(:status, account: silenced_account)
 
       expect(subject).to include(status.id)
-      expect(subject).not_to include(silenced_status.id)
+      expect(subject).to_not include(silenced_status.id)
     end
 
     context 'without local_only option' do
+      subject { described_class.new(viewer).get(20).map(&:id) }
+
       let(:viewer) { nil }
 
       let!(:local_account)  { Fabricate(:account, domain: nil) }
@@ -48,8 +52,6 @@ RSpec.describe PublicFeed, type: :model do
       let!(:remote_status)  { Fabricate(:status, account: remote_account) }
       let!(:local_only_status) { Fabricate(:status, account: local_account, local_only: true) }
 
-      subject { described_class.new(viewer).get(20).map(&:id) }
-
       context 'without a viewer' do
         let(:viewer) { nil }
 
@@ -62,7 +64,7 @@ RSpec.describe PublicFeed, type: :model do
         end
 
         it 'does not include local-only statuses' do
-          expect(subject).not_to include(local_only_status.id)
+          expect(subject).to_not include(local_only_status.id)
         end
       end
 
@@ -78,12 +80,14 @@ RSpec.describe PublicFeed, type: :model do
         end
 
         it 'does not include local-only statuses' do
-          expect(subject).not_to include(local_only_status.id)
+          expect(subject).to_not include(local_only_status.id)
         end
       end
     end
 
     context 'without local_only option but allow_local_only' do
+      subject { described_class.new(viewer, allow_local_only: true).get(20).map(&:id) }
+
       let(:viewer) { nil }
 
       let!(:local_account)  { Fabricate(:account, domain: nil) }
@@ -92,8 +96,6 @@ RSpec.describe PublicFeed, type: :model do
       let!(:remote_status)  { Fabricate(:status, account: remote_account) }
       let!(:local_only_status) { Fabricate(:status, account: local_account, local_only: true) }
 
-      subject { described_class.new(viewer, allow_local_only: true).get(20).map(&:id) }
-
       context 'without a viewer' do
         let(:viewer) { nil }
 
@@ -106,7 +108,7 @@ RSpec.describe PublicFeed, type: :model do
         end
 
         it 'does not include local-only statuses' do
-          expect(subject).not_to include(local_only_status.id)
+          expect(subject).to_not include(local_only_status.id)
         end
       end
 
@@ -128,24 +130,24 @@ RSpec.describe PublicFeed, type: :model do
     end
 
     context 'with a local_only option set' do
+      subject { described_class.new(viewer, local: true).get(20).map(&:id) }
+
       let!(:local_account)  { Fabricate(:account, domain: nil) }
       let!(:remote_account) { Fabricate(:account, domain: 'test.com') }
       let!(:local_status)   { Fabricate(:status, account: local_account) }
       let!(:remote_status)  { Fabricate(:status, account: remote_account) }
       let!(:local_only_status) { Fabricate(:status, account: local_account, local_only: true) }
 
-      subject { described_class.new(viewer, local: true).get(20).map(&:id) }
-
       context 'without a viewer' do
         let(:viewer) { nil }
 
         it 'does not include remote instances statuses' do
           expect(subject).to include(local_status.id)
-          expect(subject).not_to include(remote_status.id)
+          expect(subject).to_not include(remote_status.id)
         end
 
         it 'does not include local-only statuses' do
-          expect(subject).not_to include(local_only_status.id)
+          expect(subject).to_not include(local_only_status.id)
         end
       end
 
@@ -154,13 +156,13 @@ RSpec.describe PublicFeed, type: :model do
 
         it 'does not include remote instances statuses' do
           expect(subject).to include(local_status.id)
-          expect(subject).not_to include(remote_status.id)
+          expect(subject).to_not include(remote_status.id)
         end
 
         it 'is not affected by personal domain blocks' do
           viewer.block_domain!('test.com')
           expect(subject).to include(local_status.id)
-          expect(subject).not_to include(remote_status.id)
+          expect(subject).to_not include(remote_status.id)
         end
 
         it 'includes local-only statuses' do
@@ -170,18 +172,18 @@ RSpec.describe PublicFeed, type: :model do
     end
 
     context 'with a remote_only option set' do
+      subject { described_class.new(viewer, remote: true).get(20).map(&:id) }
+
       let!(:local_account)  { Fabricate(:account, domain: nil) }
       let!(:remote_account) { Fabricate(:account, domain: 'test.com') }
       let!(:local_status)   { Fabricate(:status, account: local_account) }
       let!(:remote_status)  { Fabricate(:status, account: remote_account) }
 
-      subject { described_class.new(viewer, remote: true).get(20).map(&:id) }
-
       context 'without a viewer' do
         let(:viewer) { nil }
 
         it 'does not include local instances statuses' do
-          expect(subject).not_to include(local_status.id)
+          expect(subject).to_not include(local_status.id)
           expect(subject).to include(remote_status.id)
         end
       end
@@ -190,25 +192,25 @@ RSpec.describe PublicFeed, type: :model do
         let(:viewer) { Fabricate(:account, username: 'viewer') }
 
         it 'does not include local instances statuses' do
-          expect(subject).not_to include(local_status.id)
+          expect(subject).to_not include(local_status.id)
           expect(subject).to include(remote_status.id)
         end
       end
     end
 
     describe 'with an account passed in' do
+      subject { described_class.new(@account).get(20).map(&:id) }
+
       before do
         @account = Fabricate(:account)
       end
 
-      subject { described_class.new(@account).get(20).map(&:id) }
-
       it 'excludes statuses from accounts blocked by the account' do
         blocked = Fabricate(:account)
         @account.block!(blocked)
         blocked_status = Fabricate(:status, account: blocked)
 
-        expect(subject).not_to include(blocked_status.id)
+        expect(subject).to_not include(blocked_status.id)
       end
 
       it 'excludes statuses from accounts who have blocked the account' do
@@ -216,7 +218,7 @@ RSpec.describe PublicFeed, type: :model do
         blocker.block!(@account)
         blocked_status = Fabricate(:status, account: blocker)
 
-        expect(subject).not_to include(blocked_status.id)
+        expect(subject).to_not include(blocked_status.id)
       end
 
       it 'excludes statuses from accounts muted by the account' do
@@ -224,7 +226,7 @@ RSpec.describe PublicFeed, type: :model do
         @account.mute!(muted)
         muted_status = Fabricate(:status, account: muted)
 
-        expect(subject).not_to include(muted_status.id)
+        expect(subject).to_not include(muted_status.id)
       end
 
       it 'excludes statuses from accounts from personally blocked domains' do
@@ -232,7 +234,7 @@ RSpec.describe PublicFeed, type: :model do
         @account.block_domain!(blocked.domain)
         blocked_status = Fabricate(:status, account: blocked)
 
-        expect(subject).not_to include(blocked_status.id)
+        expect(subject).to_not include(blocked_status.id)
       end
 
       context 'with language preferences' do
@@ -244,7 +246,7 @@ RSpec.describe PublicFeed, type: :model do
 
           expect(subject).to include(en_status.id)
           expect(subject).to include(es_status.id)
-          expect(subject).not_to include(fr_status.id)
+          expect(subject).to_not include(fr_status.id)
         end
 
         it 'includes all languages when user does not have a setting' do
diff --git a/spec/models/relay_spec.rb b/spec/models/relay_spec.rb
index 12dc0f20f..86c1762c1 100644
--- a/spec/models/relay_spec.rb
+++ b/spec/models/relay_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Relay, type: :model do
diff --git a/spec/models/remote_follow_spec.rb b/spec/models/remote_follow_spec.rb
index 5b4c19b5b..ea36b0076 100644
--- a/spec/models/remote_follow_spec.rb
+++ b/spec/models/remote_follow_spec.rb
@@ -17,7 +17,7 @@ RSpec.describe RemoteFollow do
       let(:attrs) { { acct: 'gargron@quitter.no' } }
 
       it 'returns acct' do
-        is_expected.to eq 'gargron@quitter.no'
+        expect(subject).to eq 'gargron@quitter.no'
       end
     end
 
@@ -25,7 +25,7 @@ RSpec.describe RemoteFollow do
       let(:attrs) { {} }
 
       it do
-        is_expected.to be_nil
+        expect(subject).to be_nil
       end
     end
   end
@@ -37,7 +37,7 @@ RSpec.describe RemoteFollow do
       let(:attrs) { { acct: 'gargron@quitter.no' } }
 
       it do
-        is_expected.to be true
+        expect(subject).to be true
       end
     end
 
@@ -45,12 +45,14 @@ RSpec.describe RemoteFollow do
       let(:attrs) { {} }
 
       it do
-        is_expected.to be false
+        expect(subject).to be false
       end
     end
   end
 
   describe '#subscribe_address_for' do
+    subject { remote_follow.subscribe_address_for(account) }
+
     before do
       remote_follow.valid?
     end
@@ -58,10 +60,8 @@ RSpec.describe RemoteFollow do
     let(:attrs)   { { acct: 'gargron@quitter.no' } }
     let(:account) { Fabricate(:account, username: 'alice') }
 
-    subject { remote_follow.subscribe_address_for(account) }
-
     it 'returns subscribe address' do
-      is_expected.to eq 'https://quitter.no/main/ostatussub?profile=https%3A%2F%2Fcb6e6126.ngrok.io%2Fusers%2Falice'
+      expect(subject).to eq 'https://quitter.no/main/ostatussub?profile=https%3A%2F%2Fcb6e6126.ngrok.io%2Fusers%2Falice'
     end
   end
 end
diff --git a/spec/models/report_filter_spec.rb b/spec/models/report_filter_spec.rb
index 099c0731d..8269c4579 100644
--- a/spec/models/report_filter_spec.rb
+++ b/spec/models/report_filter_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe ReportFilter do
diff --git a/spec/models/report_spec.rb b/spec/models/report_spec.rb
index 874be4132..20a048c33 100644
--- a/spec/models/report_spec.rb
+++ b/spec/models/report_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Report do
@@ -33,7 +35,7 @@ describe Report do
     end
 
     it 'assigns to a given account' do
-      is_expected.to eq current_account.id
+      expect(subject).to eq current_account.id
     end
   end
 
@@ -48,7 +50,7 @@ describe Report do
     end
 
     it 'unassigns' do
-      is_expected.to be_nil
+      expect(subject).to be_nil
     end
   end
 
@@ -119,12 +121,6 @@ describe Report do
   end
 
   describe 'validations' do
-    it 'has a valid fabricator' do
-      report = Fabricate(:report)
-      report.valid?
-      expect(report).to be_valid
-    end
-
     it 'is invalid if comment is longer than 1000 characters' do
       report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001))
       report.valid?
diff --git a/spec/models/rule_spec.rb b/spec/models/rule_spec.rb
index 8666bda71..c9b9c5502 100644
--- a/spec/models/rule_spec.rb
+++ b/spec/models/rule_spec.rb
@@ -1,5 +1,19 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-RSpec.describe Rule, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe Rule do
+  describe 'scopes' do
+    describe 'ordered' do
+      let(:deleted_rule) { Fabricate(:rule, deleted_at: 10.days.ago) }
+      let(:first_rule) { Fabricate(:rule, deleted_at: nil, priority: 1) }
+      let(:last_rule) { Fabricate(:rule, deleted_at: nil, priority: 10) }
+
+      it 'finds the correct records' do
+        results = described_class.ordered
+
+        expect(results).to eq([first_rule, last_rule])
+      end
+    end
+  end
 end
diff --git a/spec/models/scheduled_status_spec.rb b/spec/models/scheduled_status_spec.rb
index f8c9d8b81..294fa9f36 100644
--- a/spec/models/scheduled_status_spec.rb
+++ b/spec/models/scheduled_status_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ScheduledStatus, type: :model do
diff --git a/spec/models/session_activation_spec.rb b/spec/models/session_activation_spec.rb
index 450dc1399..375199d57 100644
--- a/spec/models/session_activation_spec.rb
+++ b/spec/models/session_activation_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe SessionActivation, type: :model do
     let(:session_activation) { Fabricate(:session_activation, user_agent: 'Chrome/62.0.3202.89') }
 
     it 'sets a Browser instance as detection' do
-      expect(session_activation.detection).to be_kind_of Browser::Chrome
+      expect(session_activation.detection).to be_a Browser::Chrome
     end
   end
 
@@ -44,7 +44,7 @@ RSpec.describe SessionActivation, type: :model do
       let(:id) { nil }
 
       it 'returns nil' do
-        is_expected.to be nil
+        expect(subject).to be_nil
       end
     end
 
@@ -54,7 +54,7 @@ RSpec.describe SessionActivation, type: :model do
 
       context 'id exists as session_id' do
         it 'returns true' do
-          is_expected.to be true
+          expect(subject).to be true
         end
       end
 
@@ -64,7 +64,7 @@ RSpec.describe SessionActivation, type: :model do
         end
 
         it 'returns false' do
-          is_expected.to be false
+          expect(subject).to be false
         end
       end
     end
@@ -80,7 +80,7 @@ RSpec.describe SessionActivation, type: :model do
     end
 
     it 'returns an instance of SessionActivation' do
-      expect(described_class.activate(**options)).to be_kind_of SessionActivation
+      expect(described_class.activate(**options)).to be_a SessionActivation
     end
   end
 
@@ -89,7 +89,7 @@ RSpec.describe SessionActivation, type: :model do
       let(:id) { nil }
 
       it 'returns nil' do
-        expect(described_class.deactivate(id)).to be nil
+        expect(described_class.deactivate(id)).to be_nil
       end
     end
 
@@ -118,8 +118,8 @@ RSpec.describe SessionActivation, type: :model do
     let(:id) { '1' }
 
     it 'calls where.destroy_all' do
-      expect(described_class).to receive_message_chain(:where, :destroy_all)
-        .with('session_id != ?', id).with(no_args)
+      expect(described_class).to receive_message_chain(:where, :not, :destroy_all)
+        .with(session_id: id).with(no_args)
 
       described_class.exclusive(id)
     end
diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb
index 3ccc21d6c..826a13878 100644
--- a/spec/models/setting_spec.rb
+++ b/spec/models/setting_spec.rb
@@ -38,7 +38,7 @@ RSpec.describe Setting, type: :model do
       let(:cache_value)       { 'cache-value' }
 
       it 'calls not RailsSettings::Base#[]' do
-        expect(RailsSettings::Base).not_to receive(:[]).with(key)
+        expect(RailsSettings::Base).to_not receive(:[]).with(key)
         described_class[key]
       end
 
@@ -104,7 +104,7 @@ RSpec.describe Setting, type: :model do
           ActiveSupport::Notifications.subscribed callback, 'sql.active_record' do
             described_class[key]
           end
-          expect(callback).not_to have_received(:call)
+          expect(callback).to_not have_received(:call)
         end
 
         it 'returns the cached value' do
@@ -127,7 +127,7 @@ RSpec.describe Setting, type: :model do
     let(:records)          { [original_setting] }
 
     it 'returns a Hash' do
-      expect(described_class.all_as_records).to be_kind_of Hash
+      expect(described_class.all_as_records).to be_a Hash
     end
 
     context 'records includes Setting with var as the key' do
@@ -146,7 +146,7 @@ RSpec.describe Setting, type: :model do
         it 'includes Setting with value of default_value' do
           setting = described_class.all_as_records[key]
 
-          expect(setting).to be_kind_of Setting
+          expect(setting).to be_a Setting
           expect(setting).to have_attributes(var: key)
           expect(setting).to have_attributes(value: 'default_value')
         end
@@ -163,17 +163,17 @@ RSpec.describe Setting, type: :model do
   end
 
   describe '.default_settings' do
+    subject { described_class.default_settings }
+
     before do
       allow(RailsSettings::Default).to receive(:enabled?).and_return(enabled)
     end
 
-    subject { described_class.default_settings }
-
     context 'RailsSettings::Default.enabled? is false' do
       let(:enabled) { false }
 
       it 'returns {}' do
-        is_expected.to eq({})
+        expect(subject).to eq({})
       end
     end
 
@@ -181,7 +181,7 @@ RSpec.describe Setting, type: :model do
       let(:enabled) { true }
 
       it 'returns instance of RailsSettings::Default' do
-        is_expected.to be_kind_of RailsSettings::Default
+        expect(subject).to be_a RailsSettings::Default
       end
     end
   end
diff --git a/spec/models/status_edit_spec.rb b/spec/models/status_edit_spec.rb
index 2ecafef73..2d3351452 100644
--- a/spec/models/status_edit_spec.rb
+++ b/spec/models/status_edit_spec.rb
@@ -1,5 +1,13 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-RSpec.describe StatusEdit, type: :model do
-  pending "add some examples to (or delete) #{__FILE__}"
+describe StatusEdit do
+  describe '#reblog?' do
+    it 'returns false' do
+      record = described_class.new
+
+      expect(record).to_not be_a_reblog
+    end
+  end
 end
diff --git a/spec/models/status_pin_spec.rb b/spec/models/status_pin_spec.rb
index c18faca78..c4ebf96da 100644
--- a/spec/models/status_pin_spec.rb
+++ b/spec/models/status_pin_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe StatusPin, type: :model do
diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb
index e0a7aba7e..04e5c26af 100644
--- a/spec/models/status_spec.rb
+++ b/spec/models/status_spec.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Status, type: :model do
+  subject { Fabricate(:status, account: alice) }
+
   let(:alice) { Fabricate(:account, username: 'alice') }
   let(:bob)   { Fabricate(:account, username: 'bob') }
   let(:other) { Fabricate(:status, account: bob, text: 'Skulls for the skull god! The enemy\'s gates are sideways!') }
 
-  subject { Fabricate(:status, account: alice) }
-
   describe '#local?' do
     it 'returns true when no remote URI is set' do
       expect(subject.local?).to be true
@@ -204,14 +206,14 @@ RSpec.describe Status, type: :model do
   end
 
   describe 'on create' do
+    subject { Status.new }
+
     let(:local_account) { Fabricate(:account, username: 'local', domain: nil) }
     let(:remote_account) { Fabricate(:account, username: 'remote', domain: 'example.com') }
 
-    subject { Status.new }
-
     describe 'on a status that ends with the local-only emoji' do
       before do
-        subject.text = 'A toot ' + subject.local_only_emoji
+        subject.text = "A toot #{subject.local_only_emoji}"
       end
 
       context 'if the status originates from this instance' do
@@ -241,11 +243,11 @@ RSpec.describe Status, type: :model do
   end
 
   describe '.mutes_map' do
+    subject { Status.mutes_map([status.conversation.id], account) }
+
     let(:status)  { Fabricate(:status) }
     let(:account) { Fabricate(:account) }
 
-    subject { Status.mutes_map([status.conversation.id], account) }
-
     it 'returns a hash' do
       expect(subject).to be_a Hash
     end
@@ -257,11 +259,11 @@ RSpec.describe Status, type: :model do
   end
 
   describe '.favourites_map' do
+    subject { Status.favourites_map([status], account) }
+
     let(:status)  { Fabricate(:status) }
     let(:account) { Fabricate(:account) }
 
-    subject { Status.favourites_map([status], account) }
-
     it 'returns a hash' do
       expect(subject).to be_a Hash
     end
@@ -273,11 +275,11 @@ RSpec.describe Status, type: :model do
   end
 
   describe '.reblogs_map' do
+    subject { Status.reblogs_map([status], account) }
+
     let(:status)  { Fabricate(:status) }
     let(:account) { Fabricate(:account) }
 
-    subject { Status.reblogs_map([status], account) }
-
     it 'returns a hash' do
       expect(subject).to be_a Hash
     end
@@ -289,52 +291,52 @@ RSpec.describe Status, type: :model do
   end
 
   describe '.as_direct_timeline' do
+    subject(:results) { Status.as_direct_timeline(account) }
+
     let(:account) { Fabricate(:account) }
     let(:followed) { Fabricate(:account) }
     let(:not_followed) { Fabricate(:account) }
 
-    before do
-      Fabricate(:follow, account: account, target_account: followed)
-
-      @self_public_status = Fabricate(:status, account: account, visibility: :public)
-      @self_direct_status = Fabricate(:status, account: account, visibility: :direct)
-      @followed_public_status = Fabricate(:status, account: followed, visibility: :public)
-      @followed_direct_status = Fabricate(:status, account: followed, visibility: :direct)
-      @not_followed_direct_status = Fabricate(:status, account: not_followed, visibility: :direct)
+    let!(:self_public_status) { Fabricate(:status, account: account, visibility: :public) }
+    let!(:self_direct_status) { Fabricate(:status, account: account, visibility: :direct) }
+    let!(:followed_public_status) { Fabricate(:status, account: followed, visibility: :public) }
+    let!(:followed_direct_status) { Fabricate(:status, account: followed, visibility: :direct) }
+    let!(:not_followed_direct_status) { Fabricate(:status, account: not_followed, visibility: :direct) }
 
-      @results = Status.as_direct_timeline(account)
+    before do
+      account.follow!(followed)
     end
 
     it 'does not include public statuses from self' do
-      expect(@results).to_not include(@self_public_status)
+      expect(results).to_not include(self_public_status)
     end
 
     it 'includes direct statuses from self' do
-      expect(@results).to include(@self_direct_status)
+      expect(results).to include(self_direct_status)
     end
 
     it 'does not include public statuses from followed' do
-      expect(@results).to_not include(@followed_public_status)
+      expect(results).to_not include(followed_public_status)
     end
 
     it 'does not include direct statuses not mentioning recipient from followed' do
-      expect(@results).to_not include(@followed_direct_status)
+      expect(results).to_not include(followed_direct_status)
     end
 
     it 'does not include direct statuses not mentioning recipient from non-followed' do
-      expect(@results).to_not include(@not_followed_direct_status)
+      expect(results).to_not include(not_followed_direct_status)
     end
 
     it 'includes direct statuses mentioning recipient from followed' do
-      Fabricate(:mention, account: account, status: @followed_direct_status)
+      Fabricate(:mention, account: account, status: followed_direct_status)
       results2 = Status.as_direct_timeline(account)
-      expect(results2).to include(@followed_direct_status)
+      expect(results2).to include(followed_direct_status)
     end
 
     it 'includes direct statuses mentioning recipient from non-followed' do
-      Fabricate(:mention, account: account, status: @not_followed_direct_status)
+      Fabricate(:mention, account: account, status: not_followed_direct_status)
       results2 = Status.as_direct_timeline(account)
-      expect(results2).to include(@not_followed_direct_status)
+      expect(results2).to include(not_followed_direct_status)
     end
   end
 
diff --git a/spec/models/status_stat_spec.rb b/spec/models/status_stat_spec.rb
index af1a6f288..749ca097d 100644
--- a/spec/models/status_stat_spec.rb
+++ b/spec/models/status_stat_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe StatusStat, type: :model do
diff --git a/spec/models/status_trend_spec.rb b/spec/models/status_trend_spec.rb
index 6b82204a6..9678b838a 100644
--- a/spec/models/status_trend_spec.rb
+++ b/spec/models/status_trend_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe StatusTrend, type: :model do
diff --git a/spec/models/system_key_spec.rb b/spec/models/system_key_spec.rb
index a138bc131..a4e8b7784 100644
--- a/spec/models/system_key_spec.rb
+++ b/spec/models/system_key_spec.rb
@@ -1,5 +1,6 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe SystemKey, type: :model do
-
 end
diff --git a/spec/models/tag_feed_spec.rb b/spec/models/tag_feed_spec.rb
index 45f7c3329..d8683b86f 100644
--- a/spec/models/tag_feed_spec.rb
+++ b/spec/models/tag_feed_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe TagFeed, type: :service do
@@ -65,7 +67,7 @@ describe TagFeed, type: :service do
       expect(results).to include(status)
     end
 
-    context 'on a local-only status' do
+    context 'when the feed contains a local-only status' do
       let!(:status) { Fabricate(:status, tags: [tag1], local_only: true) }
 
       it 'does not show local-only statuses without a viewer' do
diff --git a/spec/models/tag_follow_spec.rb b/spec/models/tag_follow_spec.rb
index 50c04d2e4..88409bb28 100644
--- a/spec/models/tag_follow_spec.rb
+++ b/spec/models/tag_follow_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe TagFollow, type: :model do
diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb
index 102d2f625..4d6e5c380 100644
--- a/spec/models/tag_spec.rb
+++ b/spec/models/tag_spec.rb
@@ -1,18 +1,19 @@
 # frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Tag do
   describe 'validations' do
     it 'invalid with #' do
-      expect(described_class.new(name: '#hello_world')).not_to be_valid
+      expect(described_class.new(name: '#hello_world')).to_not be_valid
     end
 
     it 'invalid with .' do
-      expect(described_class.new(name: '.abcdef123')).not_to be_valid
+      expect(described_class.new(name: '.abcdef123')).to_not be_valid
     end
 
     it 'invalid with spaces' do
-      expect(described_class.new(name: 'hello world')).not_to be_valid
+      expect(described_class.new(name: 'hello world')).to_not be_valid
     end
 
     it 'valid with aesthetic' do
diff --git a/spec/models/trends/statuses_spec.rb b/spec/models/trends/statuses_spec.rb
index 5f338a65e..29a20a595 100644
--- a/spec/models/trends/statuses_spec.rb
+++ b/spec/models/trends/statuses_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Trends::Statuses do
@@ -76,7 +78,7 @@ RSpec.describe Trends::Statuses do
     before do
       13.times { reblog(status1, today) }
       13.times { reblog(status2, today) }
-       4.times { reblog(status3, today) }
+      4.times { reblog(status3, today) }
     end
 
     context do
diff --git a/spec/models/trends/tags_spec.rb b/spec/models/trends/tags_spec.rb
index f48c73503..09ac918d0 100644
--- a/spec/models/trends/tags_spec.rb
+++ b/spec/models/trends/tags_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Trends::Tags do
@@ -22,7 +24,9 @@ RSpec.describe Trends::Tags do
   end
 
   describe '#query' do
-    pending
+    it 'returns a composable query scope' do
+      expect(subject.query).to be_a Trends::Query
+    end
   end
 
   describe '#refresh' do
diff --git a/spec/models/unavailable_domain_spec.rb b/spec/models/unavailable_domain_spec.rb
index 3f2621034..5469ff693 100644
--- a/spec/models/unavailable_domain_spec.rb
+++ b/spec/models/unavailable_domain_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe UnavailableDomain, type: :model do
diff --git a/spec/models/user_invite_request_spec.rb b/spec/models/user_invite_request_spec.rb
index 1be38d8a4..95e128439 100644
--- a/spec/models/user_invite_request_spec.rb
+++ b/spec/models/user_invite_request_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe UserInviteRequest, type: :model do
diff --git a/spec/models/user_role_spec.rb b/spec/models/user_role_spec.rb
index 28019593e..97456c106 100644
--- a/spec/models/user_role_spec.rb
+++ b/spec/models/user_role_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe UserRole, type: :model do
@@ -58,7 +60,7 @@ RSpec.describe UserRole, type: :model do
   end
 
   describe '#permissions_as_keys=' do
-    let(:input) { }
+    let(:input) {}
 
     before do
       subject.permissions_as_keys = input
@@ -127,7 +129,7 @@ RSpec.describe UserRole, type: :model do
     subject { described_class.everyone }
 
     it 'returns a role' do
-      expect(subject).to be_kind_of(described_class)
+      expect(subject).to be_a(described_class)
     end
 
     it 'is identified as the everyone role' do
@@ -139,7 +141,7 @@ RSpec.describe UserRole, type: :model do
     end
 
     it 'has negative position' do
-      expect(subject.position).to eq -1
+      expect(subject.position).to eq(-1)
     end
   end
 
@@ -147,7 +149,7 @@ RSpec.describe UserRole, type: :model do
     subject { described_class.nobody }
 
     it 'returns a role' do
-      expect(subject).to be_kind_of(described_class)
+      expect(subject).to be_a(described_class)
     end
 
     it 'is identified as the nobody role' do
@@ -159,7 +161,7 @@ RSpec.describe UserRole, type: :model do
     end
 
     it 'has negative position' do
-      expect(subject.position).to eq -1
+      expect(subject.position).to eq(-1)
     end
   end
 
diff --git a/spec/models/user_settings/namespace_spec.rb b/spec/models/user_settings/namespace_spec.rb
new file mode 100644
index 000000000..ae2fa7b48
--- /dev/null
+++ b/spec/models/user_settings/namespace_spec.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe UserSettings::Namespace do
+  subject { described_class.new(name) }
+
+  let(:name) { :foo }
+
+  describe '#setting' do
+    before do
+      subject.setting :bar, default: 'baz'
+    end
+
+    it 'adds setting to definitions' do
+      expect(subject.definitions[:'foo.bar']).to have_attributes(name: :bar, namespace: :foo, default_value: 'baz')
+    end
+  end
+
+  describe '#definitions' do
+    it 'returns a hash' do
+      expect(subject.definitions).to be_a Hash
+    end
+  end
+end
diff --git a/spec/models/user_settings/setting_spec.rb b/spec/models/user_settings/setting_spec.rb
new file mode 100644
index 000000000..9884ae4f8
--- /dev/null
+++ b/spec/models/user_settings/setting_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe UserSettings::Setting do
+  subject { described_class.new(name, options) }
+
+  let(:name)      { :foo }
+  let(:options)   { { default: default, namespace: namespace } }
+  let(:default)   { false }
+  let(:namespace) { nil }
+
+  describe '#default_value' do
+    context 'when default value is a primitive value' do
+      it 'returns default value' do
+        expect(subject.default_value).to eq default
+      end
+    end
+
+    context 'when default value is a proc' do
+      let(:default) { -> { 'bar' } }
+
+      it 'returns value from proc' do
+        expect(subject.default_value).to eq 'bar'
+      end
+    end
+  end
+
+  describe '#type' do
+    it 'returns a type' do
+      expect(subject.type).to be_a ActiveModel::Type::Value
+    end
+
+    context 'when default value is a boolean' do
+      let(:default) { false }
+
+      it 'returns boolean' do
+        expect(subject.type).to be_a ActiveModel::Type::Boolean
+      end
+    end
+
+    context 'when default value is a string' do
+      let(:default) { '' }
+
+      it 'returns string' do
+        expect(subject.type).to be_a ActiveModel::Type::String
+      end
+    end
+
+    context 'when default value is a lambda returning a boolean' do
+      let(:default) { -> { false } }
+
+      it 'returns boolean' do
+        expect(subject.type).to be_a ActiveModel::Type::Boolean
+      end
+    end
+
+    context 'when default value is a lambda returning a string' do
+      let(:default) { -> { '' } }
+
+      it 'returns boolean' do
+        expect(subject.type).to be_a ActiveModel::Type::String
+      end
+    end
+  end
+
+  describe '#type_cast' do
+    context 'when default value is a boolean' do
+      let(:default) { false }
+
+      it 'returns boolean' do
+        expect(subject.type_cast('1')).to be true
+      end
+    end
+
+    context 'when default value is a string' do
+      let(:default) { '' }
+
+      it 'returns string' do
+        expect(subject.type_cast(1)).to eq '1'
+      end
+    end
+  end
+
+  describe '#to_a' do
+    it 'returns an array' do
+      expect(subject.to_a).to eq [name, default]
+    end
+  end
+
+  describe '#key' do
+    context 'when there is no namespace' do
+      it 'returnsn a symbol' do
+        expect(subject.key).to eq :foo
+      end
+    end
+
+    context 'when there is a namespace' do
+      let(:namespace) { :bar }
+
+      it 'returns a symbol' do
+        expect(subject.key).to eq :'bar.foo'
+      end
+    end
+  end
+end
diff --git a/spec/models/user_settings_spec.rb b/spec/models/user_settings_spec.rb
new file mode 100644
index 000000000..f0e4272fd
--- /dev/null
+++ b/spec/models/user_settings_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe UserSettings do
+  subject { described_class.new(json) }
+
+  let(:json) { {} }
+
+  describe '#[]' do
+    context 'when setting is not set' do
+      it 'returns default value' do
+        expect(subject[:always_send_emails]).to be false
+      end
+    end
+
+    context 'when setting is set' do
+      let(:json) { { default_language: 'fr' } }
+
+      it 'returns value' do
+        expect(subject[:default_language]).to eq 'fr'
+      end
+    end
+
+    context 'when setting was not defined' do
+      it 'raises error' do
+        expect { subject[:foo] }.to raise_error UserSettings::KeyError
+      end
+    end
+  end
+
+  describe '#[]=' do
+    context 'when value matches type' do
+      before do
+        subject[:always_send_emails] = true
+      end
+
+      it 'updates value' do
+        expect(subject[:always_send_emails]).to be true
+      end
+    end
+
+    context 'when value needs to be type-cast' do
+      before do
+        subject[:always_send_emails] = '1'
+      end
+
+      it 'updates value with a type-cast' do
+        expect(subject[:always_send_emails]).to be true
+      end
+    end
+  end
+
+  describe '#update' do
+    before do
+      subject.update(always_send_emails: true, default_language: 'fr', default_privacy: nil)
+    end
+
+    it 'updates values' do
+      expect(subject[:always_send_emails]).to be true
+      expect(subject[:default_language]).to eq 'fr'
+    end
+
+    it 'does not set values that are nil' do
+      expect(subject.as_json).to_not include(default_privacy: nil)
+    end
+  end
+
+  describe '#as_json' do
+    let(:json) { { default_language: 'fr' } }
+
+    it 'returns hash' do
+      expect(subject.as_json).to eq json
+    end
+  end
+
+  describe '.keys' do
+    it 'returns an array' do
+      expect(described_class.keys).to be_a Array
+    end
+  end
+
+  describe '.definition_for' do
+    context 'when key is defined' do
+      it 'returns a setting' do
+        expect(described_class.definition_for(:always_send_emails)).to be_a UserSettings::Setting
+      end
+    end
+
+    context 'when key is not defined' do
+      it 'returns nil' do
+        expect(described_class.definition_for(:foo)).to be_nil
+      end
+    end
+  end
+
+  describe '.definition_for?' do
+    context 'when key is defined' do
+      it 'returns true' do
+        expect(described_class.definition_for?(:always_send_emails)).to be true
+      end
+    end
+
+    context 'when key is not defined' do
+      it 'returns false' do
+        expect(described_class.definition_for?(:foo)).to be false
+      end
+    end
+  end
+end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index a7da31e60..ab883927a 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1,7 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 require 'devise_two_factor/spec_helpers'
 
 RSpec.describe User, type: :model do
+  let(:password) { 'abcd1234' }
+  let(:account) { Fabricate(:account, username: 'alice') }
+
   it_behaves_like 'two_factor_backupable'
 
   describe 'otp_secret' do
@@ -43,7 +48,7 @@ RSpec.describe User, type: :model do
     it 'cleans out empty string from languages' do
       user = Fabricate.build(:user, chosen_languages: [''])
       user.valid?
-      expect(user.chosen_languages).to eq nil
+      expect(user.chosen_languages).to be_nil
     end
   end
 
@@ -96,9 +101,6 @@ RSpec.describe User, type: :model do
     end
   end
 
-  let(:account) { Fabricate(:account, username: 'alice') }
-  let(:password) { 'abcd1234' }
-
   describe 'blacklist' do
     around(:each) do |example|
       old_blacklist = Rails.configuration.x.email_blacklist
@@ -110,19 +112,19 @@ RSpec.describe User, type: :model do
       Rails.configuration.x.email_domains_blacklist = old_blacklist
     end
 
-    it 'should allow a non-blacklisted user to be created' do
+    it 'allows a non-blacklisted user to be created' do
       user = User.new(email: 'foo@example.com', account: account, password: password, agreement: true)
 
       expect(user.valid?).to be_truthy
     end
 
-    it 'should not allow a blacklisted user to be created' do
+    it 'does not allow a blacklisted user to be created' do
       user = User.new(email: 'foo@mvrht.com', account: account, password: password, agreement: true)
 
       expect(user.valid?).to be_falsey
     end
 
-    it 'should not allow a subdomain blacklisted user to be created' do
+    it 'does not allow a subdomain blacklisted user to be created' do
       user = User.new(email: 'foo@mvrht.com.topdomain.tld', account: account, password: password, agreement: true)
 
       expect(user.valid?).to be_falsey
@@ -142,10 +144,136 @@ RSpec.describe User, type: :model do
   end
 
   describe '#confirm' do
-    it 'sets email to unconfirmed_email' do
-      user = Fabricate.build(:user, confirmed_at: Time.now.utc, unconfirmed_email: 'new-email@example.com')
-      user.confirm
-      expect(user.email).to eq 'new-email@example.com'
+    subject { user.confirm }
+
+    let(:new_email) { 'new-email@example.com' }
+
+    before do
+      allow(TriggerWebhookWorker).to receive(:perform_async)
+    end
+
+    context 'when the user is already confirmed' do
+      let!(:user) { Fabricate(:user, confirmed_at: Time.now.utc, approved: true, unconfirmed_email: new_email) }
+
+      it 'sets email to unconfirmed_email' do
+        expect { subject }.to change { user.reload.email }.to(new_email)
+      end
+
+      it 'does not trigger the account.approved Web Hook' do
+        subject
+        expect(TriggerWebhookWorker).to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
+      end
+    end
+
+    context 'when the user is a new user' do
+      let(:user) { Fabricate(:user, confirmed_at: nil, unconfirmed_email: new_email) }
+
+      context 'when the user is already approved' do
+        around(:example) do |example|
+          registrations_mode = Setting.registrations_mode
+          Setting.registrations_mode = 'approved'
+
+          example.run
+
+          Setting.registrations_mode = registrations_mode
+        end
+
+        before do
+          user.approve!
+        end
+
+        it 'sets email to unconfirmed_email' do
+          expect { subject }.to change { user.reload.email }.to(new_email)
+        end
+
+        it 'triggers the account.approved Web Hook' do
+          user.confirm
+          expect(TriggerWebhookWorker).to have_received(:perform_async).with('account.approved', 'Account', user.account_id).once
+        end
+      end
+
+      context 'when the user does not require explicit approval' do
+        around(:example) do |example|
+          registrations_mode = Setting.registrations_mode
+          Setting.registrations_mode = 'open'
+
+          example.run
+
+          Setting.registrations_mode = registrations_mode
+        end
+
+        it 'sets email to unconfirmed_email' do
+          expect { subject }.to change { user.reload.email }.to(new_email)
+        end
+
+        it 'triggers the account.approved Web Hook' do
+          user.confirm
+          expect(TriggerWebhookWorker).to have_received(:perform_async).with('account.approved', 'Account', user.account_id).once
+        end
+      end
+
+      context 'when the user requires explicit approval but is not approved' do
+        around(:example) do |example|
+          registrations_mode = Setting.registrations_mode
+          Setting.registrations_mode = 'approved'
+
+          example.run
+
+          Setting.registrations_mode = registrations_mode
+        end
+
+        it 'sets email to unconfirmed_email' do
+          expect { subject }.to change { user.reload.email }.to(new_email)
+        end
+
+        it 'does not trigger the account.approved Web Hook' do
+          subject
+          expect(TriggerWebhookWorker).to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
+        end
+      end
+    end
+  end
+
+  describe '#approve!' do
+    subject { user.approve! }
+
+    around(:example) do |example|
+      registrations_mode = Setting.registrations_mode
+      Setting.registrations_mode = 'approved'
+
+      example.run
+
+      Setting.registrations_mode = registrations_mode
+    end
+
+    before do
+      allow(TriggerWebhookWorker).to receive(:perform_async)
+    end
+
+    context 'when the user is already confirmed' do
+      let(:user) { Fabricate(:user, confirmed_at: Time.now.utc, approved: false) }
+
+      it 'sets the approved flag' do
+        expect { subject }.to change { user.reload.approved? }.to(true)
+      end
+
+      it 'triggers the account.approved Web Hook' do
+        subject
+        expect(TriggerWebhookWorker).to have_received(:perform_async).with('account.approved', 'Account', user.account_id).once
+      end
+    end
+
+    context 'when the user is not confirmed' do
+      let(:user) { Fabricate(:user, confirmed_at: nil, approved: false) }
+
+      it 'sets the approved flag' do
+        expect { subject }.to change { user.reload.approved? }.to(true)
+      end
+
+      it 'does not trigger the account.approved Web Hook' do
+        subject
+        expect(TriggerWebhookWorker).to_not have_received(:perform_async).with('account.approved', 'Account', user.account_id)
+      end
     end
   end
 
@@ -159,7 +287,7 @@ RSpec.describe User, type: :model do
     it 'saves nil for otp_secret' do
       user = Fabricate.build(:user, otp_secret: 'oldotpcode')
       user.disable_two_factor!
-      expect(user.reload.otp_secret).to be nil
+      expect(user.reload.otp_secret).to be_nil
     end
 
     it 'saves cleared otp_backup_codes' do
@@ -185,9 +313,9 @@ RSpec.describe User, type: :model do
   end
 
   describe 'settings' do
-    it 'is instance of Settings::ScopedSettings' do
+    it 'is instance of UserSettings' do
       user = Fabricate(:user)
-      expect(user.settings).to be_kind_of Settings::ScopedSettings
+      expect(user.settings).to be_a UserSettings
     end
   end
 
@@ -220,17 +348,17 @@ RSpec.describe User, type: :model do
       Rails.configuration.x.email_domains_whitelist = old_whitelist
     end
 
-    it 'should not allow a user to be created unless they are whitelisted' do
+    it 'does not allow a user to be created unless they are whitelisted' do
       user = User.new(email: 'foo@example.com', account: account, password: password, agreement: true)
       expect(user.valid?).to be_falsey
     end
 
-    it 'should allow a user to be created if they are whitelisted' do
+    it 'allows a user to be created if they are whitelisted' do
       user = User.new(email: 'foo@mastodon.space', account: account, password: password, agreement: true)
       expect(user.valid?).to be_truthy
     end
 
-    it 'should not allow a user with a whitelisted top domain as subdomain in their email address to be created' do
+    it 'does not allow a user with a whitelisted top domain as subdomain in their email address to be created' do
       user = User.new(email: 'foo@mastodon.space.userdomain.com', account: account, password: password, agreement: true)
       expect(user.valid?).to be_falsey
     end
@@ -242,7 +370,7 @@ RSpec.describe User, type: :model do
         Rails.configuration.x.email_domains_blacklist = old_blacklist
       end
 
-      it 'should not allow a user to be created with a specific blacklisted subdomain even if the top domain is whitelisted' do
+      it 'does not allow a user to be created with a specific blacklisted subdomain even if the top domain is whitelisted' do
         Rails.configuration.x.email_domains_blacklist = 'blacklisted.mastodon.space'
 
         user = User.new(email: 'foo@blacklisted.mastodon.space', account: account, password: password)
@@ -251,16 +379,6 @@ RSpec.describe User, type: :model do
     end
   end
 
-  it_behaves_like 'Settings-extended' do
-    def create!
-      User.create!(account: Fabricate(:account, user: nil), email: 'foo@mastodon.space', password: 'abcd1234', agreement: true)
-    end
-
-    def fabricate
-      Fabricate(:user)
-    end
-  end
-
   describe 'token_for_app' do
     let(:user) { Fabricate(:user) }
     let(:app) { Fabricate(:application, owner: user) }
@@ -283,6 +401,7 @@ RSpec.describe User, type: :model do
 
   describe '#disable!' do
     subject(:user) { Fabricate(:user, disabled: false, current_sign_in_at: current_sign_in_at, last_sign_in_at: nil) }
+
     let(:current_sign_in_at) { Time.zone.now }
 
     before do
@@ -371,6 +490,7 @@ RSpec.describe User, type: :model do
 
   describe '#active_for_authentication?' do
     subject { user.active_for_authentication? }
+
     let(:user) { Fabricate(:user, disabled: disabled, confirmed_at: confirmed_at) }
 
     context 'when user is disabled' do
diff --git a/spec/models/web/push_subscription_spec.rb b/spec/models/web/push_subscription_spec.rb
index bd5719593..e925e4c4c 100644
--- a/spec/models/web/push_subscription_spec.rb
+++ b/spec/models/web/push_subscription_spec.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Web::PushSubscription, type: :model do
+  subject { described_class.new(data: data) }
+
   let(:account) { Fabricate(:account) }
 
   let(:policy) { 'all' }
@@ -19,8 +23,6 @@ RSpec.describe Web::PushSubscription, type: :model do
     }
   end
 
-  subject { described_class.new(data: data) }
-
   describe '#pushable?' do
     let(:notification_type) { :mention }
     let(:notification) { Fabricate(:notification, account: account, type: notification_type) }
@@ -29,7 +31,7 @@ RSpec.describe Web::PushSubscription, type: :model do
       context "when notification is a #{type}" do
         let(:notification_type) { type }
 
-        it "returns boolean corresponding to alert setting" do
+        it 'returns boolean corresponding to alert setting' do
           expect(subject.pushable?(notification)).to eq data[:alerts][type]
         end
       end
@@ -39,7 +41,7 @@ RSpec.describe Web::PushSubscription, type: :model do
       let(:policy) { 'all' }
 
       it 'returns true' do
-        expect(subject.pushable?(notification)).to eq true
+        expect(subject.pushable?(notification)).to be true
       end
     end
 
@@ -47,7 +49,7 @@ RSpec.describe Web::PushSubscription, type: :model do
       let(:policy) { 'none' }
 
       it 'returns false' do
-        expect(subject.pushable?(notification)).to eq false
+        expect(subject.pushable?(notification)).to be false
       end
     end
 
@@ -60,13 +62,13 @@ RSpec.describe Web::PushSubscription, type: :model do
         end
 
         it 'returns true' do
-          expect(subject.pushable?(notification)).to eq true
+          expect(subject.pushable?(notification)).to be true
         end
       end
 
       context 'and notification is not from someone you follow' do
         it 'returns false' do
-          expect(subject.pushable?(notification)).to eq false
+          expect(subject.pushable?(notification)).to be false
         end
       end
     end
@@ -80,13 +82,13 @@ RSpec.describe Web::PushSubscription, type: :model do
         end
 
         it 'returns true' do
-          expect(subject.pushable?(notification)).to eq true
+          expect(subject.pushable?(notification)).to be true
         end
       end
 
       context 'and notification is not from someone who follows you' do
         it 'returns false' do
-          expect(subject.pushable?(notification)).to eq false
+          expect(subject.pushable?(notification)).to be false
         end
       end
     end
diff --git a/spec/models/web/setting_spec.rb b/spec/models/web/setting_spec.rb
index 6657d4030..b7ff3c868 100644
--- a/spec/models/web/setting_spec.rb
+++ b/spec/models/web/setting_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Web::Setting, type: :model do
diff --git a/spec/models/webauthn_credentials_spec.rb b/spec/models/webauthn_credentials_spec.rb
index a63ae6cd2..1a2a2f909 100644
--- a/spec/models/webauthn_credentials_spec.rb
+++ b/spec/models/webauthn_credentials_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe WebauthnCredential, type: :model do
@@ -35,8 +37,8 @@ RSpec.describe WebauthnCredential, type: :model do
     end
 
     it 'is invalid if already exist a webauthn credential with the same external id' do
-      existing_webauthn_credential = Fabricate(:webauthn_credential, external_id: "_Typ0ygudDnk9YUVWLQayw")
-      new_webauthn_credential = Fabricate.build(:webauthn_credential, external_id: "_Typ0ygudDnk9YUVWLQayw")
+      existing_webauthn_credential = Fabricate(:webauthn_credential, external_id: '_Typ0ygudDnk9YUVWLQayw')
+      new_webauthn_credential = Fabricate.build(:webauthn_credential, external_id: '_Typ0ygudDnk9YUVWLQayw')
 
       new_webauthn_credential.valid?
 
diff --git a/spec/models/webhook_spec.rb b/spec/models/webhook_spec.rb
index 60c3d9524..fcf3dd14f 100644
--- a/spec/models/webhook_spec.rb
+++ b/spec/models/webhook_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe Webhook, type: :model do
diff --git a/spec/policies/account_policy_spec.rb b/spec/policies/account_policy_spec.rb
index 0f23fd97e..d96153233 100644
--- a/spec/policies/account_policy_spec.rb
+++ b/spec/policies/account_policy_spec.rb
@@ -116,4 +116,44 @@ RSpec.describe AccountPolicy do
       end
     end
   end
+
+  permissions :review? do
+    context 'admin' do
+      it 'permits' do
+        expect(subject).to permit(admin)
+      end
+    end
+
+    context 'not admin' do
+      it 'denies' do
+        expect(subject).to_not permit(john)
+      end
+    end
+  end
+
+  permissions :destroy? do
+    context 'admin' do
+      context 'with a temporarily suspended account' do
+        before { allow(alice).to receive(:suspended_temporarily?).and_return(true) }
+
+        it 'permits' do
+          expect(subject).to permit(admin, alice)
+        end
+      end
+
+      context 'with a not temporarily suspended account' do
+        before { allow(alice).to receive(:suspended_temporarily?).and_return(false) }
+
+        it 'denies' do
+          expect(subject).to_not permit(admin, alice)
+        end
+      end
+    end
+
+    context 'not admin' do
+      it 'denies' do
+        expect(subject).to_not permit(john, alice)
+      end
+    end
+  end
 end
diff --git a/spec/policies/account_warning_preset_policy_spec.rb b/spec/policies/account_warning_preset_policy_spec.rb
new file mode 100644
index 000000000..63bf33de2
--- /dev/null
+++ b/spec/policies/account_warning_preset_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe AccountWarningPresetPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :create?, :update?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/admin/status_policy_spec.rb b/spec/policies/admin/status_policy_spec.rb
new file mode 100644
index 000000000..9e81a4f5f
--- /dev/null
+++ b/spec/policies/admin/status_policy_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe Admin::StatusPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+  let(:status) { Fabricate(:status) }
+
+  permissions :index?, :update?, :review?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+
+  permissions :show? do
+    context 'with an admin' do
+      context 'with a public visible status' do
+        before { allow(status).to receive(:public_visibility?).and_return(true) }
+
+        it 'permits' do
+          expect(policy).to permit(admin, status)
+        end
+      end
+
+      context 'with a not public visible status' do
+        before { allow(status).to receive(:public_visibility?).and_return(false) }
+
+        it 'denies' do
+          expect(policy).to_not permit(admin, status)
+        end
+      end
+    end
+
+    context 'with a non admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, status)
+      end
+    end
+  end
+end
diff --git a/spec/policies/announcement_policy_spec.rb b/spec/policies/announcement_policy_spec.rb
new file mode 100644
index 000000000..3d230b3cb
--- /dev/null
+++ b/spec/policies/announcement_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe AnnouncementPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :create?, :update?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/appeal_policy_spec.rb b/spec/policies/appeal_policy_spec.rb
new file mode 100644
index 000000000..d7498eb9f
--- /dev/null
+++ b/spec/policies/appeal_policy_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe AppealPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+  let(:appeal) { Fabricate(:appeal) }
+
+  permissions :index? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+
+  permissions :reject? do
+    context 'with an admin' do
+      context 'with a pending appeal' do
+        before { allow(appeal).to receive(:pending?).and_return(true) }
+
+        it 'permits' do
+          expect(policy).to permit(admin, appeal)
+        end
+      end
+
+      context 'with a not pending appeal' do
+        before { allow(appeal).to receive(:pending?).and_return(false) }
+
+        it 'denies' do
+          expect(policy).to_not permit(admin, appeal)
+        end
+      end
+    end
+
+    context 'with a non admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, appeal)
+      end
+    end
+  end
+end
diff --git a/spec/policies/canonical_email_block_policy_spec.rb b/spec/policies/canonical_email_block_policy_spec.rb
new file mode 100644
index 000000000..0e55febfa
--- /dev/null
+++ b/spec/policies/canonical_email_block_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe CanonicalEmailBlockPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :show?, :test?, :create?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/delivery_policy_spec.rb b/spec/policies/delivery_policy_spec.rb
new file mode 100644
index 000000000..fbcbf390d
--- /dev/null
+++ b/spec/policies/delivery_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe DeliveryPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :clear_delivery_errors?, :restart_delivery?, :stop_delivery? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/email_domain_block_policy_spec.rb b/spec/policies/email_domain_block_policy_spec.rb
index 913075c3d..e7c455907 100644
--- a/spec/policies/email_domain_block_policy_spec.rb
+++ b/spec/policies/email_domain_block_policy_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe EmailDomainBlockPolicy do
   let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
-  permissions :index?, :create?, :destroy? do
+  permissions :index?, :show?, :create?, :destroy? do
     context 'admin' do
       it 'permits' do
         expect(subject).to permit(admin, EmailDomainBlock)
diff --git a/spec/policies/follow_recommendation_policy_spec.rb b/spec/policies/follow_recommendation_policy_spec.rb
new file mode 100644
index 000000000..01f4da0be
--- /dev/null
+++ b/spec/policies/follow_recommendation_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe FollowRecommendationPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :show?, :suppress?, :unsuppress? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/ip_block_policy_spec.rb b/spec/policies/ip_block_policy_spec.rb
new file mode 100644
index 000000000..3cfa85863
--- /dev/null
+++ b/spec/policies/ip_block_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe IpBlockPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :show?, :create?, :update?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/preview_card_policy_spec.rb b/spec/policies/preview_card_policy_spec.rb
new file mode 100644
index 000000000..d6675c5b3
--- /dev/null
+++ b/spec/policies/preview_card_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe PreviewCardPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :review? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/preview_card_provider_policy_spec.rb b/spec/policies/preview_card_provider_policy_spec.rb
new file mode 100644
index 000000000..8d3715de9
--- /dev/null
+++ b/spec/policies/preview_card_provider_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe PreviewCardProviderPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :review? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/rule_policy_spec.rb b/spec/policies/rule_policy_spec.rb
new file mode 100644
index 000000000..0e45f6df0
--- /dev/null
+++ b/spec/policies/rule_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe RulePolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :create?, :update?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/policies/settings_policy_spec.rb b/spec/policies/settings_policy_spec.rb
index e16ee51a4..3268c1622 100644
--- a/spec/policies/settings_policy_spec.rb
+++ b/spec/policies/settings_policy_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe SettingsPolicy do
   let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
-  permissions :update?, :show? do
+  permissions :update?, :show?, :destroy? do
     context 'admin?' do
       it 'permits' do
         expect(subject).to permit(admin, Settings)
diff --git a/spec/policies/status_policy_spec.rb b/spec/policies/status_policy_spec.rb
index 2afcfe96e..38b9c4fdb 100644
--- a/spec/policies/status_policy_spec.rb
+++ b/spec/policies/status_policy_spec.rb
@@ -39,6 +39,14 @@ RSpec.describe StatusPolicy, type: :model do
       expect(subject).to permit(alice, status)
     end
 
+    it 'grants access when direct and non-owner viewer is mentioned and mentions are loaded' do
+      status.visibility = :direct
+      status.mentions = [Fabricate(:mention, account: bob)]
+      status.mentions.load
+
+      expect(subject).to permit(bob, status)
+    end
+
     it 'denies access when direct and viewer is not mentioned' do
       viewer = Fabricate(:account)
       status.visibility = :direct
@@ -75,14 +83,14 @@ RSpec.describe StatusPolicy, type: :model do
     end
 
     it 'denies access when local-only and the viewer is not logged in' do
-      allow(status).to receive(:local_only?) { true }
+      allow(status).to receive(:local_only?).and_return(true)
 
       expect(subject).to_not permit(nil, status)
     end
 
     it 'denies access when local-only and the viewer is from another domain' do
       viewer = Fabricate(:account, domain: 'remote-domain')
-      allow(status).to receive(:local_only?) { true }
+      allow(status).to receive(:local_only?).and_return(true)
       expect(subject).to_not permit(viewer, status)
     end
   end
diff --git a/spec/policies/tag_policy_spec.rb b/spec/policies/tag_policy_spec.rb
index 9be7140fc..fb09fdd3b 100644
--- a/spec/policies/tag_policy_spec.rb
+++ b/spec/policies/tag_policy_spec.rb
@@ -8,7 +8,7 @@ RSpec.describe TagPolicy do
   let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
   let(:john)    { Fabricate(:account) }
 
-  permissions :index?, :show?, :update? do
+  permissions :index?, :show?, :update?, :review? do
     context 'staff?' do
       it 'permits' do
         expect(subject).to permit(admin, Tag)
diff --git a/spec/policies/webhook_policy_spec.rb b/spec/policies/webhook_policy_spec.rb
new file mode 100644
index 000000000..1eac8932d
--- /dev/null
+++ b/spec/policies/webhook_policy_spec.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+require 'pundit/rspec'
+
+describe WebhookPolicy do
+  let(:policy) { described_class }
+  let(:admin)   { Fabricate(:user, role: UserRole.find_by(name: 'Admin')).account }
+  let(:john)    { Fabricate(:account) }
+
+  permissions :index?, :create?, :show?, :update?, :enable?, :disable?, :rotate_secret?, :destroy? do
+    context 'with an admin' do
+      it 'permits' do
+        expect(policy).to permit(admin, Tag)
+      end
+    end
+
+    context 'with a non-admin' do
+      it 'denies' do
+        expect(policy).to_not permit(john, Tag)
+      end
+    end
+  end
+end
diff --git a/spec/presenters/familiar_followers_presenter_spec.rb b/spec/presenters/familiar_followers_presenter_spec.rb
index 17be4b971..607e3002f 100644
--- a/spec/presenters/familiar_followers_presenter_spec.rb
+++ b/spec/presenters/familiar_followers_presenter_spec.rb
@@ -4,12 +4,12 @@ require 'rails_helper'
 
 RSpec.describe FamiliarFollowersPresenter do
   describe '#accounts' do
+    subject { described_class.new(requested_accounts, account.id) }
+
     let(:account) { Fabricate(:account) }
     let(:familiar_follower) { Fabricate(:account) }
     let(:requested_accounts) { Fabricate.times(2, :account) }
 
-    subject { described_class.new(requested_accounts, account.id) }
-
     before do
       familiar_follower.follow!(requested_accounts.first)
       account.follow!(familiar_follower)
diff --git a/spec/presenters/instance_presenter_spec.rb b/spec/presenters/instance_presenter_spec.rb
index f4c415d2f..f20dce593 100644
--- a/spec/presenters/instance_presenter_spec.rb
+++ b/spec/presenters/instance_presenter_spec.rb
@@ -1,7 +1,9 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe InstancePresenter do
-  let(:instance_presenter) { InstancePresenter.new }
+  let(:instance_presenter) { described_class.new }
 
   describe '#description' do
     around do |example|
@@ -10,9 +12,9 @@ describe InstancePresenter do
       Setting.site_short_description = site_description
     end
 
-    it "delegates site_description to Setting" do
-      Setting.site_short_description = "Site desc"
-      expect(instance_presenter.description).to eq "Site desc"
+    it 'delegates site_description to Setting' do
+      Setting.site_short_description = 'Site desc'
+      expect(instance_presenter.description).to eq 'Site desc'
     end
   end
 
@@ -23,9 +25,9 @@ describe InstancePresenter do
       Setting.site_extended_description = site_extended_description
     end
 
-    it "delegates site_extended_description to Setting" do
-      Setting.site_extended_description = "Extended desc"
-      expect(instance_presenter.extended_description).to eq "Extended desc"
+    it 'delegates site_extended_description to Setting' do
+      Setting.site_extended_description = 'Extended desc'
+      expect(instance_presenter.extended_description).to eq 'Extended desc'
     end
   end
 
@@ -36,9 +38,9 @@ describe InstancePresenter do
       Setting.site_contact_email = site_contact_email
     end
 
-    it "delegates contact_email to Setting" do
-      Setting.site_contact_email = "admin@example.com"
-      expect(instance_presenter.contact.email).to eq "admin@example.com"
+    it 'delegates contact_email to Setting' do
+      Setting.site_contact_email = 'admin@example.com'
+      expect(instance_presenter.contact.email).to eq 'admin@example.com'
     end
   end
 
@@ -49,15 +51,15 @@ describe InstancePresenter do
       Setting.site_contact_username = site_contact_username
     end
 
-    it "returns the account for the site contact username" do
-      Setting.site_contact_username = "aaa"
-      account = Fabricate(:account, username: "aaa")
+    it 'returns the account for the site contact username' do
+      Setting.site_contact_username = 'aaa'
+      account = Fabricate(:account, username: 'aaa')
       expect(instance_presenter.contact.account).to eq(account)
     end
   end
 
   describe '#user_count' do
-    it "returns the number of site users" do
+    it 'returns the number of site users' do
       Rails.cache.write 'user_count', 123
 
       expect(instance_presenter.user_count).to eq(123)
@@ -65,7 +67,7 @@ describe InstancePresenter do
   end
 
   describe '#status_count' do
-    it "returns the number of local statuses" do
+    it 'returns the number of local statuses' do
       Rails.cache.write 'local_status_count', 234
 
       expect(instance_presenter.status_count).to eq(234)
@@ -73,7 +75,7 @@ describe InstancePresenter do
   end
 
   describe '#domain_count' do
-    it "returns the number of known domains" do
+    it 'returns the number of known domains' do
       Rails.cache.write 'distinct_domain_count', 345
 
       expect(instance_presenter.domain_count).to eq(345)
@@ -87,8 +89,28 @@ describe InstancePresenter do
   end
 
   describe '#source_url' do
-    it 'returns "https://github.com/glitch-soc/mastodon"' do
-      expect(instance_presenter.source_url).to eq('https://github.com/glitch-soc/mastodon')
+    context 'with the GITHUB_REPOSITORY env variable set' do
+      around do |example|
+        ClimateControl.modify GITHUB_REPOSITORY: 'other/repo' do
+          example.run
+        end
+      end
+
+      it 'uses the env variable to build a repo URL' do
+        expect(instance_presenter.source_url).to eq('https://github.com/other/repo')
+      end
+    end
+
+    context 'without the GITHUB_REPOSITORY env variable set' do
+      around do |example|
+        ClimateControl.modify GITHUB_REPOSITORY: nil do
+          example.run
+        end
+      end
+
+      it 'defaults to the core glitch-soc repo URL' do
+        expect(instance_presenter.source_url).to eq('https://github.com/glitch-soc/mastodon')
+      end
     end
   end
 
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 02827a388..c204fcdbd 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -1,13 +1,16 @@
+# frozen_string_literal: true
+
 ENV['RAILS_ENV'] ||= 'test'
-require File.expand_path('../../config/environment', __FILE__)
+require File.expand_path('../config/environment', __dir__)
 
-abort("The Rails environment is running in production mode!") if Rails.env.production?
+abort('The Rails environment is running in production mode!') if Rails.env.production?
 
 require 'spec_helper'
 require 'rspec/rails'
 require 'webmock/rspec'
 require 'paperclip/matchers'
 require 'capybara/rspec'
+require 'chewy/rspec'
 
 Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
 
@@ -33,7 +36,7 @@ Devise::Test::ControllerHelpers.module_eval do
 end
 
 RSpec.configure do |config|
-  config.fixture_path = "#{::Rails.root}/spec/fixtures"
+  config.fixture_path = "#{Rails.root}/spec/fixtures"
   config.use_transactional_fixtures = true
   config.order = 'random'
   config.infer_spec_type_from_file_location!
@@ -43,6 +46,7 @@ RSpec.configure do |config|
   config.include Devise::Test::ControllerHelpers, type: :view
   config.include Paperclip::Shoulda::Matchers
   config.include ActiveSupport::Testing::TimeHelpers
+  config.include Chewy::Rspec::Helpers
   config.include Redisable
 
   config.before :each, type: :feature do
@@ -71,11 +75,11 @@ end
 RSpec::Matchers.define_negated_matcher :not_change, :change
 
 def request_fixture(name)
-  File.read(Rails.root.join('spec', 'fixtures', 'requests', name))
+  Rails.root.join('spec', 'fixtures', 'requests', name).read
 end
 
 def attachment_fixture(name)
-  File.open(Rails.root.join('spec', 'fixtures', 'files', name))
+  Rails.root.join('spec', 'fixtures', 'files', name).open
 end
 
 def stub_jsonld_contexts!
diff --git a/spec/requests/catch_all_route_request_spec.rb b/spec/requests/catch_all_route_request_spec.rb
index f965f5522..e600bedfe 100644
--- a/spec/requests/catch_all_route_request_spec.rb
+++ b/spec/requests/catch_all_route_request_spec.rb
@@ -1,21 +1,23 @@
-require "rails_helper"
+# frozen_string_literal: true
 
-describe "The catch all route" do
-  describe "with a simple value" do
-    it "returns a 404 page as html" do
-      get "/test"
+require 'rails_helper'
 
-      expect(response.status).to eq 404
-      expect(response.media_type).to eq "text/html"
+describe 'The catch all route' do
+  describe 'with a simple value' do
+    it 'returns a 404 page as html' do
+      get '/test'
+
+      expect(response).to have_http_status 404
+      expect(response.media_type).to eq 'text/html'
     end
   end
 
-  describe "with an implied format" do
-    it "returns a 404 page as html" do
-      get "/test.test"
+  describe 'with an implied format' do
+    it 'returns a 404 page as html' do
+      get '/test.test'
 
-      expect(response.status).to eq 404
-      expect(response.media_type).to eq "text/html"
+      expect(response).to have_http_status 404
+      expect(response.media_type).to eq 'text/html'
     end
   end
 end
diff --git a/spec/requests/host_meta_request_spec.rb b/spec/requests/host_meta_request_spec.rb
index 0ca641461..ec26ecba7 100644
--- a/spec/requests/host_meta_request_spec.rb
+++ b/spec/requests/host_meta_request_spec.rb
@@ -1,12 +1,14 @@
-require "rails_helper"
+# frozen_string_literal: true
 
-describe "The host_meta route" do
-  describe "requested without accepts headers" do
-    it "returns an xml response" do
+require 'rails_helper'
+
+describe 'The host_meta route' do
+  describe 'requested without accepts headers' do
+    it 'returns an xml response' do
       get host_meta_url
 
       expect(response).to have_http_status(200)
-      expect(response.media_type).to eq "application/xrd+xml"
+      expect(response.media_type).to eq 'application/xrd+xml'
     end
   end
 end
diff --git a/spec/requests/localization_spec.rb b/spec/requests/localization_spec.rb
index 0bc2786ac..39eeee5f0 100644
--- a/spec/requests/localization_spec.rb
+++ b/spec/requests/localization_spec.rb
@@ -10,7 +10,7 @@ describe 'Localization' do
   it 'uses a specific region when provided' do
     headers = { 'Accept-Language' => 'zh-HK' }
 
-    get "/auth/sign_in", headers: headers
+    get '/auth/sign_in', headers: headers
 
     expect(response.body).to include(
       I18n.t('auth.login', locale: 'zh-HK')
@@ -20,7 +20,7 @@ describe 'Localization' do
   it 'falls back to a locale when region missing' do
     headers = { 'Accept-Language' => 'es-FAKE' }
 
-    get "/auth/sign_in", headers: headers
+    get '/auth/sign_in', headers: headers
 
     expect(response.body).to include(
       I18n.t('auth.login', locale: 'es')
@@ -30,7 +30,7 @@ describe 'Localization' do
   it 'falls back to english when locale is missing' do
     headers = { 'Accept-Language' => '12-FAKE' }
 
-    get "/auth/sign_in", headers: headers
+    get '/auth/sign_in', headers: headers
 
     expect(response.body).to include(
       I18n.t('auth.login', locale: 'en')
diff --git a/spec/requests/webfinger_request_spec.rb b/spec/requests/webfinger_request_spec.rb
index 209fda72a..68a1478be 100644
--- a/spec/requests/webfinger_request_spec.rb
+++ b/spec/requests/webfinger_request_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe 'The webfinger route' do
diff --git a/spec/routing/accounts_routing_spec.rb b/spec/routing/accounts_routing_spec.rb
index 3f0e9b3e9..8b2c124fd 100644
--- a/spec/routing/accounts_routing_spec.rb
+++ b/spec/routing/accounts_routing_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe 'Routes under accounts/' do
diff --git a/spec/routing/api_routing_spec.rb b/spec/routing/api_routing_spec.rb
index 2683ccb8d..a822fba4c 100644
--- a/spec/routing/api_routing_spec.rb
+++ b/spec/routing/api_routing_spec.rb
@@ -5,99 +5,99 @@ require 'rails_helper'
 describe 'API routes' do
   describe 'Credentials routes' do
     it 'routes to verify credentials' do
-      expect(get('/api/v1/accounts/verify_credentials')).
-        to route_to('api/v1/accounts/credentials#show')
+      expect(get('/api/v1/accounts/verify_credentials'))
+        .to route_to('api/v1/accounts/credentials#show')
     end
 
     it 'routes to update credentials' do
-      expect(patch('/api/v1/accounts/update_credentials')).
-        to route_to('api/v1/accounts/credentials#update')
+      expect(patch('/api/v1/accounts/update_credentials'))
+        .to route_to('api/v1/accounts/credentials#update')
     end
   end
 
   describe 'Account routes' do
     it 'routes to statuses' do
-      expect(get('/api/v1/accounts/user/statuses')).
-        to route_to('api/v1/accounts/statuses#index', account_id: 'user')
+      expect(get('/api/v1/accounts/user/statuses'))
+        .to route_to('api/v1/accounts/statuses#index', account_id: 'user')
     end
 
     it 'routes to followers' do
-      expect(get('/api/v1/accounts/user/followers')).
-        to route_to('api/v1/accounts/follower_accounts#index', account_id: 'user')
+      expect(get('/api/v1/accounts/user/followers'))
+        .to route_to('api/v1/accounts/follower_accounts#index', account_id: 'user')
     end
 
     it 'routes to following' do
-      expect(get('/api/v1/accounts/user/following')).
-        to route_to('api/v1/accounts/following_accounts#index', account_id: 'user')
+      expect(get('/api/v1/accounts/user/following'))
+        .to route_to('api/v1/accounts/following_accounts#index', account_id: 'user')
     end
 
     it 'routes to search' do
-      expect(get('/api/v1/accounts/search')).
-        to route_to('api/v1/accounts/search#show')
+      expect(get('/api/v1/accounts/search'))
+        .to route_to('api/v1/accounts/search#show')
     end
 
     it 'routes to relationships' do
-      expect(get('/api/v1/accounts/relationships')).
-        to route_to('api/v1/accounts/relationships#index')
+      expect(get('/api/v1/accounts/relationships'))
+        .to route_to('api/v1/accounts/relationships#index')
     end
   end
 
   describe 'Statuses routes' do
     it 'routes reblogged_by' do
-      expect(get('/api/v1/statuses/123/reblogged_by')).
-        to route_to('api/v1/statuses/reblogged_by_accounts#index', status_id: '123')
+      expect(get('/api/v1/statuses/123/reblogged_by'))
+        .to route_to('api/v1/statuses/reblogged_by_accounts#index', status_id: '123')
     end
 
     it 'routes favourited_by' do
-      expect(get('/api/v1/statuses/123/favourited_by')).
-        to route_to('api/v1/statuses/favourited_by_accounts#index', status_id: '123')
+      expect(get('/api/v1/statuses/123/favourited_by'))
+        .to route_to('api/v1/statuses/favourited_by_accounts#index', status_id: '123')
     end
 
     it 'routes reblog' do
-      expect(post('/api/v1/statuses/123/reblog')).
-        to route_to('api/v1/statuses/reblogs#create', status_id: '123')
+      expect(post('/api/v1/statuses/123/reblog'))
+        .to route_to('api/v1/statuses/reblogs#create', status_id: '123')
     end
 
     it 'routes unreblog' do
-      expect(post('/api/v1/statuses/123/unreblog')).
-        to route_to('api/v1/statuses/reblogs#destroy', status_id: '123')
+      expect(post('/api/v1/statuses/123/unreblog'))
+        .to route_to('api/v1/statuses/reblogs#destroy', status_id: '123')
     end
 
     it 'routes favourite' do
-      expect(post('/api/v1/statuses/123/favourite')).
-        to route_to('api/v1/statuses/favourites#create', status_id: '123')
+      expect(post('/api/v1/statuses/123/favourite'))
+        .to route_to('api/v1/statuses/favourites#create', status_id: '123')
     end
 
     it 'routes unfavourite' do
-      expect(post('/api/v1/statuses/123/unfavourite')).
-        to route_to('api/v1/statuses/favourites#destroy', status_id: '123')
+      expect(post('/api/v1/statuses/123/unfavourite'))
+        .to route_to('api/v1/statuses/favourites#destroy', status_id: '123')
     end
 
     it 'routes mute' do
-      expect(post('/api/v1/statuses/123/mute')).
-        to route_to('api/v1/statuses/mutes#create', status_id: '123')
+      expect(post('/api/v1/statuses/123/mute'))
+        .to route_to('api/v1/statuses/mutes#create', status_id: '123')
     end
 
     it 'routes unmute' do
-      expect(post('/api/v1/statuses/123/unmute')).
-        to route_to('api/v1/statuses/mutes#destroy', status_id: '123')
+      expect(post('/api/v1/statuses/123/unmute'))
+        .to route_to('api/v1/statuses/mutes#destroy', status_id: '123')
     end
   end
 
   describe 'Timeline routes' do
     it 'routes to home timeline' do
-      expect(get('/api/v1/timelines/home')).
-        to route_to('api/v1/timelines/home#show')
+      expect(get('/api/v1/timelines/home'))
+        .to route_to('api/v1/timelines/home#show')
     end
 
     it 'routes to public timeline' do
-      expect(get('/api/v1/timelines/public')).
-        to route_to('api/v1/timelines/public#show')
+      expect(get('/api/v1/timelines/public'))
+        .to route_to('api/v1/timelines/public#show')
     end
 
     it 'routes to tag timeline' do
-      expect(get('/api/v1/timelines/tag/test')).
-        to route_to('api/v1/timelines/tag#show', id: 'test')
+      expect(get('/api/v1/timelines/tag/test'))
+        .to route_to('api/v1/timelines/tag#show', id: 'test')
     end
   end
 end
diff --git a/spec/routing/well_known_routes_spec.rb b/spec/routing/well_known_routes_spec.rb
index 2e25605c2..8cf08c13c 100644
--- a/spec/routing/well_known_routes_spec.rb
+++ b/spec/routing/well_known_routes_spec.rb
@@ -1,15 +1,19 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
-describe 'the host-meta route' do
-  it 'routes to correct place with xml format' do
-    expect(get('/.well-known/host-meta')).
-      to route_to('well_known/host_meta#show', format: 'xml')
+describe 'Well Known routes' do
+  describe 'the host-meta route' do
+    it 'routes to correct place with xml format' do
+      expect(get('/.well-known/host-meta'))
+        .to route_to('well_known/host_meta#show', format: 'xml')
+    end
   end
-end
 
-describe 'the webfinger route' do
-  it 'routes to correct place with json format' do
-    expect(get('/.well-known/webfinger')).
-      to route_to('well_known/webfinger#show')
+  describe 'the webfinger route' do
+    it 'routes to correct place with json format' do
+      expect(get('/.well-known/webfinger'))
+        .to route_to('well_known/webfinger#show')
+    end
   end
 end
diff --git a/spec/serializers/activitypub/device_serializer_spec.rb b/spec/serializers/activitypub/device_serializer_spec.rb
new file mode 100644
index 000000000..2a3be8212
--- /dev/null
+++ b/spec/serializers/activitypub/device_serializer_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe ActivityPub::DeviceSerializer do
+  let(:serialization) do
+    JSON.parse(
+      ActiveModelSerializers::SerializableResource.new(
+        record, serializer: described_class
+      ).to_json
+    )
+  end
+  let(:record) { Fabricate(:device) }
+
+  describe 'type' do
+    it 'returns correct serialized type' do
+      expect(serialization['type']).to eq('Device')
+    end
+  end
+end
diff --git a/spec/serializers/activitypub/note_spec.rb b/spec/serializers/activitypub/note_serializer_spec.rb
index 55bfbc16b..7ea47baef 100644
--- a/spec/serializers/activitypub/note_spec.rb
+++ b/spec/serializers/activitypub/note_serializer_spec.rb
@@ -3,6 +3,8 @@
 require 'rails_helper'
 
 describe ActivityPub::NoteSerializer do
+  subject { JSON.parse(@serialization.to_json) }
+
   let!(:account) { Fabricate(:account) }
   let!(:other)   { Fabricate(:account) }
   let!(:parent)  { Fabricate(:status, account: account, visibility: :public) }
@@ -16,8 +18,6 @@ describe ActivityPub::NoteSerializer do
     @serialization = ActiveModelSerializers::SerializableResource.new(parent, serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter)
   end
 
-  subject { JSON.parse(@serialization.to_json) }
-
   it 'has a Note type' do
     expect(subject['type']).to eql('Note')
   end
diff --git a/spec/serializers/activitypub/one_time_key_serializer_spec.rb b/spec/serializers/activitypub/one_time_key_serializer_spec.rb
new file mode 100644
index 000000000..6fe1f0618
--- /dev/null
+++ b/spec/serializers/activitypub/one_time_key_serializer_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe ActivityPub::OneTimeKeySerializer do
+  let(:serialization) do
+    JSON.parse(
+      ActiveModelSerializers::SerializableResource.new(
+        record, serializer: described_class
+      ).to_json
+    )
+  end
+  let(:record) { Fabricate(:one_time_key) }
+
+  describe 'type' do
+    it 'returns correct serialized type' do
+      expect(serialization['type']).to eq('Curve25519Key')
+    end
+  end
+end
diff --git a/spec/serializers/activitypub/undo_like_serializer_spec.rb b/spec/serializers/activitypub/undo_like_serializer_spec.rb
new file mode 100644
index 000000000..43cf7192e
--- /dev/null
+++ b/spec/serializers/activitypub/undo_like_serializer_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe ActivityPub::UndoLikeSerializer do
+  let(:serialization) do
+    JSON.parse(
+      ActiveModelSerializers::SerializableResource.new(
+        record, serializer: described_class
+      ).to_json
+    )
+  end
+  let(:record) { Fabricate(:favourite) }
+
+  describe 'type' do
+    it 'returns correct serialized type' do
+      expect(serialization['type']).to eq('Undo')
+    end
+  end
+end
diff --git a/spec/serializers/activitypub/update_poll_spec.rb b/spec/serializers/activitypub/update_poll_serializer_spec.rb
index f9e035eab..4360808b5 100644
--- a/spec/serializers/activitypub/update_poll_spec.rb
+++ b/spec/serializers/activitypub/update_poll_serializer_spec.rb
@@ -3,6 +3,8 @@
 require 'rails_helper'
 
 describe ActivityPub::UpdatePollSerializer do
+  subject { JSON.parse(@serialization.to_json) }
+
   let(:account) { Fabricate(:account) }
   let(:poll)    { Fabricate(:poll, account: account) }
   let!(:status) { Fabricate(:status, account: account, poll: poll) }
@@ -11,8 +13,6 @@ describe ActivityPub::UpdatePollSerializer do
     @serialization = ActiveModelSerializers::SerializableResource.new(status, serializer: ActivityPub::UpdatePollSerializer, adapter: ActivityPub::Adapter)
   end
 
-  subject { JSON.parse(@serialization.to_json) }
-
   it 'has a Update type' do
     expect(subject['type']).to eql('Update')
   end
diff --git a/spec/serializers/activitypub/vote_serializer_spec.rb b/spec/serializers/activitypub/vote_serializer_spec.rb
new file mode 100644
index 000000000..c329542d7
--- /dev/null
+++ b/spec/serializers/activitypub/vote_serializer_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe ActivityPub::VoteSerializer do
+  let(:serialization) do
+    JSON.parse(
+      ActiveModelSerializers::SerializableResource.new(
+        record, serializer: described_class
+      ).to_json
+    )
+  end
+  let(:record) { Fabricate(:poll_vote) }
+
+  describe 'type' do
+    it 'returns correct serialized type' do
+      expect(serialization['type']).to eq('Create')
+    end
+  end
+end
diff --git a/spec/serializers/rest/account_serializer_spec.rb b/spec/serializers/rest/account_serializer_spec.rb
new file mode 100644
index 000000000..528639943
--- /dev/null
+++ b/spec/serializers/rest/account_serializer_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe REST::AccountSerializer do
+  subject { JSON.parse(ActiveModelSerializers::SerializableResource.new(account, serializer: REST::AccountSerializer).to_json) }
+
+  let(:role)    { Fabricate(:user_role, name: 'Role', highlighted: true) }
+  let(:user)    { Fabricate(:user, role: role) }
+  let(:account) { user.account }
+
+  context 'when the account is suspended' do
+    before do
+      account.suspend!
+    end
+
+    it 'returns empty roles' do
+      expect(subject['roles']).to eq []
+    end
+  end
+
+  context 'when the account has a highlighted role' do
+    let(:role) { Fabricate(:user_role, name: 'Role', highlighted: true) }
+
+    it 'returns the expected role' do
+      expect(subject['roles'].first).to include({ 'name' => 'Role' })
+    end
+  end
+
+  context 'when the account has a non-highlighted role' do
+    let(:role) { Fabricate(:user_role, name: 'Role', highlighted: false) }
+
+    it 'returns empty roles' do
+      expect(subject['roles']).to eq []
+    end
+  end
+
+  context 'when the account is memorialized' do
+    before do
+      account.memorialize!
+    end
+
+    it 'marks it as such' do
+      expect(subject['memorial']).to be true
+    end
+  end
+end
diff --git a/spec/serializers/rest/encrypted_message_serializer_spec.rb b/spec/serializers/rest/encrypted_message_serializer_spec.rb
new file mode 100644
index 000000000..e0e70a3b8
--- /dev/null
+++ b/spec/serializers/rest/encrypted_message_serializer_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe REST::EncryptedMessageSerializer do
+  let(:serialization) do
+    JSON.parse(
+      ActiveModelSerializers::SerializableResource.new(
+        record, serializer: described_class
+      ).to_json
+    )
+  end
+  let(:record) { Fabricate(:encrypted_message) }
+
+  describe 'account' do
+    it 'returns the associated account' do
+      expect(serialization['account_id']).to eq(record.from_account.id.to_s)
+    end
+  end
+end
diff --git a/spec/serializers/rest/instance_serializer_spec.rb b/spec/serializers/rest/instance_serializer_spec.rb
new file mode 100644
index 000000000..15a5de18d
--- /dev/null
+++ b/spec/serializers/rest/instance_serializer_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe REST::InstanceSerializer do
+  let(:serialization) do
+    JSON.parse(
+      ActiveModelSerializers::SerializableResource.new(
+        record, serializer: described_class
+      ).to_json
+    )
+  end
+  let(:record) { InstancePresenter.new }
+
+  describe 'usage' do
+    it 'returns recent usage data' do
+      expect(serialization['usage']).to eq({ 'users' => { 'active_month' => 0 } })
+    end
+  end
+end
diff --git a/spec/serializers/rest/keys/claim_result_serializer_spec.rb b/spec/serializers/rest/keys/claim_result_serializer_spec.rb
new file mode 100644
index 000000000..cf9416f03
--- /dev/null
+++ b/spec/serializers/rest/keys/claim_result_serializer_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe REST::Keys::ClaimResultSerializer do
+  let(:serialization) do
+    JSON.parse(
+      ActiveModelSerializers::SerializableResource.new(
+        record, serializer: described_class
+      ).to_json
+    )
+  end
+  let(:record) { Keys::ClaimService::Result.new(Account.new(id: 123), 456) }
+
+  describe 'account' do
+    it 'returns the associated account' do
+      expect(serialization['account_id']).to eq('123')
+    end
+  end
+end
diff --git a/spec/serializers/rest/keys/device_serializer_spec.rb b/spec/serializers/rest/keys/device_serializer_spec.rb
new file mode 100644
index 000000000..c15e197cb
--- /dev/null
+++ b/spec/serializers/rest/keys/device_serializer_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe REST::Keys::DeviceSerializer do
+  let(:serialization) do
+    JSON.parse(
+      ActiveModelSerializers::SerializableResource.new(
+        record, serializer: described_class
+      ).to_json
+    )
+  end
+  let(:record) { Device.new(name: 'Device name') }
+
+  describe 'name' do
+    it 'returns the name' do
+      expect(serialization['name']).to eq('Device name')
+    end
+  end
+end
diff --git a/spec/serializers/rest/keys/query_result_serializer_spec.rb b/spec/serializers/rest/keys/query_result_serializer_spec.rb
new file mode 100644
index 000000000..983780ae9
--- /dev/null
+++ b/spec/serializers/rest/keys/query_result_serializer_spec.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe REST::Keys::QueryResultSerializer do
+  let(:serialization) do
+    JSON.parse(
+      ActiveModelSerializers::SerializableResource.new(
+        record, serializer: described_class
+      ).to_json
+    )
+  end
+  let(:record) { Keys::QueryService::Result.new(Account.new(id: 123), []) }
+
+  describe 'account' do
+    it 'returns the associated account id' do
+      expect(serialization['account_id']).to eq('123')
+    end
+  end
+end
diff --git a/spec/serializers/rest/suggestion_serializer_spec.rb b/spec/serializers/rest/suggestion_serializer_spec.rb
new file mode 100644
index 000000000..b3c086208
--- /dev/null
+++ b/spec/serializers/rest/suggestion_serializer_spec.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe REST::SuggestionSerializer do
+  let(:serialization) do
+    JSON.parse(
+      ActiveModelSerializers::SerializableResource.new(
+        record, serializer: described_class
+      ).to_json
+    )
+  end
+  let(:record) do
+    AccountSuggestions::Suggestion.new(
+      account: account,
+      source: 'SuggestionSource'
+    )
+  end
+  let(:account) { Fabricate(:account) }
+
+  describe 'account' do
+    it 'returns the associated account' do
+      expect(serialization['account']['id']).to eq(account.id.to_s)
+    end
+  end
+end
diff --git a/spec/services/account_search_service_spec.rb b/spec/services/account_search_service_spec.rb
index 81cbc175e..bb819bb6c 100644
--- a/spec/services/account_search_service_spec.rb
+++ b/spec/services/account_search_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe AccountSearchService, type: :service do
@@ -63,7 +65,7 @@ describe AccountSearchService, type: :service do
         allow(ResolveAccountService).to receive(:new).and_return(service)
 
         results = subject.call('newuser@remote.com', nil, limit: 10, resolve: false)
-        expect(service).not_to have_received(:call)
+        expect(service).to_not have_received(:call)
       end
     end
 
@@ -76,7 +78,7 @@ describe AccountSearchService, type: :service do
       expect(results).to eq [partial]
     end
 
-    it "does not return suspended remote accounts" do
+    it 'does not return suspended remote accounts' do
       remote  = Fabricate(:account, username: 'a', domain: 'remote', display_name: 'e', suspended: true)
       results = subject.call('a@example.com', nil, limit: 2)
 
diff --git a/spec/services/account_statuses_cleanup_service_spec.rb b/spec/services/account_statuses_cleanup_service_spec.rb
index 257655c41..e83063f73 100644
--- a/spec/services/account_statuses_cleanup_service_spec.rb
+++ b/spec/services/account_statuses_cleanup_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe AccountStatusesCleanupService, type: :service do
@@ -42,8 +44,8 @@ describe AccountStatusesCleanupService, type: :service do
 
       context 'when called repeatedly with a budget of 2' do
         it 'reports 2 then 1 deleted statuses' do
-         expect(subject.call(account_policy, 2)).to eq 2
-         expect(subject.call(account_policy, 2)).to eq 1
+          expect(subject.call(account_policy, 2)).to eq 2
+          expect(subject.call(account_policy, 2)).to eq 1
         end
 
         it 'actually deletes the statuses in the expected order' do
diff --git a/spec/services/activitypub/fetch_featured_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_collection_service_spec.rb
index e6336dc1b..59d332599 100644
--- a/spec/services/activitypub/fetch_featured_collection_service_spec.rb
+++ b/spec/services/activitypub/fetch_featured_collection_service_spec.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
+  subject { described_class.new }
+
   let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/account', featured_collection_url: 'https://example.com/account/pinned') }
 
   let!(:known_status) { Fabricate(:status, account: actor, uri: 'https://example.com/account/pinned/1') }
@@ -56,8 +60,6 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
     }.with_indifferent_access
   end
 
-  subject { described_class.new }
-
   shared_examples 'sets pinned posts' do
     before do
       stub_request(:get, 'https://example.com/account/pinned/1').to_return(status: 200, body: Oj.dump(status_json_1))
@@ -109,7 +111,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
             type: 'CollectionPage',
             partOf: actor.featured_collection_url,
             items: items,
-          }
+          },
         }.with_indifferent_access
       end
 
diff --git a/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb b/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb
index 6ca22c9fc..071e4d92d 100644
--- a/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb
+++ b/spec/services/activitypub/fetch_featured_tags_collection_service_spec.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService, type: :service do
+  subject { described_class.new }
+
   let(:collection_url) { 'https://example.com/account/tags' }
   let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/account') }
 
@@ -21,15 +25,13 @@ RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService, type: :service d
     }.with_indifferent_access
   end
 
-  subject { described_class.new }
-
   shared_examples 'sets featured tags' do
     before do
       subject.call(actor, collection_url)
     end
 
     it 'sets expected tags as pinned tags' do
-      expect(actor.featured_tags.map(&:display_name)).to match_array ['Foo', 'bar', 'baZ']
+      expect(actor.featured_tags.map(&:display_name)).to match_array %w(Foo bar baZ)
     end
   end
 
@@ -81,7 +83,7 @@ RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService, type: :service d
             type: 'CollectionPage',
             partOf: collection_url,
             items: items,
-          }
+          },
         }.with_indifferent_access
       end
 
diff --git a/spec/services/activitypub/fetch_remote_account_service_spec.rb b/spec/services/activitypub/fetch_remote_account_service_spec.rb
index ec6f1f41d..868bc2a58 100644
--- a/spec/services/activitypub/fetch_remote_account_service_spec.rb
+++ b/spec/services/activitypub/fetch_remote_account_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::FetchRemoteAccountService, type: :service do
diff --git a/spec/services/activitypub/fetch_remote_actor_service_spec.rb b/spec/services/activitypub/fetch_remote_actor_service_spec.rb
index 20117c66d..a72c6941e 100644
--- a/spec/services/activitypub/fetch_remote_actor_service_spec.rb
+++ b/spec/services/activitypub/fetch_remote_actor_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::FetchRemoteActorService, type: :service do
diff --git a/spec/services/activitypub/fetch_remote_key_service_spec.rb b/spec/services/activitypub/fetch_remote_key_service_spec.rb
index 3186c4270..0ec0c2736 100644
--- a/spec/services/activitypub/fetch_remote_key_service_spec.rb
+++ b/spec/services/activitypub/fetch_remote_key_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::FetchRemoteKeyService, type: :service do
diff --git a/spec/services/activitypub/fetch_remote_status_service_spec.rb b/spec/services/activitypub/fetch_remote_status_service_spec.rb
index 7359ca0b4..1c39db21f 100644
--- a/spec/services/activitypub/fetch_remote_status_service_spec.rb
+++ b/spec/services/activitypub/fetch_remote_status_service_spec.rb
@@ -1,8 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
   include ActionView::Helpers::TextHelper
 
+  subject { described_class.new }
+
   let!(:sender) { Fabricate(:account, domain: 'foo.bar', uri: 'https://foo.bar') }
   let!(:recipient) { Fabricate(:account) }
 
@@ -11,15 +15,13 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
   let(:note) do
     {
       '@context': 'https://www.w3.org/ns/activitystreams',
-      id: "https://foo.bar/@foo/1234",
+      id: 'https://foo.bar/@foo/1234',
       type: 'Note',
       content: 'Lorem ipsum',
       attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
     }
   end
 
-  subject { described_class.new }
-
   before do
     stub_request(:get, 'https://foo.bar/watch?v=12345').to_return(status: 404, body: '')
     stub_request(:get, object[:id]).to_return(body: Oj.dump(object))
@@ -46,7 +48,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
       let(:object) do
         {
           '@context': 'https://www.w3.org/ns/activitystreams',
-          id: "https://foo.bar/@foo/1234",
+          id: 'https://foo.bar/@foo/1234',
           type: 'Video',
           name: 'Nyan Cat 10 hours remix',
           attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
@@ -54,13 +56,13 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
             {
               type: 'Link',
               mimeType: 'application/x-bittorrent',
-              href: "https://foo.bar/12345.torrent",
+              href: 'https://foo.bar/12345.torrent',
             },
 
             {
               type: 'Link',
               mimeType: 'text/html',
-              href: "https://foo.bar/watch?v=12345",
+              href: 'https://foo.bar/watch?v=12345',
             },
           ],
         }
@@ -70,8 +72,8 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
         status = sender.statuses.first
 
         expect(status).to_not be_nil
-        expect(status.url).to eq "https://foo.bar/watch?v=12345"
-        expect(strip_tags(status.text)).to eq "Nyan Cat 10 hours remixhttps://foo.bar/watch?v=12345"
+        expect(status.url).to eq 'https://foo.bar/watch?v=12345'
+        expect(strip_tags(status.text)).to eq 'Nyan Cat 10 hours remixhttps://foo.bar/watch?v=12345'
       end
     end
 
@@ -79,7 +81,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
       let(:object) do
         {
           '@context': 'https://www.w3.org/ns/activitystreams',
-          id: "https://foo.bar/@foo/1234",
+          id: 'https://foo.bar/@foo/1234',
           type: 'Audio',
           name: 'Nyan Cat 10 hours remix',
           attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
@@ -87,13 +89,13 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
             {
               type: 'Link',
               mimeType: 'application/x-bittorrent',
-              href: "https://foo.bar/12345.torrent",
+              href: 'https://foo.bar/12345.torrent',
             },
 
             {
               type: 'Link',
               mimeType: 'text/html',
-              href: "https://foo.bar/watch?v=12345",
+              href: 'https://foo.bar/watch?v=12345',
             },
           ],
         }
@@ -103,8 +105,8 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
         status = sender.statuses.first
 
         expect(status).to_not be_nil
-        expect(status.url).to eq "https://foo.bar/watch?v=12345"
-        expect(strip_tags(status.text)).to eq "Nyan Cat 10 hours remixhttps://foo.bar/watch?v=12345"
+        expect(status.url).to eq 'https://foo.bar/watch?v=12345'
+        expect(strip_tags(status.text)).to eq 'Nyan Cat 10 hours remixhttps://foo.bar/watch?v=12345'
       end
     end
 
@@ -112,10 +114,10 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
       let(:object) do
         {
           '@context': 'https://www.w3.org/ns/activitystreams',
-          id: "https://foo.bar/@foo/1234",
+          id: 'https://foo.bar/@foo/1234',
           type: 'Event',
           name: "Let's change the world",
-          attributedTo: ActivityPub::TagManager.instance.uri_for(sender)
+          attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
         }
       end
 
@@ -123,7 +125,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
         status = sender.statuses.first
 
         expect(status).to_not be_nil
-        expect(status.url).to eq "https://foo.bar/@foo/1234"
+        expect(status.url).to eq 'https://foo.bar/@foo/1234'
         expect(strip_tags(status.text)).to eq "Let's change the worldhttps://foo.bar/@foo/1234"
       end
     end
@@ -132,7 +134,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
       let(:note) do
         {
           '@context': 'https://www.w3.org/ns/activitystreams',
-          id: "https://real.address/@foo/1234",
+          id: 'https://real.address/@foo/1234',
           type: 'Note',
           content: 'Lorem ipsum',
           attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
@@ -154,7 +156,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
       let(:object) do
         {
           '@context': 'https://www.w3.org/ns/activitystreams',
-          id: "https://foo.bar/@foo/1234/create",
+          id: 'https://foo.bar/@foo/1234/create',
           type: 'Create',
           actor: ActivityPub::TagManager.instance.uri_for(sender),
           object: note,
@@ -174,11 +176,11 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
       let(:object) do
         {
           '@context': 'https://www.w3.org/ns/activitystreams',
-          id: "https://foo.bar/@foo/1234/create",
+          id: 'https://foo.bar/@foo/1234/create',
           type: 'Create',
           actor: ActivityPub::TagManager.instance.uri_for(sender),
           object: {
-            id: "https://real.address/@foo/1234",
+            id: 'https://real.address/@foo/1234',
             type: 'Note',
             content: 'Lorem ipsum',
             attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
@@ -208,7 +210,7 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
         let(:object) do
           {
             '@context': 'https://www.w3.org/ns/activitystreams',
-            id: "https://foo.bar/@foo/1234/create",
+            id: 'https://foo.bar/@foo/1234/create',
             type: 'Create',
             actor: ActivityPub::TagManager.instance.uri_for(sender),
             object: note.merge(updated: '2021-09-08T22:39:25Z'),
@@ -223,4 +225,98 @@ RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
       end
     end
   end
+
+  context 'statuses referencing other statuses' do
+    before do
+      stub_const 'ActivityPub::FetchRemoteStatusService::DISCOVERIES_PER_REQUEST', 5
+    end
+
+    context 'using inReplyTo' do
+      let(:object) do
+        {
+          '@context': 'https://www.w3.org/ns/activitystreams',
+          id: 'https://foo.bar/@foo/1',
+          type: 'Note',
+          content: 'Lorem ipsum',
+          inReplyTo: 'https://foo.bar/@foo/2',
+          attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
+        }
+      end
+
+      before do
+        8.times do |i|
+          status_json = {
+            '@context': 'https://www.w3.org/ns/activitystreams',
+            id: "https://foo.bar/@foo/#{i}",
+            type: 'Note',
+            content: 'Lorem ipsum',
+            inReplyTo: "https://foo.bar/@foo/#{i + 1}",
+            attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
+            to: 'as:Public',
+          }.with_indifferent_access
+          stub_request(:get, "https://foo.bar/@foo/#{i}").to_return(status: 200, body: status_json.to_json, headers: { 'Content-Type': 'application/activity+json' })
+        end
+      end
+
+      it 'creates at least some statuses' do
+        expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_least(2)
+      end
+
+      it 'creates no more account than the limit allows' do
+        expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_most(5)
+      end
+    end
+
+    context 'using replies' do
+      let(:object) do
+        {
+          '@context': 'https://www.w3.org/ns/activitystreams',
+          id: 'https://foo.bar/@foo/1',
+          type: 'Note',
+          content: 'Lorem ipsum',
+          replies: {
+            type: 'Collection',
+            id: 'https://foo.bar/@foo/1/replies',
+            first: {
+              type: 'CollectionPage',
+              partOf: 'https://foo.bar/@foo/1/replies',
+              items: ['https://foo.bar/@foo/2'],
+            },
+          },
+          attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
+        }
+      end
+
+      before do
+        8.times do |i|
+          status_json = {
+            '@context': 'https://www.w3.org/ns/activitystreams',
+            id: "https://foo.bar/@foo/#{i}",
+            type: 'Note',
+            content: 'Lorem ipsum',
+            replies: {
+              type: 'Collection',
+              id: "https://foo.bar/@foo/#{i}/replies",
+              first: {
+                type: 'CollectionPage',
+                partOf: "https://foo.bar/@foo/#{i}/replies",
+                items: ["https://foo.bar/@foo/#{i + 1}"],
+              },
+            },
+            attributedTo: ActivityPub::TagManager.instance.uri_for(sender),
+            to: 'as:Public',
+          }.with_indifferent_access
+          stub_request(:get, "https://foo.bar/@foo/#{i}").to_return(status: 200, body: status_json.to_json, headers: { 'Content-Type': 'application/activity+json' })
+        end
+      end
+
+      it 'creates at least some statuses' do
+        expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_least(2)
+      end
+
+      it 'creates no more account than the limit allows' do
+        expect { subject.call(object[:id], prefetched_body: Oj.dump(object)) }.to change { sender.statuses.count }.by_at_most(5)
+      end
+    end
+  end
 end
diff --git a/spec/services/activitypub/fetch_replies_service_spec.rb b/spec/services/activitypub/fetch_replies_service_spec.rb
index fe49b18c1..bf8e29676 100644
--- a/spec/services/activitypub/fetch_replies_service_spec.rb
+++ b/spec/services/activitypub/fetch_replies_service_spec.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::FetchRepliesService, type: :service do
+  subject { described_class.new }
+
   let(:actor)          { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/account') }
   let(:status)         { Fabricate(:status, account: actor) }
   let(:collection_uri) { 'http://example.com/replies/1' }
@@ -28,8 +32,6 @@ RSpec.describe ActivityPub::FetchRepliesService, type: :service do
     }.with_indifferent_access
   end
 
-  subject { described_class.new }
-
   describe '#call' do
     context 'when the payload is a Collection with inlined replies' do
       context 'when passing the collection itself' do
@@ -90,7 +92,7 @@ RSpec.describe ActivityPub::FetchRepliesService, type: :service do
             type: 'CollectionPage',
             partOf: collection_uri,
             items: items,
-          }
+          },
         }.with_indifferent_access
       end
 
diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb
index 2b20d17b1..491b8ed5a 100644
--- a/spec/services/activitypub/process_account_service_spec.rb
+++ b/spec/services/activitypub/process_account_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::ProcessAccountService, type: :service do
@@ -12,7 +14,7 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
         attachment: [
           { type: 'PropertyValue', name: 'Pronouns', value: 'They/them' },
           { type: 'PropertyValue', name: 'Occupation', value: 'Unit test' },
-          { type: 'PropertyValue', name: 'non-string', value: ['foo', 'bar'] },
+          { type: 'PropertyValue', name: 'non-string', value: %w(foo bar) },
         ],
       }.with_indifferent_access
     end
@@ -31,6 +33,8 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
   end
 
   context 'when account is not suspended' do
+    subject { described_class.new.call('alice', 'example.com', payload) }
+
     let!(:account) { Fabricate(:account, username: 'alice', domain: 'example.com') }
 
     let(:payload) do
@@ -46,8 +50,6 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
       allow(Admin::SuspensionWorker).to receive(:perform_async)
     end
 
-    subject { described_class.new.call('alice', 'example.com', payload) }
-
     it 'suspends account remotely' do
       expect(subject.suspended?).to be true
       expect(subject.suspension_origin_remote?).to be true
@@ -60,6 +62,8 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
   end
 
   context 'when account is suspended' do
+    subject { described_class.new.call('alice', 'example.com', payload) }
+
     let!(:account) { Fabricate(:account, username: 'alice', domain: 'example.com', display_name: '') }
 
     let(:payload) do
@@ -78,8 +82,6 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
       account.suspend!(origin: suspension_origin)
     end
 
-    subject { described_class.new.call('alice', 'example.com', payload) }
-
     context 'locally' do
       let(:suspension_origin) { :local }
 
@@ -172,10 +174,10 @@ RSpec.describe ActivityPub::ProcessAccountService, type: :service do
             {
               type: 'Mention',
               href: "https://foo.test/users/#{i + 1}",
-              name: "@user#{i + 1 }",
-            }
+              name: "@user#{i + 1}",
+            },
           ],
-          to: [ 'as:Public', "https://foo.test/users/#{i + 1}" ]
+          to: ['as:Public', "https://foo.test/users/#{i + 1}"],
         }.with_indifferent_access
         featured_json = {
           '@context': ['https://www.w3.org/ns/activitystreams'],
diff --git a/spec/services/activitypub/process_collection_service_spec.rb b/spec/services/activitypub/process_collection_service_spec.rb
index 093a188a2..1433d0c50 100644
--- a/spec/services/activitypub/process_collection_service_spec.rb
+++ b/spec/services/activitypub/process_collection_service_spec.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
+  subject { described_class.new }
+
   let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/account') }
 
   let(:payload) do
@@ -19,8 +23,6 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
 
   let(:json) { Oj.dump(payload) }
 
-  subject { described_class.new }
-
   describe '#call' do
     context 'when actor is suspended' do
       before do
@@ -39,7 +41,7 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
           end
 
           it 'does not process payload' do
-            expect(ActivityPub::Activity).not_to receive(:factory)
+            expect(ActivityPub::Activity).to_not receive(:factory)
             subject.call(json, actor)
           end
         end
@@ -69,7 +71,7 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
 
       it 'does not process payload if no signature exists' do
         expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil)
-        expect(ActivityPub::Activity).not_to receive(:factory)
+        expect(ActivityPub::Activity).to_not receive(:factory)
 
         subject.call(json, forwarder)
       end
@@ -87,7 +89,7 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
         payload['signature'] = { 'type' => 'RsaSignature2017' }
 
         expect_any_instance_of(ActivityPub::LinkedDataSignature).to receive(:verify_actor!).and_return(nil)
-        expect(ActivityPub::Activity).not_to receive(:factory)
+        expect(ActivityPub::Activity).to_not receive(:factory)
 
         subject.call(json, forwarder)
       end
@@ -95,11 +97,11 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
       context 'when receiving a fabricated status' do
         let!(:actor) do
           Fabricate(:account,
-            username: 'bob',
-            domain: 'example.com',
-            uri: 'https://example.com/users/bob',
-            public_key: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuuYyoyfsRkYnXRotMsId\nW3euBDDfiv9oVqOxUVC7bhel8KednIMrMCRWFAkgJhbrlzbIkjVr68o1MP9qLcn7\nCmH/BXHp7yhuFTr4byjdJKpwB+/i2jNEsvDH5jR8WTAeTCe0x/QHg21V3F7dSI5m\nCCZ/1dSIyOXLRTWVlfDlm3rE4ntlCo+US3/7oSWbg/4/4qEnt1HC32kvklgScxua\n4LR5ATdoXa5bFoopPWhul7MJ6NyWCyQyScUuGdlj8EN4kmKQJvphKHrI9fvhgOuG\nTvhTR1S5InA4azSSchY0tXEEw/VNxraeX0KPjbgr6DPcwhPd/m0nhVDq0zVyVBBD\nMwIDAQAB\n-----END PUBLIC KEY-----\n",
-            private_key: nil)
+                    username: 'bob',
+                    domain: 'example.com',
+                    uri: 'https://example.com/users/bob',
+                    public_key: "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuuYyoyfsRkYnXRotMsId\nW3euBDDfiv9oVqOxUVC7bhel8KednIMrMCRWFAkgJhbrlzbIkjVr68o1MP9qLcn7\nCmH/BXHp7yhuFTr4byjdJKpwB+/i2jNEsvDH5jR8WTAeTCe0x/QHg21V3F7dSI5m\nCCZ/1dSIyOXLRTWVlfDlm3rE4ntlCo+US3/7oSWbg/4/4qEnt1HC32kvklgScxua\n4LR5ATdoXa5bFoopPWhul7MJ6NyWCyQyScUuGdlj8EN4kmKQJvphKHrI9fvhgOuG\nTvhTR1S5InA4azSSchY0tXEEw/VNxraeX0KPjbgr6DPcwhPd/m0nhVDq0zVyVBBD\nMwIDAQAB\n-----END PUBLIC KEY-----\n",
+                    private_key: nil)
         end
 
         let(:payload) do
@@ -107,48 +109,48 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
             '@context': [
               'https://www.w3.org/ns/activitystreams',
               nil,
-              {'object': 'https://www.w3.org/ns/activitystreams#object'}
+              { object: 'https://www.w3.org/ns/activitystreams#object' },
             ],
-            'id': 'https://example.com/users/bob/fake-status/activity',
-            'type': 'Create',
-            'actor': 'https://example.com/users/bob',
-            'published': '2022-01-22T15:00:00Z',
-            'to': [
-              'https://www.w3.org/ns/activitystreams#Public'
+            id: 'https://example.com/users/bob/fake-status/activity',
+            type: 'Create',
+            actor: 'https://example.com/users/bob',
+            published: '2022-01-22T15:00:00Z',
+            to: [
+              'https://www.w3.org/ns/activitystreams#Public',
             ],
-            'cc': [
-              'https://example.com/users/bob/followers'
+            cc: [
+              'https://example.com/users/bob/followers',
             ],
-            'signature': {
-              'type': 'RsaSignature2017',
-              'creator': 'https://example.com/users/bob#main-key',
-              'created': '2022-03-09T21:57:25Z',
-              'signatureValue': 'WculK0LelTQ0MvGwU9TPoq5pFzFfGYRDCJqjZ232/Udj4CHqDTGOSw5UTDLShqBOyycCkbZGrQwXG+dpyDpQLSe1UVPZ5TPQtc/9XtI57WlS2nMNpdvRuxGnnb2btPdesXZ7n3pCxo0zjaXrJMe0mqQh5QJO22mahb4bDwwmfTHgbD3nmkD+fBfGi+UV2qWwqr+jlV4L4JqNkh0gWljF5KTePLRRZCuWiQ/FAt7c67636cdIPf7fR+usjuZltTQyLZKEGuK8VUn2Gkfsx5qns7Vcjvlz1JqlAjyO8HPBbzTTHzUG2nUOIgC3PojCSWv6mNTmRGoLZzOscCAYQA6cKw=='
+            signature: {
+              type: 'RsaSignature2017',
+              creator: 'https://example.com/users/bob#main-key',
+              created: '2022-03-09T21:57:25Z',
+              signatureValue: 'WculK0LelTQ0MvGwU9TPoq5pFzFfGYRDCJqjZ232/Udj4CHqDTGOSw5UTDLShqBOyycCkbZGrQwXG+dpyDpQLSe1UVPZ5TPQtc/9XtI57WlS2nMNpdvRuxGnnb2btPdesXZ7n3pCxo0zjaXrJMe0mqQh5QJO22mahb4bDwwmfTHgbD3nmkD+fBfGi+UV2qWwqr+jlV4L4JqNkh0gWljF5KTePLRRZCuWiQ/FAt7c67636cdIPf7fR+usjuZltTQyLZKEGuK8VUn2Gkfsx5qns7Vcjvlz1JqlAjyO8HPBbzTTHzUG2nUOIgC3PojCSWv6mNTmRGoLZzOscCAYQA6cKw==',
             },
             '@id': 'https://example.com/users/bob/statuses/107928807471117876/activity',
             '@type': 'https://www.w3.org/ns/activitystreams#Create',
             'https://www.w3.org/ns/activitystreams#actor': {
-              '@id': 'https://example.com/users/bob'
+              '@id': 'https://example.com/users/bob',
             },
             'https://www.w3.org/ns/activitystreams#cc': {
-              '@id': 'https://example.com/users/bob/followers'
+              '@id': 'https://example.com/users/bob/followers',
             },
-            'object': {
-              'id': 'https://example.com/users/bob/fake-status',
-              'type': 'Note',
-              'published': '2022-01-22T15:00:00Z',
-              'url': 'https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=puck-was-here',
-              'attributedTo': 'https://example.com/users/bob',
-              'to': [
-                'https://www.w3.org/ns/activitystreams#Public'
+            object: {
+              id: 'https://example.com/users/bob/fake-status',
+              type: 'Note',
+              published: '2022-01-22T15:00:00Z',
+              url: 'https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=puck-was-here',
+              attributedTo: 'https://example.com/users/bob',
+              to: [
+                'https://www.w3.org/ns/activitystreams#Public',
               ],
-              'cc': [
-                'https://example.com/users/bob/followers'
+              cc: [
+                'https://example.com/users/bob/followers',
               ],
-              'sensitive': false,
-              'atomUri': 'https://example.com/users/bob/fake-status',
-              'conversation': 'tag:example.com,2022-03-09:objectId=15:objectType=Conversation',
-              'content': '<p>puck was here</p>',
+              sensitive: false,
+              atomUri: 'https://example.com/users/bob/fake-status',
+              conversation: 'tag:example.com,2022-03-09:objectId=15:objectType=Conversation',
+              content: '<p>puck was here</p>',
 
               '@id': 'https://example.com/users/bob/statuses/107928807471117876',
               '@type': 'https://www.w3.org/ns/activitystreams#Note',
@@ -156,21 +158,21 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
               'http://ostatus.org#conversation': 'tag:example.com,2022-03-09:objectId=15:objectType=Conversation',
               'https://www.w3.org/ns/activitystreams#attachment': [],
               'https://www.w3.org/ns/activitystreams#attributedTo': {
-                '@id': 'https://example.com/users/bob'
+                '@id': 'https://example.com/users/bob',
               },
               'https://www.w3.org/ns/activitystreams#cc': {
-                '@id': 'https://example.com/users/bob/followers'
+                '@id': 'https://example.com/users/bob/followers',
               },
               'https://www.w3.org/ns/activitystreams#content': [
                 '<p>hello world</p>',
                 {
                   '@value': '<p>hello world</p>',
-                  '@language': 'en'
-                }
+                  '@language': 'en',
+                },
               ],
               'https://www.w3.org/ns/activitystreams#published': {
                 '@type': 'http://www.w3.org/2001/XMLSchema#dateTime',
-                '@value': '2022-03-09T21:55:07Z'
+                '@value': '2022-03-09T21:55:07Z',
               },
               'https://www.w3.org/ns/activitystreams#replies': {
                 '@id': 'https://example.com/users/bob/statuses/107928807471117876/replies',
@@ -179,51 +181,51 @@ RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
                   '@type': 'https://www.w3.org/ns/activitystreams#CollectionPage',
                   'https://www.w3.org/ns/activitystreams#items': [],
                   'https://www.w3.org/ns/activitystreams#next': {
-                    '@id': 'https://example.com/users/bob/statuses/107928807471117876/replies?only_other_accounts=true&page=true'
+                    '@id': 'https://example.com/users/bob/statuses/107928807471117876/replies?only_other_accounts=true&page=true',
                   },
                   'https://www.w3.org/ns/activitystreams#partOf': {
-                    '@id': 'https://example.com/users/bob/statuses/107928807471117876/replies'
-                  }
-                }
+                    '@id': 'https://example.com/users/bob/statuses/107928807471117876/replies',
+                  },
+                },
               },
               'https://www.w3.org/ns/activitystreams#sensitive': false,
               'https://www.w3.org/ns/activitystreams#tag': [],
               'https://www.w3.org/ns/activitystreams#to': {
-                '@id': 'https://www.w3.org/ns/activitystreams#Public'
+                '@id': 'https://www.w3.org/ns/activitystreams#Public',
               },
               'https://www.w3.org/ns/activitystreams#url': {
-                '@id': 'https://example.com/@bob/107928807471117876'
-              }
+                '@id': 'https://example.com/@bob/107928807471117876',
+              },
             },
             'https://www.w3.org/ns/activitystreams#published': {
               '@type': 'http://www.w3.org/2001/XMLSchema#dateTime',
-              '@value': '2022-03-09T21:55:07Z'
+              '@value': '2022-03-09T21:55:07Z',
             },
             'https://www.w3.org/ns/activitystreams#to': {
-              '@id': 'https://www.w3.org/ns/activitystreams#Public'
-            }
+              '@id': 'https://www.w3.org/ns/activitystreams#Public',
+            },
           }
         end
 
         it 'does not process forged payload' do
-          expect(ActivityPub::Activity).not_to receive(:factory).with(
+          expect(ActivityPub::Activity).to_not receive(:factory).with(
             hash_including(
               'object' => hash_including(
                 'id' => 'https://example.com/users/bob/fake-status'
               )
             ),
-            anything(),
-            anything()
+            anything,
+            anything
           )
 
-          expect(ActivityPub::Activity).not_to receive(:factory).with(
+          expect(ActivityPub::Activity).to_not receive(:factory).with(
             hash_including(
               'object' => hash_including(
                 'content' => '<p>puck was here</p>'
               )
             ),
-            anything(),
-            anything()
+            anything,
+            anything
           )
 
           subject.call(json, forwarder)
diff --git a/spec/services/activitypub/process_status_update_service_spec.rb b/spec/services/activitypub/process_status_update_service_spec.rb
index 750369d57..e9f23b9cf 100644
--- a/spec/services/activitypub/process_status_update_service_spec.rb
+++ b/spec/services/activitypub/process_status_update_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 def poll_option_json(name, votes)
@@ -5,21 +7,9 @@ def poll_option_json(name, votes)
 end
 
 RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
-  let!(:status) { Fabricate(:status, text: 'Hello world', account: Fabricate(:account, domain: 'example.com')) }
-
-  let(:alice) { Fabricate(:account) }
-  let(:bob) { Fabricate(:account) }
-
-  let(:mentions) { [] }
-  let(:tags) { [] }
-  let(:media_attachments) { [] }
-
-  before do
-    mentions.each { |a| Fabricate(:mention, status: status, account: a) }
-    tags.each { |t| status.tags << t }
-    media_attachments.each { |m| status.media_attachments << m }
-  end
+  subject { described_class.new }
 
+  let!(:status) { Fabricate(:status, text: 'Hello world', account: Fabricate(:account, domain: 'example.com')) }
   let(:payload) do
     {
       '@context': 'https://www.w3.org/ns/activitystreams',
@@ -34,10 +24,20 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
       ],
     }
   end
-
   let(:json) { Oj.load(Oj.dump(payload)) }
 
-  subject { described_class.new }
+  let(:alice) { Fabricate(:account) }
+  let(:bob) { Fabricate(:account) }
+
+  let(:mentions) { [] }
+  let(:tags) { [] }
+  let(:media_attachments) { [] }
+
+  before do
+    mentions.each { |a| Fabricate(:mention, status: status, account: a) }
+    tags.each { |t| status.tags << t }
+    media_attachments.each { |m| status.media_attachments << m }
+  end
 
   describe '#call' do
     it 'updates text' do
@@ -104,20 +104,19 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
     end
 
     context 'when the status has not been explicitly edited and features a poll' do
-      let(:account)    { Fabricate(:account, domain: 'example.com') }
+      let(:account) { Fabricate(:account, domain: 'example.com') }
       let!(:expiration) { 10.days.from_now.utc }
       let!(:status) do
         Fabricate(:status,
-          text: 'Hello world',
-          account: account,
-          poll_attributes: {
-            options: %w(Foo Bar),
-            account: account,
-            multiple: false,
-            hide_totals: false,
-            expires_at: expiration
-          }
-        )
+                  text: 'Hello world',
+                  account: account,
+                  poll_attributes: {
+                    options: %w(Foo Bar),
+                    account: account,
+                    multiple: false,
+                    hide_totals: false,
+                    expires_at: expiration,
+                  })
       end
 
       let(:payload) do
@@ -156,20 +155,19 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
     end
 
     context 'when the status changes a poll despite being not explicitly marked as updated' do
-      let(:account)    { Fabricate(:account, domain: 'example.com') }
+      let(:account) { Fabricate(:account, domain: 'example.com') }
       let!(:expiration) { 10.days.from_now.utc }
       let!(:status) do
         Fabricate(:status,
-          text: 'Hello world',
-          account: account,
-          poll_attributes: {
-            options: %w(Foo Bar),
-            account: account,
-            multiple: false,
-            hide_totals: false,
-            expires_at: expiration
-          }
-        )
+                  text: 'Hello world',
+                  account: account,
+                  poll_attributes: {
+                    options: %w(Foo Bar),
+                    account: account,
+                    multiple: false,
+                    hide_totals: false,
+                    expires_at: expiration,
+                  })
       end
 
       let(:payload) do
@@ -216,11 +214,11 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
       end
 
       it 'does not create any edits' do
-        expect { subject.call(status, json) }.not_to change { status.reload.edits.pluck(&:id) }
+        expect { subject.call(status, json) }.to_not change { status.reload.edits.pluck(&:id) }
       end
 
       it 'does not update the text, spoiler_text or edited_at' do
-        expect { subject.call(status, json) }.not_to change { s = status.reload; [s.text, s.spoiler_text, s.edited_at] }
+        expect { subject.call(status, json) }.to_not change { s = status.reload; [s.text, s.spoiler_text, s.edited_at] }
       end
     end
 
@@ -344,7 +342,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
           updated: '2021-09-08T22:39:25Z',
           attachment: [
             { type: 'Image', mediaType: 'image/png', url: 'https://example.com/foo.png' },
-          ]
+          ],
         }
       end
 
@@ -376,7 +374,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
           updated: '2021-09-08T22:39:25Z',
           attachment: [
             { type: 'Image', mediaType: 'image/png', url: 'https://example.com/foo.png', name: 'A picture' },
-          ]
+          ],
         }
       end
 
@@ -414,7 +412,7 @@ RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
       end
 
       it 'removes poll' do
-        expect(status.reload.poll).to eq nil
+        expect(status.reload.poll).to be_nil
       end
 
       it 'records media change in edit' do
diff --git a/spec/services/activitypub/synchronize_followers_service_spec.rb b/spec/services/activitypub/synchronize_followers_service_spec.rb
index 75dcf204b..c9a513e24 100644
--- a/spec/services/activitypub/synchronize_followers_service_spec.rb
+++ b/spec/services/activitypub/synchronize_followers_service_spec.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ActivityPub::SynchronizeFollowersService, type: :service do
+  subject { described_class.new }
+
   let(:actor)          { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/account', inbox_url: 'http://example.com/inbox') }
   let(:alice)          { Fabricate(:account, username: 'alice') }
   let(:bob)            { Fabricate(:account, username: 'bob') }
@@ -25,8 +29,6 @@ RSpec.describe ActivityPub::SynchronizeFollowersService, type: :service do
     }.with_indifferent_access
   end
 
-  subject { described_class.new }
-
   shared_examples 'synchronizes followers' do
     before do
       alice.follow!(actor)
@@ -91,7 +93,7 @@ RSpec.describe ActivityPub::SynchronizeFollowersService, type: :service do
             type: 'CollectionPage',
             partOf: collection_uri,
             items: items,
-          }
+          },
         }.with_indifferent_access
       end
 
diff --git a/spec/services/after_block_domain_from_account_service_spec.rb b/spec/services/after_block_domain_from_account_service_spec.rb
index 006e3f4d2..b75f92372 100644
--- a/spec/services/after_block_domain_from_account_service_spec.rb
+++ b/spec/services/after_block_domain_from_account_service_spec.rb
@@ -1,11 +1,13 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AfterBlockDomainFromAccountService, type: :service do
+  subject { AfterBlockDomainFromAccountService.new }
+
   let!(:wolf) { Fabricate(:account, username: 'wolf', domain: 'evil.org', inbox_url: 'https://evil.org/inbox', protocol: :activitypub) }
   let!(:alice) { Fabricate(:account, username: 'alice') }
 
-  subject { AfterBlockDomainFromAccountService.new }
-
   before do
     stub_jsonld_contexts!
     allow(ActivityPub::DeliveryWorker).to receive(:perform_async)
diff --git a/spec/services/after_block_service_spec.rb b/spec/services/after_block_service_spec.rb
index 337766d06..d81bba1d8 100644
--- a/spec/services/after_block_service_spec.rb
+++ b/spec/services/after_block_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AfterBlockService, type: :service do
diff --git a/spec/services/app_sign_up_service_spec.rb b/spec/services/app_sign_up_service_spec.rb
index 8ec4d4a7a..253230496 100644
--- a/spec/services/app_sign_up_service_spec.rb
+++ b/spec/services/app_sign_up_service_spec.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AppSignUpService, type: :service do
+  subject { described_class.new }
+
   let(:app) { Fabricate(:application, scopes: 'read write') }
   let(:good_params) { { username: 'alice', password: '12345678', email: 'good@email.com', agreement: true } }
   let(:remote_ip) { IPAddr.new('198.0.2.1') }
 
-  subject { described_class.new }
-
   describe '#call' do
     it 'returns nil when registrations are closed' do
       tmp = Setting.registrations_mode
diff --git a/spec/services/authorize_follow_service_spec.rb b/spec/services/authorize_follow_service_spec.rb
index 888d694b6..63d9e2a0f 100644
--- a/spec/services/authorize_follow_service_spec.rb
+++ b/spec/services/authorize_follow_service_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe AuthorizeFollowService, type: :service do
-  let(:sender) { Fabricate(:account, username: 'alice') }
-
   subject { AuthorizeFollowService.new }
 
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   describe 'local' do
     let(:bob) { Fabricate(:account, username: 'bob') }
 
diff --git a/spec/services/batched_remove_status_service_spec.rb b/spec/services/batched_remove_status_service_spec.rb
index 920edeb13..9bedf3744 100644
--- a/spec/services/batched_remove_status_service_spec.rb
+++ b/spec/services/batched_remove_status_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe BatchedRemoveStatusService, type: :service do
diff --git a/spec/services/block_domain_service_spec.rb b/spec/services/block_domain_service_spec.rb
index 242b02fff..0ab97b8ce 100644
--- a/spec/services/block_domain_service_spec.rb
+++ b/spec/services/block_domain_service_spec.rb
@@ -1,14 +1,16 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe BlockDomainService, type: :service do
+  subject { BlockDomainService.new }
+
   let!(:bad_account) { Fabricate(:account, username: 'badguy666', domain: 'evil.org') }
   let!(:bad_status1) { Fabricate(:status, account: bad_account, text: 'You suck') }
   let!(:bad_status2) { Fabricate(:status, account: bad_account, text: 'Hahaha') }
   let!(:bad_attachment) { Fabricate(:media_attachment, account: bad_account, status: bad_status2, file: attachment_fixture('attachment.jpg')) }
   let!(:already_banned_account) { Fabricate(:account, username: 'badguy', domain: 'evil.org', suspended: true, silenced: true) }
 
-  subject { BlockDomainService.new }
-
   describe 'for a suspension' do
     before do
       subject.call(DomainBlock.create!(domain: 'evil.org', severity: :suspend))
@@ -67,9 +69,9 @@ RSpec.describe BlockDomainService, type: :service do
     end
 
     it 'leaves the domains status and attachments, but clears media' do
-      expect { bad_status1.reload }.not_to raise_error
-      expect { bad_status2.reload }.not_to raise_error
-      expect { bad_attachment.reload }.not_to raise_error
+      expect { bad_status1.reload }.to_not raise_error
+      expect { bad_status2.reload }.to_not raise_error
+      expect { bad_attachment.reload }.to_not raise_error
       expect(bad_attachment.file.exists?).to be false
     end
   end
diff --git a/spec/services/block_service_spec.rb b/spec/services/block_service_spec.rb
index a53e1f928..75f07f5ad 100644
--- a/spec/services/block_service_spec.rb
+++ b/spec/services/block_service_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe BlockService, type: :service do
-  let(:sender) { Fabricate(:account, username: 'alice') }
-
   subject { BlockService.new }
 
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   describe 'local' do
     let(:bob) { Fabricate(:account, username: 'bob') }
 
diff --git a/spec/services/bootstrap_timeline_service_spec.rb b/spec/services/bootstrap_timeline_service_spec.rb
index 16f3e9962..670ac652f 100644
--- a/spec/services/bootstrap_timeline_service_spec.rb
+++ b/spec/services/bootstrap_timeline_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe BootstrapTimelineService, type: :service do
@@ -32,6 +34,5 @@ RSpec.describe BootstrapTimelineService, type: :service do
         expect(service).to_not have_received(:call)
       end
     end
-
   end
 end
diff --git a/spec/services/clear_domain_media_service_spec.rb b/spec/services/clear_domain_media_service_spec.rb
index 45b92e2c9..987507579 100644
--- a/spec/services/clear_domain_media_service_spec.rb
+++ b/spec/services/clear_domain_media_service_spec.rb
@@ -1,22 +1,24 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ClearDomainMediaService, type: :service do
+  subject { ClearDomainMediaService.new }
+
   let!(:bad_account) { Fabricate(:account, username: 'badguy666', domain: 'evil.org') }
   let!(:bad_status1) { Fabricate(:status, account: bad_account, text: 'You suck') }
   let!(:bad_status2) { Fabricate(:status, account: bad_account, text: 'Hahaha') }
   let!(:bad_attachment) { Fabricate(:media_attachment, account: bad_account, status: bad_status2, file: attachment_fixture('attachment.jpg')) }
 
-  subject { ClearDomainMediaService.new }
-
   describe 'for a silence with reject media' do
     before do
       subject.call(DomainBlock.create!(domain: 'evil.org', severity: :silence, reject_media: true))
     end
 
     it 'leaves the domains status and attachments, but clears media' do
-      expect { bad_status1.reload }.not_to raise_error
-      expect { bad_status2.reload }.not_to raise_error
-      expect { bad_attachment.reload }.not_to raise_error
+      expect { bad_status1.reload }.to_not raise_error
+      expect { bad_status2.reload }.to_not raise_error
+      expect { bad_attachment.reload }.to_not raise_error
       expect(bad_attachment.file.exists?).to be false
     end
   end
diff --git a/spec/services/delete_account_service_spec.rb b/spec/services/delete_account_service_spec.rb
index 1fbe4d07c..61e5c3c9b 100644
--- a/spec/services/delete_account_service_spec.rb
+++ b/spec/services/delete_account_service_spec.rb
@@ -1,7 +1,11 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe DeleteAccountService, type: :service do
   shared_examples 'common behavior' do
+    subject { described_class.new.call(account) }
+
     let!(:status) { Fabricate(:status, account: account) }
     let!(:mention) { Fabricate(:mention, account: local_follower) }
     let!(:status_with_mention) { Fabricate(:status, account: account, mentions: [mention]) }
@@ -23,8 +27,6 @@ RSpec.describe DeleteAccountService, type: :service do
 
     let!(:account_note) { Fabricate(:account_note, account: account) }
 
-    subject { described_class.new.call(account) }
-
     it 'deletes associated owned records' do
       expect { subject }.to change {
         [
@@ -50,17 +52,17 @@ RSpec.describe DeleteAccountService, type: :service do
 
     it 'deletes associated target notifications' do
       expect { subject }.to change {
-        [
-          'poll', 'favourite', 'status', 'mention', 'follow'
-        ].map { |type| Notification.where(type: type).count }
+        %w(
+          poll favourite status mention follow
+        ).map { |type| Notification.where(type: type).count }
       }.from([1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0])
     end
   end
 
   describe '#call on local account' do
     before do
-      stub_request(:post, "https://alice.com/inbox").to_return(status: 201)
-      stub_request(:post, "https://bob.com/inbox").to_return(status: 201)
+      stub_request(:post, 'https://alice.com/inbox').to_return(status: 201)
+      stub_request(:post, 'https://bob.com/inbox').to_return(status: 201)
     end
 
     let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', protocol: :activitypub) }
@@ -72,16 +74,16 @@ RSpec.describe DeleteAccountService, type: :service do
 
       it 'sends a delete actor activity to all known inboxes' do
         subject
-        expect(a_request(:post, "https://alice.com/inbox")).to have_been_made.once
-        expect(a_request(:post, "https://bob.com/inbox")).to have_been_made.once
+        expect(a_request(:post, 'https://alice.com/inbox')).to have_been_made.once
+        expect(a_request(:post, 'https://bob.com/inbox')).to have_been_made.once
       end
     end
   end
 
   describe '#call on remote account' do
     before do
-      stub_request(:post, "https://alice.com/inbox").to_return(status: 201)
-      stub_request(:post, "https://bob.com/inbox").to_return(status: 201)
+      stub_request(:post, 'https://alice.com/inbox').to_return(status: 201)
+      stub_request(:post, 'https://bob.com/inbox').to_return(status: 201)
     end
 
     include_examples 'common behavior' do
diff --git a/spec/services/fan_out_on_write_service_spec.rb b/spec/services/fan_out_on_write_service_spec.rb
index 59e15d230..3b554f9ea 100644
--- a/spec/services/fan_out_on_write_service_spec.rb
+++ b/spec/services/fan_out_on_write_service_spec.rb
@@ -1,16 +1,17 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe FanOutOnWriteService, type: :service do
+  subject { described_class.new }
+
   let(:last_active_at) { Time.now.utc }
+  let(:status) { Fabricate(:status, account: alice, visibility: visibility, text: 'Hello @bob #hoge') }
 
   let!(:alice) { Fabricate(:user, current_sign_in_at: last_active_at).account }
   let!(:bob)   { Fabricate(:user, current_sign_in_at: last_active_at, account_attributes: { username: 'bob' }).account }
   let!(:tom)   { Fabricate(:user, current_sign_in_at: last_active_at).account }
 
-  subject { described_class.new }
-
-  let(:status) { Fabricate(:status, account: alice, visibility: visibility, text: 'Hello @bob #hoge') }
-
   before do
     bob.follow!(alice)
     tom.follow!(alice)
diff --git a/spec/services/favourite_service_spec.rb b/spec/services/favourite_service_spec.rb
index 9781f0d78..613ae203e 100644
--- a/spec/services/favourite_service_spec.rb
+++ b/spec/services/favourite_service_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe FavouriteService, type: :service do
-  let(:sender) { Fabricate(:account, username: 'alice') }
-
   subject { FavouriteService.new }
 
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   describe 'local' do
     let(:bob)    { Fabricate(:account) }
     let(:status) { Fabricate(:status, account: bob) }
@@ -23,7 +25,7 @@ RSpec.describe FavouriteService, type: :service do
     let(:status) { Fabricate(:status, account: bob) }
 
     before do
-      stub_request(:post, "http://example.com/inbox").to_return(status: 200, body: "", headers: {})
+      stub_request(:post, 'http://example.com/inbox').to_return(status: 200, body: '', headers: {})
       subject.call(sender, status)
     end
 
@@ -32,7 +34,7 @@ RSpec.describe FavouriteService, type: :service do
     end
 
     it 'sends a like activity' do
-      expect(a_request(:post, "http://example.com/inbox")).to have_been_made.once
+      expect(a_request(:post, 'http://example.com/inbox')).to have_been_made.once
     end
   end
 end
diff --git a/spec/services/fetch_link_card_service_spec.rb b/spec/services/fetch_link_card_service_spec.rb
index 4914c2753..d79ab7a43 100644
--- a/spec/services/fetch_link_card_service_spec.rb
+++ b/spec/services/fetch_link_card_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe FetchLinkCardService, type: :service do
@@ -30,7 +32,7 @@ RSpec.describe FetchLinkCardService, type: :service do
 
       it 'works with SJIS' do
         expect(a_request(:get, 'http://example.com/sjis')).to have_been_made.at_least_once
-        expect(status.preview_cards.first.title).to eq("SJISのページ")
+        expect(status.preview_cards.first.title).to eq('SJISのページ')
       end
     end
 
@@ -39,7 +41,7 @@ RSpec.describe FetchLinkCardService, type: :service do
 
       it 'works with SJIS even with wrong charset header' do
         expect(a_request(:get, 'http://example.com/sjis_with_wrong_charset')).to have_been_made.at_least_once
-        expect(status.preview_cards.first.title).to eq("SJISのページ")
+        expect(status.preview_cards.first.title).to eq('SJISのページ')
       end
     end
 
@@ -48,7 +50,7 @@ RSpec.describe FetchLinkCardService, type: :service do
 
       it 'works with koi8-r' do
         expect(a_request(:get, 'http://example.com/koi8-r')).to have_been_made.at_least_once
-        expect(status.preview_cards.first.title).to eq("Московя начинаетъ только въ XVI ст. привлекать внимане иностранцевъ.")
+        expect(status.preview_cards.first.title).to eq('Московя начинаетъ только въ XVI ст. привлекать внимане иностранцевъ.')
       end
     end
 
@@ -66,7 +68,7 @@ RSpec.describe FetchLinkCardService, type: :service do
 
       it 'works with Japanese path string' do
         expect(a_request(:get, 'http://example.com/日本語')).to have_been_made.at_least_once
-        expect(status.preview_cards.first.title).to eq("SJISのページ")
+        expect(status.preview_cards.first.title).to eq('SJISのページ')
       end
     end
 
diff --git a/spec/services/fetch_oembed_service_spec.rb b/spec/services/fetch_oembed_service_spec.rb
index 88f0113ed..8a0b49222 100644
--- a/spec/services/fetch_oembed_service_spec.rb
+++ b/spec/services/fetch_oembed_service_spec.rb
@@ -6,9 +6,9 @@ describe FetchOEmbedService, type: :service do
   subject { described_class.new }
 
   before do
-    stub_request(:get, "https://host.test/provider.json").to_return(status: 404)
-    stub_request(:get, "https://host.test/provider.xml").to_return(status: 404)
-    stub_request(:get, "https://host.test/empty_provider.json").to_return(status: 200)
+    stub_request(:get, 'https://host.test/provider.json').to_return(status: 404)
+    stub_request(:get, 'https://host.test/provider.xml').to_return(status: 404)
+    stub_request(:get, 'https://host.test/empty_provider.json').to_return(status: 200)
   end
 
   describe 'discover_provider' do
@@ -18,7 +18,7 @@ describe FetchOEmbedService, type: :service do
           stub_request(:get, 'https://www.youtube.com/watch?v=IPSbNdBmWKE').to_return(
             status: 200,
             headers: { 'Content-Type': 'text/html' },
-            body: request_fixture('oembed_youtube.html'),
+            body: request_fixture('oembed_youtube.html')
           )
           stub_request(:get, 'https://www.youtube.com/oembed?format=json&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DIPSbNdBmWKE').to_return(
             status: 200,
@@ -62,7 +62,7 @@ describe FetchOEmbedService, type: :service do
 
         it 'does not cache OEmbed endpoint' do
           subject.call('https://host.test/oembed.html', format: :xml)
-          expect(Rails.cache.exist?('oembed_endpoint:host.test')).to eq false
+          expect(Rails.cache.exist?('oembed_endpoint:host.test')).to be false
         end
       end
 
@@ -83,7 +83,7 @@ describe FetchOEmbedService, type: :service do
 
         it 'does not cache OEmbed endpoint' do
           subject.call('https://host.test/oembed.html')
-          expect(Rails.cache.exist?('oembed_endpoint:host.test')).to eq false
+          expect(Rails.cache.exist?('oembed_endpoint:host.test')).to be false
         end
       end
 
@@ -104,7 +104,7 @@ describe FetchOEmbedService, type: :service do
 
         it 'does not cache OEmbed endpoint' do
           subject.call('https://host.test/oembed.html')
-          expect(Rails.cache.exist?('oembed_endpoint:host.test')).to eq false
+          expect(Rails.cache.exist?('oembed_endpoint:host.test')).to be false
         end
       end
 
@@ -151,7 +151,6 @@ describe FetchOEmbedService, type: :service do
           expect(subject.format).to eq :json
         end
       end
-
     end
 
     context 'when endpoint is cached' do
diff --git a/spec/services/fetch_remote_status_service_spec.rb b/spec/services/fetch_remote_status_service_spec.rb
index fe5f1aed1..694a75dc2 100644
--- a/spec/services/fetch_remote_status_service_spec.rb
+++ b/spec/services/fetch_remote_status_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe FetchRemoteStatusService, type: :service do
@@ -7,7 +9,7 @@ RSpec.describe FetchRemoteStatusService, type: :service do
   let(:note) do
     {
       '@context': 'https://www.w3.org/ns/activitystreams',
-      id: "https://example.org/@foo/1234",
+      id: 'https://example.org/@foo/1234',
       type: 'Note',
       content: 'Lorem ipsum',
       attributedTo: ActivityPub::TagManager.instance.uri_for(account),
@@ -15,7 +17,8 @@ RSpec.describe FetchRemoteStatusService, type: :service do
   end
 
   context 'protocol is :activitypub' do
-    subject { described_class.new.call(note[:id], prefetched_body) }
+    subject { described_class.new.call(note[:id], prefetched_body: prefetched_body) }
+
     let(:prefetched_body) { Oj.dump(note) }
 
     before do
diff --git a/spec/services/fetch_resource_service_spec.rb b/spec/services/fetch_resource_service_spec.rb
index c0c96ab69..da7e42351 100644
--- a/spec/services/fetch_resource_service_spec.rb
+++ b/spec/services/fetch_resource_service_spec.rb
@@ -1,13 +1,16 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe FetchResourceService, type: :service do
   describe '#call' do
-    let(:url) { 'http://example.com' }
-
     subject { described_class.new.call(url) }
 
+    let(:url) { 'http://example.com' }
+
     context 'with blank url' do
       let(:url) { '' }
+
       it { is_expected.to be_nil }
     end
 
@@ -21,7 +24,7 @@ RSpec.describe FetchResourceService, type: :service do
 
     context 'when OpenSSL::SSL::SSLError is raised' do
       before do
-        request = double()
+        request = double
         allow(Request).to receive(:new).and_return(request)
         allow(request).to receive(:add_headers)
         allow(request).to receive(:on_behalf_of)
@@ -33,7 +36,7 @@ RSpec.describe FetchResourceService, type: :service do
 
     context 'when HTTP::ConnectionError is raised' do
       before do
-        request = double()
+        request = double
         allow(Request).to receive(:new).and_return(request)
         allow(request).to receive(:add_headers)
         allow(request).to receive(:on_behalf_of)
@@ -62,6 +65,7 @@ RSpec.describe FetchResourceService, type: :service do
 
       before do
         stub_request(:get, url).to_return(status: 200, body: body, headers: headers)
+        stub_request(:get, 'http://example.com/foo').to_return(status: 200, body: json, headers: { 'Content-Type' => 'application/activity+json' })
       end
 
       it 'signs request' do
@@ -72,7 +76,7 @@ RSpec.describe FetchResourceService, type: :service do
       context 'when content type is application/atom+xml' do
         let(:content_type) { 'application/atom+xml' }
 
-        it { is_expected.to eq nil }
+        it { is_expected.to be_nil }
       end
 
       context 'when content type is activity+json' do
@@ -89,13 +93,8 @@ RSpec.describe FetchResourceService, type: :service do
         it { is_expected.to eq [1, { prefetched_body: body, id: true }] }
       end
 
-      before do
-        stub_request(:get, url).to_return(status: 200, body: body, headers: headers)
-        stub_request(:get, 'http://example.com/foo').to_return(status: 200, body: json, headers: { 'Content-Type' => 'application/activity+json' })
-      end
-
       context 'when link header is present' do
-        let(:headers) { { 'Link' => '<http://example.com/foo>; rel="alternate"; type="application/activity+json"', } }
+        let(:headers) { { 'Link' => '<http://example.com/foo>; rel="alternate"; type="application/activity+json"' } }
 
         it { is_expected.to eq [1, { prefetched_body: json, id: true }] }
       end
diff --git a/spec/services/follow_service_spec.rb b/spec/services/follow_service_spec.rb
index 412c04d76..67a8b2c54 100644
--- a/spec/services/follow_service_spec.rb
+++ b/spec/services/follow_service_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe FollowService, type: :service do
-  let(:sender) { Fabricate(:account, username: 'alice') }
-
   subject { FollowService.new }
 
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   context 'local account' do
     describe 'locked account' do
       let(:bob) { Fabricate(:account, locked: true, username: 'bob') }
@@ -140,7 +142,7 @@ RSpec.describe FollowService, type: :service do
     let(:bob) { Fabricate(:account, username: 'bob', domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') }
 
     before do
-      stub_request(:post, "http://example.com/inbox").to_return(status: 200, body: "", headers: {})
+      stub_request(:post, 'http://example.com/inbox').to_return(status: 200, body: '', headers: {})
       subject.call(sender, bob)
     end
 
diff --git a/spec/services/import_service_spec.rb b/spec/services/import_service_spec.rb
index e2d182920..f081f2d9d 100644
--- a/spec/services/import_service_spec.rb
+++ b/spec/services/import_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ImportService, type: :service do
@@ -8,7 +10,7 @@ RSpec.describe ImportService, type: :service do
   let!(:eve)     { Fabricate(:account, username: 'eve', domain: 'example.com', locked: false, protocol: :activitypub, inbox_url: 'https://example.com/inbox') }
 
   before do
-    stub_request(:post, "https://example.com/inbox").to_return(status: 200)
+    stub_request(:post, 'https://example.com/inbox').to_return(status: 200)
   end
 
   context 'import old-style list of muted users' do
@@ -18,6 +20,7 @@ RSpec.describe ImportService, type: :service do
 
     describe 'when no accounts are muted' do
       let(:import) { Import.create(account: account, type: 'muting', data: csv) }
+
       it 'mutes the listed accounts, including notifications' do
         subject.call(import)
         expect(account.muting.count).to eq 2
@@ -55,6 +58,7 @@ RSpec.describe ImportService, type: :service do
 
     describe 'when no accounts are muted' do
       let(:import) { Import.create(account: account, type: 'muting', data: csv) }
+
       it 'mutes the listed accounts, respecting notifications' do
         subject.call(import)
         expect(account.muting.count).to eq 2
@@ -95,6 +99,7 @@ RSpec.describe ImportService, type: :service do
 
     describe 'when no accounts are followed' do
       let(:import) { Import.create(account: account, type: 'following', data: csv) }
+
       it 'follows the listed accounts, including boosts' do
         subject.call(import)
 
@@ -136,6 +141,7 @@ RSpec.describe ImportService, type: :service do
 
     describe 'when no accounts are followed' do
       let(:import) { Import.create(account: account, type: 'following', data: csv) }
+
       it 'follows the listed accounts, respecting boosts' do
         subject.call(import)
         expect(account.following.count).to eq 1
@@ -178,18 +184,17 @@ RSpec.describe ImportService, type: :service do
   context 'utf-8 encoded domains' do
     subject { ImportService.new }
 
-    let!(:nare)     { Fabricate(:account, username: 'nare', domain: 'թութ.հայ', locked: false, protocol: :activitypub, inbox_url: 'https://թութ.հայ/inbox') }
+    let!(:nare) { Fabricate(:account, username: 'nare', domain: 'թութ.հայ', locked: false, protocol: :activitypub, inbox_url: 'https://թութ.հայ/inbox') }
+    let(:csv) { attachment_fixture('utf8-followers.txt') }
+    let(:import) { Import.create(account: account, type: 'following', data: csv) }
 
     # Make sure to not actually go to the remote server
     before do
-      stub_request(:post, "https://թութ.հայ/inbox").to_return(status: 200)
+      stub_request(:post, 'https://թութ.հայ/inbox').to_return(status: 200)
     end
 
-    let(:csv) { attachment_fixture('utf8-followers.txt') }
-    let(:import) { Import.create(account: account, type: 'following', data: csv) }
-
     it 'follows the listed account' do
-    expect(account.follow_requests.count).to eq 0
+      expect(account.follow_requests.count).to eq 0
       subject.call(import)
       expect(account.follow_requests.count).to eq 1
     end
@@ -199,6 +204,9 @@ RSpec.describe ImportService, type: :service do
     subject { ImportService.new }
 
     let(:csv) { attachment_fixture('bookmark-imports.txt') }
+    let(:local_account)  { Fabricate(:account, username: 'foo', domain: '') }
+    let!(:remote_status) { Fabricate(:status, uri: 'https://example.com/statuses/1312') }
+    let!(:direct_status) { Fabricate(:status, uri: 'https://example.com/statuses/direct', visibility: :direct) }
 
     around(:each) do |example|
       local_before = Rails.configuration.x.local_domain
@@ -210,10 +218,6 @@ RSpec.describe ImportService, type: :service do
       Rails.configuration.x.local_domain = local_before
     end
 
-    let(:local_account)  { Fabricate(:account, username: 'foo', domain: '') }
-    let!(:remote_status) { Fabricate(:status, uri: 'https://example.com/statuses/1312') }
-    let!(:direct_status) { Fabricate(:status, uri: 'https://example.com/statuses/direct', visibility: :direct) }
-
     before do
       service = double
       allow(ActivityPub::FetchRemoteStatusService).to receive(:new).and_return(service)
@@ -224,12 +228,13 @@ RSpec.describe ImportService, type: :service do
 
     describe 'when no bookmarks are set' do
       let(:import) { Import.create(account: account, type: 'bookmarks', data: csv) }
+
       it 'adds the toots the user has access to to bookmarks' do
         local_status = Fabricate(:status, account: local_account, uri: 'https://local.com/users/foo/statuses/42', id: 42, local: true)
         subject.call(import)
         expect(account.bookmarks.map(&:status).map(&:id)).to include(local_status.id)
         expect(account.bookmarks.map(&:status).map(&:id)).to include(remote_status.id)
-        expect(account.bookmarks.map(&:status).map(&:id)).not_to include(direct_status.id)
+        expect(account.bookmarks.map(&:status).map(&:id)).to_not include(direct_status.id)
         expect(account.bookmarks.count).to eq 3
       end
     end
diff --git a/spec/services/mute_service_spec.rb b/spec/services/mute_service_spec.rb
index 57d8c41de..50f74ff27 100644
--- a/spec/services/mute_service_spec.rb
+++ b/spec/services/mute_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe MuteService, type: :service do
diff --git a/spec/services/notify_service_spec.rb b/spec/services/notify_service_spec.rb
index 67dd0483b..616a7aa20 100644
--- a/spec/services/notify_service_spec.rb
+++ b/spec/services/notify_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe NotifyService, type: :service do
@@ -46,13 +48,14 @@ RSpec.describe NotifyService, type: :service do
     recipient.suspend!
     expect { subject }.to_not change(Notification, :count)
   end
-  
+
   context 'for direct messages' do
     let(:activity) { Fabricate(:mention, account: recipient, status: Fabricate(:status, account: sender, visibility: :direct)) }
     let(:type)     { :mention }
 
     before do
-      user.settings.interactions = user.settings.interactions.merge('must_be_following_dm' => enabled)
+      user.settings.update('interactions.must_be_following_dm': enabled)
+      user.save
     end
 
     context 'if recipient is supposed to be following sender' do
@@ -153,8 +156,8 @@ RSpec.describe NotifyService, type: :service do
     before do
       ActionMailer::Base.deliveries.clear
 
-      notification_emails = user.settings.notification_emails
-      user.settings.notification_emails = notification_emails.merge('follow' => enabled)
+      user.settings.update('notification_emails.follow': enabled)
+      user.save
     end
 
     context 'when email notification is enabled' do
diff --git a/spec/services/post_status_service_spec.rb b/spec/services/post_status_service_spec.rb
index d21270c79..33153c3d0 100644
--- a/spec/services/post_status_service_spec.rb
+++ b/spec/services/post_status_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe PostStatusService, type: :service do
@@ -5,7 +7,7 @@ RSpec.describe PostStatusService, type: :service do
 
   it 'creates a new status' do
     account = Fabricate(:account)
-    text = "test status update"
+    text = 'test status update'
 
     status = subject.call(account, text: text)
 
@@ -16,7 +18,7 @@ RSpec.describe PostStatusService, type: :service do
   it 'creates a new response status' do
     in_reply_to_status = Fabricate(:status)
     account = Fabricate(:account)
-    text = "test status update"
+    text = 'test status update'
 
     status = subject.call(account, text: text, thread: in_reply_to_status)
 
@@ -50,7 +52,7 @@ RSpec.describe PostStatusService, type: :service do
     end
 
     it 'does not change statuses count' do
-      expect { subject.call(account, text: 'Hi future!', scheduled_at: future, thread: previous_status) }.not_to change { [account.statuses_count, previous_status.replies_count] }
+      expect { subject.call(account, text: 'Hi future!', scheduled_at: future, thread: previous_status) }.to_not change { [account.statuses_count, previous_status.replies_count] }
     end
   end
 
@@ -58,7 +60,7 @@ RSpec.describe PostStatusService, type: :service do
     boosted_status = Fabricate(:status)
     in_reply_to_status = Fabricate(:status, reblog: boosted_status)
     account = Fabricate(:account)
-    text = "test status update"
+    text = 'test status update'
 
     status = subject.call(account, text: text, thread: in_reply_to_status)
 
@@ -75,7 +77,7 @@ RSpec.describe PostStatusService, type: :service do
   end
 
   it 'creates a status with spoiler text' do
-    spoiler_text = "spoiler text"
+    spoiler_text = 'spoiler text'
 
     status = create_status_with_options(spoiler_text: spoiler_text)
 
@@ -101,14 +103,14 @@ RSpec.describe PostStatusService, type: :service do
     status = create_status_with_options(visibility: :private)
 
     expect(status).to be_persisted
-    expect(status.visibility).to eq "private"
+    expect(status.visibility).to eq 'private'
   end
 
   it 'creates a status with limited visibility for silenced users' do
     status = subject.call(Fabricate(:account, silenced: true), text: 'test', visibility: :public)
 
     expect(status).to be_persisted
-    expect(status.visibility).to eq "unlisted"
+    expect(status.visibility).to eq 'unlisted'
   end
 
   it 'creates a status for the given application' do
@@ -135,10 +137,29 @@ RSpec.describe PostStatusService, type: :service do
     allow(ProcessMentionsService).to receive(:new).and_return(mention_service)
     account = Fabricate(:account)
 
-    status = subject.call(account, text: "test status update")
+    status = subject.call(account, text: 'test status update')
 
     expect(ProcessMentionsService).to have_received(:new)
-    expect(mention_service).to have_received(:call).with(status)
+    expect(mention_service).to have_received(:call).with(status, save_records: false)
+  end
+
+  it 'safeguards mentions' do
+    account = Fabricate(:account)
+    mentioned_account = Fabricate(:account, username: 'alice')
+    unexpected_mentioned_account = Fabricate(:account, username: 'bob')
+
+    expect do
+      subject.call(account, text: '@alice hm, @bob is really annoying lately', allowed_mentions: [mentioned_account.id])
+    end.to raise_error(an_instance_of(PostStatusService::UnexpectedMentionsError).and(having_attributes(accounts: [unexpected_mentioned_account])))
+  end
+
+  it 'processes duplicate mentions correctly' do
+    account = Fabricate(:account)
+    mentioned_account = Fabricate(:account, username: 'alice')
+
+    expect do
+      subject.call(account, text: '@alice @alice @alice hey @alice')
+    end.to_not raise_error
   end
 
   it 'processes hashtags' do
@@ -147,7 +168,7 @@ RSpec.describe PostStatusService, type: :service do
     allow(ProcessHashtagsService).to receive(:new).and_return(hashtags_service)
     account = Fabricate(:account)
 
-    status = subject.call(account, text: "test status update")
+    status = subject.call(account, text: 'test status update')
 
     expect(ProcessHashtagsService).to have_received(:new)
     expect(hashtags_service).to have_received(:call).with(status)
@@ -159,7 +180,7 @@ RSpec.describe PostStatusService, type: :service do
 
     account = Fabricate(:account)
 
-    status = subject.call(account, text: "test status update")
+    status = subject.call(account, text: 'test status update')
 
     expect(DistributionWorker).to have_received(:perform_async).with(status.id)
     expect(ActivityPub::DistributionWorker).to have_received(:perform_async).with(status.id)
@@ -169,7 +190,7 @@ RSpec.describe PostStatusService, type: :service do
     allow(LinkCrawlWorker).to receive(:perform_async)
     account = Fabricate(:account)
 
-    status = subject.call(account, text: "test status update")
+    status = subject.call(account, text: 'test status update')
 
     expect(LinkCrawlWorker).to have_received(:perform_async).with(status.id)
   end
@@ -180,8 +201,8 @@ RSpec.describe PostStatusService, type: :service do
 
     status = subject.call(
       account,
-      text: "test status update",
-      media_ids: [media.id],
+      text: 'test status update',
+      media_ids: [media.id]
     )
 
     expect(media.reload.status).to eq status
@@ -193,11 +214,11 @@ RSpec.describe PostStatusService, type: :service do
 
     status = subject.call(
       account,
-      text: "test status update",
-      media_ids: [media.id],
+      text: 'test status update',
+      media_ids: [media.id]
     )
 
-    expect(media.reload.status).to eq nil
+    expect(media.reload.status).to be_nil
   end
 
   it 'does not allow attaching more than 4 files' do
@@ -206,18 +227,18 @@ RSpec.describe PostStatusService, type: :service do
     expect do
       subject.call(
         account,
-        text: "test status update",
+        text: 'test status update',
         media_ids: [
           Fabricate(:media_attachment, account: account),
           Fabricate(:media_attachment, account: account),
           Fabricate(:media_attachment, account: account),
           Fabricate(:media_attachment, account: account),
           Fabricate(:media_attachment, account: account),
-        ].map(&:id),
+        ].map(&:id)
       )
     end.to raise_error(
       Mastodon::ValidationError,
-      I18n.t('media_attachments.validations.too_many'),
+      I18n.t('media_attachments.validations.too_many')
     )
   end
 
@@ -231,15 +252,15 @@ RSpec.describe PostStatusService, type: :service do
     expect do
       subject.call(
         account,
-        text: "test status update",
+        text: 'test status update',
         media_ids: [
           video,
           image,
-        ].map(&:id),
+        ].map(&:id)
       )
     end.to raise_error(
       Mastodon::ValidationError,
-      I18n.t('media_attachments.validations.images_and_video'),
+      I18n.t('media_attachments.validations.images_and_video')
     )
   end
 
diff --git a/spec/services/precompute_feed_service_spec.rb b/spec/services/precompute_feed_service_spec.rb
index 86b93b5d2..86ab59b29 100644
--- a/spec/services/precompute_feed_service_spec.rb
+++ b/spec/services/precompute_feed_service_spec.rb
@@ -7,6 +7,7 @@ RSpec.describe PrecomputeFeedService, type: :service do
 
   describe 'call' do
     let(:account) { Fabricate(:account) }
+
     it 'fills a user timeline with statuses' do
       account = Fabricate(:account)
       status = Fabricate(:status, account: account)
@@ -30,7 +31,7 @@ RSpec.describe PrecomputeFeedService, type: :service do
 
       subject.call(account)
 
-      expect(redis.zscore(FeedManager.instance.key(:home, account.id), reblog.id)).to eq nil
+      expect(redis.zscore(FeedManager.instance.key(:home, account.id), reblog.id)).to be_nil
     end
   end
 end
diff --git a/spec/services/process_mentions_service_spec.rb b/spec/services/process_mentions_service_spec.rb
index 5b9d17a4c..adc45c60a 100644
--- a/spec/services/process_mentions_service_spec.rb
+++ b/spec/services/process_mentions_service_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ProcessMentionsService, type: :service do
-  let(:account) { Fabricate(:account, username: 'alice') }
-
   subject { ProcessMentionsService.new }
 
+  let(:account) { Fabricate(:account, username: 'alice') }
+
   context 'when mentions contain blocked accounts' do
     let(:non_blocked_account)          { Fabricate(:account) }
     let(:individually_blocked_account) { Fabricate(:account) }
@@ -47,9 +49,22 @@ RSpec.describe ProcessMentionsService, type: :service do
         end
       end
 
+      context 'mentioning a user several times when not saving records' do
+        let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
+        let(:status)       { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct} @#{remote_user.acct} @#{remote_user.acct}", visibility: :public) }
+
+        before do
+          subject.call(status, save_records: false)
+        end
+
+        it 'creates exactly one mention' do
+          expect(status.mentions.size).to eq 1
+        end
+      end
+
       context 'with an IDN domain' do
         let!(:remote_user) { Fabricate(:account, username: 'sneak', protocol: :activitypub, domain: 'xn--hresiar-mxa.ch', inbox_url: 'http://example.com/inbox') }
-        let!(:status) { Fabricate(:status, account: account, text: "Hello @sneak@hæresiar.ch") }
+        let!(:status) { Fabricate(:status, account: account, text: 'Hello @sneak@hæresiar.ch') }
 
         before do
           subject.call(status)
@@ -62,7 +77,7 @@ RSpec.describe ProcessMentionsService, type: :service do
 
       context 'with an IDN TLD' do
         let!(:remote_user) { Fabricate(:account, username: 'foo', protocol: :activitypub, domain: 'xn--y9a3aq.xn--y9a3aq', inbox_url: 'http://example.com/inbox') }
-        let!(:status) { Fabricate(:status, account: account, text: "Hello @foo@հայ.հայ") }
+        let!(:status) { Fabricate(:status, account: account, text: 'Hello @foo@հայ.հայ') }
 
         before do
           subject.call(status)
@@ -78,8 +93,8 @@ RSpec.describe ProcessMentionsService, type: :service do
       let!(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox', last_webfingered_at: nil) }
 
       before do
-        stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
-        stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com").to_return(status: 500)
+        stub_request(:get, 'https://example.com/.well-known/host-meta').to_return(status: 404)
+        stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:remote_user@example.com').to_return(status: 500)
         subject.call(status)
       end
 
diff --git a/spec/services/purge_domain_service_spec.rb b/spec/services/purge_domain_service_spec.rb
index 59285f126..310affa5e 100644
--- a/spec/services/purge_domain_service_spec.rb
+++ b/spec/services/purge_domain_service_spec.rb
@@ -1,13 +1,15 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe PurgeDomainService, type: :service do
+  subject { PurgeDomainService.new }
+
   let!(:old_account) { Fabricate(:account, domain: 'obsolete.org') }
   let!(:old_status1) { Fabricate(:status, account: old_account) }
   let!(:old_status2) { Fabricate(:status, account: old_account) }
   let!(:old_attachment) { Fabricate(:media_attachment, account: old_account, status: old_status2, file: attachment_fixture('attachment.jpg')) }
 
-  subject { PurgeDomainService.new }
-
   describe 'for a suspension' do
     before do
       subject.call('obsolete.org')
diff --git a/spec/services/reblog_service_spec.rb b/spec/services/reblog_service_spec.rb
index c0ae5eedc..c00472229 100644
--- a/spec/services/reblog_service_spec.rb
+++ b/spec/services/reblog_service_spec.rb
@@ -1,15 +1,17 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ReblogService, type: :service do
   let(:alice)  { Fabricate(:account, username: 'alice') }
 
   context 'creates a reblog with appropriate visibility' do
+    subject { ReblogService.new }
+
     let(:visibility)        { :public }
     let(:reblog_visibility) { :public }
     let(:status)            { Fabricate(:status, account: alice, visibility: visibility) }
 
-    subject { ReblogService.new }
-
     before do
       subject.call(alice, status, visibility: reblog_visibility)
     end
@@ -45,11 +47,11 @@ RSpec.describe ReblogService, type: :service do
   end
 
   context 'ActivityPub' do
+    subject { ReblogService.new }
+
     let(:bob)    { Fabricate(:account, username: 'bob', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }
     let(:status) { Fabricate(:status, account: bob) }
 
-    subject { ReblogService.new }
-
     before do
       stub_request(:post, bob.inbox_url)
       allow(ActivityPub::DistributionWorker).to receive(:perform_async)
diff --git a/spec/services/reject_follow_service_spec.rb b/spec/services/reject_follow_service_spec.rb
index e14bfa78d..be9363d84 100644
--- a/spec/services/reject_follow_service_spec.rb
+++ b/spec/services/reject_follow_service_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe RejectFollowService, type: :service do
-  let(:sender) { Fabricate(:account, username: 'alice') }
-
   subject { RejectFollowService.new }
 
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   describe 'local' do
     let(:bob) { Fabricate(:account) }
 
diff --git a/spec/services/remove_from_follwers_service_spec.rb b/spec/services/remove_from_followers_service_spec.rb
index a83f6f49a..21cea2e4f 100644
--- a/spec/services/remove_from_follwers_service_spec.rb
+++ b/spec/services/remove_from_followers_service_spec.rb
@@ -1,13 +1,15 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe RemoveFromFollowersService, type: :service do
-  let(:bob) { Fabricate(:account, username: 'bob') }
-
   subject { RemoveFromFollowersService.new }
 
+  let(:bob) { Fabricate(:account, username: 'bob') }
+
   describe 'local' do
     let(:sender) { Fabricate(:account, username: 'alice') }
- 
+
     before do
       Follow.create(account: sender, target_account: bob)
       subject.call(bob, sender)
diff --git a/spec/services/remove_status_service_spec.rb b/spec/services/remove_status_service_spec.rb
index 482068d58..a836109a0 100644
--- a/spec/services/remove_status_service_spec.rb
+++ b/spec/services/remove_status_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe RemoveStatusService, type: :service do
@@ -37,29 +39,29 @@ RSpec.describe RemoveStatusService, type: :service do
     it 'sends Delete activity to followers' do
       subject.call(@status)
       expect(a_request(:post, 'http://example.com/inbox').with(
-        body: hash_including({
-          'type' => 'Delete',
-          'object' => {
-            'type' => 'Tombstone',
-            'id' => ActivityPub::TagManager.instance.uri_for(@status),
-            'atomUri' => OStatus::TagManager.instance.uri_for(@status),
-          },
-        })
-      )).to have_been_made.once
+               body: hash_including({
+                 'type' => 'Delete',
+                 'object' => {
+                   'type' => 'Tombstone',
+                   'id' => ActivityPub::TagManager.instance.uri_for(@status),
+                   'atomUri' => OStatus::TagManager.instance.uri_for(@status),
+                 },
+               })
+             )).to have_been_made.once
     end
 
     it 'sends Delete activity to rebloggers' do
       subject.call(@status)
       expect(a_request(:post, 'http://example2.com/inbox').with(
-        body: hash_including({
-          'type' => 'Delete',
-          'object' => {
-            'type' => 'Tombstone',
-            'id' => ActivityPub::TagManager.instance.uri_for(@status),
-            'atomUri' => OStatus::TagManager.instance.uri_for(@status),
-          },
-        })
-      )).to have_been_made.once
+               body: hash_including({
+                 'type' => 'Delete',
+                 'object' => {
+                   'type' => 'Tombstone',
+                   'id' => ActivityPub::TagManager.instance.uri_for(@status),
+                   'atomUri' => OStatus::TagManager.instance.uri_for(@status),
+                 },
+               })
+             )).to have_been_made.once
     end
 
     it 'remove status from notifications' do
@@ -78,14 +80,14 @@ RSpec.describe RemoveStatusService, type: :service do
     it 'sends Undo activity to followers' do
       subject.call(@status)
       expect(a_request(:post, 'http://example.com/inbox').with(
-        body: hash_including({
-          'type' => 'Undo',
-          'object' => hash_including({
-            'type' => 'Announce',
-            'object' => ActivityPub::TagManager.instance.uri_for(@original_status),
-          }),
-        })
-      )).to have_been_made.once
+               body: hash_including({
+                 'type' => 'Undo',
+                 'object' => hash_including({
+                   'type' => 'Announce',
+                   'object' => ActivityPub::TagManager.instance.uri_for(@original_status),
+                 }),
+               })
+             )).to have_been_made.once
     end
   end
 
@@ -98,14 +100,14 @@ RSpec.describe RemoveStatusService, type: :service do
     it 'sends Undo activity to followers' do
       subject.call(@status)
       expect(a_request(:post, 'http://example.com/inbox').with(
-        body: hash_including({
-          'type' => 'Undo',
-          'object' => hash_including({
-            'type' => 'Announce',
-            'object' => ActivityPub::TagManager.instance.uri_for(@original_status),
-          }),
-        })
-      )).to have_been_made.once
+               body: hash_including({
+                 'type' => 'Undo',
+                 'object' => hash_including({
+                   'type' => 'Announce',
+                   'object' => ActivityPub::TagManager.instance.uri_for(@original_status),
+                 }),
+               })
+             )).to have_been_made.once
     end
   end
 end
diff --git a/spec/services/report_service_spec.rb b/spec/services/report_service_spec.rb
index 02bc42ac1..452400f72 100644
--- a/spec/services/report_service_spec.rb
+++ b/spec/services/report_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ReportService, type: :service do
@@ -29,13 +31,13 @@ RSpec.describe ReportService, type: :service do
   end
 
   context 'when the reported status is a DM' do
-    let(:target_account) { Fabricate(:account) }
-    let(:status) { Fabricate(:status, account: target_account, visibility: :direct) }
-
     subject do
       -> { described_class.new.call(source_account, target_account, status_ids: [status.id]) }
     end
 
+    let(:target_account) { Fabricate(:account) }
+    let(:status) { Fabricate(:status, account: target_account, visibility: :direct) }
+
     context 'when it is addressed to the reporter' do
       before do
         status.mentions.create(account: source_account)
@@ -85,16 +87,17 @@ RSpec.describe ReportService, type: :service do
   end
 
   context 'when other reports already exist for the same target' do
-    let!(:target_account) { Fabricate(:account) }
-    let!(:other_report)   { Fabricate(:report, target_account: target_account) }
-
     subject do
       -> {  described_class.new.call(source_account, target_account) }
     end
 
+    let!(:target_account) { Fabricate(:account) }
+    let!(:other_report)   { Fabricate(:report, target_account: target_account) }
+
     before do
       ActionMailer::Base.deliveries.clear
-      source_account.user.settings.notification_emails['report'] = true
+      source_account.user.settings['notification_emails.report'] = true
+      source_account.user.save
     end
 
     it 'does not send an e-mail' do
diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb
index 654606bea..3ce1f7f2b 100644
--- a/spec/services/resolve_account_service_spec.rb
+++ b/spec/services/resolve_account_service_spec.rb
@@ -1,14 +1,16 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe ResolveAccountService, type: :service do
   subject { described_class.new }
 
   before do
-    stub_request(:get, "https://example.com/.well-known/host-meta").to_return(status: 404)
-    stub_request(:get, "https://quitter.no/avatar/7477-300-20160211190340.png").to_return(request_fixture('avatar.txt'))
-    stub_request(:get, "https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com").to_return(request_fixture('activitypub-webfinger.txt'))
-    stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor.txt'))
-    stub_request(:get, "https://ap.example.com/users/foo.atom").to_return(request_fixture('activitypub-feed.txt'))
+    stub_request(:get, 'https://example.com/.well-known/host-meta').to_return(status: 404)
+    stub_request(:get, 'https://quitter.no/avatar/7477-300-20160211190340.png').to_return(request_fixture('avatar.txt'))
+    stub_request(:get, 'https://ap.example.com/.well-known/webfinger?resource=acct:foo@ap.example.com').to_return(request_fixture('activitypub-webfinger.txt'))
+    stub_request(:get, 'https://ap.example.com/users/foo').to_return(request_fixture('activitypub-actor.txt'))
+    stub_request(:get, 'https://ap.example.com/users/foo.atom').to_return(request_fixture('activitypub-feed.txt'))
     stub_request(:get, %r{https://ap.example.com/users/foo/\w+}).to_return(status: 404)
     stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:hoge@example.com').to_return(status: 410)
   end
@@ -56,8 +58,8 @@ RSpec.describe ResolveAccountService, type: :service do
 
   context 'when there is an LRDD endpoint but no resolvable account' do
     before do
-      stub_request(:get, "https://quitter.no/.well-known/host-meta").to_return(request_fixture('.host-meta.txt'))
-      stub_request(:get, "https://quitter.no/.well-known/webfinger?resource=acct:catsrgr8@quitter.no").to_return(status: 404)
+      stub_request(:get, 'https://quitter.no/.well-known/host-meta').to_return(request_fixture('.host-meta.txt'))
+      stub_request(:get, 'https://quitter.no/.well-known/webfinger?resource=acct:catsrgr8@quitter.no').to_return(status: 404)
     end
 
     it 'returns nil' do
@@ -67,7 +69,7 @@ RSpec.describe ResolveAccountService, type: :service do
 
   context 'when there is no LRDD endpoint nor resolvable account' do
     before do
-      stub_request(:get, "https://example.com/.well-known/webfinger?resource=acct:catsrgr8@example.com").to_return(status: 404)
+      stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:catsrgr8@example.com').to_return(status: 404)
     end
 
     it 'returns nil' do
@@ -108,7 +110,7 @@ RSpec.describe ResolveAccountService, type: :service do
     it 'returns new remote account' do
       account = subject.call('Foo@redirected.example.com')
 
-      expect(account.activitypub?).to eq true
+      expect(account.activitypub?).to be true
       expect(account.acct).to eq 'foo@ap.example.com'
       expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
     end
@@ -123,7 +125,7 @@ RSpec.describe ResolveAccountService, type: :service do
     it 'returns new remote account' do
       account = subject.call('Foo@redirected.example.com')
 
-      expect(account.activitypub?).to eq true
+      expect(account.activitypub?).to be true
       expect(account.acct).to eq 'foo@ap.example.com'
       expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
     end
@@ -146,20 +148,20 @@ RSpec.describe ResolveAccountService, type: :service do
     it 'returns new remote account' do
       account = subject.call('foo@ap.example.com')
 
-      expect(account.activitypub?).to eq true
+      expect(account.activitypub?).to be true
       expect(account.domain).to eq 'ap.example.com'
       expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
     end
 
     context 'with multiple types' do
       before do
-        stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor-individual.txt'))
+        stub_request(:get, 'https://ap.example.com/users/foo').to_return(request_fixture('activitypub-actor-individual.txt'))
       end
 
       it 'returns new remote account' do
         account = subject.call('foo@ap.example.com')
 
-        expect(account.activitypub?).to eq true
+        expect(account.activitypub?).to be true
         expect(account.domain).to eq 'ap.example.com'
         expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
         expect(account.actor_type).to eq 'Person'
@@ -174,7 +176,7 @@ RSpec.describe ResolveAccountService, type: :service do
     it 'returns new remote account' do
       account = subject.call('foo@ap.example.com')
 
-      expect(account.activitypub?).to eq true
+      expect(account.activitypub?).to be true
       expect(account.domain).to eq 'ap.example.com'
       expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
       expect(account.uri).to eq 'https://ap.example.com/users/foo'
@@ -190,12 +192,12 @@ RSpec.describe ResolveAccountService, type: :service do
 
   context 'with an already-known acct: URI changing ActivityPub id' do
     let!(:old_account) { Fabricate(:account, username: 'foo', domain: 'ap.example.com', uri: 'https://old.example.com/users/foo', last_webfingered_at: nil) }
-    let!(:status)    { Fabricate(:status, account: old_account, text: 'foo') }
+    let!(:status) { Fabricate(:status, account: old_account, text: 'foo') }
 
     it 'returns new remote account' do
       account = subject.call('foo@ap.example.com')
 
-      expect(account.activitypub?).to eq true
+      expect(account.activitypub?).to be true
       expect(account.domain).to eq 'ap.example.com'
       expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox'
       expect(account.uri).to eq 'https://ap.example.com/users/foo'
diff --git a/spec/services/resolve_url_service_spec.rb b/spec/services/resolve_url_service_spec.rb
index b3e3defbf..3598311ee 100644
--- a/spec/services/resolve_url_service_spec.rb
+++ b/spec/services/resolve_url_service_spec.rb
@@ -133,7 +133,7 @@ describe ResolveURLService, type: :service do
       let!(:status) { Fabricate(:status, account: poster, visibility: :public) }
       let(:url)     { 'https://link.to/foobar' }
       let(:status_url) { ActivityPub::TagManager.instance.url_for(status) }
-      let(:uri)     { ActivityPub::TagManager.instance.uri_for(status) }
+      let(:uri) { ActivityPub::TagManager.instance.uri_for(status) }
 
       before do
         stub_request(:get, url).to_return(status: 302, headers: { 'Location' => status_url })
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index 5b52662ba..1ad0efe0a 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -13,8 +13,8 @@ describe SearchService, type: :service do
         results = subject.call('', nil, 10)
 
         expect(results).to eq(empty_results)
-        expect(AccountSearchService).not_to have_received(:new)
-        expect(Tag).not_to have_received(:search_for)
+        expect(AccountSearchService).to_not have_received(:new)
+        expect(Tag).to_not have_received(:search_for)
       end
     end
 
@@ -77,20 +77,22 @@ describe SearchService, type: :service do
         it 'includes the tag in the results' do
           query = '#tag'
           tag = Tag.new
-          allow(Tag).to receive(:search_for).with('tag', 10, 0, exclude_unreviewed: nil).and_return([tag])
+          allow(Tag).to receive(:search_for).with('tag', 10, 0, { exclude_unreviewed: nil }).and_return([tag])
 
           results = subject.call(query, nil, 10)
           expect(Tag).to have_received(:search_for).with('tag', 10, 0, exclude_unreviewed: nil)
           expect(results).to eq empty_results.merge(hashtags: [tag])
         end
+
         it 'does not include tag when starts with @ character' do
           query = '@username'
           allow(Tag).to receive(:search_for)
 
           results = subject.call(query, nil, 10)
-          expect(Tag).not_to have_received(:search_for)
+          expect(Tag).to_not have_received(:search_for)
           expect(results).to eq empty_results
         end
+
         it 'does not include account when starts with # character' do
           query = '#tag'
           allow(AccountSearchService).to receive(:new)
diff --git a/spec/services/suspend_account_service_spec.rb b/spec/services/suspend_account_service_spec.rb
index 126b13986..4489bfed5 100644
--- a/spec/services/suspend_account_service_spec.rb
+++ b/spec/services/suspend_account_service_spec.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe SuspendAccountService, type: :service do
   shared_examples 'common behavior' do
+    subject { described_class.new.call(account) }
+
     let!(:local_follower) { Fabricate(:user, current_sign_in_at: 1.hour.ago).account }
     let!(:list)           { Fabricate(:list, account: local_follower) }
 
-    subject { described_class.new.call(account) }
-
     before do
       allow(FeedManager.instance).to receive(:unmerge_from_home).and_return(nil)
       allow(FeedManager.instance).to receive(:unmerge_from_list).and_return(nil)
diff --git a/spec/services/unallow_domain_service_spec.rb b/spec/services/unallow_domain_service_spec.rb
index b93945b9a..48e310a9d 100644
--- a/spec/services/unallow_domain_service_spec.rb
+++ b/spec/services/unallow_domain_service_spec.rb
@@ -1,6 +1,10 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe UnallowDomainService, type: :service do
+  subject { UnallowDomainService.new }
+
   let!(:bad_account) { Fabricate(:account, username: 'badguy666', domain: 'evil.org') }
   let!(:bad_status1) { Fabricate(:status, account: bad_account, text: 'You suck') }
   let!(:bad_status2) { Fabricate(:status, account: bad_account, text: 'Hahaha') }
@@ -8,8 +12,6 @@ RSpec.describe UnallowDomainService, type: :service do
   let!(:already_banned_account) { Fabricate(:account, username: 'badguy', domain: 'evil.org', suspended: true, silenced: true) }
   let!(:domain_allow) { Fabricate(:domain_allow, domain: 'evil.org') }
 
-  subject { UnallowDomainService.new }
-
   context 'in limited federation mode' do
     before do
       allow(subject).to receive(:whitelist_mode?).and_return(true)
diff --git a/spec/services/unblock_service_spec.rb b/spec/services/unblock_service_spec.rb
index 10448b340..8098d7e6d 100644
--- a/spec/services/unblock_service_spec.rb
+++ b/spec/services/unblock_service_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe UnblockService, type: :service do
-  let(:sender) { Fabricate(:account, username: 'alice') }
-
   subject { UnblockService.new }
 
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   describe 'local' do
     let(:bob) { Fabricate(:account) }
 
diff --git a/spec/services/unfollow_service_spec.rb b/spec/services/unfollow_service_spec.rb
index bb5bef5c9..a12f01fa5 100644
--- a/spec/services/unfollow_service_spec.rb
+++ b/spec/services/unfollow_service_spec.rb
@@ -1,10 +1,12 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe UnfollowService, type: :service do
-  let(:sender) { Fabricate(:account, username: 'alice') }
-
   subject { UnfollowService.new }
 
+  let(:sender) { Fabricate(:account, username: 'alice') }
+
   describe 'local' do
     let(:bob) { Fabricate(:account, username: 'bob') }
 
diff --git a/spec/services/unmute_service_spec.rb b/spec/services/unmute_service_spec.rb
index 8463eb283..2edb6cfc2 100644
--- a/spec/services/unmute_service_spec.rb
+++ b/spec/services/unmute_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe UnmuteService, type: :service do
diff --git a/spec/services/unsuspend_account_service_spec.rb b/spec/services/unsuspend_account_service_spec.rb
index 987eb09e2..5d7012093 100644
--- a/spec/services/unsuspend_account_service_spec.rb
+++ b/spec/services/unsuspend_account_service_spec.rb
@@ -1,12 +1,14 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe UnsuspendAccountService, type: :service do
   shared_examples 'common behavior' do
+    subject { described_class.new.call(account) }
+
     let!(:local_follower) { Fabricate(:user, current_sign_in_at: 1.hour.ago).account }
     let!(:list)           { Fabricate(:list, account: local_follower) }
 
-    subject { described_class.new.call(account) }
-
     before do
       allow(FeedManager.instance).to receive(:merge_into_home).and_return(nil)
       allow(FeedManager.instance).to receive(:merge_into_list).and_return(nil)
diff --git a/spec/services/update_account_service_spec.rb b/spec/services/update_account_service_spec.rb
index c2dc791e4..a711a8ae7 100644
--- a/spec/services/update_account_service_spec.rb
+++ b/spec/services/update_account_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe UpdateAccountService, type: :service do
diff --git a/spec/services/update_status_service_spec.rb b/spec/services/update_status_service_spec.rb
index 71a73be5b..d6923722a 100644
--- a/spec/services/update_status_service_spec.rb
+++ b/spec/services/update_status_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe UpdateStatusService, type: :service do
@@ -87,16 +89,40 @@ RSpec.describe UpdateStatusService, type: :service do
     end
   end
 
+  context 'when already-attached media changes' do
+    let!(:status) { Fabricate(:status, text: 'Foo') }
+    let!(:media_attachment) { Fabricate(:media_attachment, account: status.account, description: 'Old description') }
+
+    before do
+      status.media_attachments << media_attachment
+      subject.call(status, status.account_id, text: 'Foo', media_ids: [media_attachment.id], media_attributes: [{ id: media_attachment.id, description: 'New description' }])
+    end
+
+    it 'does not detach media attachment' do
+      expect(media_attachment.reload.status_id).to eq status.id
+    end
+
+    it 'updates the media attachment description' do
+      expect(media_attachment.reload.description).to eq 'New description'
+    end
+
+    it 'saves edit history' do
+      expect(status.edits.map { |edit| edit.ordered_media_attachments.map(&:description) }).to eq [['Old description'], ['New description']]
+    end
+  end
+
   context 'when poll changes' do
     let(:account) { Fabricate(:account) }
-    let!(:status) { Fabricate(:status, text: 'Foo', account: account, poll_attributes: {options: %w(Foo Bar), account: account, multiple: false, hide_totals: false, expires_at: 7.days.from_now }) }
+    let!(:status) { Fabricate(:status, text: 'Foo', account: account, poll_attributes: { options: %w(Foo Bar), account: account, multiple: false, hide_totals: false, expires_at: 7.days.from_now }) }
     let!(:poll)   { status.poll }
     let!(:voter) { Fabricate(:account) }
 
     before do
       status.update(poll: poll)
       VoteService.new.call(voter, poll, [0])
-      subject.call(status, status.account_id, text: 'Foo', poll: { options: %w(Bar Baz Foo), expires_in: 5.days.to_i })
+      Sidekiq::Testing.fake! do
+        subject.call(status, status.account_id, text: 'Foo', poll: { options: %w(Bar Baz Foo), expires_in: 5.days.to_i })
+      end
     end
 
     it 'updates poll' do
@@ -114,6 +140,11 @@ RSpec.describe UpdateStatusService, type: :service do
     it 'saves edit history' do
       expect(status.edits.pluck(:poll_options)).to eq [%w(Foo Bar), %w(Bar Baz Foo)]
     end
+
+    it 'requeues expiration notification' do
+      poll = status.poll.reload
+      expect(PollExpirationNotifyWorker).to have_enqueued_sidekiq_job(poll.id).at(poll.expires_at + 5.minutes)
+    end
   end
 
   context 'when mentions in text change' do
diff --git a/spec/services/verify_link_service_spec.rb b/spec/services/verify_link_service_spec.rb
index 391560f1c..ea9ccc3fc 100644
--- a/spec/services/verify_link_service_spec.rb
+++ b/spec/services/verify_link_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 RSpec.describe VerifyLinkService, type: :service do
@@ -150,5 +152,27 @@ RSpec.describe VerifyLinkService, type: :service do
         expect(field.verified?).to be true
       end
     end
+
+    context 'when the link contains a link with a missing protocol slash' do
+      # This was seen in the wild where a user had three pages:
+      # 1. their mastodon profile, which linked to github and the personal website
+      # 2. their personal website correctly linking back to mastodon
+      # 3. a github profile that was linking to the personal website, but with
+      #    a malformed protocol of http:/
+      #
+      # This caused link verification between the mastodon profile and the
+      # website to fail.
+      #
+      # apparently github allows the user to enter website URLs with a single
+      # slash and makes no attempts to correct that.
+      let(:html) { '<a href="http:/unrelated.example">Hello</a>' }
+
+      it 'does not crash' do
+        # We could probably put more effort into perhaps auto-correcting the
+        # link and following it anyway, but at the very least we shouldn't let
+        # exceptions bubble up
+        expect(field.verified?).to be false
+      end
+    end
   end
 end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 0414ba9ed..25f314002 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,10 +1,14 @@
+# frozen_string_literal: true
+
 GC.disable
 
 if ENV['DISABLE_SIMPLECOV'] != 'true'
   require 'simplecov'
   SimpleCov.start 'rails' do
-    add_group 'Services', 'app/services'
+    add_group 'Policies', 'app/policies'
     add_group 'Presenters', 'app/presenters'
+    add_group 'Serializers', 'app/serializers'
+    add_group 'Services', 'app/services'
     add_group 'Validators', 'app/validators'
   end
 end
@@ -12,7 +16,7 @@ end
 gc_counter = -1
 
 RSpec.configure do |config|
-  config.example_status_persistence_file_path = "tmp/rspec/examples.txt"
+  config.example_status_persistence_file_path = 'tmp/rspec/examples.txt'
   config.expect_with :rspec do |expectations|
     expectations.include_chain_clauses_in_custom_matcher_descriptions = true
   end
@@ -60,7 +64,7 @@ end
 
 def expect_push_bulk_to_match(klass, matcher)
   expect(Sidekiq::Client).to receive(:push_bulk).with(hash_including({
-    "class" => klass,
-    "args" => matcher
+    'class' => klass,
+    'args' => matcher,
   }))
 end
diff --git a/spec/support/examples/lib/admin/checks.rb b/spec/support/examples/lib/admin/checks.rb
new file mode 100644
index 000000000..b50faa77b
--- /dev/null
+++ b/spec/support/examples/lib/admin/checks.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+shared_examples 'a check available to devops users' do
+  describe 'skip?' do
+    context 'when user can view devops' do
+      before { allow(user).to receive(:can?).with(:view_devops).and_return(true) }
+
+      it 'returns false' do
+        expect(check.skip?).to be false
+      end
+    end
+
+    context 'when user cannot view devops' do
+      before { allow(user).to receive(:can?).with(:view_devops).and_return(false) }
+
+      it 'returns true' do
+        expect(check.skip?).to be true
+      end
+    end
+  end
+end
diff --git a/spec/support/examples/lib/settings/scoped_settings.rb b/spec/support/examples/lib/settings/scoped_settings.rb
index 2457dcfbf..106adb4fa 100644
--- a/spec/support/examples/lib/settings/scoped_settings.rb
+++ b/spec/support/examples/lib/settings/scoped_settings.rb
@@ -3,13 +3,13 @@
 shared_examples 'ScopedSettings' do
   describe '[]' do
     it 'inherits default settings' do
-      expect(Setting.boost_modal).to eq false
-      expect(Setting.interactions['must_be_follower']).to eq false
+      expect(Setting.boost_modal).to be false
+      expect(Setting.interactions['must_be_follower']).to be false
 
       settings = create!
 
-      expect(settings['boost_modal']).to eq false
-      expect(settings['interactions']['must_be_follower']).to eq false
+      expect(settings['boost_modal']).to be false
+      expect(settings['interactions']['must_be_follower']).to be false
     end
   end
 
@@ -17,16 +17,16 @@ shared_examples 'ScopedSettings' do
     # expecting [] and []= works
 
     it 'returns records merged with default values except hashes' do
-      expect(Setting.boost_modal).to eq false
-      expect(Setting.delete_modal).to eq true
+      expect(Setting.boost_modal).to be false
+      expect(Setting.delete_modal).to be true
 
       settings = create!
       settings['boost_modal'] = true
 
       records = settings.all_as_records
 
-      expect(records['boost_modal'].value).to eq true
-      expect(records['delete_modal'].value).to eq true
+      expect(records['boost_modal'].value).to be true
+      expect(records['delete_modal'].value).to be true
     end
   end
 
@@ -34,15 +34,15 @@ shared_examples 'ScopedSettings' do
     # expecting [] and []= works.
 
     it 'reads settings' do
-      expect(Setting.boost_modal).to eq false
+      expect(Setting.boost_modal).to be false
       settings = create!
-      expect(settings.boost_modal).to eq false
+      expect(settings.boost_modal).to be false
     end
 
     it 'updates settings' do
       settings = fabricate
       settings.boost_modal = true
-      expect(settings['boost_modal']).to eq true
+      expect(settings['boost_modal']).to be true
     end
   end
 
@@ -54,13 +54,13 @@ shared_examples 'ScopedSettings' do
 
     Setting.save!
 
-    expect(settings['boost_modal']).to eq true
-    expect(settings['interactions']['must_be_follower']).to eq true
+    expect(settings['boost_modal']).to be true
+    expect(settings['interactions']['must_be_follower']).to be true
 
     Rails.cache.clear
 
-    expect(settings['boost_modal']).to eq true
-    expect(settings['interactions']['must_be_follower']).to eq true
+    expect(settings['boost_modal']).to be true
+    expect(settings['interactions']['must_be_follower']).to be true
   end
 
   xit 'does not mutate defaults via the cache' do
@@ -69,6 +69,6 @@ shared_examples 'ScopedSettings' do
     # This mutates the global settings default such that future
     # instances will inherit the incorrect starting values
 
-    expect(fabricate.settings['interactions']['must_be_follower']).to eq false
+    expect(fabricate.settings['interactions']['must_be_follower']).to be false
   end
 end
diff --git a/spec/support/matchers/json/match_json_schema.rb b/spec/support/matchers/json/match_json_schema.rb
index 5d9c9a618..3a275199e 100644
--- a/spec/support/matchers/json/match_json_schema.rb
+++ b/spec/support/matchers/json/match_json_schema.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 RSpec::Matchers.define :match_json_schema do |schema|
   match do |input_json|
     schema_path = Rails.root.join('spec', 'support', 'schema', "#{schema}.json").to_s
diff --git a/spec/support/matchers/model/model_have_error_on_field.rb b/spec/support/matchers/model/model_have_error_on_field.rb
index 85bdd8215..0f9c81a47 100644
--- a/spec/support/matchers/model/model_have_error_on_field.rb
+++ b/spec/support/matchers/model/model_have_error_on_field.rb
@@ -1,10 +1,10 @@
+# frozen_string_literal: true
+
 RSpec::Matchers.define :model_have_error_on_field do |expected|
   match do |record|
-    if record.errors.empty?
-      record.valid?
-    end
+    record.valid? if record.errors.empty?
 
-    record.errors.has_key?(expected)
+    record.errors.key?(expected)
   end
 
   failure_message do |record|
diff --git a/spec/support/stories/profile_stories.rb b/spec/support/stories/profile_stories.rb
index 0c4a14d1c..de7ae17e6 100644
--- a/spec/support/stories/profile_stories.rb
+++ b/spec/support/stories/profile_stories.rb
@@ -20,8 +20,8 @@ module ProfileStories
   end
 
   def with_alice_as_local_user
-    @alice_bio = '@alice and @bob are fictional characters commonly used as'\
-                 'placeholder names in #cryptology, as well as #science and'\
+    @alice_bio = '@alice and @bob are fictional characters commonly used as' \
+                 'placeholder names in #cryptology, as well as #science and' \
                  'engineering 📖 literature. Not affiliated with @pepe.'
 
     @alice = Fabricate(
diff --git a/spec/validators/blacklisted_email_validator_spec.rb b/spec/validators/blacklisted_email_validator_spec.rb
index 351de0707..a642405ae 100644
--- a/spec/validators/blacklisted_email_validator_spec.rb
+++ b/spec/validators/blacklisted_email_validator_spec.rb
@@ -4,16 +4,16 @@ require 'rails_helper'
 
 RSpec.describe BlacklistedEmailValidator, type: :validator do
   describe '#validate' do
+    subject { described_class.new.validate(user); errors }
+
     let(:user)   { double(email: 'info@mail.com', sign_up_ip: '1.2.3.4', errors: errors) }
     let(:errors) { double(add: nil) }
 
     before do
-      allow(user).to receive(:valid_invitation?) { false }
+      allow(user).to receive(:valid_invitation?).and_return(false)
       allow_any_instance_of(described_class).to receive(:blocked_email_provider?) { blocked_email }
     end
 
-    subject { described_class.new.validate(user); errors }
-
     context 'when e-mail provider is blocked' do
       let(:blocked_email) { true }
 
@@ -26,7 +26,7 @@ RSpec.describe BlacklistedEmailValidator, type: :validator do
       let(:blocked_email) { false }
 
       it 'does not add errors' do
-        expect(subject).not_to have_received(:add).with(:email, :blocked)
+        expect(subject).to_not have_received(:add).with(:email, :blocked)
       end
 
       context 'when canonical e-mail is blocked' do
diff --git a/spec/validators/disallowed_hashtags_validator_spec.rb b/spec/validators/disallowed_hashtags_validator_spec.rb
index 9deec0bb9..896fd4fc5 100644
--- a/spec/validators/disallowed_hashtags_validator_spec.rb
+++ b/spec/validators/disallowed_hashtags_validator_spec.rb
@@ -11,7 +11,7 @@ RSpec.describe DisallowedHashtagsValidator, type: :validator do
       described_class.new.validate(status)
     end
 
-    let(:status) { double(errors: errors, local?: local, reblog?: reblog, text: disallowed_tags.map { |x| '#' + x }.join(' ')) }
+    let(:status) { double(errors: errors, local?: local, reblog?: reblog, text: disallowed_tags.map { |x| "##{x}" }.join(' ')) }
     let(:errors) { double(add: nil) }
 
     context 'for a remote reblog' do
@@ -19,7 +19,7 @@ RSpec.describe DisallowedHashtagsValidator, type: :validator do
       let(:reblog) { true }
 
       it 'does not add errors' do
-        expect(errors).not_to have_received(:add).with(:text, any_args)
+        expect(errors).to_not have_received(:add).with(:text, any_args)
       end
     end
 
@@ -31,7 +31,7 @@ RSpec.describe DisallowedHashtagsValidator, type: :validator do
         let(:disallowed_tags) { [] }
 
         it 'does not add errors' do
-          expect(errors).not_to have_received(:add).with(:text, any_args)
+          expect(errors).to_not have_received(:add).with(:text, any_args)
         end
       end
 
diff --git a/spec/validators/email_mx_validator_spec.rb b/spec/validators/email_mx_validator_spec.rb
index 4feedd0c7..a11b8e01e 100644
--- a/spec/validators/email_mx_validator_spec.rb
+++ b/spec/validators/email_mx_validator_spec.rb
@@ -28,6 +28,49 @@ describe EmailMxValidator do
       end
     end
 
+    it 'adds no error if there are DNS records for the e-mail domain' do
+      resolver = double
+
+      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
+      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([Resolv::DNS::Resource::IN::A.new('192.0.2.42')])
+      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
+      allow(resolver).to receive(:timeouts=).and_return(nil)
+      allow(Resolv::DNS).to receive(:open).and_yield(resolver)
+
+      subject.validate(user)
+      expect(user.errors).to_not have_received(:add)
+    end
+
+    it 'adds an error if the TagManager fails to normalize domain' do
+      double = instance_double(TagManager)
+      allow(TagManager).to receive(:instance).and_return(double)
+      allow(double).to receive(:normalize_domain).with('example.com').and_raise(Addressable::URI::InvalidURIError)
+
+      user = double(email: 'foo@example.com', errors: double(add: nil))
+      subject.validate(user)
+      expect(user.errors).to have_received(:add)
+    end
+
+    it 'adds an error if the domain email portion is blank' do
+      user = double(email: 'foo@', errors: double(add: nil))
+      subject.validate(user)
+      expect(user.errors).to have_received(:add)
+    end
+
+    it 'adds an error if the email domain name contains empty labels' do
+      resolver = double
+
+      allow(resolver).to receive(:getresources).with('example..com', Resolv::DNS::Resource::IN::MX).and_return([])
+      allow(resolver).to receive(:getresources).with('example..com', Resolv::DNS::Resource::IN::A).and_return([Resolv::DNS::Resource::IN::A.new('192.0.2.42')])
+      allow(resolver).to receive(:getresources).with('example..com', Resolv::DNS::Resource::IN::AAAA).and_return([])
+      allow(resolver).to receive(:timeouts=).and_return(nil)
+      allow(Resolv::DNS).to receive(:open).and_yield(resolver)
+
+      user = double(email: 'foo@example..com', sign_up_ip: '1.2.3.4', errors: double(add: nil))
+      subject.validate(user)
+      expect(user.errors).to have_received(:add)
+    end
+
     it 'adds an error if there are no DNS records for the e-mail domain' do
       resolver = double
 
diff --git a/spec/validators/follow_limit_validator_spec.rb b/spec/validators/follow_limit_validator_spec.rb
index cc8fbb631..94ba0c47f 100644
--- a/spec/validators/follow_limit_validator_spec.rb
+++ b/spec/validators/follow_limit_validator_spec.rb
@@ -22,7 +22,7 @@ RSpec.describe FollowLimitValidator, type: :validator do
       let(:_nil)    { true }
 
       it 'not calls errors.add' do
-        expect(errors).not_to have_received(:add).with(:base, any_args)
+        expect(errors).to_not have_received(:add).with(:base, any_args)
       end
     end
 
@@ -43,7 +43,7 @@ RSpec.describe FollowLimitValidator, type: :validator do
         let(:limit_reached) { false }
 
         it 'not calls errors.add' do
-          expect(errors).not_to have_received(:add).with(:base, any_args)
+          expect(errors).to_not have_received(:add).with(:base, any_args)
         end
       end
     end
diff --git a/spec/validators/note_length_validator_spec.rb b/spec/validators/note_length_validator_spec.rb
index 6e9b4e132..390ac8d90 100644
--- a/spec/validators/note_length_validator_spec.rb
+++ b/spec/validators/note_length_validator_spec.rb
@@ -15,7 +15,7 @@ describe NoteLengthValidator do
     end
 
     it 'counts URLs as 23 characters flat' do
-      text   = ('a' * 476) + " http://#{'b' * 30}.com/example"
+      text = ('a' * 476) + " http://#{'b' * 30}.com/example"
       account = double(note: text, errors: double(add: nil))
 
       subject.validate_each(account, 'note', text)
@@ -23,7 +23,7 @@ describe NoteLengthValidator do
     end
 
     it 'does not count non-autolinkable URLs as 23 characters flat' do
-      text   = ('a' * 476) + "http://#{'b' * 30}.com/example"
+      text = ('a' * 476) + "http://#{'b' * 30}.com/example"
       account = double(note: text, errors: double(add: nil))
 
       subject.validate_each(account, 'note', text)
diff --git a/spec/validators/poll_validator_spec.rb b/spec/validators/poll_validator_spec.rb
index 941b83401..f3f4b1288 100644
--- a/spec/validators/poll_validator_spec.rb
+++ b/spec/validators/poll_validator_spec.rb
@@ -15,13 +15,14 @@ RSpec.describe PollValidator, type: :validator do
     let(:expires_at) { 1.day.from_now }
 
     it 'have no errors' do
-      expect(errors).not_to have_received(:add)
+      expect(errors).to_not have_received(:add)
     end
 
     context 'expires just 5 min ago' do
       let(:expires_at) { 5.minutes.from_now }
+
       it 'not calls errors add' do
-        expect(errors).not_to have_received(:add)
+        expect(errors).to_not have_received(:add)
       end
     end
   end
diff --git a/spec/validators/status_length_validator_spec.rb b/spec/validators/status_length_validator_spec.rb
index 4c80a59e6..7e06b9bd9 100644
--- a/spec/validators/status_length_validator_spec.rb
+++ b/spec/validators/status_length_validator_spec.rb
@@ -7,13 +7,13 @@ describe StatusLengthValidator do
     it 'does not add errors onto remote statuses' do
       status = double(local?: false)
       subject.validate(status)
-      expect(status).not_to receive(:errors)
+      expect(status).to_not receive(:errors)
     end
 
     it 'does not add errors onto local reblogs' do
       status = double(local?: false, reblog?: true)
       subject.validate(status)
-      expect(status).not_to receive(:errors)
+      expect(status).to_not receive(:errors)
     end
 
     it 'adds an error when content warning is over MAX_CHARS characters' do
@@ -65,7 +65,7 @@ describe StatusLengthValidator do
     it 'counts only the front part of remote usernames' do
       username = '@alice'
       chars = StatusLengthValidator::MAX_CHARS - 1 - username.length
-      text   = ('a' * 475) + " #{username}@#{'b' * 30}.com"
+      text   = ('a' * chars) + " #{username}@#{'b' * 30}.com"
       status = double(spoiler_text: '', text: text, errors: double(add: nil), local?: true, reblog?: false)
 
       subject.validate(status)
diff --git a/spec/validators/unreserved_username_validator_spec.rb b/spec/validators/unreserved_username_validator_spec.rb
index 746b3866c..3c6f71c59 100644
--- a/spec/validators/unreserved_username_validator_spec.rb
+++ b/spec/validators/unreserved_username_validator_spec.rb
@@ -11,18 +11,18 @@ RSpec.describe UnreservedUsernameValidator, type: :validator do
 
     let(:validator) { described_class.new }
     let(:account)   { double(username: username, errors: errors) }
-    let(:errors )   { double(add: nil) }
+    let(:errors) { double(add: nil) }
 
     context '@username.blank?' do
-      let(:username)  { nil }
+      let(:username) { nil }
 
       it 'not calls errors.add' do
-        expect(errors).not_to have_received(:add).with(:username, any_args)
+        expect(errors).to_not have_received(:add).with(:username, any_args)
       end
     end
 
     context '!@username.blank?' do
-      let(:username)  { 'f' }
+      let(:username) { 'f' }
 
       context 'reserved_username?' do
         let(:reserved_username) { true }
@@ -36,7 +36,7 @@ RSpec.describe UnreservedUsernameValidator, type: :validator do
         let(:reserved_username) { false }
 
         it 'not calls errors.add' do
-          expect(errors).not_to have_received(:add).with(:username, any_args)
+          expect(errors).to_not have_received(:add).with(:username, any_args)
         end
       end
     end
diff --git a/spec/validators/url_validator_spec.rb b/spec/validators/url_validator_spec.rb
index 85eadeb63..966261b50 100644
--- a/spec/validators/url_validator_spec.rb
+++ b/spec/validators/url_validator_spec.rb
@@ -27,7 +27,7 @@ RSpec.describe URLValidator, type: :validator do
       let(:compliant) { true }
 
       it 'not calls errors.add' do
-        expect(errors).not_to have_received(:add).with(attribute, any_args)
+        expect(errors).to_not have_received(:add).with(attribute, any_args)
       end
     end
   end
diff --git a/spec/workers/activitypub/distribute_poll_update_worker_spec.rb b/spec/workers/activitypub/distribute_poll_update_worker_spec.rb
index d68a695b7..947acab3b 100644
--- a/spec/workers/activitypub/distribute_poll_update_worker_spec.rb
+++ b/spec/workers/activitypub/distribute_poll_update_worker_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe ActivityPub::DistributePollUpdateWorker do
diff --git a/spec/workers/activitypub/distribution_worker_spec.rb b/spec/workers/activitypub/distribution_worker_spec.rb
index 3a5900d9b..06d6ac738 100644
--- a/spec/workers/activitypub/distribution_worker_spec.rb
+++ b/spec/workers/activitypub/distribution_worker_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe ActivityPub::DistributionWorker do
@@ -34,7 +36,7 @@ describe ActivityPub::DistributionWorker do
     end
 
     context 'with direct status' do
-      let(:mentioned_account) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://foo.bar/inbox')}
+      let(:mentioned_account) { Fabricate(:account, protocol: :activitypub, inbox_url: 'https://foo.bar/inbox') }
 
       before do
         status.update(visibility: :direct)
diff --git a/spec/workers/activitypub/move_distribution_worker_spec.rb b/spec/workers/activitypub/move_distribution_worker_spec.rb
index af8c44cc0..4df6b2f16 100644
--- a/spec/workers/activitypub/move_distribution_worker_spec.rb
+++ b/spec/workers/activitypub/move_distribution_worker_spec.rb
@@ -1,9 +1,11 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe ActivityPub::MoveDistributionWorker do
   subject { described_class.new }
 
-  let(:migration)   { Fabricate(:account_migration) }
+  let(:migration) { Fabricate(:account_migration) }
   let(:follower) { Fabricate(:account, protocol: :activitypub, inbox_url: 'http://example.com') }
   let(:blocker) { Fabricate(:account, protocol: :activitypub, inbox_url: 'http://example2.com') }
 
@@ -15,9 +17,9 @@ describe ActivityPub::MoveDistributionWorker do
 
     it 'delivers to followers and known blockers' do
       expect_push_bulk_to_match(ActivityPub::DeliveryWorker, [
-        [kind_of(String), migration.account.id, 'http://example.com'],
-        [kind_of(String), migration.account.id, 'http://example2.com']
-      ])
+                                  [kind_of(String), migration.account.id, 'http://example.com'],
+                                  [kind_of(String), migration.account.id, 'http://example2.com'],
+                                ])
       subject.perform(migration.id)
     end
   end
diff --git a/spec/workers/activitypub/processing_worker_spec.rb b/spec/workers/activitypub/processing_worker_spec.rb
index b42c0bdbc..6b57f16a9 100644
--- a/spec/workers/activitypub/processing_worker_spec.rb
+++ b/spec/workers/activitypub/processing_worker_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe ActivityPub::ProcessingWorker do
diff --git a/spec/workers/activitypub/status_update_distribution_worker_spec.rb b/spec/workers/activitypub/status_update_distribution_worker_spec.rb
index c014c6790..cf55a461d 100644
--- a/spec/workers/activitypub/status_update_distribution_worker_spec.rb
+++ b/spec/workers/activitypub/status_update_distribution_worker_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe ActivityPub::StatusUpdateDistributionWorker do
diff --git a/spec/workers/activitypub/update_distribution_worker_spec.rb b/spec/workers/activitypub/update_distribution_worker_spec.rb
index 0e057fd0b..7b1e6ff54 100644
--- a/spec/workers/activitypub/update_distribution_worker_spec.rb
+++ b/spec/workers/activitypub/update_distribution_worker_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe ActivityPub::UpdateDistributionWorker do
diff --git a/spec/workers/admin/account_deletion_worker_spec.rb b/spec/workers/admin/account_deletion_worker_spec.rb
new file mode 100644
index 000000000..631cab664
--- /dev/null
+++ b/spec/workers/admin/account_deletion_worker_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Admin::AccountDeletionWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    let(:account) { Fabricate(:account) }
+    let(:service) { instance_double(DeleteAccountService, call: true) }
+
+    it 'calls delete account service' do
+      allow(DeleteAccountService).to receive(:new).and_return(service)
+      worker.perform(account.id)
+
+      expect(service).to have_received(:call).with(account, { reserve_email: true, reserve_username: true })
+    end
+  end
+end
diff --git a/spec/workers/cache_buster_worker_spec.rb b/spec/workers/cache_buster_worker_spec.rb
new file mode 100644
index 000000000..adeb287fa
--- /dev/null
+++ b/spec/workers/cache_buster_worker_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe CacheBusterWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    let(:path) { 'https://example.com' }
+    let(:service) { instance_double(CacheBuster, bust: true) }
+
+    it 'calls the cache buster' do
+      allow(CacheBuster).to receive(:new).and_return(service)
+      worker.perform(path)
+
+      expect(service).to have_received(:bust).with(path)
+    end
+  end
+end
diff --git a/spec/workers/domain_block_worker_spec.rb b/spec/workers/domain_block_worker_spec.rb
index bd8fc4a62..8b98443fa 100644
--- a/spec/workers/domain_block_worker_spec.rb
+++ b/spec/workers/domain_block_worker_spec.rb
@@ -20,7 +20,7 @@ describe DomainBlockWorker do
     it 'returns true for non-existent domain block' do
       result = subject.perform('aaa')
 
-      expect(result).to eq(true)
+      expect(result).to be(true)
     end
   end
 end
diff --git a/spec/workers/domain_clear_media_worker_spec.rb b/spec/workers/domain_clear_media_worker_spec.rb
index 36251b1ec..f21d1fe18 100644
--- a/spec/workers/domain_clear_media_worker_spec.rb
+++ b/spec/workers/domain_clear_media_worker_spec.rb
@@ -20,7 +20,7 @@ describe DomainClearMediaWorker do
     it 'returns true for non-existent domain block' do
       result = subject.perform('aaa')
 
-      expect(result).to eq(true)
+      expect(result).to be(true)
     end
   end
 end
diff --git a/spec/workers/feed_insert_worker_spec.rb b/spec/workers/feed_insert_worker_spec.rb
index fb34970fc..16f7d73e0 100644
--- a/spec/workers/feed_insert_worker_spec.rb
+++ b/spec/workers/feed_insert_worker_spec.rb
@@ -15,8 +15,8 @@ describe FeedInsertWorker do
         allow(FeedManager).to receive(:instance).and_return(instance)
         result = subject.perform(nil, follower.id)
 
-        expect(result).to eq true
-        expect(instance).not_to have_received(:push_to_home)
+        expect(result).to be true
+        expect(instance).to_not have_received(:push_to_home)
       end
 
       it 'skips push with missing account' do
@@ -24,8 +24,8 @@ describe FeedInsertWorker do
         allow(FeedManager).to receive(:instance).and_return(instance)
         result = subject.perform(status.id, nil)
 
-        expect(result).to eq true
-        expect(instance).not_to have_received(:push_to_home)
+        expect(result).to be true
+        expect(instance).to_not have_received(:push_to_home)
       end
     end
 
@@ -36,7 +36,7 @@ describe FeedInsertWorker do
         result = subject.perform(status.id, follower.id)
 
         expect(result).to be_nil
-        expect(instance).not_to have_received(:push_to_home)
+        expect(instance).to_not have_received(:push_to_home)
       end
 
       it 'pushes the status onto the home timeline without filter' do
diff --git a/spec/workers/move_worker_spec.rb b/spec/workers/move_worker_spec.rb
index 3ca6aaf4d..e93060adb 100644
--- a/spec/workers/move_worker_spec.rb
+++ b/spec/workers/move_worker_spec.rb
@@ -3,6 +3,8 @@
 require 'rails_helper'
 
 describe MoveWorker do
+  subject { described_class.new }
+
   let(:local_follower)   { Fabricate(:account) }
   let(:blocking_account) { Fabricate(:account) }
   let(:muting_account)   { Fabricate(:account) }
@@ -14,8 +16,6 @@ describe MoveWorker do
 
   let(:block_service) { double }
 
-  subject { described_class.new }
-
   before do
     local_follower.follow!(source_account)
     blocking_account.block!(source_account)
diff --git a/spec/workers/poll_expiration_notify_worker_spec.rb b/spec/workers/poll_expiration_notify_worker_spec.rb
new file mode 100644
index 000000000..78cbc1ee4
--- /dev/null
+++ b/spec/workers/poll_expiration_notify_worker_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PollExpirationNotifyWorker do
+  let(:worker) { described_class.new }
+  let(:account) { Fabricate(:account, domain: remote? ? 'example.com' : nil) }
+  let(:status) { Fabricate(:status, account: account) }
+  let(:poll) { Fabricate(:poll, status: status, account: account) }
+  let(:remote?) { false }
+  let(:poll_vote) { Fabricate(:poll_vote, poll: poll) }
+
+  describe '#perform' do
+    around do |example|
+      Sidekiq::Testing.fake! do
+        example.run
+      end
+    end
+
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+
+    context 'when poll is not expired' do
+      it 'requeues job' do
+        worker.perform(poll.id)
+        expect(described_class.sidekiq_options_hash['lock']).to be :until_executing
+        expect(described_class).to have_enqueued_sidekiq_job(poll.id).at(poll.expires_at + 5.minutes)
+      end
+    end
+
+    context 'when poll is expired' do
+      before do
+        poll_vote
+
+        travel_to poll.expires_at + 5.minutes
+
+        worker.perform(poll.id)
+      end
+
+      context 'when poll is local' do
+        it 'notifies voters' do
+          expect(ActivityPub::DistributePollUpdateWorker).to have_enqueued_sidekiq_job(poll.status.id)
+        end
+
+        it 'notifies owner' do
+          expect(LocalNotificationWorker).to have_enqueued_sidekiq_job(poll.account.id, poll.id, 'Poll', 'poll')
+        end
+
+        it 'notifies local voters' do
+          expect(LocalNotificationWorker).to have_enqueued_sidekiq_job(poll_vote.account.id, poll.id, 'Poll', 'poll')
+        end
+      end
+
+      context 'when poll is remote' do
+        let(:remote?) { true }
+
+        it 'does not notify remote voters' do
+          expect(ActivityPub::DistributePollUpdateWorker).to_not have_enqueued_sidekiq_job(poll.status.id)
+        end
+
+        it 'does not notify owner' do
+          expect(LocalNotificationWorker).to_not have_enqueued_sidekiq_job(poll.account.id, poll.id, 'Poll', 'poll')
+        end
+
+        it 'notifies local voters' do
+          expect(LocalNotificationWorker).to have_enqueued_sidekiq_job(poll_vote.account.id, poll.id, 'Poll', 'poll')
+        end
+      end
+    end
+  end
+end
diff --git a/spec/workers/post_process_media_worker_spec.rb b/spec/workers/post_process_media_worker_spec.rb
new file mode 100644
index 000000000..33072704b
--- /dev/null
+++ b/spec/workers/post_process_media_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PostProcessMediaWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/push_conversation_worker_spec.rb b/spec/workers/push_conversation_worker_spec.rb
new file mode 100644
index 000000000..5fbb4c685
--- /dev/null
+++ b/spec/workers/push_conversation_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PushConversationWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/push_encrypted_message_worker_spec.rb b/spec/workers/push_encrypted_message_worker_spec.rb
new file mode 100644
index 000000000..3cd04ce7b
--- /dev/null
+++ b/spec/workers/push_encrypted_message_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PushEncryptedMessageWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/push_update_worker_spec.rb b/spec/workers/push_update_worker_spec.rb
new file mode 100644
index 000000000..c8f94fa82
--- /dev/null
+++ b/spec/workers/push_update_worker_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe PushUpdateWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      account_id = nil
+      status_id = nil
+
+      expect { worker.perform(account_id, status_id) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/redownload_avatar_worker_spec.rb b/spec/workers/redownload_avatar_worker_spec.rb
new file mode 100644
index 000000000..b44ae9f03
--- /dev/null
+++ b/spec/workers/redownload_avatar_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe RedownloadAvatarWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/redownload_header_worker_spec.rb b/spec/workers/redownload_header_worker_spec.rb
new file mode 100644
index 000000000..767ae7a5a
--- /dev/null
+++ b/spec/workers/redownload_header_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe RedownloadHeaderWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/refollow_worker_spec.rb b/spec/workers/refollow_worker_spec.rb
index d9c2293b6..1dac15385 100644
--- a/spec/workers/refollow_worker_spec.rb
+++ b/spec/workers/refollow_worker_spec.rb
@@ -4,6 +4,7 @@ require 'rails_helper'
 
 describe RefollowWorker do
   subject { described_class.new }
+
   let(:account) { Fabricate(:account, domain: 'example.org', protocol: :activitypub) }
   let(:alice)   { Fabricate(:account, domain: nil, username: 'alice') }
   let(:bob)     { Fabricate(:account, domain: nil, username: 'bob') }
diff --git a/spec/workers/regeneration_worker_spec.rb b/spec/workers/regeneration_worker_spec.rb
index c6bdfa0e5..147a76be5 100644
--- a/spec/workers/regeneration_worker_spec.rb
+++ b/spec/workers/regeneration_worker_spec.rb
@@ -20,7 +20,7 @@ describe RegenerationWorker do
     it 'fails when account does not exist' do
       result = subject.perform('aaa')
 
-      expect(result).to eq(true)
+      expect(result).to be(true)
     end
   end
 end
diff --git a/spec/workers/remove_featured_tag_worker_spec.rb b/spec/workers/remove_featured_tag_worker_spec.rb
new file mode 100644
index 000000000..a64bd0605
--- /dev/null
+++ b/spec/workers/remove_featured_tag_worker_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe RemoveFeaturedTagWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      account_id = nil
+      featured_tag_id = nil
+      expect { worker.perform(account_id, featured_tag_id) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/resolve_account_worker_spec.rb b/spec/workers/resolve_account_worker_spec.rb
new file mode 100644
index 000000000..6f3cff099
--- /dev/null
+++ b/spec/workers/resolve_account_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe ResolveAccountWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb b/spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb
index 8f20725c8..9ed8da1ba 100644
--- a/spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb
+++ b/spec/workers/scheduler/accounts_statuses_cleanup_scheduler_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
 require 'rails_helper'
 
 describe Scheduler::AccountsStatusesCleanupScheduler do
@@ -19,11 +21,10 @@ describe Scheduler::AccountsStatusesCleanupScheduler do
     [
       {
         'concurrency' => 2,
-        'queues' => ['push', 'default'],
+        'queues' => %w(push default),
       },
     ]
   end
-  let(:retry_size) { 0 }
 
   before do
     queue_stub = double
@@ -33,7 +34,6 @@ describe Scheduler::AccountsStatusesCleanupScheduler do
     allow(Sidekiq::ProcessSet).to receive(:new).and_return(process_set_stub)
 
     sidekiq_stats_stub = double
-    allow(sidekiq_stats_stub).to receive(:retry_size).and_return(retry_size)
     allow(Sidekiq::Stats).to receive(:new).and_return(sidekiq_stats_stub)
 
     # Create a bunch of old statuses
@@ -70,19 +70,11 @@ describe Scheduler::AccountsStatusesCleanupScheduler do
         expect(subject.under_load?).to be true
       end
     end
-
-    context 'when there is a huge amount of jobs to retry' do
-      let(:retry_size) { 1_000_000 }
-
-      it 'returns true' do
-        expect(subject.under_load?).to be true
-      end
-    end
   end
 
   describe '#get_budget' do
     context 'on a single thread' do
-      let(:process_set_stub) { [ { 'concurrency' => 1, 'queues' => ['push', 'default'] } ] }
+      let(:process_set_stub) { [{ 'concurrency' => 1, 'queues' => %w(push default) }] }
 
       it 'returns a low value' do
         expect(subject.compute_budget).to be < 10
@@ -92,7 +84,7 @@ describe Scheduler::AccountsStatusesCleanupScheduler do
     context 'on a lot of threads' do
       let(:process_set_stub) do
         [
-          { 'concurrency' => 2, 'queues' => ['push', 'default'] },
+          { 'concurrency' => 2, 'queues' => %w(push default) },
           { 'concurrency' => 2, 'queues' => ['push'] },
           { 'concurrency' => 2, 'queues' => ['push'] },
           { 'concurrency' => 2, 'queues' => ['push'] },
diff --git a/spec/workers/scheduler/follow_recommendations_scheduler_spec.rb b/spec/workers/scheduler/follow_recommendations_scheduler_spec.rb
new file mode 100644
index 000000000..18d5260e4
--- /dev/null
+++ b/spec/workers/scheduler/follow_recommendations_scheduler_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::FollowRecommendationsScheduler do
+  let!(:target_accounts) do
+    Fabricate.times(3, :account) do
+      statuses(count: 6)
+    end
+  end
+  let!(:follower_accounts) do
+    Fabricate.times(5, :account) do
+      statuses(count: 6)
+    end
+  end
+
+  describe '#perform' do
+    subject(:scheduled_run) { described_class.new.perform }
+
+    context 'when there are accounts to recommend' do
+      before do
+        # Follow the target accounts by follow accounts to make them recommendable
+        follower_accounts.each do |follower_account|
+          target_accounts.each do |target_account|
+            Fabricate(:follow, account: follower_account, target_account: target_account)
+          end
+        end
+      end
+
+      it 'creates recommendations' do
+        expect { scheduled_run }.to change(FollowRecommendation, :count).from(0).to(target_accounts.size)
+        expect(redis.zrange('follow_recommendations:en', 0, -1)).to match_array(target_accounts.pluck(:id).map(&:to_s))
+      end
+    end
+
+    context 'when there are no accounts to recommend' do
+      it 'does not create follow recommendations' do
+        expect { scheduled_run }.to_not change(FollowRecommendation, :count)
+        expect(redis.zrange('follow_recommendations:en', 0, -1)).to be_empty
+      end
+    end
+  end
+end
diff --git a/spec/workers/scheduler/indexing_scheduler_spec.rb b/spec/workers/scheduler/indexing_scheduler_spec.rb
new file mode 100644
index 000000000..568f0fc84
--- /dev/null
+++ b/spec/workers/scheduler/indexing_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::IndexingScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/instance_refresh_scheduler_spec.rb b/spec/workers/scheduler/instance_refresh_scheduler_spec.rb
new file mode 100644
index 000000000..8f686a699
--- /dev/null
+++ b/spec/workers/scheduler/instance_refresh_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::InstanceRefreshScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/ip_cleanup_scheduler_spec.rb b/spec/workers/scheduler/ip_cleanup_scheduler_spec.rb
new file mode 100644
index 000000000..50af03011
--- /dev/null
+++ b/spec/workers/scheduler/ip_cleanup_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::IpCleanupScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/pghero_scheduler_spec.rb b/spec/workers/scheduler/pghero_scheduler_spec.rb
new file mode 100644
index 000000000..e404e5fe4
--- /dev/null
+++ b/spec/workers/scheduler/pghero_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::PgheroScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/scheduled_statuses_scheduler_spec.rb b/spec/workers/scheduler/scheduled_statuses_scheduler_spec.rb
new file mode 100644
index 000000000..13c853c62
--- /dev/null
+++ b/spec/workers/scheduler/scheduled_statuses_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::ScheduledStatusesScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/suspended_user_cleanup_scheduler_spec.rb b/spec/workers/scheduler/suspended_user_cleanup_scheduler_spec.rb
new file mode 100644
index 000000000..25f0e1fce
--- /dev/null
+++ b/spec/workers/scheduler/suspended_user_cleanup_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::SuspendedUserCleanupScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/trends/refresh_scheduler_spec.rb b/spec/workers/scheduler/trends/refresh_scheduler_spec.rb
new file mode 100644
index 000000000..c0c5f032b
--- /dev/null
+++ b/spec/workers/scheduler/trends/refresh_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::Trends::RefreshScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/trends/review_notifications_scheduler_spec.rb b/spec/workers/scheduler/trends/review_notifications_scheduler_spec.rb
new file mode 100644
index 000000000..cc971c24b
--- /dev/null
+++ b/spec/workers/scheduler/trends/review_notifications_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::Trends::ReviewNotificationsScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/scheduler/user_cleanup_scheduler_spec.rb b/spec/workers/scheduler/user_cleanup_scheduler_spec.rb
new file mode 100644
index 000000000..990979500
--- /dev/null
+++ b/spec/workers/scheduler/user_cleanup_scheduler_spec.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::UserCleanupScheduler do
+  subject { described_class.new }
+
+  let!(:new_unconfirmed_user) { Fabricate(:user) }
+  let!(:old_unconfirmed_user) { Fabricate(:user) }
+  let!(:confirmed_user)       { Fabricate(:user) }
+  let!(:moderation_note)      { Fabricate(:account_moderation_note, account: Fabricate(:account), target_account: old_unconfirmed_user.account) }
+
+  describe '#perform' do
+    before do
+      # Need to update the already-existing users because their initialization overrides confirmation_sent_at
+      new_unconfirmed_user.update!(confirmed_at: nil, confirmation_sent_at: Time.now.utc)
+      old_unconfirmed_user.update!(confirmed_at: nil, confirmation_sent_at: 1.week.ago)
+      confirmed_user.update!(confirmed_at: 1.day.ago)
+    end
+
+    it 'deletes the old unconfirmed user' do
+      expect { subject.perform }.to change { User.exists?(old_unconfirmed_user.id) }.from(true).to(false)
+    end
+
+    it "deletes the old unconfirmed user's account" do
+      expect { subject.perform }.to change { Account.exists?(old_unconfirmed_user.account_id) }.from(true).to(false)
+    end
+
+    it 'does not delete the new unconfirmed user or their account' do
+      subject.perform
+      expect(User.exists?(new_unconfirmed_user.id)).to be true
+      expect(Account.exists?(new_unconfirmed_user.account_id)).to be true
+    end
+
+    it 'does not delete the confirmed user or their account' do
+      subject.perform
+      expect(User.exists?(confirmed_user.id)).to be true
+      expect(Account.exists?(confirmed_user.account_id)).to be true
+    end
+  end
+end
diff --git a/spec/workers/scheduler/vacuum_scheduler_spec.rb b/spec/workers/scheduler/vacuum_scheduler_spec.rb
new file mode 100644
index 000000000..36ecc93d8
--- /dev/null
+++ b/spec/workers/scheduler/vacuum_scheduler_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Scheduler::VacuumScheduler do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/unfollow_follow_worker_spec.rb b/spec/workers/unfollow_follow_worker_spec.rb
index 5ea4256a9..8025b88c0 100644
--- a/spec/workers/unfollow_follow_worker_spec.rb
+++ b/spec/workers/unfollow_follow_worker_spec.rb
@@ -3,13 +3,13 @@
 require 'rails_helper'
 
 describe UnfollowFollowWorker do
+  subject { described_class.new }
+
   let(:local_follower)   { Fabricate(:account) }
   let(:source_account)   { Fabricate(:account) }
   let(:target_account)   { Fabricate(:account) }
   let(:show_reblogs)     { true }
 
-  subject { described_class.new }
-
   before do
     local_follower.follow!(source_account, reblogs: show_reblogs)
   end
diff --git a/spec/workers/unpublish_announcement_worker_spec.rb b/spec/workers/unpublish_announcement_worker_spec.rb
new file mode 100644
index 000000000..c742c30bc
--- /dev/null
+++ b/spec/workers/unpublish_announcement_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe UnpublishAnnouncementWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/verify_account_links_worker_spec.rb b/spec/workers/verify_account_links_worker_spec.rb
new file mode 100644
index 000000000..227591392
--- /dev/null
+++ b/spec/workers/verify_account_links_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe VerifyAccountLinksWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error for missing record' do
+      expect { worker.perform(nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/spec/workers/web/push_notification_worker_spec.rb b/spec/workers/web/push_notification_worker_spec.rb
index 5bc24f888..822ef5257 100644
--- a/spec/workers/web/push_notification_worker_spec.rb
+++ b/spec/workers/web/push_notification_worker_spec.rb
@@ -37,7 +37,7 @@ describe Web::PushNotificationWorker do
       expect(a_request(:post, endpoint).with(headers: {
         'Content-Encoding' => 'aesgcm',
         'Content-Type' => 'application/octet-stream',
-        'Crypto-Key' => 'dh=BAgtUks5d90kFmxGevk9tH7GEmvz9DB0qcEMUsOBgKwMf-TMjsKIIG6LQvGcFAf6jcmAod15VVwmYwGIIxE4VWE;p256ecdsa=' + vapid_public_key.delete('='),
+        'Crypto-Key' => "dh=BAgtUks5d90kFmxGevk9tH7GEmvz9DB0qcEMUsOBgKwMf-TMjsKIIG6LQvGcFAf6jcmAod15VVwmYwGIIxE4VWE;p256ecdsa=#{vapid_public_key.delete('=')}",
         'Encryption' => 'salt=WJeVM-RY-F9351SVxTFx_g',
         'Ttl' => '172800',
         'Urgency' => 'normal',
diff --git a/spec/workers/webhooks/delivery_worker_spec.rb b/spec/workers/webhooks/delivery_worker_spec.rb
new file mode 100644
index 000000000..daf8a3e28
--- /dev/null
+++ b/spec/workers/webhooks/delivery_worker_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Webhooks::DeliveryWorker do
+  let(:worker) { described_class.new }
+
+  describe 'perform' do
+    it 'runs without error' do
+      expect { worker.perform(nil, nil) }.to_not raise_error
+    end
+  end
+end
diff --git a/streaming/index.js b/streaming/index.js
index 45ea26bd6..94568ee9a 100644
--- a/streaming/index.js
+++ b/streaming/index.js
@@ -7,6 +7,7 @@ const express = require('express');
 const http = require('http');
 const redis = require('redis');
 const pg = require('pg');
+const dbUrlToConfig = require('pg-connection-string').parse;
 const log = require('npmlog');
 const url = require('url');
 const uuid = require('uuid');
@@ -15,7 +16,6 @@ const WebSocket = require('ws');
 const { JSDOM } = require('jsdom');
 
 const env = process.env.NODE_ENV || 'development';
-const alwaysRequireAuth = process.env.LIMITED_FEDERATION_MODE === 'true' || process.env.WHITELIST_MODE === 'true' || process.env.AUTHORIZED_FETCH === 'true';
 
 dotenv.config({
   path: env === 'production' ? '.env.production' : '.env',
@@ -24,43 +24,6 @@ dotenv.config({
 log.level = process.env.LOG_LEVEL || 'verbose';
 
 /**
- * @param {string} dbUrl
- * @return {Object.<string, any>}
- */
-const dbUrlToConfig = (dbUrl) => {
-  if (!dbUrl) {
-    return {};
-  }
-
-  const params = url.parse(dbUrl, true);
-  const config = {};
-
-  if (params.auth) {
-    [config.user, config.password] = params.auth.split(':');
-  }
-
-  if (params.hostname) {
-    config.host = params.hostname;
-  }
-
-  if (params.port) {
-    config.port = params.port;
-  }
-
-  if (params.pathname) {
-    config.database = params.pathname.split('/')[1];
-  }
-
-  const ssl = params.query && params.query.ssl;
-
-  if (ssl && ssl === 'true' || ssl === '1') {
-    config.ssl = true;
-  }
-
-  return config;
-};
-
-/**
  * @param {Object.<string, any>} defaultConfig
  * @param {string} redisUrl
  */
@@ -117,9 +80,10 @@ const startMaster = () => {
   log.warn(`Starting streaming API server master with ${numWorkers} workers`);
 };
 
-const startWorker = async (workerId) => {
-  log.warn(`Starting worker ${workerId}`);
-
+/**
+ * @return {Object.<string, any>}
+ */
+const pgConfigFromEnv = () => {
   const pgConfigs = {
     development: {
       user:     process.env.DB_USER || pg.defaults.user,
@@ -127,7 +91,6 @@ const startWorker = async (workerId) => {
       database: process.env.DB_NAME || 'mastodon_development',
       host:     process.env.DB_HOST || pg.defaults.host,
       port:     process.env.DB_PORT || pg.defaults.port,
-      max:      10,
     },
 
     production: {
@@ -136,20 +99,48 @@ const startWorker = async (workerId) => {
       database: process.env.DB_NAME || 'mastodon_production',
       host:     process.env.DB_HOST || 'localhost',
       port:     process.env.DB_PORT || 5432,
-      max:      10,
     },
   };
 
-  if (!!process.env.DB_SSLMODE && process.env.DB_SSLMODE !== 'disable') {
-    pgConfigs.development.ssl = true;
-    pgConfigs.production.ssl = true;
+  let baseConfig;
+
+  if (process.env.DATABASE_URL) {
+    baseConfig = dbUrlToConfig(process.env.DATABASE_URL);
+  } else {
+    baseConfig = pgConfigs[env];
+
+    if (process.env.DB_SSLMODE) {
+      switch(process.env.DB_SSLMODE) {
+      case 'disable':
+      case '':
+        baseConfig.ssl = false;
+        break;
+      case 'no-verify':
+        baseConfig.ssl = { rejectUnauthorized: false };
+        break;
+      default:
+        baseConfig.ssl = {};
+        break;
+      }
+    }
   }
 
+  return {
+    ...baseConfig,
+    max: process.env.DB_POOL || 10,
+    connectionTimeoutMillis: 15000,
+    application_name: '',
+  };
+};
+
+const startWorker = async (workerId) => {
+  log.warn(`Starting worker ${workerId}`);
+
   const app = express();
 
   app.set('trust proxy', process.env.TRUSTED_PROXY_IP ? process.env.TRUSTED_PROXY_IP.split(/(?:\s*,\s*|\s+)/) : 'loopback,uniquelocal');
 
-  const pgPool = new pg.Pool(Object.assign(pgConfigs[env], dbUrlToConfig(process.env.DATABASE_URL)));
+  const pgPool = new pg.Pool(pgConfigFromEnv());
   const server = http.createServer(app);
   const redisNamespace = process.env.REDIS_NAMESPACE || null;
 
@@ -355,22 +346,17 @@ const startWorker = async (workerId) => {
    * @param {boolean=} required
    * @return {Promise.<void>}
    */
-  const accountFromRequest = (req, required = true) => new Promise((resolve, reject) => {
+  const accountFromRequest = (req) => new Promise((resolve, reject) => {
     const authorization = req.headers.authorization;
     const location      = url.parse(req.url, true);
     const accessToken   = location.query.access_token || req.headers['sec-websocket-protocol'];
 
     if (!authorization && !accessToken) {
-      if (required) {
-        const err = new Error('Missing access token');
-        err.status = 401;
+      const err = new Error('Missing access token');
+      err.status = 401;
 
-        reject(err);
-        return;
-      } else {
-        resolve();
-        return;
-      }
+      reject(err);
+      return;
     }
 
     const token = authorization ? authorization.replace(/^Bearer /, '') : accessToken;
@@ -474,7 +460,7 @@ const startWorker = async (workerId) => {
     // variables. OAuth scope checks are moved to the point of subscription
     // to a specific stream.
 
-    accountFromRequest(info.req, alwaysRequireAuth).then(() => {
+    accountFromRequest(info.req).then(() => {
       callback(true, undefined, undefined);
     }).catch(err => {
       log.error(info.req.requestId, err.toString());
@@ -548,7 +534,7 @@ const startWorker = async (workerId) => {
       return;
     }
 
-    accountFromRequest(req, alwaysRequireAuth).then(() => checkScopes(req, channelNameFromPath(req))).then(() => {
+    accountFromRequest(req).then(() => checkScopes(req, channelNameFromPath(req))).then(() => {
       subscribeHttpToSystemChannel(req, res);
     }).then(() => {
       next();
@@ -858,6 +844,27 @@ const startWorker = async (workerId) => {
     res.end('OK');
   });
 
+  app.get('/metrics', (req, res) => server.getConnections((err, count) => {
+    res.writeHeader(200, { 'Content-Type': 'application/openmetrics-text; version=1.0.0; charset=utf-8' });
+    res.write('# TYPE connected_clients gauge\n');
+    res.write('# HELP connected_clients The number of clients connected to the streaming server\n');
+    res.write(`connected_clients ${count}.0\n`);
+    res.write('# TYPE connected_channels gauge\n');
+    res.write('# HELP connected_channels The number of Redis channels the streaming server is subscribed to\n');
+    res.write(`connected_channels ${Object.keys(subs).length}.0\n`);
+    res.write('# TYPE pg_pool_total_connections gauge\n');
+    res.write('# HELP pg_pool_total_connections The total number of clients existing within the pool\n');
+    res.write(`pg_pool_total_connections ${pgPool.totalCount}.0\n`);
+    res.write('# TYPE pg_pool_idle_connections gauge\n');
+    res.write('# HELP pg_pool_idle_connections The number of clients which are not checked out but are currently idle in the pool\n');
+    res.write(`pg_pool_idle_connections ${pgPool.idleCount}.0\n`);
+    res.write('# TYPE pg_pool_waiting_queries gauge\n');
+    res.write('# HELP pg_pool_waiting_queries The number of queued requests waiting on a client when all clients are checked out\n');
+    res.write(`pg_pool_waiting_queries ${pgPool.waitingCount}.0\n`);
+    res.write('# EOF\n');
+    res.end();
+  }));
+
   app.use(authenticationMiddleware);
   app.use(errorMiddleware);
 
diff --git a/stylelint.config.js b/stylelint.config.js
index 0f8267a81..1a153adb9 100644
--- a/stylelint.config.js
+++ b/stylelint.config.js
@@ -2,6 +2,8 @@ module.exports = {
   extends: ['stylelint-config-standard-scss'],
   ignoreFiles: [
     'app/javascript/styles/mastodon/reset.scss',
+    'app/javascript/flavours/glitch/styles/reset.scss',
+    'app/javascript/styles/win95.scss',
     'node_modules/**/*',
     'vendor/**/*',
   ],
@@ -10,19 +12,30 @@ module.exports = {
     'color-function-notation': null,
     'color-hex-length': null,
     'declaration-block-no-redundant-longhand-properties': null,
-    'max-line-length': null,
     'no-descending-specificity': null,
     'no-duplicate-selectors': null,
     'number-max-precision': 8,
-    'property-no-unknown': null,
     'property-no-vendor-prefix': null,
     'selector-class-pattern': null,
     'selector-id-pattern': null,
-    'string-quotes': null,
     'value-keyword-case': null,
     'value-no-vendor-prefix': null,
 
     'scss/dollar-variable-empty-line-before': null,
     'scss/no-global-function-names': null,
   },
+  overrides: [
+    {
+      'files': ['app/javascript/styles/mailer.scss'],
+      rules: {
+        'property-no-unknown': [
+          true,
+          {
+            ignoreProperties: [
+              '/^mso-/',
+            ] },
+        ],
+      },
+    },
+  ],
 };
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 000000000..505b19d89
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,13 @@
+{
+  "compilerOptions": {
+    "jsx": "react",
+    "target": "esnext",
+    "moduleResolution": "node",
+    "allowJs": true,
+    "noEmit": true,
+    "strict": true,
+    "esModuleInterop": true,
+    "skipLibCheck": true
+  },
+  "include": ["app/javascript/mastodon", "app/javascript/packs"]
+}
diff --git a/yarn.lock b/yarn.lock
index a5c219371..64a94dc41 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7,12 +7,13 @@
   resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.0.1.tgz#b38b444ad3aa5fedbb15f2f746dcd934226a12dd"
   integrity sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==
 
-"@ampproject/remapping@^2.1.0":
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.1.2.tgz#4edca94973ded9630d20101cd8559cedb8d8bd34"
-  integrity sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==
+"@ampproject/remapping@^2.2.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
+  integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
   dependencies:
-    "@jridgewell/trace-mapping" "^0.3.0"
+    "@jridgewell/gen-mapping" "^0.1.0"
+    "@jridgewell/trace-mapping" "^0.3.9"
 
 "@apideck/better-ajv-errors@^0.3.1":
   version "0.3.3"
@@ -23,14 +24,7 @@
     jsonpointer "^5.0.0"
     leven "^3.1.0"
 
-"@babel/code-frame@7.12.11":
-  version "7.12.11"
-  resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz"
-  integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==
-  dependencies:
-    "@babel/highlight" "^7.10.4"
-
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.14.5", "@babel/code-frame@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
   integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
@@ -47,43 +41,54 @@
   resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.20.10.tgz#9d92fa81b87542fff50e848ed585b4212c1d34ec"
   integrity sha512-sEnuDPpOJR/fcafHMjpcpGN5M2jbUGUHwmuWKM/YdPzeEDJg8bgmbcWQFUfE32MQjti1koACvoPVsDe8Uq+idg==
 
-"@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.20.12", "@babel/core@^7.7.2":
-  version "7.20.12"
-  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.20.12.tgz#7930db57443c6714ad216953d1356dac0eb8496d"
-  integrity sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==
+"@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.21.3", "@babel/core@^7.7.2":
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e"
+  integrity sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw==
   dependencies:
-    "@ampproject/remapping" "^2.1.0"
+    "@ampproject/remapping" "^2.2.0"
     "@babel/code-frame" "^7.18.6"
-    "@babel/generator" "^7.20.7"
+    "@babel/generator" "^7.21.3"
     "@babel/helper-compilation-targets" "^7.20.7"
-    "@babel/helper-module-transforms" "^7.20.11"
-    "@babel/helpers" "^7.20.7"
-    "@babel/parser" "^7.20.7"
+    "@babel/helper-module-transforms" "^7.21.2"
+    "@babel/helpers" "^7.21.0"
+    "@babel/parser" "^7.21.3"
     "@babel/template" "^7.20.7"
-    "@babel/traverse" "^7.20.12"
-    "@babel/types" "^7.20.7"
+    "@babel/traverse" "^7.21.3"
+    "@babel/types" "^7.21.3"
     convert-source-map "^1.7.0"
     debug "^4.1.0"
     gensync "^1.0.0-beta.2"
     json5 "^2.2.2"
     semver "^6.3.0"
 
-"@babel/eslint-parser@^7.19.1":
-  version "7.19.1"
-  resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.19.1.tgz#4f68f6b0825489e00a24b41b6a1ae35414ecd2f4"
-  integrity sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==
+"@babel/generator@^7.15.4":
+  version "7.21.1"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.1.tgz#951cc626057bc0af2c35cd23e9c64d384dea83dd"
+  integrity sha512-1lT45bAYlQhFn/BHivJs43AiW2rg3/UbLyShGfF3C0KmHvO5fSghWd5kBJy30kpRRucGzXStvnnCFniCR2kXAA==
   dependencies:
-    "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1"
-    eslint-visitor-keys "^2.1.0"
-    semver "^6.3.0"
+    "@babel/types" "^7.21.0"
+    "@jridgewell/gen-mapping" "^0.3.2"
+    "@jridgewell/trace-mapping" "^0.3.17"
+    jsesc "^2.5.1"
 
-"@babel/generator@^7.20.7", "@babel/generator@^7.7.2":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a"
-  integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==
+"@babel/generator@^7.21.0", "@babel/generator@^7.21.3":
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce"
+  integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA==
   dependencies:
-    "@babel/types" "^7.20.7"
+    "@babel/types" "^7.21.3"
     "@jridgewell/gen-mapping" "^0.3.2"
+    "@jridgewell/trace-mapping" "^0.3.17"
+    jsesc "^2.5.1"
+
+"@babel/generator@^7.7.2":
+  version "7.17.10"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.10.tgz#c281fa35b0c349bbe9d02916f4ae08fc85ed7189"
+  integrity sha512-46MJZZo9y3o4kmhBVc7zW7i8dtR1oIK/sdO5NcfcZRhTGYi+KKJRtHNgsU6c4VUcJmUNV/LQdebD/9Dlv4K+Tg==
+  dependencies:
+    "@babel/types" "^7.17.10"
+    "@jridgewell/gen-mapping" "^0.1.0"
     jsesc "^2.5.1"
 
 "@babel/helper-annotate-as-pure@^7.18.6":
@@ -101,13 +106,13 @@
     "@babel/helper-explode-assignable-expression" "^7.18.6"
     "@babel/types" "^7.18.6"
 
-"@babel/helper-builder-react-jsx@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.18.6.tgz#b3a302c0eb4949e5356b400cb752a91e93bf9b79"
-  integrity sha512-2ndBVP5f9zwHWQeBr5EgqTAvFhPDViMW969bbJzRhKUUylnC39CdFZdVmqk+UtkxIpwm/efPgm3SzXUSlJnjAw==
+"@babel/helper-builder-react-jsx@^7.19.0":
+  version "7.19.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.19.0.tgz#a1f4fef805388eda4b3c1bd8994dc585b0afa351"
+  integrity sha512-xvrbORmJ13lWrqyMErk4vczhXNNWdOSg1BZ+R/7D34SjDjToR5g3M5UpD6MyUekstI50qAHLWA1j7w5o1WK2Pw==
   dependencies:
     "@babel/helper-annotate-as-pure" "^7.18.6"
-    "@babel/types" "^7.18.6"
+    "@babel/types" "^7.19.0"
 
 "@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.0", "@babel/helper-compilation-targets@^7.20.7":
   version "7.20.7"
@@ -120,17 +125,32 @@
     lru-cache "^5.1.1"
     semver "^6.3.0"
 
-"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.20.7":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.7.tgz#d0e1f8d7e4ed5dac0389364d9c0c191d948ade6f"
-  integrity sha512-LtoWbDXOaidEf50hmdDqn9g8VEzsorMexoWMQdQODbvmqYmaF23pBP5VNPAGIFHsFQCIeKokDiz3CH5Y2jlY6w==
+"@babel/helper-create-class-features-plugin@^7.18.6":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.0.tgz#64f49ecb0020532f19b1d014b03bccaa1ab85fb9"
+  integrity sha512-Q8wNiMIdwsv5la5SPxNYzzkPnjgC0Sy0i7jLkVOCdllu/xcVNkr3TeZzbHBJrj+XXRqzX5uCyCoV9eu6xUG7KQ==
   dependencies:
     "@babel/helper-annotate-as-pure" "^7.18.6"
     "@babel/helper-environment-visitor" "^7.18.9"
-    "@babel/helper-function-name" "^7.19.0"
-    "@babel/helper-member-expression-to-functions" "^7.20.7"
+    "@babel/helper-function-name" "^7.21.0"
+    "@babel/helper-member-expression-to-functions" "^7.21.0"
+    "@babel/helper-optimise-call-expression" "^7.18.6"
+    "@babel/helper-replace-supers" "^7.20.7"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
+    "@babel/helper-split-export-declaration" "^7.18.6"
+
+"@babel/helper-create-class-features-plugin@^7.21.0":
+  version "7.21.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.4.tgz#3a017163dc3c2ba7deb9a7950849a9586ea24c18"
+  integrity sha512-46QrX2CQlaFRF4TkwfTt6nJD7IHq8539cCL7SDpqWSDeJKY1xylKKY5F/33mJhLZ3mFvKv2gGrVS6NkyF6qs+Q==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.18.6"
+    "@babel/helper-environment-visitor" "^7.18.9"
+    "@babel/helper-function-name" "^7.21.0"
+    "@babel/helper-member-expression-to-functions" "^7.21.0"
     "@babel/helper-optimise-call-expression" "^7.18.6"
     "@babel/helper-replace-supers" "^7.20.7"
+    "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0"
     "@babel/helper-split-export-declaration" "^7.18.6"
 
 "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.19.0":
@@ -165,6 +185,14 @@
   dependencies:
     "@babel/types" "^7.18.6"
 
+"@babel/helper-function-name@^7.15.4", "@babel/helper-function-name@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4"
+  integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==
+  dependencies:
+    "@babel/template" "^7.20.7"
+    "@babel/types" "^7.21.0"
+
 "@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0":
   version "7.19.0"
   resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz#941574ed5390682e872e52d3f38ce9d1bef4648c"
@@ -173,7 +201,7 @@
     "@babel/template" "^7.18.10"
     "@babel/types" "^7.19.0"
 
-"@babel/helper-hoist-variables@^7.18.6":
+"@babel/helper-hoist-variables@^7.15.4", "@babel/helper-hoist-variables@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
   integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==
@@ -187,6 +215,13 @@
   dependencies:
     "@babel/types" "^7.20.7"
 
+"@babel/helper-member-expression-to-functions@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.0.tgz#319c6a940431a133897148515877d2f3269c3ba5"
+  integrity sha512-Muu8cdZwNN6mRRNG6lAYErJ5X3bRevgYR2O8wN0yn7jJSnGDu6eG59RfT29JHxGUovyfrh6Pj0XzmR7drNVL3Q==
+  dependencies:
+    "@babel/types" "^7.21.0"
+
 "@babel/helper-module-imports@^7.0.0-beta.49", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
@@ -194,10 +229,10 @@
   dependencies:
     "@babel/types" "^7.18.6"
 
-"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.6", "@babel/helper-module-transforms@^7.20.11":
-  version "7.20.11"
-  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.20.11.tgz#df4c7af713c557938c50ea3ad0117a7944b2f1b0"
-  integrity sha512-uRy78kN4psmji1s2QtbtcCSaj/LILFDp0f/ymhpQH5QY3nljUZCaNWz9X1dEj/8MBdBEFECs7yRhKn8i7NjZgg==
+"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.19.6", "@babel/helper-module-transforms@^7.21.2":
+  version "7.21.2"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.2.tgz#160caafa4978ac8c00ac66636cb0fa37b024e2d2"
+  integrity sha512-79yj2AR4U/Oqq/WOV7Lx6hUjau1Zfo4cI+JLAVYeMV5XIlbOhmjEk5ulbTc9fMpmlojzZHkUUxAiK+UKn+hNQQ==
   dependencies:
     "@babel/helper-environment-visitor" "^7.18.9"
     "@babel/helper-module-imports" "^7.18.6"
@@ -205,8 +240,8 @@
     "@babel/helper-split-export-declaration" "^7.18.6"
     "@babel/helper-validator-identifier" "^7.19.1"
     "@babel/template" "^7.20.7"
-    "@babel/traverse" "^7.20.10"
-    "@babel/types" "^7.20.7"
+    "@babel/traverse" "^7.21.2"
+    "@babel/types" "^7.21.2"
 
 "@babel/helper-optimise-call-expression@^7.18.6":
   version "7.18.6"
@@ -256,7 +291,14 @@
   dependencies:
     "@babel/types" "^7.18.9"
 
-"@babel/helper-split-export-declaration@^7.18.6":
+"@babel/helper-skip-transparent-expression-wrappers@^7.20.0":
+  version "7.20.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684"
+  integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==
+  dependencies:
+    "@babel/types" "^7.20.0"
+
+"@babel/helper-split-export-declaration@^7.15.4", "@babel/helper-split-export-declaration@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075"
   integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==
@@ -268,6 +310,11 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz#38d3acb654b4701a9b77fb0615a96f775c3a9e63"
   integrity sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==
 
+"@babel/helper-validator-identifier@^7.14.0":
+  version "7.15.7"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
+  integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==
+
 "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1":
   version "7.19.1"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2"
@@ -278,6 +325,11 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
   integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
 
+"@babel/helper-validator-option@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180"
+  integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==
+
 "@babel/helper-wrap-function@^7.18.9":
   version "7.18.10"
   resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.10.tgz#a7fcd3ab9b1be4c9b52cf7d7fdc1e88c2ce93396"
@@ -288,16 +340,16 @@
     "@babel/traverse" "^7.18.10"
     "@babel/types" "^7.18.10"
 
-"@babel/helpers@^7.20.7":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.20.7.tgz#04502ff0feecc9f20ecfaad120a18f011a8e6dce"
-  integrity sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==
+"@babel/helpers@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.0.tgz#9dd184fb5599862037917cdc9eecb84577dc4e7e"
+  integrity sha512-XXve0CBtOW0pd7MRzzmoyuSj0e3SEzj8pgyFxnTT1NJZL38BD1MK7yYrm8yefRPIDvNNe14xR4FdbHwpInD4rA==
   dependencies:
     "@babel/template" "^7.20.7"
-    "@babel/traverse" "^7.20.7"
-    "@babel/types" "^7.20.7"
+    "@babel/traverse" "^7.21.0"
+    "@babel/types" "^7.21.0"
 
-"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6":
+"@babel/highlight@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
   integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
@@ -306,10 +358,30 @@
     chalk "^2.0.0"
     js-tokens "^4.0.0"
 
-"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b"
-  integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==
+"@babel/parser@^7.1.0":
+  version "7.3.1"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.1.tgz#8f4ffd45f779e6132780835ffa7a215fa0b2d181"
+  integrity sha512-ATz6yX/L8LEnC3dtLQnIx4ydcPxhLcoy9Vl6re00zb2w5lG6itY6Vhnr1KFRPq/FHNsgl/gh2mjNN20f9iJTTA==
+
+"@babel/parser@^7.12.13":
+  version "7.15.8"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.15.8.tgz#7bacdcbe71bdc3ff936d510c15dcea7cf0b99016"
+  integrity sha512-BRYa3wcQnjS/nqI8Ac94pYYpJfojHVvVXJ97+IDCImX4Jc8W8Xv1+47enbruk+q1etOpsQNwnfFcNGw+gtPGxA==
+
+"@babel/parser@^7.14.7", "@babel/parser@^7.20.7":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.0.tgz#cc09288743b867763cb927ba101ccdf0b600b7e4"
+  integrity sha512-ONjtg4renj14A9pj3iA5T5+r5Eijxbr2eNIkMBTC74occDSsRZUpe8vowmowAjFR1imWlkD8eEmjYXiREZpGZg==
+
+"@babel/parser@^7.15.4":
+  version "7.21.2"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.2.tgz#dacafadfc6d7654c3051a66d6fe55b6cb2f2a0b3"
+  integrity sha512-URpaIJQwEkEC2T9Kn+Ai6Xe/02iNaVCuT/PtoRz3GPVJVDpPd7mLo+VddTbhCRU9TXqW5mSrQfXZyi8kDKOVpQ==
+
+"@babel/parser@^7.21.0", "@babel/parser@^7.21.3":
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3"
+  integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==
 
 "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6":
   version "7.18.6"
@@ -354,17 +426,6 @@
     "@babel/helper-plugin-utils" "^7.18.6"
     "@babel/plugin-syntax-class-static-block" "^7.14.5"
 
-"@babel/plugin-proposal-decorators@^7.20.7":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.7.tgz#05d37453c2ce818f3e47bbeda9468c8de947eecc"
-  integrity sha512-JB45hbUweYpwAGjkiM7uCyXMENH2lG+9r3G2E+ttc2PRXAoEkpfd/KW5jDg4j8RS6tLtTG1jZi9LbHZVSfs1/A==
-  dependencies:
-    "@babel/helper-create-class-features-plugin" "^7.20.7"
-    "@babel/helper-plugin-utils" "^7.20.2"
-    "@babel/helper-replace-supers" "^7.20.7"
-    "@babel/helper-split-export-declaration" "^7.18.6"
-    "@babel/plugin-syntax-decorators" "^7.19.0"
-
 "@babel/plugin-proposal-dynamic-import@^7.18.6":
   version "7.18.6"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94"
@@ -495,13 +556,6 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/plugin-syntax-decorators@^7.19.0":
-  version "7.19.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz#5f13d1d8fce96951bea01a10424463c9a5b3a599"
-  integrity sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.19.0"
-
 "@babel/plugin-syntax-dynamic-import@^7.8.3":
   version "7.8.3"
   resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz"
@@ -600,6 +654,13 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
+"@babel/plugin-syntax-typescript@^7.20.0":
+  version "7.20.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7"
+  integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.19.0"
+
 "@babel/plugin-syntax-typescript@^7.7.2":
   version "7.14.5"
   resolved "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz"
@@ -798,13 +859,13 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.6"
 
-"@babel/plugin-transform-react-inline-elements@^7.18.6":
-  version "7.18.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-inline-elements/-/plugin-transform-react-inline-elements-7.18.6.tgz#d0676948eb5a11d547de6add7e8a2c522ec708f5"
-  integrity sha512-uo3yD1EXhDxmk1Y/CeFDdHS5t22IOUBooLPFOrrjfpYmDM9Vg61xbIaWeWkbYQ7Aq0zMf30/FfKoQgFwyqw6Bg==
+"@babel/plugin-transform-react-inline-elements@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-inline-elements/-/plugin-transform-react-inline-elements-7.21.0.tgz#921fe634c422987677b133b335ae3f3aecddda2a"
+  integrity sha512-lOSFD/67qxOwQ7q6KpkAbBFeGI5xy1Oya2zipYxPSmG2C210CRJyQdzOl1A368J1nv2gOFTgRXdsRjb83jioLw==
   dependencies:
-    "@babel/helper-builder-react-jsx" "^7.18.6"
-    "@babel/helper-plugin-utils" "^7.18.6"
+    "@babel/helper-builder-react-jsx" "^7.19.0"
+    "@babel/helper-plugin-utils" "^7.20.2"
 
 "@babel/plugin-transform-react-jsx-development@^7.18.6":
   version "7.18.6"
@@ -847,13 +908,13 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.6"
 
-"@babel/plugin-transform-runtime@^7.19.6":
-  version "7.19.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz#9d2a9dbf4e12644d6f46e5e75bfbf02b5d6e9194"
-  integrity sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==
+"@babel/plugin-transform-runtime@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.21.0.tgz#2a884f29556d0a68cd3d152dcc9e6c71dfb6eee8"
+  integrity sha512-ReY6pxwSzEU0b3r2/T/VhqMKg/AkceBT19X0UptA3/tYi5Pe2eXgEUH+NNMC5nok6c6XQz5tyVTUpuezRfSMSg==
   dependencies:
     "@babel/helper-module-imports" "^7.18.6"
-    "@babel/helper-plugin-utils" "^7.19.0"
+    "@babel/helper-plugin-utils" "^7.20.2"
     babel-plugin-polyfill-corejs2 "^0.3.3"
     babel-plugin-polyfill-corejs3 "^0.6.0"
     babel-plugin-polyfill-regenerator "^0.4.1"
@@ -895,6 +956,15 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.18.9"
 
+"@babel/plugin-transform-typescript@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.21.0.tgz#f0956a153679e3b377ae5b7f0143427151e4c848"
+  integrity sha512-xo///XTPp3mDzTtrqXoBlK9eiAYW3wv9JXglcn/u1bi60RW11dEUxIgA8cbnDhutS1zacjMRmAwxE0gMklLnZg==
+  dependencies:
+    "@babel/helper-create-class-features-plugin" "^7.21.0"
+    "@babel/helper-plugin-utils" "^7.20.2"
+    "@babel/plugin-syntax-typescript" "^7.20.0"
+
 "@babel/plugin-transform-unicode-escapes@^7.18.10":
   version "7.18.10"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246"
@@ -1014,6 +1084,15 @@
     "@babel/plugin-transform-react-jsx-development" "^7.18.6"
     "@babel/plugin-transform-react-pure-annotations" "^7.18.6"
 
+"@babel/preset-typescript@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.21.0.tgz#bcbbca513e8213691fe5d4b23d9251e01f00ebff"
+  integrity sha512-myc9mpoVA5m1rF8K8DgLEatOYFDpwC+RkMkjZ0Du6uI62YvDe8uxIEYVs/VCdSJ097nlALiU/yBC7//3nI+hNg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.20.2"
+    "@babel/helper-validator-option" "^7.21.0"
+    "@babel/plugin-transform-typescript" "^7.21.0"
+
 "@babel/runtime-corejs3@^7.10.2":
   version "7.10.3"
   resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.10.3.tgz"
@@ -1029,14 +1108,14 @@
   dependencies:
     regenerator-runtime "^0.12.0"
 
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.18.9", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.7", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd"
-  integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==
+"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673"
+  integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==
   dependencies:
     regenerator-runtime "^0.13.11"
 
-"@babel/template@^7.18.10", "@babel/template@^7.20.7", "@babel/template@^7.3.3":
+"@babel/template@^7.18.10", "@babel/template@^7.20.7":
   version "7.20.7"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
   integrity sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==
@@ -1045,26 +1124,100 @@
     "@babel/parser" "^7.20.7"
     "@babel/types" "^7.20.7"
 
-"@babel/traverse@^7.18.10", "@babel/traverse@^7.20.10", "@babel/traverse@^7.20.12", "@babel/traverse@^7.20.7", "@babel/traverse@^7.7.2":
-  version "7.20.12"
-  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.12.tgz#7f0f787b3a67ca4475adef1f56cb94f6abd4a4b5"
-  integrity sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==
+"@babel/template@^7.3.3":
+  version "7.12.13"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327"
+  integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==
+  dependencies:
+    "@babel/code-frame" "^7.12.13"
+    "@babel/parser" "^7.12.13"
+    "@babel/types" "^7.12.13"
+
+"@babel/traverse@^7.18.10", "@babel/traverse@^7.20.7", "@babel/traverse@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.0.tgz#0e1807abd5db98e6a19c204b80ed1e3f5bca0edc"
+  integrity sha512-Xdt2P1H4LKTO8ApPfnO1KmzYMFpp7D/EinoXzLYN/cHcBNrVCAkAtGUcXnHXrl/VGktureU6fkQrHSBE2URfoA==
   dependencies:
     "@babel/code-frame" "^7.18.6"
-    "@babel/generator" "^7.20.7"
+    "@babel/generator" "^7.21.0"
     "@babel/helper-environment-visitor" "^7.18.9"
-    "@babel/helper-function-name" "^7.19.0"
+    "@babel/helper-function-name" "^7.21.0"
     "@babel/helper-hoist-variables" "^7.18.6"
     "@babel/helper-split-export-declaration" "^7.18.6"
-    "@babel/parser" "^7.20.7"
-    "@babel/types" "^7.20.7"
+    "@babel/parser" "^7.21.0"
+    "@babel/types" "^7.21.0"
     debug "^4.1.0"
     globals "^11.1.0"
 
-"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
-  version "7.20.7"
-  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f"
-  integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==
+"@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3":
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67"
+  integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ==
+  dependencies:
+    "@babel/code-frame" "^7.18.6"
+    "@babel/generator" "^7.21.3"
+    "@babel/helper-environment-visitor" "^7.18.9"
+    "@babel/helper-function-name" "^7.21.0"
+    "@babel/helper-hoist-variables" "^7.18.6"
+    "@babel/helper-split-export-declaration" "^7.18.6"
+    "@babel/parser" "^7.21.3"
+    "@babel/types" "^7.21.3"
+    debug "^4.1.0"
+    globals "^11.1.0"
+
+"@babel/traverse@^7.7.2":
+  version "7.15.4"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.15.4.tgz#ff8510367a144bfbff552d9e18e28f3e2889c22d"
+  integrity sha512-W6lQD8l4rUbQR/vYgSuCAE75ADyyQvOpFVsvPPdkhf6lATXAsQIG9YdtOcu8BB1dZ0LKu+Zo3c1wEcbKeuhdlA==
+  dependencies:
+    "@babel/code-frame" "^7.14.5"
+    "@babel/generator" "^7.15.4"
+    "@babel/helper-function-name" "^7.15.4"
+    "@babel/helper-hoist-variables" "^7.15.4"
+    "@babel/helper-split-export-declaration" "^7.15.4"
+    "@babel/parser" "^7.15.4"
+    "@babel/types" "^7.15.4"
+    debug "^4.1.0"
+    globals "^11.1.0"
+
+"@babel/types@^7.0.0", "@babel/types@^7.0.0-beta.49", "@babel/types@^7.12.13", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
+  version "7.14.0"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.0.tgz#3fc3fc74e0cdad878182e5f66cc6bcab1915a802"
+  integrity sha512-O2LVLdcnWplaGxiPBz12d0HcdN8QdxdsWYhz5LSeuukV/5mn2xUUc3gBeU4QBYPJ18g/UToe8F532XJ608prmg==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.14.0"
+    to-fast-properties "^2.0.0"
+
+"@babel/types@^7.15.4":
+  version "7.21.2"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.2.tgz#92246f6e00f91755893c2876ad653db70c8310d1"
+  integrity sha512-3wRZSs7jiFaB8AjxiiD+VqN5DTG2iRvJGQ+qYFrs/654lg6kGTQWIOFjlBo5RaXuAZjBmP3+OQH4dmhqiiyYxw==
+  dependencies:
+    "@babel/helper-string-parser" "^7.19.4"
+    "@babel/helper-validator-identifier" "^7.19.1"
+    to-fast-properties "^2.0.0"
+
+"@babel/types@^7.17.10":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.6.tgz#5d781dd10a3f0c9f1f931bd19de5eb26ec31acf0"
+  integrity sha512-NdBNzPDwed30fZdDQtVR7ZgaO4UKjuaQFH9VArS+HMnurlOY0JWN+4ROlu/iapMFwjRQU4pOG4StZfDmulEwGA==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.18.6"
+    to-fast-properties "^2.0.0"
+
+"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.19.0", "@babel/types@^7.20.0", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0":
+  version "7.21.0"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.0.tgz#1da00d89c2f18b226c9207d96edbeb79316a1819"
+  integrity sha512-uR7NWq2VNFnDi7EYqiRz2Jv/VQIu38tu64Zy8TX2nQFQ6etJ9V/Rr2msW8BS132mum2rL645qpDrLtAJtVpuow==
+  dependencies:
+    "@babel/helper-string-parser" "^7.19.4"
+    "@babel/helper-validator-identifier" "^7.19.1"
+    to-fast-properties "^2.0.0"
+
+"@babel/types@^7.21.2", "@babel/types@^7.21.3":
+  version "7.21.3"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05"
+  integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg==
   dependencies:
     "@babel/helper-string-parser" "^7.19.4"
     "@babel/helper-validator-identifier" "^7.19.1"
@@ -1075,10 +1228,30 @@
   resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz"
   integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
 
-"@csstools/selector-specificity@^2.0.2":
-  version "2.0.2"
-  resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz#1bfafe4b7ed0f3e4105837e056e0a89b108ebe36"
-  integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==
+"@csstools/css-parser-algorithms@^2.0.1":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.0.1.tgz#ff02629c7c95d1f4f8ea84d5ef1173461610535e"
+  integrity sha512-B9/8PmOtU6nBiibJg0glnNktQDZ3rZnGn/7UmDfrm2vMtrdlXO3p7ErE95N0up80IRk9YEtB5jyj/TmQ1WH3dw==
+
+"@csstools/css-tokenizer@^2.1.0":
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-2.1.0.tgz#fee4de3d444db3ce9007f3af6474af8ba3e4b930"
+  integrity sha512-dtqFyoJBHUxGi9zPZdpCKP1xk8tq6KPHJ/NY4qWXiYo6IcSGwzk3L8x2XzZbbyOyBs9xQARoGveU2AsgLj6D2A==
+
+"@csstools/media-query-list-parser@^2.0.1":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@csstools/media-query-list-parser/-/media-query-list-parser-2.0.1.tgz#d85a366811563a5d002755ed10e5212a1613c91d"
+  integrity sha512-X2/OuzEbjaxhzm97UJ+95GrMeT29d1Ib+Pu+paGLuRWZnWRK9sI9r3ikmKXPWGA1C4y4JEdBEFpp9jEqCvLeRA==
+
+"@csstools/selector-specificity@^2.1.1":
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz#c9c61d9fe5ca5ac664e1153bb0aa0eba1c6d6308"
+  integrity sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==
+
+"@discoveryjs/json-ext@0.5.7":
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
+  integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
 
 "@emotion/babel-plugin@^11.7.1":
   version "11.9.2"
@@ -1163,21 +1336,38 @@
   resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz"
   integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
 
-"@eslint/eslintrc@^0.4.3":
-  version "0.4.3"
-  resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz"
-  integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==
+"@eslint-community/eslint-utils@^4.2.0":
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz#a831e6e468b4b2b5ae42bf658bea015bf10bc518"
+  integrity sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ==
+  dependencies:
+    eslint-visitor-keys "^3.3.0"
+
+"@eslint-community/regexpp@^4.4.0":
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403"
+  integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ==
+
+"@eslint/eslintrc@^2.0.1":
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d"
+  integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw==
   dependencies:
     ajv "^6.12.4"
-    debug "^4.1.1"
-    espree "^7.3.0"
-    globals "^13.9.0"
-    ignore "^4.0.6"
+    debug "^4.3.2"
+    espree "^9.5.0"
+    globals "^13.19.0"
+    ignore "^5.2.0"
     import-fresh "^3.2.1"
-    js-yaml "^3.13.1"
-    minimatch "^3.0.4"
+    js-yaml "^4.1.0"
+    minimatch "^3.1.2"
     strip-json-comments "^3.1.1"
 
+"@eslint/js@8.36.0":
+  version "8.36.0"
+  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe"
+  integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg==
+
 "@floating-ui/core@^1.0.1":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.0.1.tgz#00e64d74e911602c8533957af0cce5af6b2e93c8"
@@ -1207,24 +1397,29 @@
   resolved "https://registry.npmjs.org/@gamestdio/websocket/-/websocket-0.3.2.tgz"
   integrity sha512-J3n5SKim+ZoLbe44hRGI/VYAwSMCeIJuBy+FfP6EZaujEpNchPRFcIsVQLWAwpU1bP2Ji63rC+rEUOd1vjUB6Q==
 
-"@github/webauthn-json@^0.5.7":
-  version "0.5.7"
-  resolved "https://registry.npmjs.org/@github/webauthn-json/-/webauthn-json-0.5.7.tgz"
-  integrity sha512-SUYsttDxFSvWvvJssJpwzjmRCqYfdfqC9VCmAHQYfdKCVelyJteCHo9/lK1CB72mx/jrl6cFNY08aua4J2jIyg==
+"@github/webauthn-json@^2.1.1":
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/@github/webauthn-json/-/webauthn-json-2.1.1.tgz#648e63fc28050917d2882cc2b27817a88cb420fc"
+  integrity sha512-XrftRn4z75SnaJOmZQbt7Mk+IIjqVHw+glDGOxuHwXkZBZh/MBoRS7MHjSZMDaLhT4RjN2VqiEU7EOYleuJWSQ==
 
-"@humanwhocodes/config-array@^0.5.0":
-  version "0.5.0"
-  resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz"
-  integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==
+"@humanwhocodes/config-array@^0.11.8":
+  version "0.11.8"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
+  integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==
   dependencies:
-    "@humanwhocodes/object-schema" "^1.2.0"
+    "@humanwhocodes/object-schema" "^1.2.1"
     debug "^4.1.1"
-    minimatch "^3.0.4"
+    minimatch "^3.0.5"
 
-"@humanwhocodes/object-schema@^1.2.0":
-  version "1.2.0"
-  resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz"
-  integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==
+"@humanwhocodes/module-importer@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+  integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
+"@humanwhocodes/object-schema@^1.2.1":
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+  integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
 
 "@istanbuljs/load-nyc-config@^1.0.0":
   version "1.1.0"
@@ -1242,109 +1437,109 @@
   resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz"
   integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==
 
-"@jest/console@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.3.1.tgz#3e3f876e4e47616ea3b1464b9fbda981872e9583"
-  integrity sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg==
+"@jest/console@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.5.0.tgz#593a6c5c0d3f75689835f1b3b4688c4f8544cb57"
+  integrity sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==
   dependencies:
-    "@jest/types" "^29.3.1"
+    "@jest/types" "^29.5.0"
     "@types/node" "*"
     chalk "^4.0.0"
-    jest-message-util "^29.3.1"
-    jest-util "^29.3.1"
+    jest-message-util "^29.5.0"
+    jest-util "^29.5.0"
     slash "^3.0.0"
 
-"@jest/core@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.3.1.tgz#bff00f413ff0128f4debec1099ba7dcd649774a1"
-  integrity sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw==
+"@jest/core@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.5.0.tgz#76674b96904484e8214614d17261cc491e5f1f03"
+  integrity sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==
   dependencies:
-    "@jest/console" "^29.3.1"
-    "@jest/reporters" "^29.3.1"
-    "@jest/test-result" "^29.3.1"
-    "@jest/transform" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    "@jest/console" "^29.5.0"
+    "@jest/reporters" "^29.5.0"
+    "@jest/test-result" "^29.5.0"
+    "@jest/transform" "^29.5.0"
+    "@jest/types" "^29.5.0"
     "@types/node" "*"
     ansi-escapes "^4.2.1"
     chalk "^4.0.0"
     ci-info "^3.2.0"
     exit "^0.1.2"
     graceful-fs "^4.2.9"
-    jest-changed-files "^29.2.0"
-    jest-config "^29.3.1"
-    jest-haste-map "^29.3.1"
-    jest-message-util "^29.3.1"
-    jest-regex-util "^29.2.0"
-    jest-resolve "^29.3.1"
-    jest-resolve-dependencies "^29.3.1"
-    jest-runner "^29.3.1"
-    jest-runtime "^29.3.1"
-    jest-snapshot "^29.3.1"
-    jest-util "^29.3.1"
-    jest-validate "^29.3.1"
-    jest-watcher "^29.3.1"
+    jest-changed-files "^29.5.0"
+    jest-config "^29.5.0"
+    jest-haste-map "^29.5.0"
+    jest-message-util "^29.5.0"
+    jest-regex-util "^29.4.3"
+    jest-resolve "^29.5.0"
+    jest-resolve-dependencies "^29.5.0"
+    jest-runner "^29.5.0"
+    jest-runtime "^29.5.0"
+    jest-snapshot "^29.5.0"
+    jest-util "^29.5.0"
+    jest-validate "^29.5.0"
+    jest-watcher "^29.5.0"
     micromatch "^4.0.4"
-    pretty-format "^29.3.1"
+    pretty-format "^29.5.0"
     slash "^3.0.0"
     strip-ansi "^6.0.0"
 
-"@jest/environment@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.3.1.tgz#eb039f726d5fcd14698acd072ac6576d41cfcaa6"
-  integrity sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag==
+"@jest/environment@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.5.0.tgz#9152d56317c1fdb1af389c46640ba74ef0bb4c65"
+  integrity sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==
   dependencies:
-    "@jest/fake-timers" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    "@jest/fake-timers" "^29.5.0"
+    "@jest/types" "^29.5.0"
     "@types/node" "*"
-    jest-mock "^29.3.1"
+    jest-mock "^29.5.0"
 
-"@jest/expect-utils@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.3.1.tgz#531f737039e9b9e27c42449798acb5bba01935b6"
-  integrity sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g==
+"@jest/expect-utils@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.5.0.tgz#f74fad6b6e20f924582dc8ecbf2cb800fe43a036"
+  integrity sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==
   dependencies:
-    jest-get-type "^29.2.0"
+    jest-get-type "^29.4.3"
 
-"@jest/expect@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.3.1.tgz#456385b62894349c1d196f2d183e3716d4c6a6cd"
-  integrity sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg==
+"@jest/expect@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.5.0.tgz#80952f5316b23c483fbca4363ce822af79c38fba"
+  integrity sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==
   dependencies:
-    expect "^29.3.1"
-    jest-snapshot "^29.3.1"
+    expect "^29.5.0"
+    jest-snapshot "^29.5.0"
 
-"@jest/fake-timers@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.3.1.tgz#b140625095b60a44de820876d4c14da1aa963f67"
-  integrity sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A==
+"@jest/fake-timers@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.5.0.tgz#d4d09ec3286b3d90c60bdcd66ed28d35f1b4dc2c"
+  integrity sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==
   dependencies:
-    "@jest/types" "^29.3.1"
-    "@sinonjs/fake-timers" "^9.1.2"
+    "@jest/types" "^29.5.0"
+    "@sinonjs/fake-timers" "^10.0.2"
     "@types/node" "*"
-    jest-message-util "^29.3.1"
-    jest-mock "^29.3.1"
-    jest-util "^29.3.1"
+    jest-message-util "^29.5.0"
+    jest-mock "^29.5.0"
+    jest-util "^29.5.0"
 
-"@jest/globals@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.3.1.tgz#92be078228e82d629df40c3656d45328f134a0c6"
-  integrity sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q==
+"@jest/globals@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.5.0.tgz#6166c0bfc374c58268677539d0c181f9c1833298"
+  integrity sha512-S02y0qMWGihdzNbUiqSAiKSpSozSuHX5UYc7QbnHP+D9Lyw8DgGGCinrN9uSuHPeKgSSzvPom2q1nAtBvUsvPQ==
   dependencies:
-    "@jest/environment" "^29.3.1"
-    "@jest/expect" "^29.3.1"
-    "@jest/types" "^29.3.1"
-    jest-mock "^29.3.1"
+    "@jest/environment" "^29.5.0"
+    "@jest/expect" "^29.5.0"
+    "@jest/types" "^29.5.0"
+    jest-mock "^29.5.0"
 
-"@jest/reporters@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.3.1.tgz#9a6d78c109608e677c25ddb34f907b90e07b4310"
-  integrity sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA==
+"@jest/reporters@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.5.0.tgz#985dfd91290cd78ddae4914ba7921bcbabe8ac9b"
+  integrity sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==
   dependencies:
     "@bcoe/v8-coverage" "^0.2.3"
-    "@jest/console" "^29.3.1"
-    "@jest/test-result" "^29.3.1"
-    "@jest/transform" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    "@jest/console" "^29.5.0"
+    "@jest/test-result" "^29.5.0"
+    "@jest/transform" "^29.5.0"
+    "@jest/types" "^29.5.0"
     "@jridgewell/trace-mapping" "^0.3.15"
     "@types/node" "*"
     chalk "^4.0.0"
@@ -1357,70 +1552,70 @@
     istanbul-lib-report "^3.0.0"
     istanbul-lib-source-maps "^4.0.0"
     istanbul-reports "^3.1.3"
-    jest-message-util "^29.3.1"
-    jest-util "^29.3.1"
-    jest-worker "^29.3.1"
+    jest-message-util "^29.5.0"
+    jest-util "^29.5.0"
+    jest-worker "^29.5.0"
     slash "^3.0.0"
     string-length "^4.0.1"
     strip-ansi "^6.0.0"
     v8-to-istanbul "^9.0.1"
 
-"@jest/schemas@^29.0.0":
-  version "29.0.0"
-  resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a"
-  integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==
+"@jest/schemas@^29.4.3":
+  version "29.4.3"
+  resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788"
+  integrity sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==
   dependencies:
-    "@sinclair/typebox" "^0.24.1"
+    "@sinclair/typebox" "^0.25.16"
 
-"@jest/source-map@^29.2.0":
-  version "29.2.0"
-  resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.2.0.tgz#ab3420c46d42508dcc3dc1c6deee0b613c235744"
-  integrity sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ==
+"@jest/source-map@^29.4.3":
+  version "29.4.3"
+  resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20"
+  integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w==
   dependencies:
     "@jridgewell/trace-mapping" "^0.3.15"
     callsites "^3.0.0"
     graceful-fs "^4.2.9"
 
-"@jest/test-result@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.3.1.tgz#92cd5099aa94be947560a24610aa76606de78f50"
-  integrity sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw==
+"@jest/test-result@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.5.0.tgz#7c856a6ca84f45cc36926a4e9c6b57f1973f1408"
+  integrity sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==
   dependencies:
-    "@jest/console" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    "@jest/console" "^29.5.0"
+    "@jest/types" "^29.5.0"
     "@types/istanbul-lib-coverage" "^2.0.0"
     collect-v8-coverage "^1.0.0"
 
-"@jest/test-sequencer@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz#fa24b3b050f7a59d48f7ef9e0b782ab65123090d"
-  integrity sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA==
+"@jest/test-sequencer@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz#34d7d82d3081abd523dbddc038a3ddcb9f6d3cc4"
+  integrity sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==
   dependencies:
-    "@jest/test-result" "^29.3.1"
+    "@jest/test-result" "^29.5.0"
     graceful-fs "^4.2.9"
-    jest-haste-map "^29.3.1"
+    jest-haste-map "^29.5.0"
     slash "^3.0.0"
 
-"@jest/transform@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.3.1.tgz#1e6bd3da4af50b5c82a539b7b1f3770568d6e36d"
-  integrity sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug==
+"@jest/transform@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.5.0.tgz#cf9c872d0965f0cbd32f1458aa44a2b1988b00f9"
+  integrity sha512-8vbeZWqLJOvHaDfeMuoHITGKSz5qWc9u04lnWrQE3VyuSw604PzQM824ZeX9XSjUCeDiE3GuxZe5UKa8J61NQw==
   dependencies:
     "@babel/core" "^7.11.6"
-    "@jest/types" "^29.3.1"
+    "@jest/types" "^29.5.0"
     "@jridgewell/trace-mapping" "^0.3.15"
     babel-plugin-istanbul "^6.1.1"
     chalk "^4.0.0"
     convert-source-map "^2.0.0"
     fast-json-stable-stringify "^2.1.0"
     graceful-fs "^4.2.9"
-    jest-haste-map "^29.3.1"
-    jest-regex-util "^29.2.0"
-    jest-util "^29.3.1"
+    jest-haste-map "^29.5.0"
+    jest-regex-util "^29.4.3"
+    jest-util "^29.5.0"
     micromatch "^4.0.4"
     pirates "^4.0.4"
     slash "^3.0.0"
-    write-file-atomic "^4.0.1"
+    write-file-atomic "^4.0.2"
 
 "@jest/types@^25.5.0":
   version "25.5.0"
@@ -1433,9 +1628,9 @@
     chalk "^3.0.0"
 
 "@jest/types@^27.0.2":
-  version "27.5.1"
-  resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80"
-  integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==
+  version "27.2.5"
+  resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.2.5.tgz#420765c052605e75686982d24b061b4cbba22132"
+  integrity sha512-nmuM4VuDtCZcY+eTpw+0nvstwReMsjPoj7ZR80/BbixulhLaiX+fbv8oeLW8WZlJMcsGQsTmMKT/iTZu1Uy/lQ==
   dependencies:
     "@types/istanbul-lib-coverage" "^2.0.0"
     "@types/istanbul-reports" "^3.0.0"
@@ -1443,18 +1638,26 @@
     "@types/yargs" "^16.0.0"
     chalk "^4.0.0"
 
-"@jest/types@^29.3.1":
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.3.1.tgz#7c5a80777cb13e703aeec6788d044150341147e3"
-  integrity sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA==
+"@jest/types@^29.5.0":
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.5.0.tgz#f59ef9b031ced83047c67032700d8c807d6e1593"
+  integrity sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==
   dependencies:
-    "@jest/schemas" "^29.0.0"
+    "@jest/schemas" "^29.4.3"
     "@types/istanbul-lib-coverage" "^2.0.0"
     "@types/istanbul-reports" "^3.0.0"
     "@types/node" "*"
     "@types/yargs" "^17.0.8"
     chalk "^4.0.0"
 
+"@jridgewell/gen-mapping@^0.1.0":
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
+  integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==
+  dependencies:
+    "@jridgewell/set-array" "^1.0.0"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+
 "@jridgewell/gen-mapping@^0.3.2":
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
@@ -1464,22 +1667,32 @@
     "@jridgewell/sourcemap-codec" "^1.4.10"
     "@jridgewell/trace-mapping" "^0.3.9"
 
+"@jridgewell/resolve-uri@3.1.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+  integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
 "@jridgewell/resolve-uri@^3.0.3":
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.4.tgz#b876e3feefb9c8d3aa84014da28b5e52a0640d72"
   integrity sha512-cz8HFjOFfUBtvN+NXYSFMHYRdxZMaEl0XypVrhzxBgadKIXhIkRd8aMeHhmF56Sl7SuS8OnUpQ73/k9LE4VnLg==
 
-"@jridgewell/set-array@^1.0.1":
+"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
   integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
 
+"@jridgewell/sourcemap-codec@1.4.14":
+  version "1.4.14"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+  integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
 "@jridgewell/sourcemap-codec@^1.4.10":
   version "1.4.10"
   resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.10.tgz#baf57b4e2a690d4f38560171f91783656b7f8186"
   integrity sha512-Ht8wIW5v165atIX1p+JvKR5ONzUyF4Ac8DZIQ5kZs9zrb6M8SJNXpx1zn04rn65VjBMygRoMXcyYwNK0fT7bEg==
 
-"@jridgewell/trace-mapping@^0.3.0", "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.9":
+"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.9":
   version "0.3.15"
   resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774"
   integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==
@@ -1487,47 +1700,21 @@
     "@jridgewell/resolve-uri" "^3.0.3"
     "@jridgewell/sourcemap-codec" "^1.4.10"
 
-"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
-  version "5.1.1-v1"
-  resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129"
-  integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==
+"@jridgewell/trace-mapping@^0.3.17":
+  version "0.3.17"
+  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985"
+  integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==
   dependencies:
-    eslint-scope "5.1.1"
-
-"@node-redis/bloom@1.0.1":
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/@node-redis/bloom/-/bloom-1.0.1.tgz"
-  integrity sha512-mXEBvEIgF4tUzdIN89LiYsbi6//EdpFA7L8M+DHCvePXg+bfHWi+ct5VI6nHUFQE5+ohm/9wmgihCH3HSkeKsw==
+    "@jridgewell/resolve-uri" "3.1.0"
+    "@jridgewell/sourcemap-codec" "1.4.14"
 
-"@node-redis/client@1.0.5":
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/@node-redis/client/-/client-1.0.5.tgz#ebac5e2bbf12214042a37621604973a954ede755"
-  integrity sha512-ESZ3bd1f+od62h4MaBLKum+klVJfA4wAeLHcVQBkoXa1l0viFesOWnakLQqKg+UyrlJhZmXJWtu0Y9v7iTMrig==
+"@nodelib/fs.scandir@2.1.4":
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69"
+  integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==
   dependencies:
-    cluster-key-slot "1.1.0"
-    generic-pool "3.8.2"
-    redis-parser "3.0.0"
-    yallist "4.0.0"
-
-"@node-redis/graph@1.0.0":
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/@node-redis/graph/-/graph-1.0.0.tgz#baf8eaac4a400f86ea04d65ec3d65715fd7951ab"
-  integrity sha512-mRSo8jEGC0cf+Rm7q8mWMKKKqkn6EAnA9IA2S3JvUv/gaWW/73vil7GLNwion2ihTptAm05I9LkepzfIXUKX5g==
-
-"@node-redis/json@1.0.2":
-  version "1.0.2"
-  resolved "https://registry.npmjs.org/@node-redis/json/-/json-1.0.2.tgz"
-  integrity sha512-qVRgn8WfG46QQ08CghSbY4VhHFgaTY71WjpwRBGEuqGPfWwfRcIf3OqSpR7Q/45X+v3xd8mvYjywqh0wqJ8T+g==
-
-"@node-redis/search@1.0.5":
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/@node-redis/search/-/search-1.0.5.tgz#96050007eb7c50a7e47080320b4f12aca8cf94c4"
-  integrity sha512-MCOL8iCKq4v+3HgEQv8zGlSkZyXSXtERgrAJ4TSryIG/eLFy84b57KmNNa/V7M1Q2Wd2hgn2nPCGNcQtk1R1OQ==
-
-"@node-redis/time-series@1.0.2":
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/@node-redis/time-series/-/time-series-1.0.2.tgz#5dd3638374edd85ebe0aa6b0e87addc88fb9df69"
-  integrity sha512-HGQ8YooJ8Mx7l28tD7XjtB3ImLEjlUxG1wC1PAjxu6hPJqjPshUZxAICzDqDjtIbhDTf48WXXUcx8TQJB1XTKA==
+    "@nodelib/fs.stat" "2.0.4"
+    run-parallel "^1.1.9"
 
 "@nodelib/fs.scandir@2.1.5":
   version "2.1.5"
@@ -1537,12 +1724,25 @@
     "@nodelib/fs.stat" "2.0.5"
     run-parallel "^1.1.9"
 
-"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2":
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655"
+  integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==
+
+"@nodelib/fs.stat@2.0.5":
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
   integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
 
 "@nodelib/fs.walk@^1.2.3":
+  version "1.2.6"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063"
+  integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==
+  dependencies:
+    "@nodelib/fs.scandir" "2.1.4"
+    fastq "^1.6.0"
+
+"@nodelib/fs.walk@^1.2.8":
   version "1.2.8"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
   integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
@@ -1572,6 +1772,40 @@
   resolved "https://registry.yarnpkg.com/@rails/ujs/-/ujs-6.1.7.tgz#b09dc5b2105dd267e8374c47e4490240451dc7f6"
   integrity sha512-0e7WQ4LE/+LEfW2zfAw9ppsB6A8RmxbdAUPAF++UT80epY+7emuQDkKXmaK0a9lp6An50RvzezI0cIQjp1A58w==
 
+"@redis/bloom@1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71"
+  integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==
+
+"@redis/client@1.5.6":
+  version "1.5.6"
+  resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.5.6.tgz#869cc65718d7d5493ef655a71dc40f3bc64a1b28"
+  integrity sha512-dFD1S6je+A47Lj22jN/upVU2fj4huR7S9APd7/ziUXsIXDL+11GPYti4Suv5y8FuXaN+0ZG4JF+y1houEJ7ToA==
+  dependencies:
+    cluster-key-slot "1.1.2"
+    generic-pool "3.9.0"
+    yallist "4.0.0"
+
+"@redis/graph@1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@redis/graph/-/graph-1.1.0.tgz#cc2b82e5141a29ada2cce7d267a6b74baa6dd519"
+  integrity sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg==
+
+"@redis/json@1.0.4":
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/@redis/json/-/json-1.0.4.tgz#f372b5f93324e6ffb7f16aadcbcb4e5c3d39bda1"
+  integrity sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw==
+
+"@redis/search@1.1.2":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@redis/search/-/search-1.1.2.tgz#6a8f66ba90812d39c2457420f859ce8fbd8f3838"
+  integrity sha512-/cMfstG/fOh/SsE+4/BQGeuH/JJloeWuH+qJzM8dbxuWvdWibWAOAHHCZTMPhV3xIlH4/cUEIA8OV5QnYpaVoA==
+
+"@redis/time-series@1.0.4":
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.0.4.tgz#af85eb080f6934580e4d3b58046026b6c2b18717"
+  integrity sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng==
+
 "@restart/hooks@^0.4.7":
   version "0.4.7"
   resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.7.tgz#d79ca6472c01ce04389fc73d4a79af1b5e33cd39"
@@ -1616,24 +1850,24 @@
     estree-walker "^1.0.1"
     picomatch "^2.2.2"
 
-"@sinclair/typebox@^0.24.1":
-  version "0.24.20"
-  resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.20.tgz#11a657875de6008622d53f56e063a6347c51a6dd"
-  integrity sha512-kVaO5aEFZb33nPMTZBxiPEkY+slxiPtqC7QX8f9B3eGOMBvEfuMfxp9DSTTCsRJPumPKjrge4yagyssO4q6qzQ==
+"@sinclair/typebox@^0.25.16":
+  version "0.25.21"
+  resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.21.tgz#763b05a4b472c93a8db29b2c3e359d55b29ce272"
+  integrity sha512-gFukHN4t8K4+wVC+ECqeqwzBDeFeTzBXroBTqE6vcWrQGbEUpHO7LYdG0f4xnvYq4VOEwITSlHlp0JBAIFMS/g==
 
-"@sinonjs/commons@^1.7.0":
-  version "1.8.1"
-  resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz"
-  integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==
+"@sinonjs/commons@^2.0.0":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-2.0.0.tgz#fd4ca5b063554307e8327b4564bd56d3b73924a3"
+  integrity sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==
   dependencies:
     type-detect "4.0.8"
 
-"@sinonjs/fake-timers@^9.1.2":
-  version "9.1.2"
-  resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c"
-  integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==
+"@sinonjs/fake-timers@^10.0.2":
+  version "10.0.2"
+  resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz#d10549ed1f423d80639c528b6c7f5a1017747d0c"
+  integrity sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==
   dependencies:
-    "@sinonjs/commons" "^1.7.0"
+    "@sinonjs/commons" "^2.0.0"
 
 "@surma/rollup-plugin-off-main-thread@^2.2.3":
   version "2.2.3"
@@ -1688,6 +1922,11 @@
   resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
   integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
 
+"@trysound/sax@0.2.0":
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
+  integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
+
 "@types/aria-query@^4.2.0":
   version "4.2.0"
   resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz"
@@ -1704,6 +1943,17 @@
     "@types/babel__template" "*"
     "@types/babel__traverse" "*"
 
+"@types/babel__core@^7.20.0":
+  version "7.20.0"
+  resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.0.tgz#61bc5a4cae505ce98e1e36c5445e4bee060d8891"
+  integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==
+  dependencies:
+    "@babel/parser" "^7.20.7"
+    "@babel/types" "^7.20.7"
+    "@types/babel__generator" "*"
+    "@types/babel__template" "*"
+    "@types/babel__traverse" "*"
+
 "@types/babel__generator@*":
   version "7.6.1"
   resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz"
@@ -1726,11 +1976,38 @@
   dependencies:
     "@babel/types" "^7.3.0"
 
+"@types/body-parser@*":
+  version "1.19.2"
+  resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
+  integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==
+  dependencies:
+    "@types/connect" "*"
+    "@types/node" "*"
+
 "@types/color-name@^1.1.1":
   version "1.1.1"
   resolved "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz"
   integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
 
+"@types/connect@*":
+  version "3.4.35"
+  resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
+  integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
+  dependencies:
+    "@types/node" "*"
+
+"@types/emoji-mart@^3.0.9":
+  version "3.0.9"
+  resolved "https://registry.yarnpkg.com/@types/emoji-mart/-/emoji-mart-3.0.9.tgz#2f7ef5d9ec194f28029c46c81a5fc1e5b0efa73c"
+  integrity sha512-qdBo/2Y8MXaJ/2spKjDZocuq79GpnOhkwMHnK2GnVFa8WYFgfA+ei6sil3aeWQPCreOKIx9ogPpR5+7MaOqYAA==
+  dependencies:
+    "@types/react" "*"
+
+"@types/escape-html@^1.0.2":
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/@types/escape-html/-/escape-html-1.0.2.tgz#072b7b13784fb3cee9c2450c22f36405983f5e3c"
+  integrity sha512-gaBLT8pdcexFztLSPRtriHeXY/Kn4907uOCZ4Q3lncFBkheAWOuNt53ypsF8szgxbEJ513UeBzcf4utN0EzEwA==
+
 "@types/estree@0.0.39":
   version "0.0.39"
   resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
@@ -1741,6 +2018,25 @@
   resolved "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz"
   integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==
 
+"@types/express-serve-static-core@^4.17.33":
+  version "4.17.33"
+  resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz#de35d30a9d637dc1450ad18dd583d75d5733d543"
+  integrity sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==
+  dependencies:
+    "@types/node" "*"
+    "@types/qs" "*"
+    "@types/range-parser" "*"
+
+"@types/express@^4.17.17":
+  version "4.17.17"
+  resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4"
+  integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==
+  dependencies:
+    "@types/body-parser" "*"
+    "@types/express-serve-static-core" "^4.17.33"
+    "@types/qs" "*"
+    "@types/serve-static" "*"
+
 "@types/glob@^7.1.1":
   version "7.1.1"
   resolved "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz"
@@ -1757,6 +2053,11 @@
   dependencies:
     "@types/node" "*"
 
+"@types/history@^4.7.11":
+  version "4.7.11"
+  resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
+  integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
+
 "@types/hoist-non-react-statics@^3.3.0":
   version "3.3.1"
   resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz"
@@ -1765,6 +2066,18 @@
     "@types/react" "*"
     hoist-non-react-statics "^3.3.0"
 
+"@types/http-link-header@^1.0.3":
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/@types/http-link-header/-/http-link-header-1.0.3.tgz#899adf1d8d2036074514f3dbd148fb901ceff920"
+  integrity sha512-y8HkoD/vyid+5MrJ3aas0FvU3/BVBGcyG9kgxL0Zn4JwstA8CglFPnrR0RuzOjRCXwqzL5uxWC2IO7Ub0rMU2A==
+  dependencies:
+    "@types/node" "*"
+
+"@types/intl@^1.2.0":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@types/intl/-/intl-1.2.0.tgz#1245511f13064402087979f498764611a3c758fc"
+  integrity sha512-BP+KwmOvD9AR5aoxnbyyPr3fAtpjEI/bVImHsotmpuC43+z0NAmjJ9cQbX7vPCq8XcvCeAVc8E3KSQPYNaPsUQ==
+
 "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
   version "2.0.3"
   resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz"
@@ -1800,6 +2113,19 @@
     jest-diff "^25.2.1"
     pretty-format "^25.2.1"
 
+"@types/jest@^29.4.2":
+  version "29.4.2"
+  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.4.2.tgz#13ca45d528a0f81a4792b7df199f58ad46c2a0e3"
+  integrity sha512-bbne90W7is+m88ezmZrLiTpp41tIoTdvPC5t3gLoNgu/6qbGdWTC2JWqPWQRJn2Q7rVYTr8aTWqOjhGJDXyvAQ==
+  dependencies:
+    expect "^29.0.0"
+    pretty-format "^29.0.0"
+
+"@types/js-yaml@^4.0.5":
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138"
+  integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA==
+
 "@types/jsdom@^20.0.0":
   version "20.0.0"
   resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.0.tgz#4414fb629465167f8b7b3804b9e067bdd99f1791"
@@ -1819,11 +2145,26 @@
   resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz"
   integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
 
+"@types/json-schema@^7.0.9":
+  version "7.0.11"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
+  integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+
 "@types/json5@^0.0.29":
   version "0.0.29"
   resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz"
   integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
 
+"@types/lodash@^4.14.191":
+  version "4.14.191"
+  resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa"
+  integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==
+
+"@types/mime@*":
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10"
+  integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==
+
 "@types/minimatch@*":
   version "3.0.3"
   resolved "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz"
@@ -1844,11 +2185,30 @@
   resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
   integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==
 
+"@types/npmlog@^4.1.4":
+  version "4.1.4"
+  resolved "https://registry.yarnpkg.com/@types/npmlog/-/npmlog-4.1.4.tgz#30eb872153c7ead3e8688c476054ddca004115f6"
+  integrity sha512-WKG4gTr8przEZBiJ5r3s8ZIAoMXNbOgQ+j/d5O4X3x6kZJRLNvyUJuUK/KoG3+8BaOHPhp2m7WC6JKKeovDSzQ==
+
+"@types/object-assign@^4.0.30":
+  version "4.0.30"
+  resolved "https://registry.yarnpkg.com/@types/object-assign/-/object-assign-4.0.30.tgz#8949371d5a99f4381ee0f1df0a9b7a187e07e652"
+  integrity sha512-HhE8gFfLj321pa6OE59QmOdL5NgIOhkdYn7MWnZTOcHOms8XFzNgr9+A0/GbN0XEX9wTM58yg4YXKhGr69QIUw==
+
 "@types/parse-json@^4.0.0":
   version "4.0.0"
   resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz"
   integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
 
+"@types/pg@^8.6.6":
+  version "8.6.6"
+  resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.6.6.tgz#21cdf873a3e345a6e78f394677e3b3b1b543cb80"
+  integrity sha512-O2xNmXebtwVekJDD+02udOncjVcMZQuTEQEMpKJ0ZRf5E7/9JJX3izhKUcUifBkyKpljyUM6BTgy2trmviKlpw==
+  dependencies:
+    "@types/node" "*"
+    pg-protocol "*"
+    pg-types "^2.2.0"
+
 "@types/prettier@^2.1.5":
   version "2.2.3"
   resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz"
@@ -1859,10 +2219,30 @@
   resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz"
   integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
 
-"@types/q@^1.5.1":
-  version "1.5.2"
-  resolved "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz"
-  integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
+"@types/prop-types@^15.7.5":
+  version "15.7.5"
+  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
+  integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
+
+"@types/punycode@^2.1.0":
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/@types/punycode/-/punycode-2.1.0.tgz#89e4f3d09b3f92e87a80505af19be7e0c31d4e83"
+  integrity sha512-PG5aLpW6PJOeV2fHRslP4IOMWn+G+Uq8CfnyJ+PDS8ndCbU+soO+fB3NKCKo0p/Jh2Y4aPaiQZsrOXFdzpcA6g==
+
+"@types/qs@*":
+  version "6.9.7"
+  resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
+  integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
+
+"@types/raf@^3.4.0":
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/@types/raf/-/raf-3.4.0.tgz#2b72cbd55405e071f1c4d29992638e022b20acc2"
+  integrity sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==
+
+"@types/range-parser@*":
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
+  integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
 
 "@types/react-dom@<18.0.0":
   version "17.0.15"
@@ -1871,6 +2251,47 @@
   dependencies:
     "@types/react" "^17"
 
+"@types/react-dom@^16.9.18":
+  version "16.9.18"
+  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.18.tgz#1fda8b84370b1339d639a797a84c16d5a195b419"
+  integrity sha512-lmNARUX3+rNF/nmoAFqasG0jAA7q6MeGZK/fdeLwY3kAA4NPgHHrG5bNQe2B5xmD4B+x6Z6h0rEJQ7MEEgQxsw==
+  dependencies:
+    "@types/react" "^16"
+
+"@types/react-helmet@^6.1.6":
+  version "6.1.6"
+  resolved "https://registry.yarnpkg.com/@types/react-helmet/-/react-helmet-6.1.6.tgz#7d1afd8cbf099616894e8240e9ef70e3c6d7506d"
+  integrity sha512-ZKcoOdW/Tg+kiUbkFCBtvDw0k3nD4HJ/h/B9yWxN4uDO8OkRksWTO+EL+z/Qu3aHTeTll3Ro0Cc/8UhwBCMG5A==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react-immutable-proptypes@^2.1.0":
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/@types/react-immutable-proptypes/-/react-immutable-proptypes-2.1.0.tgz#c045fb48ba28c34c9d759abc3a51a04b5321b77a"
+  integrity sha512-NRH4W4mgymzyM2gnAG+i2VoOdWIBOQlJlSyAgnFiBTdJ0l8IVeyCtdWP8g6Lra59sUBj2XUO/+DkfmrRAxj6UA==
+  dependencies:
+    "@types/prop-types" "*"
+    immutable "^3.8.2"
+
+"@types/react-intl@2.3.18":
+  version "2.3.18"
+  resolved "https://registry.yarnpkg.com/@types/react-intl/-/react-intl-2.3.18.tgz#fd2d8b7f4d0a1dd05b5f1784ab0d7fe1786a690d"
+  integrity sha512-DVNJs49zUxKRZng8VuILE886Yihdsf3yLr5vHk9zJrmF8SyRSK3sxNSvikAKxNkv9hX55XBTJShz6CkJnbNjgg==
+
+"@types/react-motion@^0.0.33":
+  version "0.0.33"
+  resolved "https://registry.yarnpkg.com/@types/react-motion/-/react-motion-0.0.33.tgz#c156c400ace995584990344cc0239e41f411f425"
+  integrity sha512-R9grd4EwdDBcKKq7Zhszd8ukyy2BLKN6ooNI0V39nUl/sui+m7VI94cdebYemBteoPHmO7J7BZk+cIf+Xnk4TA==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react-overlays@^3.1.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@types/react-overlays/-/react-overlays-3.1.0.tgz#2efefa5407562c5aeb34a03336d070a0f74c6274"
+  integrity sha512-NzZZHFLj7M7+I+p5rDdVHtm6AeVYQPShVxALiLYhR9leJSX8XujPsYuY+vh7/mzFjv6XR7PxHBAdlFGNaN6QDQ==
+  dependencies:
+    react-overlays "*"
+
 "@types/react-redux@^7.1.20":
   version "7.1.20"
   resolved "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.20.tgz"
@@ -1881,6 +2302,75 @@
     hoist-non-react-statics "^3.3.0"
     redux "^4.0.0"
 
+"@types/react-redux@^7.1.25":
+  version "7.1.25"
+  resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.25.tgz#de841631205b24f9dfb4967dd4a7901e048f9a88"
+  integrity sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==
+  dependencies:
+    "@types/hoist-non-react-statics" "^3.3.0"
+    "@types/react" "*"
+    hoist-non-react-statics "^3.3.0"
+    redux "^4.0.0"
+
+"@types/react-router-dom@^5.3.3":
+  version "5.3.3"
+  resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
+  integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
+  dependencies:
+    "@types/history" "^4.7.11"
+    "@types/react" "*"
+    "@types/react-router" "*"
+
+"@types/react-router@*":
+  version "5.1.20"
+  resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.20.tgz#88eccaa122a82405ef3efbcaaa5dcdd9f021387c"
+  integrity sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==
+  dependencies:
+    "@types/history" "^4.7.11"
+    "@types/react" "*"
+
+"@types/react-select@^5.0.1":
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/@types/react-select/-/react-select-5.0.1.tgz#04fc85edd34a72675a0ab56ad4c30428aab0e444"
+  integrity sha512-h5Im0AP0dr4AVeHtrcvQrLV+gmPa7SA0AGdxl2jOhtwiE6KgXBFSogWw8az32/nusE6AQHlCOHQWjP1S/+oMWA==
+  dependencies:
+    react-select "*"
+
+"@types/react-sparklines@^1.7.2":
+  version "1.7.2"
+  resolved "https://registry.yarnpkg.com/@types/react-sparklines/-/react-sparklines-1.7.2.tgz#c14e80623abd3669a10f18d13f6fb9fbdc322f70"
+  integrity sha512-N1GwO7Ri5C5fE8+CxhiDntuSw1qYdGytBuedKrCxWpaojXm4WnfygbdBdc5sXGX7feMxDXBy9MNhxoUTwrMl4A==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react-swipeable-views@^0.13.1":
+  version "0.13.1"
+  resolved "https://registry.yarnpkg.com/@types/react-swipeable-views/-/react-swipeable-views-0.13.1.tgz#381c8513deef5426623aa851033ff4f4831ae15c"
+  integrity sha512-Nuvywkv9CkwcUgItOCBszkc/pc8YSdiKV5E1AzOJ/p32Db50LgwhJFi5b1ANPgyWxB0Q5yn69aMURHyGi3MLyg==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react-test-renderer@^18.0.0":
+  version "18.0.0"
+  resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-18.0.0.tgz#7b7f69ca98821ea5501b21ba24ea7b6139da2243"
+  integrity sha512-C7/5FBJ3g3sqUahguGi03O79b8afNeSD6T8/GU50oQrJCU0bVCCGQHaGKUbg2Ce8VQEEqTw8/HiS6lXHHdgkdQ==
+  dependencies:
+    "@types/react" "*"
+
+"@types/react-textarea-autosize@^8.0.0":
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/@types/react-textarea-autosize/-/react-textarea-autosize-8.0.0.tgz#f68f388552aaa608328b3e352d9a23ad7e0f72e4"
+  integrity sha512-KVqk+/+RMQB3ZDpk7ZTpYHauU3Ue+Y0f09POvGaEpaGb+izzbpoM47tkDGlbF37iT7JYZ8QFwLzqiOPYbQaztA==
+  dependencies:
+    react-textarea-autosize "*"
+
+"@types/react-toggle@^4.0.3":
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/@types/react-toggle/-/react-toggle-4.0.3.tgz#8db98ac8d2c5e8c03c2d3a42027555c1cd2289da"
+  integrity sha512-57QdMWeeQdRjM2/p+udgYerxUbSkmeUIW18kwUttcci6GHkgxoqCsDZfRtsCsAHcvvM5VBQdtDUEgLWo2e87mA==
+  dependencies:
+    "@types/react" "*"
+
 "@types/react-transition-group@^4.4.0":
   version "4.4.3"
   resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.3.tgz"
@@ -1906,6 +2396,28 @@
     "@types/scheduler" "*"
     csstype "^3.0.2"
 
+"@types/react@^16", "@types/react@^16.14.38":
+  version "16.14.38"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.38.tgz#b814d157ca8906603593d5106f6d733af9b79df4"
+  integrity sha512-PbEjuhwkdH6IB5Sak6BFAqpVMHY/wJxa0EG3bKkr0vWA2hSDIq3iEMhHyqjXrDFMqRzkiQkdyNXOnoELrh/9aQ==
+  dependencies:
+    "@types/prop-types" "*"
+    "@types/scheduler" "*"
+    csstype "^3.0.2"
+
+"@types/redux-immutable@^4.0.3":
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/@types/redux-immutable/-/redux-immutable-4.0.3.tgz#db92a281aa9a55a7b63bc1f20a233790305a1f06"
+  integrity sha512-wXUApt9ib9MGUqoHUMbQmQhqCkvykMHBW3z/P7DISMigFGpGRQ0kkbv7we0XNiv5sYEtEiZzNCEDm+W6ei04DA==
+  dependencies:
+    immutable "^4.0.0-rc.1"
+    redux "^4.0.0"
+
+"@types/requestidlecallback@^0.3.5":
+  version "0.3.5"
+  resolved "https://registry.yarnpkg.com/@types/requestidlecallback/-/requestidlecallback-0.3.5.tgz#132529751a4717fe7dc55fef5e930336f229543c"
+  integrity sha512-Uh49VrVTPfU0y/qIvXXYuRmd/sKLfVgQWZU1t8FWH22AIJyQbCei1aSmXdMDAijwGUFhBDpJmksiHEDsfiE/cg==
+
 "@types/resolve@1.17.1":
   version "1.17.1"
   resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
@@ -1923,11 +2435,34 @@
   resolved "https://registry.npmjs.org/@types/schema-utils/-/schema-utils-1.0.0.tgz"
   integrity sha512-YesPanU1+WCigC/Aj1Mga8UCOjHIfMNHZ3zzDsUY7lI8GlKnh/Kv2QwJOQ+jNQ36Ru7IfzSedlG14hppYaN13A==
 
+"@types/semver@^7.3.12":
+  version "7.3.13"
+  resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91"
+  integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==
+
+"@types/serve-static@*":
+  version "1.15.1"
+  resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.1.tgz#86b1753f0be4f9a1bee68d459fcda5be4ea52b5d"
+  integrity sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==
+  dependencies:
+    "@types/mime" "*"
+    "@types/node" "*"
+
+"@types/source-list-map@*":
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
+  integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==
+
 "@types/stack-utils@^2.0.0":
   version "2.0.0"
   resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz"
   integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==
 
+"@types/tapable@^1":
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310"
+  integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==
+
 "@types/testing-library__jest-dom@^5.9.1":
   version "5.9.1"
   resolved "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.1.tgz"
@@ -1935,6 +2470,11 @@
   dependencies:
     "@types/jest" "*"
 
+"@types/throng@^4.0.2":
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/@types/throng/-/throng-4.0.2.tgz#e352f5f86433e9dfbf3258414191852d25b2274f"
+  integrity sha512-7tgh3R6vwtjj01URmhWXFSkWnm4wDJjsqLm8WPwIWadYjfsKAFi0HqabMQCU2JJ4TbeSGkb51qv27bBPN5Bubw==
+
 "@types/tough-cookie@*":
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397"
@@ -1945,11 +2485,44 @@
   resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
   integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
 
+"@types/uglify-js@*":
+  version "3.17.1"
+  resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.1.tgz#e0ffcef756476410e5bce2cb01384ed878a195b5"
+  integrity sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g==
+  dependencies:
+    source-map "^0.6.1"
+
+"@types/uuid@^8.3.4":
+  version "8.3.4"
+  resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
+  integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
+
 "@types/warning@^3.0.0":
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52"
   integrity sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA==
 
+"@types/webpack-sources@*":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b"
+  integrity sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==
+  dependencies:
+    "@types/node" "*"
+    "@types/source-list-map" "*"
+    source-map "^0.7.3"
+
+"@types/webpack@^4.41.33":
+  version "4.41.33"
+  resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.33.tgz#16164845a5be6a306bcbe554a8e67f9cac215ffc"
+  integrity sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g==
+  dependencies:
+    "@types/node" "*"
+    "@types/tapable" "^1"
+    "@types/uglify-js" "*"
+    "@types/webpack-sources" "*"
+    anymatch "^3.0.0"
+    source-map "^0.6.0"
+
 "@types/yargs-parser@*":
   version "15.0.0"
   resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz"
@@ -1969,6 +2542,13 @@
   dependencies:
     "@types/yargs-parser" "*"
 
+"@types/yargs@^17.0.22":
+  version "17.0.22"
+  resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a"
+  integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==
+  dependencies:
+    "@types/yargs-parser" "*"
+
 "@types/yargs@^17.0.8":
   version "17.0.10"
   resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a"
@@ -1976,6 +2556,90 @@
   dependencies:
     "@types/yargs-parser" "*"
 
+"@typescript-eslint/eslint-plugin@^5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.55.0.tgz#bc2400c3a23305e8c9a9c04aa40933868aaaeb47"
+  integrity sha512-IZGc50rtbjk+xp5YQoJvmMPmJEYoC53SiKPXyqWfv15XoD2Y5Kju6zN0DwlmaGJp1Iw33JsWJcQ7nw0lGCGjVg==
+  dependencies:
+    "@eslint-community/regexpp" "^4.4.0"
+    "@typescript-eslint/scope-manager" "5.55.0"
+    "@typescript-eslint/type-utils" "5.55.0"
+    "@typescript-eslint/utils" "5.55.0"
+    debug "^4.3.4"
+    grapheme-splitter "^1.0.4"
+    ignore "^5.2.0"
+    natural-compare-lite "^1.4.0"
+    semver "^7.3.7"
+    tsutils "^3.21.0"
+
+"@typescript-eslint/parser@^5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.55.0.tgz#8c96a0b6529708ace1dcfa60f5e6aec0f5ed2262"
+  integrity sha512-ppvmeF7hvdhUUZWSd2EEWfzcFkjJzgNQzVST22nzg958CR+sphy8A6K7LXQZd6V75m1VKjp+J4g/PCEfSCmzhw==
+  dependencies:
+    "@typescript-eslint/scope-manager" "5.55.0"
+    "@typescript-eslint/types" "5.55.0"
+    "@typescript-eslint/typescript-estree" "5.55.0"
+    debug "^4.3.4"
+
+"@typescript-eslint/scope-manager@5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.55.0.tgz#e863bab4d4183ddce79967fe10ceb6c829791210"
+  integrity sha512-OK+cIO1ZGhJYNCL//a3ROpsd83psf4dUJ4j7pdNVzd5DmIk+ffkuUIX2vcZQbEW/IR41DYsfJTB19tpCboxQuw==
+  dependencies:
+    "@typescript-eslint/types" "5.55.0"
+    "@typescript-eslint/visitor-keys" "5.55.0"
+
+"@typescript-eslint/type-utils@5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.55.0.tgz#74bf0233523f874738677bb73cb58094210e01e9"
+  integrity sha512-ObqxBgHIXj8rBNm0yh8oORFrICcJuZPZTqtAFh0oZQyr5DnAHZWfyw54RwpEEH+fD8suZaI0YxvWu5tYE/WswA==
+  dependencies:
+    "@typescript-eslint/typescript-estree" "5.55.0"
+    "@typescript-eslint/utils" "5.55.0"
+    debug "^4.3.4"
+    tsutils "^3.21.0"
+
+"@typescript-eslint/types@5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.55.0.tgz#9830f8d3bcbecf59d12f821e5bc6960baaed41fd"
+  integrity sha512-M4iRh4AG1ChrOL6Y+mETEKGeDnT7Sparn6fhZ5LtVJF1909D5O4uqK+C5NPbLmpfZ0XIIxCdwzKiijpZUOvOug==
+
+"@typescript-eslint/typescript-estree@5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.55.0.tgz#8db7c8e47ecc03d49b05362b8db6f1345ee7b575"
+  integrity sha512-I7X4A9ovA8gdpWMpr7b1BN9eEbvlEtWhQvpxp/yogt48fy9Lj3iE3ild/1H3jKBBIYj5YYJmS2+9ystVhC7eaQ==
+  dependencies:
+    "@typescript-eslint/types" "5.55.0"
+    "@typescript-eslint/visitor-keys" "5.55.0"
+    debug "^4.3.4"
+    globby "^11.1.0"
+    is-glob "^4.0.3"
+    semver "^7.3.7"
+    tsutils "^3.21.0"
+
+"@typescript-eslint/utils@5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.55.0.tgz#34e97322e7ae5b901e7a870aabb01dad90023341"
+  integrity sha512-FkW+i2pQKcpDC3AY6DU54yl8Lfl14FVGYDgBTyGKB75cCwV3KpkpTMFi9d9j2WAJ4271LR2HeC5SEWF/CZmmfw==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.2.0"
+    "@types/json-schema" "^7.0.9"
+    "@types/semver" "^7.3.12"
+    "@typescript-eslint/scope-manager" "5.55.0"
+    "@typescript-eslint/types" "5.55.0"
+    "@typescript-eslint/typescript-estree" "5.55.0"
+    eslint-scope "^5.1.1"
+    semver "^7.3.7"
+
+"@typescript-eslint/visitor-keys@5.55.0":
+  version "5.55.0"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.55.0.tgz#01ad414fca8367706d76cdb94adf788dc5b664a2"
+  integrity sha512-q2dlHHwWgirKh1D3acnuApXG+VNXpEY5/AwRxDVuEQpxWaB0jCDe0jFMVMALJ3ebSfuOVE8/rMS+9ZOYGg1GWw==
+  dependencies:
+    "@typescript-eslint/types" "5.55.0"
+    eslint-visitor-keys "^3.3.0"
+
 "@webassemblyjs/ast@1.9.0":
   version "1.9.0"
   resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz"
@@ -2164,10 +2828,10 @@ acorn-globals@^7.0.0:
     acorn "^8.1.0"
     acorn-walk "^8.0.2"
 
-acorn-jsx@^5.3.1:
-  version "5.3.1"
-  resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz"
-  integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
+acorn-jsx@^5.3.2:
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+  integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
 
 acorn-walk@^8.0.0, acorn-walk@^8.0.2:
   version "8.2.0"
@@ -2179,15 +2843,10 @@ acorn@^6.4.1:
   resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz"
   integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
 
-acorn@^7.4.0:
-  version "7.4.1"
-  resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
-  integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-
-acorn@^8.0.4, acorn@^8.1.0, acorn@^8.5.0, acorn@^8.8.1:
-  version "8.8.1"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73"
-  integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==
+acorn@^8.0.4, acorn@^8.1.0, acorn@^8.5.0, acorn@^8.8.0, acorn@^8.8.1, acorn@^8.8.2:
+  version "8.8.2"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
+  integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
 
 agent-base@6:
   version "6.0.2"
@@ -2234,21 +2893,11 @@ ajv@^8.0.1, ajv@^8.6.0:
     require-from-string "^2.0.2"
     uri-js "^4.2.2"
 
-alphanum-sort@^1.0.0:
-  version "1.0.2"
-  resolved "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz"
-  integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=
-
 ansi-colors@^3.0.0:
   version "3.2.4"
   resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz"
   integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
 
-ansi-colors@^4.1.1:
-  version "4.1.1"
-  resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz"
-  integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
-
 ansi-escapes@^4.2.1:
   version "4.3.1"
   resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz"
@@ -2256,6 +2905,13 @@ ansi-escapes@^4.2.1:
   dependencies:
     type-fest "^0.11.0"
 
+ansi-escapes@^4.3.0:
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
+  integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
+  dependencies:
+    type-fest "^0.21.3"
+
 ansi-html-community@0.0.8:
   version "0.0.8"
   resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz"
@@ -2276,6 +2932,11 @@ ansi-regex@^5.0.0, ansi-regex@^5.0.1:
   resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz"
   integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
 
+ansi-regex@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
+  integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
+
 ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   version "3.2.1"
   resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz"
@@ -2296,6 +2957,11 @@ ansi-styles@^5.0.0:
   resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz"
   integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
 
+ansi-styles@^6.0.0:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5"
+  integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
+
 anymatch@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz"
@@ -2304,7 +2970,23 @@ anymatch@^2.0.0:
     micromatch "^3.1.4"
     normalize-path "^2.1.1"
 
-anymatch@^3.0.3, anymatch@~3.1.2:
+anymatch@^3.0.0:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
+  integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
+  dependencies:
+    normalize-path "^3.0.0"
+    picomatch "^2.0.4"
+
+anymatch@^3.0.3:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142"
+  integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==
+  dependencies:
+    normalize-path "^3.0.0"
+    picomatch "^2.0.4"
+
+anymatch@~3.1.2:
   version "3.1.2"
   resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz"
   integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
@@ -2312,7 +2994,7 @@ anymatch@^3.0.3, anymatch@~3.1.2:
     normalize-path "^3.0.0"
     picomatch "^2.0.4"
 
-"aproba@^1.0.3 || ^2.0.0", aproba@^1.1.1:
+"aproba@^1.0.3 || ^2.0.0":
   version "1.2.0"
   resolved "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz"
   integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
@@ -2345,10 +3027,12 @@ aria-query@^4.2.2:
     "@babel/runtime" "^7.10.2"
     "@babel/runtime-corejs3" "^7.10.2"
 
-aria-query@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz"
-  integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==
+aria-query@^5.0.0, aria-query@^5.1.3:
+  version "5.1.3"
+  resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e"
+  integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==
+  dependencies:
+    deep-equal "^2.0.5"
 
 arr-diff@^4.0.0:
   version "4.0.0"
@@ -2375,7 +3059,7 @@ array-flatten@^2.1.0:
   resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz"
   integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
 
-array-includes@^3.1.4, array-includes@^3.1.5, array-includes@^3.1.6:
+array-includes@^3.1.5, array-includes@^3.1.6:
   version "3.1.6"
   resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f"
   integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==
@@ -2408,14 +3092,15 @@ array-unique@^0.3.2:
   resolved "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz"
   integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
 
-array.prototype.flat@^1.2.5:
-  version "1.2.5"
-  resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz"
-  integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==
+array.prototype.flat@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2"
+  integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==
   dependencies:
     call-bind "^1.0.2"
-    define-properties "^1.1.3"
-    es-abstract "^1.19.0"
+    define-properties "^1.1.4"
+    es-abstract "^1.20.4"
+    es-shim-unscopables "^1.0.0"
 
 array.prototype.flatmap@^1.3.1:
   version "1.3.1"
@@ -2523,47 +3208,53 @@ atrament@0.2.4:
   resolved "https://registry.npmjs.org/atrament/-/atrament-0.2.4.tgz"
   integrity sha512-hSA9VwW6COMwvRhSEO4uZweZ91YGOdHqwvslNyrJZG+8mzc4qx/qMsDZBuAeXFeWZO/QKtRjIXguOUy1aNMl3A==
 
-autoprefixer@^9.8.8:
-  version "9.8.8"
-  resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.8.tgz"
-  integrity sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==
+autoprefixer@^10.4.14:
+  version "10.4.14"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d"
+  integrity sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==
   dependencies:
-    browserslist "^4.12.0"
-    caniuse-lite "^1.0.30001109"
+    browserslist "^4.21.5"
+    caniuse-lite "^1.0.30001464"
+    fraction.js "^4.2.0"
     normalize-range "^0.1.2"
-    num2fraction "^1.2.2"
-    picocolors "^0.2.1"
-    postcss "^7.0.32"
-    postcss-value-parser "^4.1.0"
+    picocolors "^1.0.0"
+    postcss-value-parser "^4.2.0"
 
-axe-core@^4.4.3:
-  version "4.4.3"
-  resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f"
-  integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w==
+available-typed-arrays@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
+  integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
 
-axios@^1.2.2:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/axios/-/axios-1.2.2.tgz#72681724c6e6a43a9fea860fc558127dbe32f9f1"
-  integrity sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==
+axe-core@^4.6.2:
+  version "4.6.3"
+  resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.6.3.tgz#fc0db6fdb65cc7a80ccf85286d91d64ababa3ece"
+  integrity sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==
+
+axios@^1.3.4:
+  version "1.3.4"
+  resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024"
+  integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==
   dependencies:
     follow-redirects "^1.15.0"
     form-data "^4.0.0"
     proxy-from-env "^1.1.0"
 
-axobject-query@^2.2.0:
-  version "2.2.0"
-  resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz"
-  integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
+axobject-query@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1"
+  integrity sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==
+  dependencies:
+    deep-equal "^2.0.5"
 
-babel-jest@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.3.1.tgz#05c83e0d128cd48c453eea851482a38782249f44"
-  integrity sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA==
+babel-jest@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5"
+  integrity sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==
   dependencies:
-    "@jest/transform" "^29.3.1"
+    "@jest/transform" "^29.5.0"
     "@types/babel__core" "^7.1.14"
     babel-plugin-istanbul "^6.1.1"
-    babel-preset-jest "^29.2.0"
+    babel-preset-jest "^29.5.0"
     chalk "^4.0.0"
     graceful-fs "^4.2.9"
     slash "^3.0.0"
@@ -2589,10 +3280,10 @@ babel-plugin-istanbul@^6.1.1:
     istanbul-lib-instrument "^5.0.4"
     test-exclude "^6.0.0"
 
-babel-plugin-jest-hoist@^29.2.0:
-  version "29.2.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz#23ee99c37390a98cfddf3ef4a78674180d823094"
-  integrity sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA==
+babel-plugin-jest-hoist@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz#a97db437936f441ec196990c9738d4b88538618a"
+  integrity sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==
   dependencies:
     "@babel/template" "^7.3.3"
     "@babel/types" "^7.3.3"
@@ -2698,12 +3389,12 @@ babel-preset-current-node-syntax@^1.0.0:
     "@babel/plugin-syntax-optional-chaining" "^7.8.3"
     "@babel/plugin-syntax-top-level-await" "^7.8.3"
 
-babel-preset-jest@^29.2.0:
-  version "29.2.0"
-  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz#3048bea3a1af222e3505e4a767a974c95a7620dc"
-  integrity sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA==
+babel-preset-jest@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz#57bc8cc88097af7ff6a5ab59d1cd29d52a5916e2"
+  integrity sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==
   dependencies:
-    babel-plugin-jest-hoist "^29.2.0"
+    babel-plugin-jest-hoist "^29.5.0"
     babel-preset-current-node-syntax "^1.0.0"
 
 balanced-match@^1.0.0:
@@ -2761,20 +3452,15 @@ bindings@^1.5.0:
   dependencies:
     file-uri-to-path "1.0.0"
 
-bluebird@^3.5.5:
-  version "3.7.2"
-  resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz"
-  integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
-
 blueimp-load-image@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmjs.org/blueimp-load-image/-/blueimp-load-image-3.0.0.tgz"
   integrity sha512-Q9rFbd4ZUNvzSFmRXx9MoG0RwWwJeMjjEUbG7WIOJgUg22Jgkow0wL5b35B6qwiBscxACW9OHdrP5s2vQ3x8DQ==
 
-blurhash@^2.0.4:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-2.0.4.tgz#60642a823b50acaaf3732ddb6c7dfd721bdfef2a"
-  integrity sha512-r/As72u2FbucLoK5NTegM/GucxJc3d8GvHc4ngo13IO/nt2HU4gONxNLq1XPN6EM/V8Y9URIa7PcSz2RZu553A==
+blurhash@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/blurhash/-/blurhash-2.0.5.tgz#efde729fc14a2f03571a6aa91b49cba80d1abe4b"
+  integrity sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==
 
 bmp-js@^0.1.0:
   version "0.1.0"
@@ -2821,7 +3507,7 @@ bonjour@^3.5.0:
     multicast-dns "^6.0.1"
     multicast-dns-service-types "^1.1.0"
 
-boolbase@^1.0.0, boolbase@~1.0.0:
+boolbase@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz"
   integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
@@ -2930,15 +3616,15 @@ browserify-zlib@^0.2.0:
   dependencies:
     pako "~1.0.5"
 
-browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.21.3, browserslist@^4.21.4:
-  version "4.21.4"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987"
-  integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==
+browserslist@^4.0.0, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5:
+  version "4.21.5"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7"
+  integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==
   dependencies:
-    caniuse-lite "^1.0.30001400"
-    electron-to-chromium "^1.4.251"
-    node-releases "^2.0.6"
-    update-browserslist-db "^1.0.9"
+    caniuse-lite "^1.0.30001449"
+    electron-to-chromium "^1.4.284"
+    node-releases "^2.0.8"
+    update-browserslist-db "^1.0.10"
 
 bser@2.1.1:
   version "2.1.1"
@@ -3011,27 +3697,6 @@ bytes@3.1.2:
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
   integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
 
-cacache@^12.0.2:
-  version "12.0.4"
-  resolved "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz"
-  integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==
-  dependencies:
-    bluebird "^3.5.5"
-    chownr "^1.1.1"
-    figgy-pudding "^3.5.1"
-    glob "^7.1.4"
-    graceful-fs "^4.1.15"
-    infer-owner "^1.0.3"
-    lru-cache "^5.1.1"
-    mississippi "^3.0.0"
-    mkdirp "^0.5.1"
-    move-concurrently "^1.0.1"
-    promise-inflight "^1.0.1"
-    rimraf "^2.6.3"
-    ssri "^6.0.1"
-    unique-filename "^1.1.1"
-    y18n "^4.0.0"
-
 cacache@^15.0.5:
   version "15.0.5"
   resolved "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz"
@@ -3078,25 +3743,6 @@ call-bind@^1.0.0, call-bind@^1.0.2:
     function-bind "^1.1.1"
     get-intrinsic "^1.0.2"
 
-caller-callsite@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz"
-  integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=
-  dependencies:
-    callsites "^2.0.0"
-
-caller-path@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz"
-  integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=
-  dependencies:
-    caller-callsite "^2.0.0"
-
-callsites@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz"
-  integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
-
 callsites@^3.0.0:
   version "3.1.0"
   resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
@@ -3131,12 +3777,17 @@ caniuse-api@^3.0.0:
     lodash.memoize "^4.1.2"
     lodash.uniq "^4.5.0"
 
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001400:
-  version "1.0.30001414"
-  resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001414.tgz"
-  integrity sha512-t55jfSaWjCdocnFdKQoO+d2ct9C59UZg4dY3OnUlSZ447r8pUtIKdp0hpAzrGFultmTC+Us+KpKi4GZl/LXlFg==
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001449, caniuse-lite@^1.0.30001464:
+  version "1.0.30001466"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001466.tgz#c1e6197c540392e09709ecaa9e3e403428c53375"
+  integrity sha512-ewtFBSfWjEmxUgNBSZItFSmVtvk9zkwkl1OfRZlKA8slltRN+/C/tuGVrF9styXkN36Yu3+SeJ1qkXxDEyNZ5w==
 
-chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2:
+chalk@5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3"
+  integrity sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==
+
+chalk@^2.0.0, chalk@^2.3.2, chalk@^2.4.2:
   version "2.4.2"
   resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz"
   integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -3200,11 +3851,6 @@ chokidar@^2.1.8:
   optionalDependencies:
     fsevents "^1.2.7"
 
-chownr@^1.1.1:
-  version "1.1.4"
-  resolved "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz"
-  integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
-
 chownr@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz"
@@ -3253,6 +3899,29 @@ clean-stack@^2.0.0:
   resolved "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz"
   integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
 
+cli-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+  integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+  dependencies:
+    restore-cursor "^3.1.0"
+
+cli-truncate@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
+  integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==
+  dependencies:
+    slice-ansi "^3.0.0"
+    string-width "^4.2.0"
+
+cli-truncate@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389"
+  integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==
+  dependencies:
+    slice-ansi "^5.0.0"
+    string-width "^5.0.0"
+
 cliui@^5.0.0:
   version "5.0.0"
   resolved "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz"
@@ -3280,25 +3949,16 @@ clone-deep@^4.0.1:
     kind-of "^6.0.2"
     shallow-clone "^3.0.0"
 
-cluster-key-slot@1.1.0:
-  version "1.1.0"
-  resolved "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz"
-  integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==
+cluster-key-slot@1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac"
+  integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==
 
 co@^4.6.0:
   version "4.6.0"
   resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz"
   integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
 
-coa@^2.0.2:
-  version "2.0.2"
-  resolved "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz"
-  integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==
-  dependencies:
-    "@types/q" "^1.5.1"
-    chalk "^2.4.1"
-    q "^1.1.2"
-
 cocoon-js-vanilla@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/cocoon-js-vanilla/-/cocoon-js-vanilla-1.3.0.tgz#1e53663f5d314e5e9b315b63eaf8ae701df113c0"
@@ -3317,12 +3977,12 @@ collection-visit@^1.0.0:
     map-visit "^1.0.0"
     object-visit "^1.0.0"
 
-color-blend@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.npmjs.org/color-blend/-/color-blend-3.0.1.tgz"
-  integrity sha512-KueDvNiKHAvVeApic0SxHZLyy4x3NELfTLzMHRpRRLi+9e2kWhpeWvtuH3Sjb92mOJYEUhRjb8z7lr4OqDv17Q==
+color-blend@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/color-blend/-/color-blend-4.0.0.tgz#e9950e9fa5d6e552ff8bb107c39f7e83a0c1a3bb"
+  integrity sha512-fYODTHhI/NG+B5GnzvuL3kiFrK/UnkUezWFTgEPBTY5V+kpyfAn95Vn9sJeeCX6omrCOdxnqCL3CvH+6sXtIbw==
 
-color-convert@^1.9.0, color-convert@^1.9.3:
+color-convert@^1.9.0:
   version "1.9.3"
   resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
   integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
@@ -3336,7 +3996,7 @@ color-convert@^2.0.1:
   dependencies:
     color-name "~1.1.4"
 
-color-name@1.1.3, color-name@^1.0.0:
+color-name@1.1.3:
   version "1.1.3"
   resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
   integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
@@ -3346,32 +4006,21 @@ color-name@~1.1.4:
   resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
 
-color-string@^1.6.0:
-  version "1.9.0"
-  resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.0.tgz"
-  integrity sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ==
-  dependencies:
-    color-name "^1.0.0"
-    simple-swizzle "^0.2.2"
-
 color-support@^1.1.3:
   version "1.1.3"
   resolved "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz"
   integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
 
-color@^3.0.0:
-  version "3.2.1"
-  resolved "https://registry.npmjs.org/color/-/color-3.2.1.tgz"
-  integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==
-  dependencies:
-    color-convert "^1.9.3"
-    color-string "^1.6.0"
-
-colord@^2.9.3:
+colord@^2.9.1, colord@^2.9.3:
   version "2.9.3"
   resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43"
   integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==
 
+colorette@^2.0.19:
+  version "2.0.19"
+  resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
+  integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
+
 colors@^1.4.0:
   version "1.4.0"
   resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz"
@@ -3384,6 +4033,11 @@ combined-stream@^1.0.8:
   dependencies:
     delayed-stream "~1.0.0"
 
+commander@^10.0.0:
+  version "10.0.0"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.0.tgz#71797971162cd3cf65f0b9d24eb28f8d303acdf1"
+  integrity sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==
+
 commander@^2.20.0:
   version "2.20.3"
   resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz"
@@ -3445,16 +4099,6 @@ concat-map@0.0.1:
   resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
   integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
 
-concat-stream@^1.5.0:
-  version "1.6.2"
-  resolved "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz"
-  integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
-  dependencies:
-    buffer-from "^1.0.0"
-    inherits "^2.0.3"
-    readable-stream "^2.2.2"
-    typedarray "^0.0.6"
-
 connect-history-api-fallback@^1.6.0:
   version "1.6.0"
   resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz"
@@ -3509,18 +4153,6 @@ cookie@0.5.0:
   resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
   integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
 
-copy-concurrently@^1.0.0:
-  version "1.0.5"
-  resolved "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz"
-  integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==
-  dependencies:
-    aproba "^1.1.1"
-    fs-write-stream-atomic "^1.0.8"
-    iferr "^0.1.5"
-    mkdirp "^0.5.1"
-    rimraf "^2.5.4"
-    run-queue "^1.0.0"
-
 copy-descriptor@^0.1.0:
   version "0.1.1"
   resolved "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz"
@@ -3548,16 +4180,6 @@ core-util-is@~1.0.0:
   resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz"
   integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
 
-cosmiconfig@^5.0.0:
-  version "5.2.1"
-  resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz"
-  integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==
-  dependencies:
-    import-fresh "^2.0.0"
-    is-directory "^0.3.1"
-    js-yaml "^3.13.1"
-    parse-json "^4.0.0"
-
 cosmiconfig@^6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982"
@@ -3569,7 +4191,7 @@ cosmiconfig@^6.0.0:
     path-type "^4.0.0"
     yaml "^1.7.2"
 
-cosmiconfig@^7.0.0, cosmiconfig@^7.1.0:
+cosmiconfig@^7.0.0:
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.1.0.tgz#1443b9afa596b670082ea46cbd8f6a62b84635f6"
   integrity sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==
@@ -3580,6 +4202,16 @@ cosmiconfig@^7.0.0, cosmiconfig@^7.1.0:
     path-type "^4.0.0"
     yaml "^1.10.0"
 
+cosmiconfig@^8.1.0:
+  version "8.1.3"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.1.3.tgz#0e614a118fcc2d9e5afc2f87d53cd09931015689"
+  integrity sha512-/UkO2JKI18b5jVMJUp0lvKFMpa/Gye+ZgZjKD+DGEN9y7NRcf/nK1A0sp67ONmKtnDCNMS44E6jrk0Yc3bDuUw==
+  dependencies:
+    import-fresh "^3.2.1"
+    js-yaml "^4.1.0"
+    parse-json "^5.0.0"
+    path-type "^4.0.0"
+
 create-ecdh@^4.0.0:
   version "4.0.4"
   resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz"
@@ -3660,18 +4292,10 @@ crypto-random-string@^2.0.0:
   resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
   integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
 
-css-color-names@0.0.4, css-color-names@^0.0.4:
-  version "0.0.4"
-  resolved "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz"
-  integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=
-
-css-declaration-sorter@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz"
-  integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==
-  dependencies:
-    postcss "^7.0.1"
-    timsort "^0.3.0"
+css-declaration-sorter@^6.3.1:
+  version "6.3.1"
+  resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz#be5e1d71b7a992433fb1c542c7a1b835e45682ec"
+  integrity sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==
 
 css-functions-list@^3.1.0:
   version "3.1.0"
@@ -3694,41 +4318,37 @@ css-loader@^5.2.7:
     schema-utils "^3.0.0"
     semver "^7.3.5"
 
-css-select-base-adapter@^0.1.1:
-  version "0.1.1"
-  resolved "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz"
-  integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==
-
-css-select@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz"
-  integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
+css-select@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
+  integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==
   dependencies:
     boolbase "^1.0.0"
-    css-what "^3.2.1"
-    domutils "^1.7.0"
-    nth-check "^1.0.2"
+    css-what "^6.1.0"
+    domhandler "^5.0.2"
+    domutils "^3.0.1"
+    nth-check "^2.0.1"
 
-css-tree@1.0.0-alpha.37:
-  version "1.0.0-alpha.37"
-  resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz"
-  integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
+css-tree@^2.2.1, css-tree@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20"
+  integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==
   dependencies:
-    mdn-data "2.0.4"
-    source-map "^0.6.1"
+    mdn-data "2.0.30"
+    source-map-js "^1.0.1"
 
-css-tree@^1.1.2:
-  version "1.1.3"
-  resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz"
-  integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
+css-tree@~2.2.0:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.2.1.tgz#36115d382d60afd271e377f9c5f67d02bd48c032"
+  integrity sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==
   dependencies:
-    mdn-data "2.0.14"
-    source-map "^0.6.1"
+    mdn-data "2.0.28"
+    source-map-js "^1.0.1"
 
-css-what@^3.2.1:
-  version "3.4.2"
-  resolved "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz"
-  integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
+css-what@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
+  integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
 
 css.escape@^1.5.1:
   version "1.5.1"
@@ -3740,80 +4360,60 @@ cssesc@^3.0.0:
   resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz"
   integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
 
-cssnano-preset-default@^4.0.8:
-  version "4.0.8"
-  resolved "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz"
-  integrity sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==
-  dependencies:
-    css-declaration-sorter "^4.0.1"
-    cssnano-util-raw-cache "^4.0.1"
-    postcss "^7.0.0"
-    postcss-calc "^7.0.1"
-    postcss-colormin "^4.0.3"
-    postcss-convert-values "^4.0.1"
-    postcss-discard-comments "^4.0.2"
-    postcss-discard-duplicates "^4.0.2"
-    postcss-discard-empty "^4.0.1"
-    postcss-discard-overridden "^4.0.1"
-    postcss-merge-longhand "^4.0.11"
-    postcss-merge-rules "^4.0.3"
-    postcss-minify-font-values "^4.0.2"
-    postcss-minify-gradients "^4.0.2"
-    postcss-minify-params "^4.0.2"
-    postcss-minify-selectors "^4.0.2"
-    postcss-normalize-charset "^4.0.1"
-    postcss-normalize-display-values "^4.0.2"
-    postcss-normalize-positions "^4.0.2"
-    postcss-normalize-repeat-style "^4.0.2"
-    postcss-normalize-string "^4.0.2"
-    postcss-normalize-timing-functions "^4.0.2"
-    postcss-normalize-unicode "^4.0.1"
-    postcss-normalize-url "^4.0.1"
-    postcss-normalize-whitespace "^4.0.2"
-    postcss-ordered-values "^4.1.2"
-    postcss-reduce-initial "^4.0.3"
-    postcss-reduce-transforms "^4.0.2"
-    postcss-svgo "^4.0.3"
-    postcss-unique-selectors "^4.0.1"
-
-cssnano-util-get-arguments@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz"
-  integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=
-
-cssnano-util-get-match@^4.0.0:
+cssnano-preset-default@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-6.0.0.tgz#058726536bdc18711c01b1d328766cbc5691cf71"
+  integrity sha512-BDxlaFzObRDXUiCCBQUNQcI+f1/aX2mgoNtXGjV6PG64POcHoDUoX+LgMWw+Q4609QhxwkcSnS65YFs42RA6qQ==
+  dependencies:
+    css-declaration-sorter "^6.3.1"
+    cssnano-utils "^4.0.0"
+    postcss-calc "^8.2.3"
+    postcss-colormin "^6.0.0"
+    postcss-convert-values "^6.0.0"
+    postcss-discard-comments "^6.0.0"
+    postcss-discard-duplicates "^6.0.0"
+    postcss-discard-empty "^6.0.0"
+    postcss-discard-overridden "^6.0.0"
+    postcss-merge-longhand "^6.0.0"
+    postcss-merge-rules "^6.0.0"
+    postcss-minify-font-values "^6.0.0"
+    postcss-minify-gradients "^6.0.0"
+    postcss-minify-params "^6.0.0"
+    postcss-minify-selectors "^6.0.0"
+    postcss-normalize-charset "^6.0.0"
+    postcss-normalize-display-values "^6.0.0"
+    postcss-normalize-positions "^6.0.0"
+    postcss-normalize-repeat-style "^6.0.0"
+    postcss-normalize-string "^6.0.0"
+    postcss-normalize-timing-functions "^6.0.0"
+    postcss-normalize-unicode "^6.0.0"
+    postcss-normalize-url "^6.0.0"
+    postcss-normalize-whitespace "^6.0.0"
+    postcss-ordered-values "^6.0.0"
+    postcss-reduce-initial "^6.0.0"
+    postcss-reduce-transforms "^6.0.0"
+    postcss-svgo "^6.0.0"
+    postcss-unique-selectors "^6.0.0"
+
+cssnano-utils@^4.0.0:
   version "4.0.0"
-  resolved "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz"
-  integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=
+  resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-4.0.0.tgz#d1da885ec04003ab19505ff0e62e029708d36b08"
+  integrity sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==
 
-cssnano-util-raw-cache@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz"
-  integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==
+cssnano@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-6.0.0.tgz#53f4cb81101cccba0809fad779f006b5d44925ee"
+  integrity sha512-RGlcbzGhzEBCHuQe3k+Udyj5M00z0pm9S+VurHXFEOXxH+y0sVrJH2sMzoyz2d8N1EScazg+DVvmgyx0lurwwA==
   dependencies:
-    postcss "^7.0.0"
+    cssnano-preset-default "^6.0.0"
+    lilconfig "^2.1.0"
 
-cssnano-util-same-parent@^4.0.0:
-  version "4.0.1"
-  resolved "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz"
-  integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==
-
-cssnano@^4.1.11:
-  version "4.1.11"
-  resolved "https://registry.npmjs.org/cssnano/-/cssnano-4.1.11.tgz"
-  integrity sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==
+csso@^5.0.5:
+  version "5.0.5"
+  resolved "https://registry.yarnpkg.com/csso/-/csso-5.0.5.tgz#f9b7fe6cc6ac0b7d90781bb16d5e9874303e2ca6"
+  integrity sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==
   dependencies:
-    cosmiconfig "^5.0.0"
-    cssnano-preset-default "^4.0.8"
-    is-resolvable "^1.0.0"
-    postcss "^7.0.0"
-
-csso@^4.0.2:
-  version "4.2.0"
-  resolved "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz"
-  integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
-  dependencies:
-    css-tree "^1.1.2"
+    css-tree "~2.2.0"
 
 cssom@^0.5.0:
   version "0.5.0"
@@ -3832,16 +4432,18 @@ cssstyle@^2.3.0:
   dependencies:
     cssom "~0.3.6"
 
+cssstyle@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a"
+  integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==
+  dependencies:
+    rrweb-cssom "^0.6.0"
+
 csstype@^3.0.2:
   version "3.0.10"
   resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz"
   integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==
 
-cyclist@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz"
-  integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=
-
 d@1, d@^1.0.1:
   version "1.0.1"
   resolved "https://registry.npmjs.org/d/-/d-1.0.1.tgz"
@@ -3864,14 +4466,23 @@ data-urls@^3.0.2:
     whatwg-mimetype "^3.0.0"
     whatwg-url "^11.0.0"
 
-debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
+data-urls@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4"
+  integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==
+  dependencies:
+    abab "^2.0.6"
+    whatwg-mimetype "^3.0.0"
+    whatwg-url "^12.0.0"
+
+debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
   version "2.6.9"
   resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
   integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
   dependencies:
     ms "2.0.0"
 
-debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.4:
+debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
   version "4.3.4"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
   integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -3898,10 +4509,10 @@ decamelize@^1.1.0, decamelize@^1.2.0:
   resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz"
   integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
 
-decimal.js@^10.4.2:
-  version "10.4.2"
-  resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.2.tgz#0341651d1d997d86065a2ce3a441fbd0d8e8b98e"
-  integrity sha512-ic1yEvwT6GuvaYwBLLY6/aFFgjZdySKTE8en/fkU3QICTmRtgtSlFn0u0BXN06InZwtfCelR7j8LRiDI/02iGA==
+decimal.js@^10.4.2, decimal.js@^10.4.3:
+  version "10.4.3"
+  resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
+  integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
 
 decode-uri-component@^0.2.0:
   version "0.2.2"
@@ -3925,6 +4536,29 @@ deep-equal@^1.0.1:
     object-keys "^1.1.1"
     regexp.prototype.flags "^1.2.0"
 
+deep-equal@^2.0.5:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.0.tgz#5caeace9c781028b9ff459f33b779346637c43e6"
+  integrity sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==
+  dependencies:
+    call-bind "^1.0.2"
+    es-get-iterator "^1.1.2"
+    get-intrinsic "^1.1.3"
+    is-arguments "^1.1.1"
+    is-array-buffer "^3.0.1"
+    is-date-object "^1.0.5"
+    is-regex "^1.1.4"
+    is-shared-array-buffer "^1.0.2"
+    isarray "^2.0.5"
+    object-is "^1.1.5"
+    object-keys "^1.1.1"
+    object.assign "^4.1.4"
+    regexp.prototype.flags "^1.4.3"
+    side-channel "^1.0.4"
+    which-boxed-primitive "^1.0.2"
+    which-collection "^1.0.1"
+    which-typed-array "^1.1.9"
+
 deep-is@^0.1.3, deep-is@~0.1.3:
   version "0.1.4"
   resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz"
@@ -4056,10 +4690,10 @@ diff-sequences@^25.2.6:
   resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz"
   integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==
 
-diff-sequences@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e"
-  integrity sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==
+diff-sequences@^29.4.3:
+  version "29.4.3"
+  resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2"
+  integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==
 
 diffie-hellman@^5.0.0:
   version "5.0.3"
@@ -4131,28 +4765,24 @@ dom-helpers@^5.0.1, dom-helpers@^5.2.0:
     "@babel/runtime" "^7.8.7"
     csstype "^3.0.2"
 
-dom-serializer@0:
-  version "0.2.2"
-  resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz"
-  integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
+dom-serializer@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
+  integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
   dependencies:
-    domelementtype "^2.0.1"
-    entities "^2.0.0"
+    domelementtype "^2.3.0"
+    domhandler "^5.0.2"
+    entities "^4.2.0"
 
 domain-browser@^1.1.1:
   version "1.2.0"
   resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz"
   integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==
 
-domelementtype@1:
-  version "1.3.1"
-  resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz"
-  integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
-
-domelementtype@^2.0.1:
-  version "2.2.0"
-  resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz"
-  integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
+domelementtype@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+  integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
 
 domexception@^4.0.0:
   version "4.0.0"
@@ -4161,20 +4791,21 @@ domexception@^4.0.0:
   dependencies:
     webidl-conversions "^7.0.0"
 
-domutils@^1.7.0:
-  version "1.7.0"
-  resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz"
-  integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
+domhandler@^5.0.1, domhandler@^5.0.2:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
+  integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
   dependencies:
-    dom-serializer "0"
-    domelementtype "1"
+    domelementtype "^2.3.0"
 
-dot-prop@^5.2.0:
-  version "5.3.0"
-  resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz"
-  integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==
+domutils@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c"
+  integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==
   dependencies:
-    is-obj "^2.0.0"
+    dom-serializer "^2.0.0"
+    domelementtype "^2.3.0"
+    domhandler "^5.0.1"
 
 dotenv@^16.0.3:
   version "16.0.3"
@@ -4186,15 +4817,10 @@ duplexer@^0.1.2:
   resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz"
   integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
 
-duplexify@^3.4.2, duplexify@^3.6.0:
-  version "3.7.1"
-  resolved "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz"
-  integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==
-  dependencies:
-    end-of-stream "^1.0.0"
-    inherits "^2.0.1"
-    readable-stream "^2.0.0"
-    stream-shift "^1.0.0"
+eastasianwidth@^0.2.0:
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
+  integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
 
 ee-first@1.1.1:
   version "1.1.1"
@@ -4208,10 +4834,10 @@ ejs@^3.1.6:
   dependencies:
     jake "^10.8.5"
 
-electron-to-chromium@^1.4.251:
-  version "1.4.254"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.254.tgz#c6203583890abf88dfc0be046cd72d3b48f8beb6"
-  integrity sha512-Sh/7YsHqQYkA6ZHuHMy24e6TE4eX6KZVsZb9E/DvU1nQRIrH4BflO/4k+83tfdYvDl+MObvlqHPRICzEdC9c6Q==
+electron-to-chromium@^1.4.284:
+  version "1.4.330"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.330.tgz#4740378db7160d7210afb29800c74048cdf10a99"
+  integrity sha512-PqyefhybrVdjAJ45HaPLtuVaehiSw7C3ya0aad+rvmV53IVyXmYRk3pwIOb2TxTDTnmgQdn46NjMMaysx79/6Q==
 
 elliptic@^6.5.3:
   version "6.5.4"
@@ -4265,7 +4891,7 @@ encodeurl@~1.0.2:
   resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz"
   integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
 
-end-of-stream@^1.0.0, end-of-stream@^1.1.0:
+end-of-stream@^1.1.0:
   version "1.4.4"
   resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz"
   integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
@@ -4281,27 +4907,15 @@ enhanced-resolve@^4.1.1, enhanced-resolve@^4.5.0:
     memory-fs "^0.5.0"
     tapable "^1.0.0"
 
-enquirer@^2.3.5:
-  version "2.3.6"
-  resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz"
-  integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==
-  dependencies:
-    ansi-colors "^4.1.1"
-
-entities@^2.0.0:
-  version "2.2.0"
-  resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz"
-  integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
-
-entities@^4.4.0:
+entities@^4.2.0, entities@^4.4.0:
   version "4.4.0"
   resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174"
   integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==
 
-errno@^0.1.3, errno@~0.1.7:
-  version "0.1.8"
-  resolved "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz"
-  integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
+errno@^0.1.3:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
+  integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
   dependencies:
     prr "~1.0.1"
 
@@ -4319,36 +4933,7 @@ error-stack-parser@^2.0.6:
   dependencies:
     stackframe "^1.1.1"
 
-es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.5:
-  version "1.20.0"
-  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.0.tgz#b2d526489cceca004588296334726329e0a6bfb6"
-  integrity sha512-URbD8tgRthKD3YcC39vbvSDrX23upXnPcnGAjQfgxXF5ID75YcENawc9ZX/9iTP9ptUyfCLIxTTuMYoRfiOVKA==
-  dependencies:
-    call-bind "^1.0.2"
-    es-to-primitive "^1.2.1"
-    function-bind "^1.1.1"
-    function.prototype.name "^1.1.5"
-    get-intrinsic "^1.1.1"
-    get-symbol-description "^1.0.0"
-    has "^1.0.3"
-    has-property-descriptors "^1.0.0"
-    has-symbols "^1.0.3"
-    internal-slot "^1.0.3"
-    is-callable "^1.2.4"
-    is-negative-zero "^2.0.2"
-    is-regex "^1.1.4"
-    is-shared-array-buffer "^1.0.2"
-    is-string "^1.0.7"
-    is-weakref "^1.0.2"
-    object-inspect "^1.12.0"
-    object-keys "^1.1.1"
-    object.assign "^4.1.2"
-    regexp.prototype.flags "^1.4.1"
-    string.prototype.trimend "^1.0.5"
-    string.prototype.trimstart "^1.0.5"
-    unbox-primitive "^1.0.2"
-
-es-abstract@^1.20.4:
+es-abstract@^1.19.0, es-abstract@^1.19.5, es-abstract@^1.20.4:
   version "1.20.4"
   resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861"
   integrity sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==
@@ -4378,6 +4963,21 @@ es-abstract@^1.20.4:
     string.prototype.trimstart "^1.0.5"
     unbox-primitive "^1.0.2"
 
+es-get-iterator@^1.1.2:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6"
+  integrity sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==
+  dependencies:
+    call-bind "^1.0.2"
+    get-intrinsic "^1.1.3"
+    has-symbols "^1.0.3"
+    is-arguments "^1.1.1"
+    is-map "^2.0.2"
+    is-set "^2.0.2"
+    is-string "^1.0.7"
+    isarray "^2.0.5"
+    stop-iteration-iterator "^1.0.0"
+
 es-shim-unscopables@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241"
@@ -4457,58 +5057,63 @@ escodegen@^2.0.0:
   optionalDependencies:
     source-map "~0.6.1"
 
-eslint-import-resolver-node@^0.3.6:
-  version "0.3.6"
-  resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz"
-  integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==
+eslint-import-resolver-node@^0.3.7:
+  version "0.3.7"
+  resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7"
+  integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==
   dependencies:
     debug "^3.2.7"
-    resolve "^1.20.0"
+    is-core-module "^2.11.0"
+    resolve "^1.22.1"
 
-eslint-module-utils@^2.7.3:
-  version "2.7.3"
-  resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee"
-  integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==
+eslint-module-utils@^2.7.4:
+  version "2.7.4"
+  resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974"
+  integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==
   dependencies:
     debug "^3.2.7"
-    find-up "^2.1.0"
 
-eslint-plugin-import@~2.26.0:
-  version "2.26.0"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b"
-  integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==
+eslint-plugin-import@~2.27.5:
+  version "2.27.5"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65"
+  integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==
   dependencies:
-    array-includes "^3.1.4"
-    array.prototype.flat "^1.2.5"
-    debug "^2.6.9"
+    array-includes "^3.1.6"
+    array.prototype.flat "^1.3.1"
+    array.prototype.flatmap "^1.3.1"
+    debug "^3.2.7"
     doctrine "^2.1.0"
-    eslint-import-resolver-node "^0.3.6"
-    eslint-module-utils "^2.7.3"
+    eslint-import-resolver-node "^0.3.7"
+    eslint-module-utils "^2.7.4"
     has "^1.0.3"
-    is-core-module "^2.8.1"
+    is-core-module "^2.11.0"
     is-glob "^4.0.3"
     minimatch "^3.1.2"
-    object.values "^1.1.5"
-    resolve "^1.22.0"
+    object.values "^1.1.6"
+    resolve "^1.22.1"
+    semver "^6.3.0"
     tsconfig-paths "^3.14.1"
 
-eslint-plugin-jsx-a11y@~6.6.1:
-  version "6.6.1"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz#93736fc91b83fdc38cc8d115deedfc3091aef1ff"
-  integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==
+eslint-plugin-jsx-a11y@~6.7.1:
+  version "6.7.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz#fca5e02d115f48c9a597a6894d5bcec2f7a76976"
+  integrity sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==
   dependencies:
-    "@babel/runtime" "^7.18.9"
-    aria-query "^4.2.2"
-    array-includes "^3.1.5"
+    "@babel/runtime" "^7.20.7"
+    aria-query "^5.1.3"
+    array-includes "^3.1.6"
+    array.prototype.flatmap "^1.3.1"
     ast-types-flow "^0.0.7"
-    axe-core "^4.4.3"
-    axobject-query "^2.2.0"
+    axe-core "^4.6.2"
+    axobject-query "^3.1.1"
     damerau-levenshtein "^1.0.8"
     emoji-regex "^9.2.2"
     has "^1.0.3"
-    jsx-ast-utils "^3.3.2"
-    language-tags "^1.0.5"
+    jsx-ast-utils "^3.3.3"
+    language-tags "=1.0.5"
     minimatch "^3.1.2"
+    object.entries "^1.1.6"
+    object.fromentries "^2.0.6"
     semver "^6.3.0"
 
 eslint-plugin-promise@~6.1.1:
@@ -4516,10 +5121,10 @@ eslint-plugin-promise@~6.1.1:
   resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816"
   integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==
 
-eslint-plugin-react@~7.31.11:
-  version "7.31.11"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz#011521d2b16dcf95795df688a4770b4eaab364c8"
-  integrity sha512-TTvq5JsT5v56wPa9OYHzsrOlHzKZKjV+aLgS+55NJP/cuzdiQPC7PfYoUjMoxlffKtvijpk7vA/jmuqRb9nohw==
+eslint-plugin-react@~7.32.2:
+  version "7.32.2"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10"
+  integrity sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg==
   dependencies:
     array-includes "^3.1.6"
     array.prototype.flatmap "^1.3.1"
@@ -4533,18 +5138,10 @@ eslint-plugin-react@~7.31.11:
     object.hasown "^1.1.2"
     object.values "^1.1.6"
     prop-types "^15.8.1"
-    resolve "^2.0.0-next.3"
+    resolve "^2.0.0-next.4"
     semver "^6.3.0"
     string.prototype.matchall "^4.0.8"
 
-eslint-scope@5.1.1, eslint-scope@^5.1.1:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
-  integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
-  dependencies:
-    esrecurse "^4.3.0"
-    estraverse "^4.1.1"
-
 eslint-scope@^4.0.3:
   version "4.0.3"
   resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz"
@@ -4553,87 +5150,91 @@ eslint-scope@^4.0.3:
     esrecurse "^4.1.0"
     estraverse "^4.1.1"
 
-eslint-utils@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz"
-  integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
+eslint-scope@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+  integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
   dependencies:
-    eslint-visitor-keys "^1.1.0"
-
-eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
-  version "1.3.0"
-  resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz"
-  integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
-
-eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
-  integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+    esrecurse "^4.3.0"
+    estraverse "^4.1.1"
 
-eslint@^7.32.0:
-  version "7.32.0"
-  resolved "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz"
-  integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==
+eslint-scope@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
+  integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
   dependencies:
-    "@babel/code-frame" "7.12.11"
-    "@eslint/eslintrc" "^0.4.3"
-    "@humanwhocodes/config-array" "^0.5.0"
+    esrecurse "^4.3.0"
+    estraverse "^5.2.0"
+
+eslint-visitor-keys@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
+  integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
+
+eslint@^8.36.0:
+  version "8.36.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf"
+  integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.2.0"
+    "@eslint-community/regexpp" "^4.4.0"
+    "@eslint/eslintrc" "^2.0.1"
+    "@eslint/js" "8.36.0"
+    "@humanwhocodes/config-array" "^0.11.8"
+    "@humanwhocodes/module-importer" "^1.0.1"
+    "@nodelib/fs.walk" "^1.2.8"
     ajv "^6.10.0"
     chalk "^4.0.0"
     cross-spawn "^7.0.2"
-    debug "^4.0.1"
+    debug "^4.3.2"
     doctrine "^3.0.0"
-    enquirer "^2.3.5"
     escape-string-regexp "^4.0.0"
-    eslint-scope "^5.1.1"
-    eslint-utils "^2.1.0"
-    eslint-visitor-keys "^2.0.0"
-    espree "^7.3.1"
-    esquery "^1.4.0"
+    eslint-scope "^7.1.1"
+    eslint-visitor-keys "^3.3.0"
+    espree "^9.5.0"
+    esquery "^1.4.2"
     esutils "^2.0.2"
     fast-deep-equal "^3.1.3"
     file-entry-cache "^6.0.1"
-    functional-red-black-tree "^1.0.1"
-    glob-parent "^5.1.2"
-    globals "^13.6.0"
-    ignore "^4.0.6"
+    find-up "^5.0.0"
+    glob-parent "^6.0.2"
+    globals "^13.19.0"
+    grapheme-splitter "^1.0.4"
+    ignore "^5.2.0"
     import-fresh "^3.0.0"
     imurmurhash "^0.1.4"
     is-glob "^4.0.0"
-    js-yaml "^3.13.1"
+    is-path-inside "^3.0.3"
+    js-sdsl "^4.1.4"
+    js-yaml "^4.1.0"
     json-stable-stringify-without-jsonify "^1.0.1"
     levn "^0.4.1"
     lodash.merge "^4.6.2"
-    minimatch "^3.0.4"
+    minimatch "^3.1.2"
     natural-compare "^1.4.0"
     optionator "^0.9.1"
-    progress "^2.0.0"
-    regexpp "^3.1.0"
-    semver "^7.2.1"
-    strip-ansi "^6.0.0"
+    strip-ansi "^6.0.1"
     strip-json-comments "^3.1.0"
-    table "^6.0.9"
     text-table "^0.2.0"
-    v8-compile-cache "^2.0.3"
 
-espree@^7.3.0, espree@^7.3.1:
-  version "7.3.1"
-  resolved "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz"
-  integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==
+espree@^9.5.0:
+  version "9.5.0"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113"
+  integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw==
   dependencies:
-    acorn "^7.4.0"
-    acorn-jsx "^5.3.1"
-    eslint-visitor-keys "^1.3.0"
+    acorn "^8.8.0"
+    acorn-jsx "^5.3.2"
+    eslint-visitor-keys "^3.3.0"
 
 esprima@^4.0.0, esprima@^4.0.1:
   version "4.0.1"
   resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz"
   integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
 
-esquery@^1.4.0:
-  version "1.4.0"
-  resolved "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz"
-  integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
+esquery@^1.4.2:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
+  integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
   dependencies:
     estraverse "^5.1.0"
 
@@ -4727,6 +5328,21 @@ execa@^5.0.0:
     signal-exit "^3.0.3"
     strip-final-newline "^2.0.0"
 
+execa@^7.0.0:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-7.1.1.tgz#3eb3c83d239488e7b409d48e8813b76bb55c9c43"
+  integrity sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==
+  dependencies:
+    cross-spawn "^7.0.3"
+    get-stream "^6.0.1"
+    human-signals "^4.3.0"
+    is-stream "^3.0.0"
+    merge-stream "^2.0.0"
+    npm-run-path "^5.1.0"
+    onetime "^6.0.0"
+    signal-exit "^3.0.7"
+    strip-final-newline "^3.0.0"
+
 exif-js@^2.3.0:
   version "2.3.0"
   resolved "https://registry.npmjs.org/exif-js/-/exif-js-2.3.0.tgz"
@@ -4757,16 +5373,16 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2:
   dependencies:
     homedir-polyfill "^1.0.1"
 
-expect@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/expect/-/expect-29.3.1.tgz#92877aad3f7deefc2e3f6430dd195b92295554a6"
-  integrity sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA==
+expect@^29.0.0, expect@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/expect/-/expect-29.5.0.tgz#68c0509156cb2a0adb8865d413b137eeaae682f7"
+  integrity sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==
   dependencies:
-    "@jest/expect-utils" "^29.3.1"
-    jest-get-type "^29.2.0"
-    jest-matcher-utils "^29.3.1"
-    jest-message-util "^29.3.1"
-    jest-util "^29.3.1"
+    "@jest/expect-utils" "^29.5.0"
+    jest-get-type "^29.4.3"
+    jest-matcher-utils "^29.5.0"
+    jest-message-util "^29.5.0"
+    jest-util "^29.5.0"
 
 express@^4.17.1, express@^4.18.2:
   version "4.18.2"
@@ -4873,9 +5489,9 @@ fastest-levenshtein@^1.0.16:
   integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
 
 fastq@^1.6.0:
-  version "1.13.0"
-  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
-  integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
+  version "1.11.0"
+  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858"
+  integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==
   dependencies:
     reusify "^1.0.4"
 
@@ -4898,11 +5514,6 @@ fb-watchman@^2.0.0:
   dependencies:
     bser "2.1.1"
 
-figgy-pudding@^3.5.1:
-  version "3.5.2"
-  resolved "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz"
-  integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==
-
 file-entry-cache@^6.0.1:
   version "6.0.1"
   resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz"
@@ -4965,15 +5576,6 @@ finalhandler@1.2.0:
     statuses "2.0.1"
     unpipe "~1.0.0"
 
-find-cache-dir@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz"
-  integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==
-  dependencies:
-    commondir "^1.0.1"
-    make-dir "^2.0.0"
-    pkg-dir "^3.0.0"
-
 find-cache-dir@^3.3.1:
   version "3.3.2"
   resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz"
@@ -4988,13 +5590,6 @@ find-root@^1.1.0:
   resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
   integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
 
-find-up@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz"
-  integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
-  dependencies:
-    locate-path "^2.0.0"
-
 find-up@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz"
@@ -5010,6 +5605,14 @@ find-up@^4.0.0, find-up@^4.1.0:
     locate-path "^5.0.0"
     path-exists "^4.0.0"
 
+find-up@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+  integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+  dependencies:
+    locate-path "^6.0.0"
+    path-exists "^4.0.0"
+
 findup-sync@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz"
@@ -5033,14 +5636,6 @@ flatted@^3.1.0:
   resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.5.tgz"
   integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
 
-flush-write-stream@^1.0.0:
-  version "1.1.1"
-  resolved "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz"
-  integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==
-  dependencies:
-    inherits "^2.0.3"
-    readable-stream "^2.3.6"
-
 follow-redirects@^1.0.0, follow-redirects@^1.15.0:
   version "1.15.2"
   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
@@ -5051,6 +5646,13 @@ font-awesome@^4.7.0:
   resolved "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz"
   integrity sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=
 
+for-each@^0.3.3:
+  version "0.3.3"
+  resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
+  integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==
+  dependencies:
+    is-callable "^1.1.3"
+
 for-in@^1.0.2:
   version "1.0.2"
   resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz"
@@ -5070,6 +5672,11 @@ forwarded@0.2.0:
   resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz"
   integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
 
+fraction.js@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
+  integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==
+
 fragment-cache@^0.2.1:
   version "0.2.1"
   resolved "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz"
@@ -5082,14 +5689,6 @@ fresh@0.5.2:
   resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz"
   integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
 
-from2@^2.1.0:
-  version "2.3.0"
-  resolved "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz"
-  integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=
-  dependencies:
-    inherits "^2.0.1"
-    readable-stream "^2.0.0"
-
 fs-extra@^8.1.0:
   version "8.1.0"
   resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz"
@@ -5116,16 +5715,6 @@ fs-minipass@^2.0.0:
   dependencies:
     minipass "^3.0.0"
 
-fs-write-stream-atomic@^1.0.8:
-  version "1.0.10"
-  resolved "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz"
-  integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=
-  dependencies:
-    graceful-fs "^4.1.2"
-    iferr "^0.1.5"
-    imurmurhash "^0.1.4"
-    readable-stream "1 || 2"
-
 fs.realpath@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
@@ -5159,20 +5748,15 @@ function.prototype.name@^1.1.5:
     es-abstract "^1.19.0"
     functions-have-names "^1.2.2"
 
-functional-red-black-tree@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz"
-  integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
-
 functions-have-names@^1.2.2:
   version "1.2.3"
   resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
   integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
 
-fuzzysort@^1.9.0:
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/fuzzysort/-/fuzzysort-1.9.0.tgz#d36d27949eae22340bb6f7ba30ea6751b92a181c"
-  integrity sha512-MOxCT0qLTwLqmEwc7UtU045RKef7mc8Qz8eR4r2bLNEq9dy/c3ZKMEFp6IEst69otkQdFZ4FfgH2dmZD+ddX1g==
+fuzzysort@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/fuzzysort/-/fuzzysort-2.0.4.tgz#a21d1ce8947eaf2797dc3b7c28c36db9d1165f84"
+  integrity sha512-Api1mJL+Ad7W7vnDZnWq5pGaXJjyencT+iKGia2PlHUcSsSzWwIQ3S1isiMpwpavjYtGd2FzhUIhnnhOULZgDw==
 
 gauge@^5.0.0:
   version "5.0.0"
@@ -5188,10 +5772,10 @@ gauge@^5.0.0:
     strip-ansi "^6.0.1"
     wide-align "^1.1.5"
 
-generic-pool@3.8.2:
-  version "3.8.2"
-  resolved "https://registry.npmjs.org/generic-pool/-/generic-pool-3.8.2.tgz"
-  integrity sha512-nGToKy6p3PAbYQ7p1UlWl6vSPwfwU6TMSWK7TTu+WUY4ZjyZQGniGGt2oNVvyNSpyZYSB43zMXVLcBm08MTMkg==
+generic-pool@3.9.0:
+  version "3.9.0"
+  resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4"
+  integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==
 
 gensync@^1.0.0-beta.2:
   version "1.0.0-beta.2"
@@ -5229,7 +5813,7 @@ get-stream@^4.0.0:
   dependencies:
     pump "^3.0.0"
 
-get-stream@^6.0.0:
+get-stream@^6.0.0, get-stream@^6.0.1:
   version "6.0.1"
   resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz"
   integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
@@ -5262,6 +5846,13 @@ glob-parent@^5.1.2, glob-parent@~5.1.2:
   dependencies:
     is-glob "^4.0.1"
 
+glob-parent@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+  integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+  dependencies:
+    is-glob "^4.0.3"
+
 glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
   version "7.2.0"
   resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz"
@@ -5274,16 +5865,15 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
-glob@^8.0.3:
-  version "8.0.3"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e"
-  integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==
+glob@^9.2.0, glob@^9.3.4:
+  version "9.3.4"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.4.tgz#e75dee24891a80c25cc7ee1dd327e126b98679af"
+  integrity sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==
   dependencies:
     fs.realpath "^1.0.0"
-    inflight "^1.0.4"
-    inherits "2"
-    minimatch "^5.0.1"
-    once "^1.3.0"
+    minimatch "^8.0.2"
+    minipass "^4.2.4"
+    path-scurry "^1.6.1"
 
 global-modules@^1.0.0:
   version "1.0.0"
@@ -5326,10 +5916,10 @@ globals@^11.1.0:
   resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz"
   integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
 
-globals@^13.6.0, globals@^13.9.0:
-  version "13.12.0"
-  resolved "https://registry.npmjs.org/globals/-/globals-13.12.0.tgz"
-  integrity sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==
+globals@^13.19.0:
+  version "13.20.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82"
+  integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==
   dependencies:
     type-fest "^0.20.2"
 
@@ -5361,11 +5951,23 @@ globjoin@^0.1.4:
   resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
   integrity sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==
 
-graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9:
+gopd@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
+  integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
+  dependencies:
+    get-intrinsic "^1.1.3"
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9:
   version "4.2.9"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96"
   integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
 
+grapheme-splitter@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
+  integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
+
 gzip-size@^6.0.0:
   version "6.0.0"
   resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz"
@@ -5410,7 +6012,7 @@ has-property-descriptors@^1.0.0:
   dependencies:
     get-intrinsic "^1.1.1"
 
-has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3:
+has-symbols@^1.0.2, has-symbols@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
   integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
@@ -5458,7 +6060,7 @@ has-values@^1.0.0:
     is-number "^3.0.0"
     kind-of "^4.0.0"
 
-has@^1.0.0, has@^1.0.3:
+has@^1.0.3:
   version "1.0.3"
   resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz"
   integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
@@ -5482,11 +6084,6 @@ hash.js@^1.0.0, hash.js@^1.0.3:
     inherits "^2.0.3"
     minimalistic-assert "^1.0.1"
 
-hex-color-regex@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz"
-  integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
-
 history@^4.10.1, history@^4.7.2:
   version "4.10.1"
   resolved "https://registry.npmjs.org/history/-/history-4.10.1.tgz"
@@ -5549,16 +6146,6 @@ hpack.js@^2.1.6:
     readable-stream "^2.0.1"
     wbuf "^1.1.0"
 
-hsl-regex@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz"
-  integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=
-
-hsla-regex@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz"
-  integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg=
-
 html-encoding-sniffer@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
@@ -5663,6 +6250,16 @@ human-signals@^2.1.0:
   resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz"
   integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
 
+human-signals@^4.3.0:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-4.3.1.tgz#ab7f811e851fca97ffbd2c1fe9a958964de321b2"
+  integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==
+
+husky@^8.0.3:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
+  integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==
+
 iconv-lite@0.4.24:
   version "0.4.24"
   resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz"
@@ -5697,40 +6294,30 @@ ieee754@^1.1.4, ieee754@^1.2.1:
   resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
   integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
 
-iferr@^0.1.5:
-  version "0.1.5"
-  resolved "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz"
-  integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE=
-
-ignore@^4.0.6:
-  version "4.0.6"
-  resolved "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz"
-  integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
-
-ignore@^5.2.0, ignore@^5.2.1:
+ignore@^5.2.0, ignore@^5.2.4:
   version "5.2.4"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
   integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
 
-immutable@^4.0.0, immutable@^4.2.2:
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.2.2.tgz#2da9ff4384a4330c36d4d1bc88e90f9e0b0ccd16"
-  integrity sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==
+immutable@^3.8.2:
+  version "3.8.2"
+  resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
+  integrity sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==
 
-import-cwd@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz"
-  integrity sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=
-  dependencies:
-    import-from "^2.1.0"
+immutable@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23"
+  integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==
 
-import-fresh@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz"
-  integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY=
-  dependencies:
-    caller-path "^2.0.0"
-    resolve-from "^3.0.0"
+immutable@^4.0.0-rc.1:
+  version "4.0.0-rc.12"
+  resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0-rc.12.tgz#ca59a7e4c19ae8d9bf74a97bdf0f6e2f2a5d0217"
+  integrity sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A==
+
+immutable@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.0.tgz#eb1738f14ffb39fd068b1dbe1296117484dd34be"
+  integrity sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==
 
 import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
   version "3.3.0"
@@ -5740,13 +6327,6 @@ import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
     parent-module "^1.0.0"
     resolve-from "^4.0.0"
 
-import-from@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz"
-  integrity sha1-M1238qev/VOqpHHUuAId7ja387E=
-  dependencies:
-    resolve-from "^3.0.0"
-
 import-lazy@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153"
@@ -5788,12 +6368,7 @@ indent-string@^4.0.0:
   resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz"
   integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
 
-indexes-of@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz"
-  integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc=
-
-infer-owner@^1.0.3, infer-owner@^1.0.4:
+infer-owner@^1.0.4:
   version "1.0.4"
   resolved "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz"
   integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==
@@ -5843,12 +6418,21 @@ internal-slot@^1.0.3:
     has "^1.0.3"
     side-channel "^1.0.4"
 
+internal-slot@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.4.tgz#8551e7baf74a7a6ba5f749cfb16aa60722f0d6f3"
+  integrity sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==
+  dependencies:
+    get-intrinsic "^1.1.3"
+    has "^1.0.3"
+    side-channel "^1.0.4"
+
 interpret@^1.4.0:
   version "1.4.0"
   resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz"
   integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
 
-intersection-observer@^0.12.0, intersection-observer@^0.12.2:
+intersection-observer@^0.12.0:
   version "0.12.2"
   resolved "https://registry.yarnpkg.com/intersection-observer/-/intersection-observer-0.12.2.tgz#4a45349cc0cd91916682b1f44c28d7ec737dc375"
   integrity sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==
@@ -5916,11 +6500,6 @@ ipaddr.js@1.9.1, ipaddr.js@^1.9.0:
   resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz"
   integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
 
-is-absolute-url@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz"
-  integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=
-
 is-absolute-url@^3.0.3:
   version "3.0.3"
   resolved "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz"
@@ -5940,24 +6519,28 @@ is-accessor-descriptor@^1.0.0:
   dependencies:
     kind-of "^6.0.0"
 
-is-arguments@^1.0.4:
+is-arguments@^1.0.4, is-arguments@^1.1.1:
   version "1.1.1"
-  resolved "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz"
+  resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
   integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
   dependencies:
     call-bind "^1.0.2"
     has-tostringtag "^1.0.0"
 
+is-array-buffer@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.1.tgz#deb1db4fcae48308d54ef2442706c0393997052a"
+  integrity sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==
+  dependencies:
+    call-bind "^1.0.2"
+    get-intrinsic "^1.1.3"
+    is-typed-array "^1.1.10"
+
 is-arrayish@^0.2.1:
   version "0.2.1"
   resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz"
   integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
 
-is-arrayish@^0.3.1:
-  version "0.3.2"
-  resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz"
-  integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==
-
 is-bigint@^1.0.1:
   version "1.0.4"
   resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz"
@@ -5987,32 +6570,15 @@ is-boolean-object@^1.1.0:
     call-bind "^1.0.2"
     has-tostringtag "^1.0.0"
 
-is-callable@^1.1.4, is-callable@^1.2.7:
+is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
   version "1.2.7"
   resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
   integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
 
-is-callable@^1.2.4:
-  version "1.2.4"
-  resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz"
-  integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==
-
-is-color-stop@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz"
-  integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=
-  dependencies:
-    css-color-names "^0.0.4"
-    hex-color-regex "^1.1.0"
-    hsl-regex "^1.0.0"
-    hsla-regex "^1.0.0"
-    rgb-regex "^1.0.1"
-    rgba-regex "^1.0.0"
-
-is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.8.1:
-  version "2.9.0"
-  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
-  integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
+is-core-module@^2.11.0, is-core-module@^2.5.0, is-core-module@^2.9.0:
+  version "2.11.0"
+  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144"
+  integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==
   dependencies:
     has "^1.0.3"
 
@@ -6030,7 +6596,7 @@ is-data-descriptor@^1.0.0:
   dependencies:
     kind-of "^6.0.0"
 
-is-date-object@^1.0.1:
+is-date-object@^1.0.1, is-date-object@^1.0.5:
   version "1.0.5"
   resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz"
   integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
@@ -6055,11 +6621,6 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2:
     is-data-descriptor "^1.0.0"
     kind-of "^6.0.2"
 
-is-directory@^0.3.1:
-  version "0.3.1"
-  resolved "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz"
-  integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
-
 is-electron@^2.2.0:
   version "2.2.1"
   resolved "https://registry.npmjs.org/is-electron/-/is-electron-2.2.1.tgz"
@@ -6092,6 +6653,11 @@ is-fullwidth-code-point@^3.0.0:
   resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz"
   integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
 
+is-fullwidth-code-point@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz#fae3167c729e7463f8461ce512b080a49268aa88"
+  integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==
+
 is-generator-fn@^2.0.0:
   version "2.1.0"
   resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz"
@@ -6111,19 +6677,16 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
   dependencies:
     is-extglob "^2.1.1"
 
+is-map@^2.0.1, is-map@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127"
+  integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==
+
 is-module@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
   integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
 
-is-nan@^1.3.2:
-  version "1.3.2"
-  resolved "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz"
-  integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
-  dependencies:
-    call-bind "^1.0.0"
-    define-properties "^1.1.3"
-
 is-negative-zero@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
@@ -6153,11 +6716,6 @@ is-obj@^1.0.1:
   resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
   integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
 
-is-obj@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz"
-  integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
-
 is-path-cwd@^2.0.0:
   version "2.2.0"
   resolved "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz"
@@ -6177,6 +6735,11 @@ is-path-inside@^2.1.0:
   dependencies:
     path-is-inside "^1.0.2"
 
+is-path-inside@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
+  integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
+
 is-plain-obj@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
@@ -6212,10 +6775,10 @@ is-regexp@^1.0.0:
   resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
   integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk=
 
-is-resolvable@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz"
-  integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
+is-set@^2.0.1, is-set@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec"
+  integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==
 
 is-shared-array-buffer@^1.0.2:
   version "1.0.2"
@@ -6234,6 +6797,11 @@ is-stream@^2.0.0:
   resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz"
   integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
 
+is-stream@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-3.0.0.tgz#e6bfd7aa6bef69f4f472ce9bb681e3e57b4319ac"
+  integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
+
 is-string@^1.0.5, is-string@^1.0.7:
   version "1.0.7"
   resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz"
@@ -6248,11 +6816,27 @@ is-symbol@^1.0.2, is-symbol@^1.0.3:
   dependencies:
     has-symbols "^1.0.2"
 
+is-typed-array@^1.1.10:
+  version "1.1.10"
+  resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f"
+  integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==
+  dependencies:
+    available-typed-arrays "^1.0.5"
+    call-bind "^1.0.2"
+    for-each "^0.3.3"
+    gopd "^1.0.1"
+    has-tostringtag "^1.0.0"
+
 is-url@^1.2.4:
   version "1.2.4"
   resolved "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz"
   integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
 
+is-weakmap@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2"
+  integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==
+
 is-weakref@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
@@ -6260,6 +6844,14 @@ is-weakref@^1.0.2:
   dependencies:
     call-bind "^1.0.2"
 
+is-weakset@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d"
+  integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==
+  dependencies:
+    call-bind "^1.0.2"
+    get-intrinsic "^1.1.1"
+
 is-windows@^1.0.1, is-windows@^1.0.2:
   version "1.0.2"
   resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz"
@@ -6280,6 +6872,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
   resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
   integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
 
+isarray@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
+  integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
+
 isexe@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
@@ -6349,82 +6946,83 @@ jake@^10.8.5:
     filelist "^1.0.1"
     minimatch "^3.0.4"
 
-jest-changed-files@^29.2.0:
-  version "29.2.0"
-  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.2.0.tgz#b6598daa9803ea6a4dce7968e20ab380ddbee289"
-  integrity sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA==
+jest-changed-files@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.5.0.tgz#e88786dca8bf2aa899ec4af7644e16d9dcf9b23e"
+  integrity sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==
   dependencies:
     execa "^5.0.0"
     p-limit "^3.1.0"
 
-jest-circus@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.3.1.tgz#177d07c5c0beae8ef2937a67de68f1e17bbf1b4a"
-  integrity sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg==
+jest-circus@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.5.0.tgz#b5926989449e75bff0d59944bae083c9d7fb7317"
+  integrity sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==
   dependencies:
-    "@jest/environment" "^29.3.1"
-    "@jest/expect" "^29.3.1"
-    "@jest/test-result" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    "@jest/environment" "^29.5.0"
+    "@jest/expect" "^29.5.0"
+    "@jest/test-result" "^29.5.0"
+    "@jest/types" "^29.5.0"
     "@types/node" "*"
     chalk "^4.0.0"
     co "^4.6.0"
     dedent "^0.7.0"
     is-generator-fn "^2.0.0"
-    jest-each "^29.3.1"
-    jest-matcher-utils "^29.3.1"
-    jest-message-util "^29.3.1"
-    jest-runtime "^29.3.1"
-    jest-snapshot "^29.3.1"
-    jest-util "^29.3.1"
+    jest-each "^29.5.0"
+    jest-matcher-utils "^29.5.0"
+    jest-message-util "^29.5.0"
+    jest-runtime "^29.5.0"
+    jest-snapshot "^29.5.0"
+    jest-util "^29.5.0"
     p-limit "^3.1.0"
-    pretty-format "^29.3.1"
+    pretty-format "^29.5.0"
+    pure-rand "^6.0.0"
     slash "^3.0.0"
     stack-utils "^2.0.3"
 
-jest-cli@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.3.1.tgz#e89dff427db3b1df50cea9a393ebd8640790416d"
-  integrity sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ==
+jest-cli@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.5.0.tgz#b34c20a6d35968f3ee47a7437ff8e53e086b4a67"
+  integrity sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==
   dependencies:
-    "@jest/core" "^29.3.1"
-    "@jest/test-result" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    "@jest/core" "^29.5.0"
+    "@jest/test-result" "^29.5.0"
+    "@jest/types" "^29.5.0"
     chalk "^4.0.0"
     exit "^0.1.2"
     graceful-fs "^4.2.9"
     import-local "^3.0.2"
-    jest-config "^29.3.1"
-    jest-util "^29.3.1"
-    jest-validate "^29.3.1"
+    jest-config "^29.5.0"
+    jest-util "^29.5.0"
+    jest-validate "^29.5.0"
     prompts "^2.0.1"
     yargs "^17.3.1"
 
-jest-config@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.3.1.tgz#0bc3dcb0959ff8662957f1259947aedaefb7f3c6"
-  integrity sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg==
+jest-config@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.5.0.tgz#3cc972faec8c8aaea9ae158c694541b79f3748da"
+  integrity sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==
   dependencies:
     "@babel/core" "^7.11.6"
-    "@jest/test-sequencer" "^29.3.1"
-    "@jest/types" "^29.3.1"
-    babel-jest "^29.3.1"
+    "@jest/test-sequencer" "^29.5.0"
+    "@jest/types" "^29.5.0"
+    babel-jest "^29.5.0"
     chalk "^4.0.0"
     ci-info "^3.2.0"
     deepmerge "^4.2.2"
     glob "^7.1.3"
     graceful-fs "^4.2.9"
-    jest-circus "^29.3.1"
-    jest-environment-node "^29.3.1"
-    jest-get-type "^29.2.0"
-    jest-regex-util "^29.2.0"
-    jest-resolve "^29.3.1"
-    jest-runner "^29.3.1"
-    jest-util "^29.3.1"
-    jest-validate "^29.3.1"
+    jest-circus "^29.5.0"
+    jest-environment-node "^29.5.0"
+    jest-get-type "^29.4.3"
+    jest-regex-util "^29.4.3"
+    jest-resolve "^29.5.0"
+    jest-runner "^29.5.0"
+    jest-util "^29.5.0"
+    jest-validate "^29.5.0"
     micromatch "^4.0.4"
     parse-json "^5.2.0"
-    pretty-format "^29.3.1"
+    pretty-format "^29.5.0"
     slash "^3.0.0"
     strip-json-comments "^3.1.1"
 
@@ -6438,223 +7036,223 @@ jest-diff@^25.2.1:
     jest-get-type "^25.2.6"
     pretty-format "^25.5.0"
 
-jest-diff@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.3.1.tgz#d8215b72fed8f1e647aed2cae6c752a89e757527"
-  integrity sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==
+jest-diff@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63"
+  integrity sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==
   dependencies:
     chalk "^4.0.0"
-    diff-sequences "^29.3.1"
-    jest-get-type "^29.2.0"
-    pretty-format "^29.3.1"
+    diff-sequences "^29.4.3"
+    jest-get-type "^29.4.3"
+    pretty-format "^29.5.0"
 
-jest-docblock@^29.2.0:
-  version "29.2.0"
-  resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.2.0.tgz#307203e20b637d97cee04809efc1d43afc641e82"
-  integrity sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A==
+jest-docblock@^29.4.3:
+  version "29.4.3"
+  resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.4.3.tgz#90505aa89514a1c7dceeac1123df79e414636ea8"
+  integrity sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==
   dependencies:
     detect-newline "^3.0.0"
 
-jest-each@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.3.1.tgz#bc375c8734f1bb96625d83d1ca03ef508379e132"
-  integrity sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA==
+jest-each@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.5.0.tgz#fc6e7014f83eac68e22b7195598de8554c2e5c06"
+  integrity sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==
   dependencies:
-    "@jest/types" "^29.3.1"
+    "@jest/types" "^29.5.0"
     chalk "^4.0.0"
-    jest-get-type "^29.2.0"
-    jest-util "^29.3.1"
-    pretty-format "^29.3.1"
-
-jest-environment-jsdom@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.3.1.tgz#14ca63c3e0ef5c63c5bcb46033e50bc649e3b639"
-  integrity sha512-G46nKgiez2Gy4zvYNhayfMEAFlVHhWfncqvqS6yCd0i+a4NsSUD2WtrKSaYQrYiLQaupHXxCRi8xxVL2M9PbhA==
-  dependencies:
-    "@jest/environment" "^29.3.1"
-    "@jest/fake-timers" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    jest-get-type "^29.4.3"
+    jest-util "^29.5.0"
+    pretty-format "^29.5.0"
+
+jest-environment-jsdom@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.5.0.tgz#cfe86ebaf1453f3297b5ff3470fbe94739c960cb"
+  integrity sha512-/KG8yEK4aN8ak56yFVdqFDzKNHgF4BAymCx2LbPNPsUshUlfAl0eX402Xm1pt+eoG9SLZEUVifqXtX8SK74KCw==
+  dependencies:
+    "@jest/environment" "^29.5.0"
+    "@jest/fake-timers" "^29.5.0"
+    "@jest/types" "^29.5.0"
     "@types/jsdom" "^20.0.0"
     "@types/node" "*"
-    jest-mock "^29.3.1"
-    jest-util "^29.3.1"
+    jest-mock "^29.5.0"
+    jest-util "^29.5.0"
     jsdom "^20.0.0"
 
-jest-environment-node@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.3.1.tgz#5023b32472b3fba91db5c799a0d5624ad4803e74"
-  integrity sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag==
+jest-environment-node@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.5.0.tgz#f17219d0f0cc0e68e0727c58b792c040e332c967"
+  integrity sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==
   dependencies:
-    "@jest/environment" "^29.3.1"
-    "@jest/fake-timers" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    "@jest/environment" "^29.5.0"
+    "@jest/fake-timers" "^29.5.0"
+    "@jest/types" "^29.5.0"
     "@types/node" "*"
-    jest-mock "^29.3.1"
-    jest-util "^29.3.1"
+    jest-mock "^29.5.0"
+    jest-util "^29.5.0"
 
 jest-get-type@^25.2.6:
   version "25.2.6"
   resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz"
   integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==
 
-jest-get-type@^29.2.0:
-  version "29.2.0"
-  resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408"
-  integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==
+jest-get-type@^29.4.3:
+  version "29.4.3"
+  resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5"
+  integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==
 
-jest-haste-map@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.3.1.tgz#af83b4347f1dae5ee8c2fb57368dc0bb3e5af843"
-  integrity sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A==
+jest-haste-map@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.5.0.tgz#69bd67dc9012d6e2723f20a945099e972b2e94de"
+  integrity sha512-IspOPnnBro8YfVYSw6yDRKh/TiCdRngjxeacCps1cQ9cgVN6+10JUcuJ1EabrgYLOATsIAigxA0rLR9x/YlrSA==
   dependencies:
-    "@jest/types" "^29.3.1"
+    "@jest/types" "^29.5.0"
     "@types/graceful-fs" "^4.1.3"
     "@types/node" "*"
     anymatch "^3.0.3"
     fb-watchman "^2.0.0"
     graceful-fs "^4.2.9"
-    jest-regex-util "^29.2.0"
-    jest-util "^29.3.1"
-    jest-worker "^29.3.1"
+    jest-regex-util "^29.4.3"
+    jest-util "^29.5.0"
+    jest-worker "^29.5.0"
     micromatch "^4.0.4"
     walker "^1.0.8"
   optionalDependencies:
     fsevents "^2.3.2"
 
-jest-leak-detector@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz#95336d020170671db0ee166b75cd8ef647265518"
-  integrity sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA==
+jest-leak-detector@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz#cf4bdea9615c72bac4a3a7ba7e7930f9c0610c8c"
+  integrity sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==
   dependencies:
-    jest-get-type "^29.2.0"
-    pretty-format "^29.3.1"
+    jest-get-type "^29.4.3"
+    pretty-format "^29.5.0"
 
-jest-matcher-utils@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz#6e7f53512f80e817dfa148672bd2d5d04914a572"
-  integrity sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ==
+jest-matcher-utils@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz#d957af7f8c0692c5453666705621ad4abc2c59c5"
+  integrity sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==
   dependencies:
     chalk "^4.0.0"
-    jest-diff "^29.3.1"
-    jest-get-type "^29.2.0"
-    pretty-format "^29.3.1"
+    jest-diff "^29.5.0"
+    jest-get-type "^29.4.3"
+    pretty-format "^29.5.0"
 
-jest-message-util@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.3.1.tgz#37bc5c468dfe5120712053dd03faf0f053bd6adb"
-  integrity sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA==
+jest-message-util@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.5.0.tgz#1f776cac3aca332ab8dd2e3b41625435085c900e"
+  integrity sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==
   dependencies:
     "@babel/code-frame" "^7.12.13"
-    "@jest/types" "^29.3.1"
+    "@jest/types" "^29.5.0"
     "@types/stack-utils" "^2.0.0"
     chalk "^4.0.0"
     graceful-fs "^4.2.9"
     micromatch "^4.0.4"
-    pretty-format "^29.3.1"
+    pretty-format "^29.5.0"
     slash "^3.0.0"
     stack-utils "^2.0.3"
 
-jest-mock@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.3.1.tgz#60287d92e5010979d01f218c6b215b688e0f313e"
-  integrity sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA==
+jest-mock@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.5.0.tgz#26e2172bcc71d8b0195081ff1f146ac7e1518aed"
+  integrity sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==
   dependencies:
-    "@jest/types" "^29.3.1"
+    "@jest/types" "^29.5.0"
     "@types/node" "*"
-    jest-util "^29.3.1"
+    jest-util "^29.5.0"
 
 jest-pnp-resolver@^1.2.2:
   version "1.2.2"
   resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz"
   integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==
 
-jest-regex-util@^29.2.0:
-  version "29.2.0"
-  resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.2.0.tgz#82ef3b587e8c303357728d0322d48bbfd2971f7b"
-  integrity sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA==
+jest-regex-util@^29.4.3:
+  version "29.4.3"
+  resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.4.3.tgz#a42616141e0cae052cfa32c169945d00c0aa0bb8"
+  integrity sha512-O4FglZaMmWXbGHSQInfXewIsd1LMn9p3ZXB/6r4FOkyhX2/iP/soMG98jGvk/A3HAN78+5VWcBGO0BJAPRh4kg==
 
-jest-resolve-dependencies@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz#a6a329708a128e68d67c49f38678a4a4a914c3bf"
-  integrity sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA==
+jest-resolve-dependencies@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz#f0ea29955996f49788bf70996052aa98e7befee4"
+  integrity sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==
   dependencies:
-    jest-regex-util "^29.2.0"
-    jest-snapshot "^29.3.1"
+    jest-regex-util "^29.4.3"
+    jest-snapshot "^29.5.0"
 
-jest-resolve@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.3.1.tgz#9a4b6b65387a3141e4a40815535c7f196f1a68a7"
-  integrity sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw==
+jest-resolve@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.5.0.tgz#b053cc95ad1d5f6327f0ac8aae9f98795475ecdc"
+  integrity sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==
   dependencies:
     chalk "^4.0.0"
     graceful-fs "^4.2.9"
-    jest-haste-map "^29.3.1"
+    jest-haste-map "^29.5.0"
     jest-pnp-resolver "^1.2.2"
-    jest-util "^29.3.1"
-    jest-validate "^29.3.1"
+    jest-util "^29.5.0"
+    jest-validate "^29.5.0"
     resolve "^1.20.0"
-    resolve.exports "^1.1.0"
+    resolve.exports "^2.0.0"
     slash "^3.0.0"
 
-jest-runner@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.3.1.tgz#a92a879a47dd096fea46bb1517b0a99418ee9e2d"
-  integrity sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA==
+jest-runner@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.5.0.tgz#6a57c282eb0ef749778d444c1d758c6a7693b6f8"
+  integrity sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==
   dependencies:
-    "@jest/console" "^29.3.1"
-    "@jest/environment" "^29.3.1"
-    "@jest/test-result" "^29.3.1"
-    "@jest/transform" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    "@jest/console" "^29.5.0"
+    "@jest/environment" "^29.5.0"
+    "@jest/test-result" "^29.5.0"
+    "@jest/transform" "^29.5.0"
+    "@jest/types" "^29.5.0"
     "@types/node" "*"
     chalk "^4.0.0"
     emittery "^0.13.1"
     graceful-fs "^4.2.9"
-    jest-docblock "^29.2.0"
-    jest-environment-node "^29.3.1"
-    jest-haste-map "^29.3.1"
-    jest-leak-detector "^29.3.1"
-    jest-message-util "^29.3.1"
-    jest-resolve "^29.3.1"
-    jest-runtime "^29.3.1"
-    jest-util "^29.3.1"
-    jest-watcher "^29.3.1"
-    jest-worker "^29.3.1"
+    jest-docblock "^29.4.3"
+    jest-environment-node "^29.5.0"
+    jest-haste-map "^29.5.0"
+    jest-leak-detector "^29.5.0"
+    jest-message-util "^29.5.0"
+    jest-resolve "^29.5.0"
+    jest-runtime "^29.5.0"
+    jest-util "^29.5.0"
+    jest-watcher "^29.5.0"
+    jest-worker "^29.5.0"
     p-limit "^3.1.0"
     source-map-support "0.5.13"
 
-jest-runtime@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.3.1.tgz#21efccb1a66911d6d8591276a6182f520b86737a"
-  integrity sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A==
-  dependencies:
-    "@jest/environment" "^29.3.1"
-    "@jest/fake-timers" "^29.3.1"
-    "@jest/globals" "^29.3.1"
-    "@jest/source-map" "^29.2.0"
-    "@jest/test-result" "^29.3.1"
-    "@jest/transform" "^29.3.1"
-    "@jest/types" "^29.3.1"
+jest-runtime@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.5.0.tgz#c83f943ee0c1da7eb91fa181b0811ebd59b03420"
+  integrity sha512-1Hr6Hh7bAgXQP+pln3homOiEZtCDZFqwmle7Ew2j8OlbkIu6uE3Y/etJQG8MLQs3Zy90xrp2C0BRrtPHG4zryw==
+  dependencies:
+    "@jest/environment" "^29.5.0"
+    "@jest/fake-timers" "^29.5.0"
+    "@jest/globals" "^29.5.0"
+    "@jest/source-map" "^29.4.3"
+    "@jest/test-result" "^29.5.0"
+    "@jest/transform" "^29.5.0"
+    "@jest/types" "^29.5.0"
     "@types/node" "*"
     chalk "^4.0.0"
     cjs-module-lexer "^1.0.0"
     collect-v8-coverage "^1.0.0"
     glob "^7.1.3"
     graceful-fs "^4.2.9"
-    jest-haste-map "^29.3.1"
-    jest-message-util "^29.3.1"
-    jest-mock "^29.3.1"
-    jest-regex-util "^29.2.0"
-    jest-resolve "^29.3.1"
-    jest-snapshot "^29.3.1"
-    jest-util "^29.3.1"
+    jest-haste-map "^29.5.0"
+    jest-message-util "^29.5.0"
+    jest-mock "^29.5.0"
+    jest-regex-util "^29.4.3"
+    jest-resolve "^29.5.0"
+    jest-snapshot "^29.5.0"
+    jest-util "^29.5.0"
     slash "^3.0.0"
     strip-bom "^4.0.0"
 
-jest-snapshot@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.3.1.tgz#17bcef71a453adc059a18a32ccbd594b8cc4e45e"
-  integrity sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA==
+jest-snapshot@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.5.0.tgz#c9c1ce0331e5b63cd444e2f95a55a73b84b1e8ce"
+  integrity sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==
   dependencies:
     "@babel/core" "^7.11.6"
     "@babel/generator" "^7.7.2"
@@ -6662,61 +7260,60 @@ jest-snapshot@^29.3.1:
     "@babel/plugin-syntax-typescript" "^7.7.2"
     "@babel/traverse" "^7.7.2"
     "@babel/types" "^7.3.3"
-    "@jest/expect-utils" "^29.3.1"
-    "@jest/transform" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    "@jest/expect-utils" "^29.5.0"
+    "@jest/transform" "^29.5.0"
+    "@jest/types" "^29.5.0"
     "@types/babel__traverse" "^7.0.6"
     "@types/prettier" "^2.1.5"
     babel-preset-current-node-syntax "^1.0.0"
     chalk "^4.0.0"
-    expect "^29.3.1"
+    expect "^29.5.0"
     graceful-fs "^4.2.9"
-    jest-diff "^29.3.1"
-    jest-get-type "^29.2.0"
-    jest-haste-map "^29.3.1"
-    jest-matcher-utils "^29.3.1"
-    jest-message-util "^29.3.1"
-    jest-util "^29.3.1"
+    jest-diff "^29.5.0"
+    jest-get-type "^29.4.3"
+    jest-matcher-utils "^29.5.0"
+    jest-message-util "^29.5.0"
+    jest-util "^29.5.0"
     natural-compare "^1.4.0"
-    pretty-format "^29.3.1"
+    pretty-format "^29.5.0"
     semver "^7.3.5"
 
-jest-util@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.3.1.tgz#1dda51e378bbcb7e3bc9d8ab651445591ed373e1"
-  integrity sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ==
+jest-util@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.5.0.tgz#24a4d3d92fc39ce90425311b23c27a6e0ef16b8f"
+  integrity sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==
   dependencies:
-    "@jest/types" "^29.3.1"
+    "@jest/types" "^29.5.0"
     "@types/node" "*"
     chalk "^4.0.0"
     ci-info "^3.2.0"
     graceful-fs "^4.2.9"
     picomatch "^2.2.3"
 
-jest-validate@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.3.1.tgz#d56fefaa2e7d1fde3ecdc973c7f7f8f25eea704a"
-  integrity sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g==
+jest-validate@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.5.0.tgz#8e5a8f36178d40e47138dc00866a5f3bd9916ffc"
+  integrity sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==
   dependencies:
-    "@jest/types" "^29.3.1"
+    "@jest/types" "^29.5.0"
     camelcase "^6.2.0"
     chalk "^4.0.0"
-    jest-get-type "^29.2.0"
+    jest-get-type "^29.4.3"
     leven "^3.1.0"
-    pretty-format "^29.3.1"
+    pretty-format "^29.5.0"
 
-jest-watcher@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.3.1.tgz#3341547e14fe3c0f79f9c3a4c62dbc3fc977fd4a"
-  integrity sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg==
+jest-watcher@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.5.0.tgz#cf7f0f949828ba65ddbbb45c743a382a4d911363"
+  integrity sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==
   dependencies:
-    "@jest/test-result" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    "@jest/test-result" "^29.5.0"
+    "@jest/types" "^29.5.0"
     "@types/node" "*"
     ansi-escapes "^4.2.1"
     chalk "^4.0.0"
     emittery "^0.13.1"
-    jest-util "^29.3.1"
+    jest-util "^29.5.0"
     string-length "^4.0.1"
 
 jest-worker@^26.2.1, jest-worker@^26.5.0:
@@ -6728,25 +7325,25 @@ jest-worker@^26.2.1, jest-worker@^26.5.0:
     merge-stream "^2.0.0"
     supports-color "^7.0.0"
 
-jest-worker@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.3.1.tgz#e9462161017a9bb176380d721cab022661da3d6b"
-  integrity sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw==
+jest-worker@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.5.0.tgz#bdaefb06811bd3384d93f009755014d8acb4615d"
+  integrity sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==
   dependencies:
     "@types/node" "*"
-    jest-util "^29.3.1"
+    jest-util "^29.5.0"
     merge-stream "^2.0.0"
     supports-color "^8.0.0"
 
-jest@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/jest/-/jest-29.3.1.tgz#c130c0d551ae6b5459b8963747fed392ddbde122"
-  integrity sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA==
+jest@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e"
+  integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==
   dependencies:
-    "@jest/core" "^29.3.1"
-    "@jest/types" "^29.3.1"
+    "@jest/core" "^29.5.0"
+    "@jest/types" "^29.5.0"
     import-local "^3.0.2"
-    jest-cli "^29.3.1"
+    jest-cli "^29.5.0"
 
 jpeg-autorotate@^7.1.1:
   version "7.1.1"
@@ -6764,6 +7361,11 @@ jpeg-js@^0.4.2:
   resolved "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.3.tgz"
   integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==
 
+js-sdsl@^4.1.4:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711"
+  integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==
+
 "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz"
@@ -6816,18 +7418,17 @@ jsdom@^20.0.0:
     ws "^8.11.0"
     xml-name-validator "^4.0.0"
 
-jsdom@^21.0.0:
-  version "21.0.0"
-  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-21.0.0.tgz#33e22f2fc44286e50ac853c7b7656c8864a4ea45"
-  integrity sha512-AIw+3ZakSUtDYvhwPwWHiZsUi3zHugpMEKlNPaurviseYoBqo0zBd3zqoUi3LPCNtPFlEP8FiW9MqCZdjb2IYA==
+jsdom@^21.1.1:
+  version "21.1.1"
+  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-21.1.1.tgz#ab796361e3f6c01bcfaeda1fea3c06197ac9d8ae"
+  integrity sha512-Jjgdmw48RKcdAIQyUD1UdBh2ecH7VqwaXPN3ehoZN6MqgVbMn+lRm1aAT1AsdJRAJpwfa4IpwgzySn61h2qu3w==
   dependencies:
     abab "^2.0.6"
-    acorn "^8.8.1"
+    acorn "^8.8.2"
     acorn-globals "^7.0.0"
-    cssom "^0.5.0"
-    cssstyle "^2.3.0"
-    data-urls "^3.0.2"
-    decimal.js "^10.4.2"
+    cssstyle "^3.0.0"
+    data-urls "^4.0.0"
+    decimal.js "^10.4.3"
     domexception "^4.0.0"
     escodegen "^2.0.0"
     form-data "^4.0.0"
@@ -6836,7 +7437,8 @@ jsdom@^21.0.0:
     https-proxy-agent "^5.0.1"
     is-potential-custom-element-name "^1.0.1"
     nwsapi "^2.2.2"
-    parse5 "^7.1.1"
+    parse5 "^7.1.2"
+    rrweb-cssom "^0.6.0"
     saxes "^6.0.0"
     symbol-tree "^3.2.4"
     tough-cookie "^4.1.2"
@@ -6844,8 +7446,8 @@ jsdom@^21.0.0:
     webidl-conversions "^7.0.0"
     whatwg-encoding "^2.0.0"
     whatwg-mimetype "^3.0.0"
-    whatwg-url "^11.0.0"
-    ws "^8.11.0"
+    whatwg-url "^12.0.1"
+    ws "^8.13.0"
     xml-name-validator "^4.0.0"
 
 jsesc@^2.5.1:
@@ -6858,7 +7460,7 @@ jsesc@~0.5.0:
   resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz"
   integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
 
-json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
+json-parse-better-errors@^1.0.2:
   version "1.0.2"
   resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz"
   integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
@@ -6938,13 +7540,13 @@ jsonpointer@^5.0.0:
   resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.0.tgz#f802669a524ec4805fa7389eadbc9921d5dc8072"
   integrity sha512-PNYZIdMjVIvVgDSYKTT63Y+KZ6IZvGRNNWcxwD+GNnUz1MKPfv30J8ueCjdwcN0nDx2SlshgyB7Oy0epAzVRRg==
 
-"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2:
-  version "3.3.2"
-  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz#afe5efe4332cd3515c065072bd4d6b0aa22152bd"
-  integrity sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q==
+"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.3:
+  version "3.3.3"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea"
+  integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==
   dependencies:
     array-includes "^3.1.5"
-    object.assign "^4.1.2"
+    object.assign "^4.1.3"
 
 keycode@^2.1.7:
   version "2.2.1"
@@ -6971,20 +7573,20 @@ klona@^2.0.4:
   resolved "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz"
   integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==
 
-known-css-properties@^0.26.0:
-  version "0.26.0"
-  resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.26.0.tgz#008295115abddc045a9f4ed7e2a84dc8b3a77649"
-  integrity sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==
+known-css-properties@^0.27.0:
+  version "0.27.0"
+  resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.27.0.tgz#82a9358dda5fe7f7bd12b5e7142c0a205393c0c5"
+  integrity sha512-uMCj6+hZYDoffuvAJjFAPz56E9uoowFHmTkqRtRq5WyC5Q6Cu/fTZKNQpX/RbzChBYLLl3lo8CjFZBAZXq9qFg==
 
 language-subtag-registry@~0.3.2:
   version "0.3.21"
   resolved "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz"
   integrity sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==
 
-language-tags@^1.0.5:
+language-tags@=1.0.5:
   version "1.0.5"
-  resolved "https://registry.npmjs.org/language-tags/-/language-tags-1.0.5.tgz"
-  integrity sha1-0yHbxNowuovzAk4ED6XBRmH5GTo=
+  resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a"
+  integrity sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==
   dependencies:
     language-subtag-registry "~0.3.2"
 
@@ -7009,17 +7611,55 @@ levn@~0.3.0:
     prelude-ls "~1.1.2"
     type-check "~0.3.2"
 
+lilconfig@2.1.0, lilconfig@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52"
+  integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==
+
 lines-and-columns@^1.1.6:
   version "1.2.4"
   resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
   integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
 
+lint-staged@^13.1.2:
+  version "13.2.0"
+  resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-13.2.0.tgz#b7abaf79c91cd36d824f17b23a4ce5209206126a"
+  integrity sha512-GbyK5iWinax5Dfw5obm2g2ccUiZXNGtAS4mCbJ0Lv4rq6iEtfBSjOYdcbOtAIFtM114t0vdpViDDetjVTSd8Vw==
+  dependencies:
+    chalk "5.2.0"
+    cli-truncate "^3.1.0"
+    commander "^10.0.0"
+    debug "^4.3.4"
+    execa "^7.0.0"
+    lilconfig "2.1.0"
+    listr2 "^5.0.7"
+    micromatch "^4.0.5"
+    normalize-path "^3.0.0"
+    object-inspect "^1.12.3"
+    pidtree "^0.6.0"
+    string-argv "^0.3.1"
+    yaml "^2.2.1"
+
+listr2@^5.0.7:
+  version "5.0.8"
+  resolved "https://registry.yarnpkg.com/listr2/-/listr2-5.0.8.tgz#a9379ffeb4bd83a68931a65fb223a11510d6ba23"
+  integrity sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==
+  dependencies:
+    cli-truncate "^2.1.0"
+    colorette "^2.0.19"
+    log-update "^4.0.0"
+    p-map "^4.0.0"
+    rfdc "^1.3.0"
+    rxjs "^7.8.0"
+    through "^2.3.8"
+    wrap-ansi "^7.0.0"
+
 loader-runner@^2.4.0:
   version "2.4.0"
   resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz"
   integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
 
-loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0:
+loader-utils@^1.2.3, loader-utils@^1.4.0:
   version "1.4.2"
   resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3"
   integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==
@@ -7037,14 +7677,6 @@ loader-utils@^2.0.0:
     emojis-list "^3.0.0"
     json5 "^2.1.2"
 
-locate-path@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz"
-  integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
-  dependencies:
-    p-locate "^2.0.0"
-    path-exists "^3.0.0"
-
 locate-path@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz"
@@ -7060,6 +7692,13 @@ locate-path@^5.0.0:
   dependencies:
     p-locate "^4.1.0"
 
+locate-path@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+  integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+  dependencies:
+    p-locate "^5.0.0"
+
 lockfile@^1.0:
   version "1.0.4"
   resolved "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz"
@@ -7132,6 +7771,16 @@ lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17
   resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
 
+log-update@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1"
+  integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==
+  dependencies:
+    ansi-escapes "^4.3.0"
+    cli-cursor "^3.1.0"
+    slice-ansi "^4.0.0"
+    wrap-ansi "^6.2.0"
+
 loglevel@^1.6.8:
   version "1.8.0"
   resolved "https://registry.npmjs.org/loglevel/-/loglevel-1.8.0.tgz"
@@ -7158,6 +7807,11 @@ lru-cache@^6.0.0:
   dependencies:
     yallist "^4.0.0"
 
+lru-cache@^7.14.1:
+  version "7.18.3"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
+  integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
+
 lz-string@^1.4.4:
   version "1.4.4"
   resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz"
@@ -7170,14 +7824,6 @@ magic-string@^0.25.0, magic-string@^0.25.7:
   dependencies:
     sourcemap-codec "^1.4.8"
 
-make-dir@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz"
-  integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
-  dependencies:
-    pify "^4.0.1"
-    semver "^5.6.0"
-
 make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
   version "3.1.0"
   resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz"
@@ -7238,15 +7884,15 @@ md5.js@^1.3.4:
     inherits "^2.0.1"
     safe-buffer "^5.1.2"
 
-mdn-data@2.0.14:
-  version "2.0.14"
-  resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz"
-  integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
+mdn-data@2.0.28:
+  version "2.0.28"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba"
+  integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==
 
-mdn-data@2.0.4:
-  version "2.0.4"
-  resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz"
-  integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
+mdn-data@2.0.30:
+  version "2.0.30"
+  resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
+  integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==
 
 media-typer@0.3.0:
   version "0.3.0"
@@ -7374,6 +8020,11 @@ mimic-fn@^2.1.0:
   resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz"
   integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
 
+mimic-fn@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
+  integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
+
 min-indent@^1.0.0:
   version "1.0.1"
   resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz"
@@ -7398,7 +8049,7 @@ minimalistic-crypto-utils@^1.0.1:
   resolved "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz"
   integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
 
-minimatch@^3.0.4, minimatch@^3.1.2:
+minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
   integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -7412,6 +8063,13 @@ minimatch@^5.0.1:
   dependencies:
     brace-expansion "^2.0.1"
 
+minimatch@^8.0.2:
+  version "8.0.3"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.3.tgz#0415cb9bb0c1d8ac758c8a673eb1d288e13f5e75"
+  integrity sha512-tEEvU9TkZgnFDCtpnrEYnPsjT7iUx42aXfs4bzmQ5sMA09/6hZY0jeZcGkXyDagiBOvkUjNo8Viom+Me6+2x7g==
+  dependencies:
+    brace-expansion "^2.0.1"
+
 minimist-options@4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
@@ -7454,6 +8112,11 @@ minipass@^3.0.0, minipass@^3.1.1:
   dependencies:
     yallist "^4.0.0"
 
+minipass@^4.0.2, minipass@^4.2.4:
+  version "4.2.5"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb"
+  integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==
+
 minizlib@^2.1.1:
   version "2.1.2"
   resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz"
@@ -7462,22 +8125,6 @@ minizlib@^2.1.1:
     minipass "^3.0.0"
     yallist "^4.0.0"
 
-mississippi@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz"
-  integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==
-  dependencies:
-    concat-stream "^1.5.0"
-    duplexify "^3.4.2"
-    end-of-stream "^1.1.0"
-    flush-write-stream "^1.0.0"
-    from2 "^2.1.0"
-    parallel-transform "^1.1.0"
-    pump "^3.0.0"
-    pumpify "^1.3.3"
-    stream-each "^1.1.0"
-    through2 "^2.0.0"
-
 mixin-deep@^1.2.0:
   version "1.3.2"
   resolved "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz"
@@ -7486,7 +8133,7 @@ mixin-deep@^1.2.0:
     for-in "^1.0.2"
     is-extendable "^1.0.1"
 
-mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
+mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5:
   version "0.5.5"
   resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz"
   integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@@ -7498,23 +8145,16 @@ mkdirp@^1.0, mkdirp@^1.0.3, mkdirp@^1.0.4:
   resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz"
   integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
 
+mkdirp@^2.1.6:
+  version "2.1.6"
+  resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19"
+  integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==
+
 mousetrap@^1.5.2:
   version "1.6.5"
   resolved "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz"
   integrity sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==
 
-move-concurrently@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz"
-  integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=
-  dependencies:
-    aproba "^1.1.1"
-    copy-concurrently "^1.0.0"
-    fs-write-stream-atomic "^1.0.8"
-    mkdirp "^0.5.1"
-    rimraf "^2.5.4"
-    run-queue "^1.0.3"
-
 mrmime@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmjs.org/mrmime/-/mrmime-1.0.0.tgz"
@@ -7575,6 +8215,11 @@ nanomatch@^1.2.9:
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
+natural-compare-lite@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4"
+  integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==
+
 natural-compare@^1.4.0:
   version "1.4.0"
   resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
@@ -7651,10 +8296,10 @@ node-libs-browser@^2.2.1:
     util "^0.11.0"
     vm-browserify "^1.0.1"
 
-node-releases@^2.0.6:
-  version "2.0.6"
-  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
-  integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
+node-releases@^2.0.8:
+  version "2.0.10"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.10.tgz#c311ebae3b6a148c89b1813fd7c4d3c024ef537f"
+  integrity sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==
 
 normalize-package-data@^2.5.0:
   version "2.5.0"
@@ -7693,11 +8338,6 @@ normalize-range@^0.1.2:
   resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz"
   integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
 
-normalize-url@^3.0.0:
-  version "3.3.0"
-  resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz"
-  integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
-
 npm-run-path@^2.0.0:
   version "2.0.2"
   resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz"
@@ -7712,6 +8352,13 @@ npm-run-path@^4.0.1:
   dependencies:
     path-key "^3.0.0"
 
+npm-run-path@^5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-5.1.0.tgz#bc62f7f3f6952d9894bd08944ba011a6ee7b7e00"
+  integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==
+  dependencies:
+    path-key "^4.0.0"
+
 npmlog@^7.0.1:
   version "7.0.1"
   resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-7.0.1.tgz#7372151a01ccb095c47d8bf1d0771a4ff1f53ac8"
@@ -7722,17 +8369,12 @@ npmlog@^7.0.1:
     gauge "^5.0.0"
     set-blocking "^2.0.0"
 
-nth-check@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz"
-  integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
+nth-check@^2.0.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+  integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
   dependencies:
-    boolbase "~1.0.0"
-
-num2fraction@^1.2.2:
-  version "1.2.2"
-  resolved "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz"
-  integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
+    boolbase "^1.0.0"
 
 nwsapi@^2.2.2:
   version "2.2.2"
@@ -7753,14 +8395,19 @@ object-copy@^0.1.0:
     define-property "^0.2.5"
     kind-of "^3.0.3"
 
-object-inspect@^1.12.0, object-inspect@^1.12.2, object-inspect@^1.9.0:
+object-inspect@^1.12.2, object-inspect@^1.9.0:
   version "1.12.2"
   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
   integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
 
-object-is@^1.0.1:
+object-inspect@^1.12.3:
+  version "1.12.3"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9"
+  integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==
+
+object-is@^1.0.1, object-is@^1.1.5:
   version "1.1.5"
-  resolved "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz"
+  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
   integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==
   dependencies:
     call-bind "^1.0.2"
@@ -7778,7 +8425,7 @@ object-visit@^1.0.0:
   dependencies:
     isobject "^3.0.0"
 
-object.assign@^4.1.2, object.assign@^4.1.4:
+object.assign@^4.1.3, object.assign@^4.1.4:
   version "4.1.4"
   resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
   integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==
@@ -7806,15 +8453,6 @@ object.fromentries@^2.0.6:
     define-properties "^1.1.4"
     es-abstract "^1.20.4"
 
-object.getownpropertydescriptors@^2.1.0:
-  version "2.1.3"
-  resolved "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz"
-  integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==
-  dependencies:
-    call-bind "^1.0.2"
-    define-properties "^1.1.3"
-    es-abstract "^1.19.1"
-
 object.hasown@^1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92"
@@ -7830,7 +8468,7 @@ object.pick@^1.3.0:
   dependencies:
     isobject "^3.0.1"
 
-object.values@^1.1.0, object.values@^1.1.5, object.values@^1.1.6:
+object.values@^1.1.6:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d"
   integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==
@@ -7863,13 +8501,20 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
   dependencies:
     wrappy "1"
 
-onetime@^5.1.2:
+onetime@^5.1.0, onetime@^5.1.2:
   version "5.1.2"
   resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz"
   integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
   dependencies:
     mimic-fn "^2.1.0"
 
+onetime@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-6.0.0.tgz#7c24c18ed1fd2e9bca4bd26806a33613c77d34b4"
+  integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==
+  dependencies:
+    mimic-fn "^4.0.0"
+
 opencollective-postinstall@^2.0.2:
   version "2.0.3"
   resolved "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz"
@@ -7928,13 +8573,6 @@ p-finally@^1.0.0:
   resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz"
   integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
 
-p-limit@^1.1.0:
-  version "1.3.0"
-  resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz"
-  integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
-  dependencies:
-    p-try "^1.0.0"
-
 p-limit@^2.0.0, p-limit@^2.2.0:
   version "2.3.0"
   resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz"
@@ -7949,13 +8587,6 @@ p-limit@^3.0.2, p-limit@^3.1.0:
   dependencies:
     yocto-queue "^0.1.0"
 
-p-locate@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz"
-  integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
-  dependencies:
-    p-limit "^1.1.0"
-
 p-locate@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz"
@@ -7970,6 +8601,13 @@ p-locate@^4.1.0:
   dependencies:
     p-limit "^2.2.0"
 
+p-locate@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+  integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+  dependencies:
+    p-limit "^3.0.2"
+
 p-map@^2.0.0:
   version "2.1.0"
   resolved "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz"
@@ -7989,11 +8627,6 @@ p-retry@^3.0.1:
   dependencies:
     retry "^0.12.0"
 
-p-try@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz"
-  integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
-
 p-try@^2.0.0:
   version "2.2.0"
   resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz"
@@ -8009,15 +8642,6 @@ pako@~1.0.5:
   resolved "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz"
   integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
 
-parallel-transform@^1.1.0:
-  version "1.2.0"
-  resolved "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz"
-  integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==
-  dependencies:
-    cyclist "^1.0.1"
-    inherits "^2.0.3"
-    readable-stream "^2.1.5"
-
 parent-module@^1.0.0:
   version "1.0.1"
   resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
@@ -8036,14 +8660,6 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5:
     pbkdf2 "^3.0.3"
     safe-buffer "^5.1.1"
 
-parse-json@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz"
-  integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=
-  dependencies:
-    error-ex "^1.3.1"
-    json-parse-better-errors "^1.0.1"
-
 parse-json@^5.0.0, parse-json@^5.2.0:
   version "5.2.0"
   resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz"
@@ -8059,10 +8675,10 @@ parse-passwd@^1.0.0:
   resolved "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz"
   integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
 
-parse5@^7.0.0, parse5@^7.1.1:
-  version "7.1.1"
-  resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.1.tgz#4649f940ccfb95d8754f37f73078ea20afe0c746"
-  integrity sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==
+parse5@^7.0.0, parse5@^7.1.1, parse5@^7.1.2:
+  version "7.1.2"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32"
+  integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==
   dependencies:
     entities "^4.4.0"
 
@@ -8121,11 +8737,24 @@ path-key@^3.0.0, path-key@^3.1.0:
   resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz"
   integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
 
-path-parse@^1.0.6, path-parse@^1.0.7:
+path-key@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-4.0.0.tgz#295588dc3aee64154f877adb9d780b81c554bf18"
+  integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==
+
+path-parse@^1.0.7:
   version "1.0.7"
   resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
   integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
 
+path-scurry@^1.6.1:
+  version "1.6.1"
+  resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.1.tgz#dab45f7bb1d3f45a0e271ab258999f4ab7e23132"
+  integrity sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA==
+  dependencies:
+    lru-cache "^7.14.1"
+    minipass "^4.0.2"
+
 path-to-regexp@0.1.7:
   version "0.1.7"
   resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz"
@@ -8166,7 +8795,7 @@ performance-now@^2.1.0:
 
 pg-connection-string@^2.5.0:
   version "2.5.0"
-  resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz"
+  resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.5.0.tgz#538cadd0f7e603fc09a12590f3b8a452c2c0cf34"
   integrity sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==
 
 pg-int8@1.0.1:
@@ -8179,12 +8808,17 @@ pg-pool@^3.4.1:
   resolved "https://registry.npmjs.org/pg-pool/-/pg-pool-3.4.1.tgz"
   integrity sha512-TVHxR/gf3MeJRvchgNHxsYsTCHQ+4wm3VIHSS19z8NC0+gioEhq1okDY1sm/TYbfoP6JLFx01s0ShvZ3puP/iQ==
 
+pg-protocol@*:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.6.0.tgz#4c91613c0315349363af2084608db843502f8833"
+  integrity sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==
+
 pg-protocol@^1.5.0:
   version "1.5.0"
   resolved "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz"
   integrity sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==
 
-pg-types@^2.1.0:
+pg-types@^2.1.0, pg-types@^2.2.0:
   version "2.2.0"
   resolved "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz"
   integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==
@@ -8215,11 +8849,6 @@ pgpass@1.x:
   dependencies:
     split2 "^4.1.0"
 
-picocolors@^0.2.1:
-  version "0.2.1"
-  resolved "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz"
-  integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==
-
 picocolors@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
@@ -8230,6 +8859,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatc
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
   integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
 
+pidtree@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.6.0.tgz#90ad7b6d42d5841e69e0a2419ef38f8883aa057c"
+  integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==
+
 piexifjs@^1.0.6:
   version "1.0.6"
   resolved "https://registry.npmjs.org/piexifjs/-/piexifjs-1.0.6.tgz"
@@ -8290,146 +8924,117 @@ posix-character-classes@^0.1.0:
   resolved "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz"
   integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
 
-postcss-calc@^7.0.1:
-  version "7.0.5"
-  resolved "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz"
-  integrity sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==
+postcss-calc@^8.2.3:
+  version "8.2.4"
+  resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5"
+  integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==
   dependencies:
-    postcss "^7.0.27"
-    postcss-selector-parser "^6.0.2"
-    postcss-value-parser "^4.0.2"
-
-postcss-colormin@^4.0.3:
-  version "4.0.3"
-  resolved "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz"
-  integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==
-  dependencies:
-    browserslist "^4.0.0"
-    color "^3.0.0"
-    has "^1.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    postcss-selector-parser "^6.0.9"
+    postcss-value-parser "^4.2.0"
 
-postcss-convert-values@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz"
-  integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==
+postcss-colormin@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-6.0.0.tgz#d4250652e952e1c0aca70c66942da93d3cdeaafe"
+  integrity sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==
   dependencies:
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    browserslist "^4.21.4"
+    caniuse-api "^3.0.0"
+    colord "^2.9.1"
+    postcss-value-parser "^4.2.0"
 
-postcss-discard-comments@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz"
-  integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==
+postcss-convert-values@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-6.0.0.tgz#ec94a954957e5c3f78f0e8f65dfcda95280b8996"
+  integrity sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==
   dependencies:
-    postcss "^7.0.0"
+    browserslist "^4.21.4"
+    postcss-value-parser "^4.2.0"
 
-postcss-discard-duplicates@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz"
-  integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==
-  dependencies:
-    postcss "^7.0.0"
+postcss-discard-comments@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-6.0.0.tgz#9ca335e8b68919f301b24ba47dde226a42e535fe"
+  integrity sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==
 
-postcss-discard-empty@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz"
-  integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==
-  dependencies:
-    postcss "^7.0.0"
+postcss-discard-duplicates@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.0.tgz#c26177a6c33070922e67e9a92c0fd23d443d1355"
+  integrity sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==
 
-postcss-discard-overridden@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz"
-  integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==
-  dependencies:
-    postcss "^7.0.0"
+postcss-discard-empty@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-6.0.0.tgz#06c1c4fce09e22d2a99e667c8550eb8a3a1b9aee"
+  integrity sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==
 
-postcss-load-config@^2.0.0:
-  version "2.1.2"
-  resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz"
-  integrity sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==
-  dependencies:
-    cosmiconfig "^5.0.0"
-    import-cwd "^2.0.0"
+postcss-discard-overridden@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-6.0.0.tgz#49c5262db14e975e349692d9024442de7cd8e234"
+  integrity sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==
 
-postcss-loader@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz"
-  integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==
+postcss-loader@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.3.0.tgz#2c4de9657cd4f07af5ab42bd60a673004da1b8cc"
+  integrity sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==
   dependencies:
-    loader-utils "^1.1.0"
-    postcss "^7.0.0"
-    postcss-load-config "^2.0.0"
-    schema-utils "^1.0.0"
+    cosmiconfig "^7.0.0"
+    klona "^2.0.4"
+    loader-utils "^2.0.0"
+    schema-utils "^3.0.0"
+    semver "^7.3.4"
 
 postcss-media-query-parser@^0.2.3:
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244"
   integrity sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=
 
-postcss-merge-longhand@^4.0.11:
-  version "4.0.11"
-  resolved "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz"
-  integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==
+postcss-merge-longhand@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-6.0.0.tgz#6f627b27db939bce316eaa97e22400267e798d69"
+  integrity sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==
   dependencies:
-    css-color-names "0.0.4"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
-    stylehacks "^4.0.0"
+    postcss-value-parser "^4.2.0"
+    stylehacks "^6.0.0"
 
-postcss-merge-rules@^4.0.3:
-  version "4.0.3"
-  resolved "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz"
-  integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==
+postcss-merge-rules@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-6.0.0.tgz#0d95bc73541156b8b4e763bd0de2c3f9d0ecf013"
+  integrity sha512-rCXkklftzEkniyv3f4mRCQzxD6oE4Quyh61uyWTUbCJ26Pv2hoz+fivJSsSBWxDBeScR4fKCfF3HHTcD7Ybqnw==
   dependencies:
-    browserslist "^4.0.0"
+    browserslist "^4.21.4"
     caniuse-api "^3.0.0"
-    cssnano-util-same-parent "^4.0.0"
-    postcss "^7.0.0"
-    postcss-selector-parser "^3.0.0"
-    vendors "^1.0.0"
+    cssnano-utils "^4.0.0"
+    postcss-selector-parser "^6.0.5"
 
-postcss-minify-font-values@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz"
-  integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==
+postcss-minify-font-values@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-6.0.0.tgz#68d4a028f9fa5f61701974724b2cc9445d8e6070"
+  integrity sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==
   dependencies:
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    postcss-value-parser "^4.2.0"
 
-postcss-minify-gradients@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz"
-  integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==
+postcss-minify-gradients@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-6.0.0.tgz#22b5c88cc63091dadbad34e31ff958404d51d679"
+  integrity sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==
   dependencies:
-    cssnano-util-get-arguments "^4.0.0"
-    is-color-stop "^1.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    colord "^2.9.1"
+    cssnano-utils "^4.0.0"
+    postcss-value-parser "^4.2.0"
 
-postcss-minify-params@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz"
-  integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==
+postcss-minify-params@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-6.0.0.tgz#2b3a85a9e3b990d7a16866f430f5fd1d5961b539"
+  integrity sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==
   dependencies:
-    alphanum-sort "^1.0.0"
-    browserslist "^4.0.0"
-    cssnano-util-get-arguments "^4.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
-    uniqs "^2.0.0"
+    browserslist "^4.21.4"
+    cssnano-utils "^4.0.0"
+    postcss-value-parser "^4.2.0"
 
-postcss-minify-selectors@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz"
-  integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==
+postcss-minify-selectors@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-6.0.0.tgz#5046c5e8680a586e5a0cad52cc9aa36d6be5bda2"
+  integrity sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==
   dependencies:
-    alphanum-sort "^1.0.0"
-    has "^1.0.0"
-    postcss "^7.0.0"
-    postcss-selector-parser "^3.0.0"
+    postcss-selector-parser "^6.0.5"
 
 postcss-modules-extract-imports@^3.0.0:
   version "3.0.0"
@@ -8459,115 +9064,90 @@ postcss-modules-values@^4.0.0:
   dependencies:
     icss-utils "^5.0.0"
 
-postcss-normalize-charset@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz"
-  integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==
-  dependencies:
-    postcss "^7.0.0"
+postcss-normalize-charset@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-6.0.0.tgz#36cc12457259064969fb96f84df491652a4b0975"
+  integrity sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==
 
-postcss-normalize-display-values@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz"
-  integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==
+postcss-normalize-display-values@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.0.tgz#8d2961415078644d8c6bbbdaf9a2fdd60f546cd4"
+  integrity sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==
   dependencies:
-    cssnano-util-get-match "^4.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    postcss-value-parser "^4.2.0"
 
-postcss-normalize-positions@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz"
-  integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==
+postcss-normalize-positions@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-6.0.0.tgz#25b96df99a69f8925f730eaee0be74416865e301"
+  integrity sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==
   dependencies:
-    cssnano-util-get-arguments "^4.0.0"
-    has "^1.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    postcss-value-parser "^4.2.0"
 
-postcss-normalize-repeat-style@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz"
-  integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==
+postcss-normalize-repeat-style@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.0.tgz#ddf30ad8762feb5b1eb97f39f251acd7b8353299"
+  integrity sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==
   dependencies:
-    cssnano-util-get-arguments "^4.0.0"
-    cssnano-util-get-match "^4.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    postcss-value-parser "^4.2.0"
 
-postcss-normalize-string@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz"
-  integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==
+postcss-normalize-string@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-6.0.0.tgz#948282647a51e409d69dde7910f0ac2ff97cb5d8"
+  integrity sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==
   dependencies:
-    has "^1.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    postcss-value-parser "^4.2.0"
 
-postcss-normalize-timing-functions@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz"
-  integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==
+postcss-normalize-timing-functions@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.0.tgz#5f13e650b8c43351989fc5de694525cc2539841c"
+  integrity sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==
   dependencies:
-    cssnano-util-get-match "^4.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    postcss-value-parser "^4.2.0"
 
-postcss-normalize-unicode@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz"
-  integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==
+postcss-normalize-unicode@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-6.0.0.tgz#741b3310f874616bdcf07764f5503695d3604730"
+  integrity sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==
   dependencies:
-    browserslist "^4.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    browserslist "^4.21.4"
+    postcss-value-parser "^4.2.0"
 
-postcss-normalize-url@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz"
-  integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==
+postcss-normalize-url@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-6.0.0.tgz#d0a31e962a16401fb7deb7754b397a323fb650b4"
+  integrity sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==
   dependencies:
-    is-absolute-url "^2.0.0"
-    normalize-url "^3.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    postcss-value-parser "^4.2.0"
 
-postcss-normalize-whitespace@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz"
-  integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==
+postcss-normalize-whitespace@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.0.tgz#accb961caa42e25ca4179b60855b79b1f7129d4d"
+  integrity sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==
   dependencies:
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    postcss-value-parser "^4.2.0"
 
-postcss-ordered-values@^4.1.2:
-  version "4.1.2"
-  resolved "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz"
-  integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==
+postcss-ordered-values@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-6.0.0.tgz#374704cdff25560d44061d17ba3c6308837a3218"
+  integrity sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==
   dependencies:
-    cssnano-util-get-arguments "^4.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    cssnano-utils "^4.0.0"
+    postcss-value-parser "^4.2.0"
 
-postcss-reduce-initial@^4.0.3:
-  version "4.0.3"
-  resolved "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz"
-  integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==
+postcss-reduce-initial@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-6.0.0.tgz#7d16e83e60e27e2fa42f56ec0b426f1da332eca7"
+  integrity sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==
   dependencies:
-    browserslist "^4.0.0"
+    browserslist "^4.21.4"
     caniuse-api "^3.0.0"
-    has "^1.0.0"
-    postcss "^7.0.0"
 
-postcss-reduce-transforms@^4.0.2:
-  version "4.0.2"
-  resolved "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz"
-  integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==
+postcss-reduce-transforms@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.0.tgz#28ff2601a6d9b96a2f039b3501526e1f4d584a46"
+  integrity sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==
   dependencies:
-    cssnano-util-get-match "^4.0.0"
-    has "^1.0.0"
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
+    postcss-value-parser "^4.2.0"
 
 postcss-resolve-nested-selector@^0.1.1:
   version "0.1.1"
@@ -8584,16 +9164,7 @@ postcss-scss@^4.0.2, postcss-scss@^4.0.6:
   resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-4.0.6.tgz#5d62a574b950a6ae12f2aa89b60d63d9e4432bfd"
   integrity sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==
 
-postcss-selector-parser@^3.0.0:
-  version "3.1.2"
-  resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz"
-  integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==
-  dependencies:
-    dot-prop "^5.2.0"
-    indexes-of "^1.0.1"
-    uniq "^1.0.1"
-
-postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.6:
+postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9:
   version "6.0.11"
   resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz#2e41dc39b7ad74046e1615185185cd0b17d0c8dc"
   integrity sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==
@@ -8601,43 +9172,27 @@ postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.2, postcss-selecto
     cssesc "^3.0.0"
     util-deprecate "^1.0.2"
 
-postcss-svgo@^4.0.3:
-  version "4.0.3"
-  resolved "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.3.tgz"
-  integrity sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==
+postcss-svgo@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-6.0.0.tgz#7b18742d38d4505a0455bbe70d52b49f00eaf69d"
+  integrity sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==
   dependencies:
-    postcss "^7.0.0"
-    postcss-value-parser "^3.0.0"
-    svgo "^1.0.0"
+    postcss-value-parser "^4.2.0"
+    svgo "^3.0.2"
 
-postcss-unique-selectors@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz"
-  integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==
+postcss-unique-selectors@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-6.0.0.tgz#c94e9b0f7bffb1203894e42294b5a1b3fb34fbe1"
+  integrity sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==
   dependencies:
-    alphanum-sort "^1.0.0"
-    postcss "^7.0.0"
-    uniqs "^2.0.0"
-
-postcss-value-parser@^3.0.0:
-  version "3.3.1"
-  resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz"
-  integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
+    postcss-selector-parser "^6.0.5"
 
-postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
+postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
   version "4.2.0"
   resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
   integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
 
-postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.27, postcss@^7.0.32:
-  version "7.0.39"
-  resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz"
-  integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==
-  dependencies:
-    picocolors "^0.2.1"
-    source-map "^0.6.1"
-
-postcss@^8.2.15, postcss@^8.4.19, postcss@^8.4.21:
+postcss@^8.2.15, postcss@^8.4.21:
   version "8.4.21"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.21.tgz#c639b719a57efc3187b13a1d765675485f4134f4"
   integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==
@@ -8678,10 +9233,10 @@ prelude-ls@~1.1.2:
   resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz"
   integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
 
-prettier@^2.8.2:
-  version "2.8.2"
-  resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.2.tgz#c4ea1b5b454d7c4b59966db2e06ed7eec5dfd160"
-  integrity sha512-BtRV9BcncDyI2tsuS19zzhzoxD8Dh8LiCx7j7tHzrkz8GFXAexeWFdi22mjE1d16dftH2qNaytVxqiRTGlMfpw==
+prettier@^2.8.7:
+  version "2.8.7"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450"
+  integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==
 
 pretty-bytes@^5.3.0, pretty-bytes@^5.4.1:
   version "5.6.0"
@@ -8708,12 +9263,12 @@ pretty-format@^27.0.2:
     ansi-styles "^5.0.0"
     react-is "^17.0.1"
 
-pretty-format@^29.3.1:
-  version "29.3.1"
-  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.3.1.tgz#1841cac822b02b4da8971dacb03e8a871b4722da"
-  integrity sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==
+pretty-format@^29.0.0, pretty-format@^29.5.0:
+  version "29.5.0"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a"
+  integrity sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==
   dependencies:
-    "@jest/schemas" "^29.0.0"
+    "@jest/schemas" "^29.4.3"
     ansi-styles "^5.0.0"
     react-is "^18.0.0"
 
@@ -8727,11 +9282,6 @@ process@^0.11.10:
   resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz"
   integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
 
-progress@^2.0.0:
-  version "2.0.3"
-  resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz"
-  integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
-
 promise-inflight@^1.0.1:
   version "1.0.1"
   resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz"
@@ -8798,14 +9348,6 @@ public-encrypt@^4.0.0:
     randombytes "^2.0.1"
     safe-buffer "^5.1.2"
 
-pump@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz"
-  integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==
-  dependencies:
-    end-of-stream "^1.1.0"
-    once "^1.3.1"
-
 pump@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz"
@@ -8814,15 +9356,6 @@ pump@^3.0.0:
     end-of-stream "^1.1.0"
     once "^1.3.1"
 
-pumpify@^1.3.3:
-  version "1.5.1"
-  resolved "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz"
-  integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==
-  dependencies:
-    duplexify "^3.6.0"
-    inherits "^2.0.3"
-    pump "^2.0.0"
-
 punycode@1.3.2:
   version "1.3.2"
   resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz"
@@ -8833,15 +9366,15 @@ punycode@1.4.1, punycode@^1.2.4:
   resolved "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz"
   integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
 
-punycode@^2.1.0, punycode@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz"
-  integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
+  integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
 
-q@^1.1.2:
-  version "1.5.1"
-  resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz"
-  integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
+pure-rand@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.0.tgz#701996ceefa253507923a0e864c17ab421c04a7c"
+  integrity sha512-rLSBxJjP+4DQOgcJAx6RZHT2he2pkhQdSnofG5VWyVl6GRq/K02ISOuOLcsMOrtKDIJb8JN2zm3FFzWNbezdPw==
 
 qs@6.11.0:
   version "6.11.0"
@@ -9026,7 +9559,7 @@ react-notification@^6.8.5:
   dependencies:
     prop-types "^15.6.2"
 
-react-overlays@^5.2.1:
+react-overlays@*, react-overlays@^5.2.1:
   version "5.2.1"
   resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-5.2.1.tgz#49dc007321adb6784e1f212403f0fb37a74ab86b"
   integrity sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==
@@ -9093,10 +9626,10 @@ react-router@^4.3.1:
     prop-types "^15.6.1"
     warning "^4.0.1"
 
-react-select@^5.7.0:
-  version "5.7.0"
-  resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.7.0.tgz#82921b38f1fcf1471a0b62304da01f2896cd8ce6"
-  integrity sha512-lJGiMxCa3cqnUr2Jjtg9YHsaytiZqeNOKeibv6WF5zbK/fPegZ1hg3y/9P1RZVLhqBTs0PfqQLKuAACednYGhQ==
+react-select@*, react-select@^5.7.2:
+  version "5.7.2"
+  resolved "https://registry.yarnpkg.com/react-select/-/react-select-5.7.2.tgz#ccd40071b9429277983bf15526e7a5773a060e09"
+  integrity sha512-cTlJkQ8YjV6T/js8wW0owTzht0hHGABh29vjLscY4HfZGkv7hc3FFTmRp9NzY/Ib1uQ36GieAKEjxpHdpCFpcA==
   dependencies:
     "@babel/runtime" "^7.12.0"
     "@emotion/cache" "^11.4.0"
@@ -9161,12 +9694,12 @@ react-test-renderer@^16.14.0:
     react-is "^16.8.6"
     scheduler "^0.19.1"
 
-react-textarea-autosize@^8.4.0:
-  version "8.4.0"
-  resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.4.0.tgz#4d0244d6a50caa897806b8c44abc0540a69bfc8c"
-  integrity sha512-YrTFaEHLgJsi8sJVYHBzYn+mkP3prGkmP2DKb/tm0t7CLJY5t1Rxix8070LAKb0wby7bl/lf2EeHkuMihMZMwQ==
+react-textarea-autosize@*, react-textarea-autosize@^8.4.1:
+  version "8.4.1"
+  resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.4.1.tgz#bcfc5462727014b808b14ee916c01e275e8a8335"
+  integrity sha512-aD2C+qK6QypknC+lCMzteOdIjoMbNlgSFmJjCV+DrfTPwp59i/it9mMNf2HDzvRjQgKAyBDPyLJhcrzElf2U4Q==
   dependencies:
-    "@babel/runtime" "^7.10.2"
+    "@babel/runtime" "^7.20.13"
     use-composed-ref "^1.3.0"
     use-latest "^1.2.1"
 
@@ -9215,7 +9748,7 @@ read-pkg@^5.2.0:
     parse-json "^5.0.0"
     type-fest "^0.6.0"
 
-"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
+readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.3.3, readable-stream@^2.3.6:
   version "2.3.7"
   resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz"
   integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -9271,29 +9804,17 @@ redent@^3.0.0:
     indent-string "^4.0.0"
     strip-indent "^3.0.0"
 
-redis-errors@^1.0.0:
-  version "1.2.0"
-  resolved "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz"
-  integrity sha1-62LSrbFeTq9GEMBK/hUpOEJQq60=
-
-redis-parser@3.0.0:
-  version "3.0.0"
-  resolved "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz"
-  integrity sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=
+redis@^4.6.5:
+  version "4.6.5"
+  resolved "https://registry.yarnpkg.com/redis/-/redis-4.6.5.tgz#f32fbde44429e96f562bb0c9b1db0143ab8cfa4f"
+  integrity sha512-O0OWA36gDQbswOdUuAhRL6mTZpHFN525HlgZgDaVNgCJIAZR3ya06NTESb0R+TUZ+BFaDpz6NnnVvoMx9meUFg==
   dependencies:
-    redis-errors "^1.0.0"
-
-"redis@^4.0.6 <4.1.0":
-  version "4.0.6"
-  resolved "https://registry.yarnpkg.com/redis/-/redis-4.0.6.tgz#a2ded4d9f4f4bad148e54781051618fc684cd858"
-  integrity sha512-IaPAxgF5dV0jx+A9l6yd6R9/PAChZIoAskDVRzUODeLDNhsMlq7OLLTmu0AwAr0xjrJ1bibW5xdpRwqIQ8Q0Xg==
-  dependencies:
-    "@node-redis/bloom" "1.0.1"
-    "@node-redis/client" "1.0.5"
-    "@node-redis/graph" "1.0.0"
-    "@node-redis/json" "1.0.2"
-    "@node-redis/search" "1.0.5"
-    "@node-redis/time-series" "1.0.2"
+    "@redis/bloom" "1.2.0"
+    "@redis/client" "1.5.6"
+    "@redis/graph" "1.1.0"
+    "@redis/json" "1.0.4"
+    "@redis/search" "1.1.2"
+    "@redis/time-series" "1.0.4"
 
 redux-immutable@^4.0.0:
   version "4.0.0"
@@ -9305,10 +9826,10 @@ redux-thunk@^2.4.2:
   resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b"
   integrity sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==
 
-redux@^4.0.0, redux@^4.2.0:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.0.tgz#46f10d6e29b6666df758780437651eeb2b969f13"
-  integrity sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==
+redux@^4.0.0, redux@^4.2.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
+  integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
   dependencies:
     "@babel/runtime" "^7.9.2"
 
@@ -9349,7 +9870,7 @@ regex-not@^1.0.0, regex-not@^1.0.2:
     extend-shallow "^3.0.2"
     safe-regex "^1.1.0"
 
-regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3:
+regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3:
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac"
   integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==
@@ -9358,11 +9879,6 @@ regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.1, regexp.prototype.f
     define-properties "^1.1.3"
     functions-have-names "^1.2.2"
 
-regexpp@^3.1.0:
-  version "3.2.0"
-  resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz"
-  integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
-
 regexpu-core@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.1.0.tgz#2f8504c3fd0ebe11215783a41541e21c79942c6d"
@@ -9484,27 +10000,36 @@ resolve-url@^0.2.1:
   resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz"
   integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
 
-resolve.exports@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz"
-  integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==
+resolve.exports@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e"
+  integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg==
 
-resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0:
-  version "1.22.0"
-  resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.0.tgz"
-  integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
+resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1:
+  version "1.22.1"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
+  integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
   dependencies:
-    is-core-module "^2.8.1"
+    is-core-module "^2.9.0"
     path-parse "^1.0.7"
     supports-preserve-symlinks-flag "^1.0.0"
 
-resolve@^2.0.0-next.3:
-  version "2.0.0-next.3"
-  resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz"
-  integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==
+resolve@^2.0.0-next.4:
+  version "2.0.0-next.4"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660"
+  integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==
   dependencies:
-    is-core-module "^2.2.0"
-    path-parse "^1.0.6"
+    is-core-module "^2.9.0"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
+restore-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+  integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+  dependencies:
+    onetime "^5.1.0"
+    signal-exit "^3.0.2"
 
 ret@~0.1.10:
   version "0.1.15"
@@ -9521,17 +10046,12 @@ reusify@^1.0.4:
   resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
   integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
 
-rgb-regex@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz"
-  integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE=
-
-rgba-regex@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz"
-  integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
+rfdc@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
+  integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
 
-rimraf@^2.5.4, rimraf@^2.6.3:
+rimraf@^2.6.3:
   version "2.7.1"
   resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz"
   integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
@@ -9545,6 +10065,13 @@ rimraf@^3.0.2:
   dependencies:
     glob "^7.1.3"
 
+rimraf@^4.4.1:
+  version "4.4.1"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.1.tgz#bd33364f67021c5b79e93d7f4fa0568c7c21b755"
+  integrity sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==
+  dependencies:
+    glob "^9.2.0"
+
 ripemd160@^2.0.0, ripemd160@^2.0.1:
   version "2.0.2"
   resolved "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz"
@@ -9570,6 +10097,11 @@ rollup@^2.43.1:
   optionalDependencies:
     fsevents "~2.3.2"
 
+rrweb-cssom@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1"
+  integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==
+
 run-parallel@^1.1.9:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
@@ -9577,12 +10109,12 @@ run-parallel@^1.1.9:
   dependencies:
     queue-microtask "^1.2.2"
 
-run-queue@^1.0.0, run-queue@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz"
-  integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=
+rxjs@^7.8.0:
+  version "7.8.0"
+  resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4"
+  integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==
   dependencies:
-    aproba "^1.1.1"
+    tslib "^2.1.0"
 
 safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
   version "5.1.2"
@@ -9626,20 +10158,15 @@ sass-loader@^10.2.0:
     schema-utils "^3.0.0"
     semver "^7.3.2"
 
-sass@^1.57.1:
-  version "1.57.1"
-  resolved "https://registry.yarnpkg.com/sass/-/sass-1.57.1.tgz#dfafd46eb3ab94817145e8825208ecf7281119b5"
-  integrity sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==
+sass@^1.60.0:
+  version "1.60.0"
+  resolved "https://registry.yarnpkg.com/sass/-/sass-1.60.0.tgz#657f0c23a302ac494b09a5ba8497b739fb5b5a81"
+  integrity sha512-updbwW6fNb5gGm8qMXzVO7V4sWf7LMXnMly/JEyfbfERbVH46Fn6q02BX7/eHTdKpE7d+oTkMMQpFWNUMfFbgQ==
   dependencies:
     chokidar ">=3.0.0 <4.0.0"
     immutable "^4.0.0"
     source-map-js ">=0.6.2 <2.0.0"
 
-sax@~1.2.4:
-  version "1.2.4"
-  resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz"
-  integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
-
 saxes@^6.0.0:
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5"
@@ -9702,7 +10229,7 @@ selfsigned@^1.10.8:
   dependencies:
     node-forge "^0.10.0"
 
-"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0:
+"semver@2 || 3 || 4 || 5", semver@^5.5.0:
   version "5.7.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
   integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@@ -9712,13 +10239,20 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
   resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz"
   integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
 
-semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
+semver@^7.3.2, semver@^7.3.4, semver@^7.3.5:
   version "7.3.7"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
   integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
   dependencies:
     lru-cache "^6.0.0"
 
+semver@^7.3.7:
+  version "7.3.8"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
+  integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
+  dependencies:
+    lru-cache "^6.0.0"
+
 send@0.18.0:
   version "0.18.0"
   resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
@@ -9863,13 +10397,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
   integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
 
-simple-swizzle@^0.2.2:
-  version "0.2.2"
-  resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz"
-  integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=
-  dependencies:
-    is-arrayish "^0.3.1"
-
 sirv@^1.0.7:
   version "1.0.19"
   resolved "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz"
@@ -9889,6 +10416,15 @@ slash@^3.0.0:
   resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
   integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
 
+slice-ansi@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
+  integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==
+  dependencies:
+    ansi-styles "^4.0.0"
+    astral-regex "^2.0.0"
+    is-fullwidth-code-point "^3.0.0"
+
 slice-ansi@^4.0.0:
   version "4.0.0"
   resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz"
@@ -9898,6 +10434,14 @@ slice-ansi@^4.0.0:
     astral-regex "^2.0.0"
     is-fullwidth-code-point "^3.0.0"
 
+slice-ansi@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a"
+  integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==
+  dependencies:
+    ansi-styles "^6.0.0"
+    is-fullwidth-code-point "^4.0.0"
+
 snapdragon-node@^2.0.1:
   version "2.1.1"
   resolved "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz"
@@ -9954,7 +10498,7 @@ source-list-map@^2.0.0:
   resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz"
   integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
 
-"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2:
+"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2:
   version "1.0.2"
   resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
   integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
@@ -9978,7 +10522,7 @@ source-map-support@0.5.13:
     buffer-from "^1.0.0"
     source-map "^0.6.0"
 
-source-map-support@~0.5.12, source-map-support@~0.5.20:
+source-map-support@~0.5.20:
   version "0.5.21"
   resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz"
   integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
@@ -10006,6 +10550,11 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
   resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 
+source-map@^0.7.3:
+  version "0.7.4"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
+  integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
+
 source-map@^0.8.0-beta.0, source-map@~0.8.0-beta.0:
   version "0.8.0-beta.0"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11"
@@ -10084,13 +10633,6 @@ sprintf-js@~1.0.2:
   resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
   integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
 
-ssri@^6.0.1:
-  version "6.0.2"
-  resolved "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz"
-  integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==
-  dependencies:
-    figgy-pudding "^3.5.1"
-
 ssri@^8.0.0:
   version "8.0.1"
   resolved "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz"
@@ -10098,11 +10640,6 @@ ssri@^8.0.0:
   dependencies:
     minipass "^3.1.1"
 
-stable@^0.1.8:
-  version "0.1.8"
-  resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz"
-  integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
-
 stack-generator@^2.0.5:
   version "2.0.5"
   resolved "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.5.tgz"
@@ -10157,6 +10694,13 @@ statuses@2.0.1:
   resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz"
   integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
 
+stop-iteration-iterator@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4"
+  integrity sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==
+  dependencies:
+    internal-slot "^1.0.4"
+
 stream-browserify@^2.0.1:
   version "2.0.2"
   resolved "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz"
@@ -10165,14 +10709,6 @@ stream-browserify@^2.0.1:
     inherits "~2.0.1"
     readable-stream "^2.0.2"
 
-stream-each@^1.1.0:
-  version "1.2.3"
-  resolved "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz"
-  integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==
-  dependencies:
-    end-of-stream "^1.1.0"
-    stream-shift "^1.0.0"
-
 stream-http@^2.7.2:
   version "2.8.3"
   resolved "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz"
@@ -10184,10 +10720,10 @@ stream-http@^2.7.2:
     to-arraybuffer "^1.0.0"
     xtend "^4.0.0"
 
-stream-shift@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz"
-  integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
+string-argv@^0.3.1:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
+  integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==
 
 string-length@^4.0.1:
   version "4.0.2"
@@ -10215,6 +10751,15 @@ string-width@^3.0.0, string-width@^3.1.0:
     is-fullwidth-code-point "^2.0.0"
     strip-ansi "^5.1.0"
 
+string-width@^5.0.0:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794"
+  integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==
+  dependencies:
+    eastasianwidth "^0.2.0"
+    emoji-regex "^9.2.2"
+    strip-ansi "^7.0.1"
+
 string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.8:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3"
@@ -10298,6 +10843,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
   dependencies:
     ansi-regex "^5.0.1"
 
+strip-ansi@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
+  integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==
+  dependencies:
+    ansi-regex "^6.0.1"
+
 strip-bom@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz"
@@ -10323,6 +10875,11 @@ strip-final-newline@^2.0.0:
   resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz"
   integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
 
+strip-final-newline@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz#52894c313fbff318835280aed60ff71ebf12b8fd"
+  integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
+
 strip-indent@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz"
@@ -10340,48 +10897,47 @@ style-search@^0.1.0:
   resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902"
   integrity sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=
 
-stylehacks@^4.0.0:
-  version "4.0.3"
-  resolved "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz"
-  integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==
+stylehacks@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-6.0.0.tgz#9fdd7c217660dae0f62e14d51c89f6c01b3cb738"
+  integrity sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==
   dependencies:
-    browserslist "^4.0.0"
-    postcss "^7.0.0"
-    postcss-selector-parser "^3.0.0"
+    browserslist "^4.21.4"
+    postcss-selector-parser "^6.0.4"
 
-stylelint-config-recommended-scss@^8.0.0:
-  version "8.0.0"
-  resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-8.0.0.tgz#1c1e93e619fe2275d4c1067928d92e0614f7d64f"
-  integrity sha512-BxjxEzRaZoQb7Iinc3p92GS6zRdRAkIuEu2ZFLTxJK2e1AIcCb5B5MXY9KOXdGTnYFZ+KKx6R4Fv9zU6CtMYPQ==
+stylelint-config-recommended-scss@^9.0.0:
+  version "9.0.0"
+  resolved "https://registry.yarnpkg.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-9.0.0.tgz#e755cf3654f3a3a6d7bdf84fe0a814595754a386"
+  integrity sha512-5e9pn3Ztfncd8s9OqvvCW7tZpYe+vGmPi7VEXX7XEp+Kj38PnKCrvFCBL+hQ7rkD4d5QzjB3BxlFEyo/30UWUw==
   dependencies:
     postcss-scss "^4.0.2"
-    stylelint-config-recommended "^9.0.0"
-    stylelint-scss "^4.0.0"
+    stylelint-config-recommended "^10.0.1"
+    stylelint-scss "^4.4.0"
 
-stylelint-config-recommended@^9.0.0:
-  version "9.0.0"
-  resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-9.0.0.tgz#1c9e07536a8cd875405f8ecef7314916d94e7e40"
-  integrity sha512-9YQSrJq4NvvRuTbzDsWX3rrFOzOlYBmZP+o513BJN/yfEmGSr0AxdvrWs0P/ilSpVV/wisamAHu5XSk8Rcf4CQ==
+stylelint-config-recommended@^10.0.1:
+  version "10.0.1"
+  resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-10.0.1.tgz#25a8828acf6cde87dac6db2950c8c4ed82a69ae1"
+  integrity sha512-TQ4xQ48tW4QSlODcti7pgSRqBZcUaBzuh0jPpfiMhwJKBPkqzTIAU+IrSWL/7BgXlOM90DjB7YaNgFpx8QWhuA==
 
-stylelint-config-standard-scss@^6.1.0:
-  version "6.1.0"
-  resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-6.1.0.tgz#a6cddd2a9430578b92fc89726a59474d5548a444"
-  integrity sha512-iZ2B5kQT2G3rUzx+437cEpdcnFOQkwnwqXuY8Z0QUwIHQVE8mnYChGAquyKFUKZRZ0pRnrciARlPaR1RBtPb0Q==
+stylelint-config-standard-scss@^7.0.1:
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/stylelint-config-standard-scss/-/stylelint-config-standard-scss-7.0.1.tgz#4ba83fa19e1508937f7e02674e085cf94fc1a145"
+  integrity sha512-m5sRdtsB1F5fnC1Ozla7ryftU47wVpO+HWd+JQTqeoG0g/oPh5EfbWfcVHbNCEtuoHfALIySiUWS20pz2hX6jA==
   dependencies:
-    stylelint-config-recommended-scss "^8.0.0"
-    stylelint-config-standard "^29.0.0"
+    stylelint-config-recommended-scss "^9.0.0"
+    stylelint-config-standard "^30.0.1"
 
-stylelint-config-standard@^29.0.0:
-  version "29.0.0"
-  resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-29.0.0.tgz#4cc0e0f05512a39bb8b8e97853247d3a95d66fa2"
-  integrity sha512-uy8tZLbfq6ZrXy4JKu3W+7lYLgRQBxYTUUB88vPgQ+ZzAxdrvcaSUW9hOMNLYBnwH+9Kkj19M2DHdZ4gKwI7tg==
+stylelint-config-standard@^30.0.1:
+  version "30.0.1"
+  resolved "https://registry.yarnpkg.com/stylelint-config-standard/-/stylelint-config-standard-30.0.1.tgz#a84d57c240c37f7db47023ab9d2e64c49090e1eb"
+  integrity sha512-NbeHOmpRQhjZh5XB1B/S4MLRWvz4xxAxeDBjzl0tY2xEcayNhLbaRGF0ZQzq+DQZLCcPpOHeS2Ru1ydbkhkmLg==
   dependencies:
-    stylelint-config-recommended "^9.0.0"
+    stylelint-config-recommended "^10.0.1"
 
-stylelint-scss@^4.0.0:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-4.2.0.tgz#e25fd390ee38a7e89fcfaec2a8f9dce2ec6ddee8"
-  integrity sha512-HHHMVKJJ5RM9pPIbgJ/XA67h9H0407G68Rm69H4fzFbFkyDMcTV1Byep3qdze5+fJ3c0U7mJrbj6S0Fg072uZA==
+stylelint-scss@^4.4.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-4.4.0.tgz#87ce9d049eff1ce67cce788780fbfda63099017e"
+  integrity sha512-Qy66a+/30aylFhPmUArHhVsHOun1qrO93LGT15uzLuLjWS7hKDfpFm34mYo1ndR4MCo8W4bEZM1+AlJRJORaaw==
   dependencies:
     lodash "^4.17.21"
     postcss-media-query-parser "^0.2.3"
@@ -10389,16 +10945,20 @@ stylelint-scss@^4.0.0:
     postcss-selector-parser "^6.0.6"
     postcss-value-parser "^4.1.0"
 
-stylelint@^14.16.1:
-  version "14.16.1"
-  resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-14.16.1.tgz#b911063530619a1bbe44c2b875fd8181ebdc742d"
-  integrity sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==
+stylelint@^15.3.0:
+  version "15.3.0"
+  resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-15.3.0.tgz#5f0f3264abeb29c54f571ea3f3934eba2c2be96d"
+  integrity sha512-9UYBYk7K9rtlKcTUDZrtntE840sZM00qyYBQHHe7tjwMNUsPsGvR6Fd43IxHEAhRrDLzpy3TVaHb6CReBB3eFg==
   dependencies:
-    "@csstools/selector-specificity" "^2.0.2"
+    "@csstools/css-parser-algorithms" "^2.0.1"
+    "@csstools/css-tokenizer" "^2.1.0"
+    "@csstools/media-query-list-parser" "^2.0.1"
+    "@csstools/selector-specificity" "^2.1.1"
     balanced-match "^2.0.0"
     colord "^2.9.3"
-    cosmiconfig "^7.1.0"
+    cosmiconfig "^8.1.0"
     css-functions-list "^3.1.0"
+    css-tree "^2.3.1"
     debug "^4.3.4"
     fast-glob "^3.2.12"
     fastest-levenshtein "^1.0.16"
@@ -10407,17 +10967,17 @@ stylelint@^14.16.1:
     globby "^11.1.0"
     globjoin "^0.1.4"
     html-tags "^3.2.0"
-    ignore "^5.2.1"
+    ignore "^5.2.4"
     import-lazy "^4.0.0"
     imurmurhash "^0.1.4"
     is-plain-object "^5.0.0"
-    known-css-properties "^0.26.0"
+    known-css-properties "^0.27.0"
     mathml-tag-names "^2.1.3"
     meow "^9.0.0"
     micromatch "^4.0.5"
     normalize-path "^3.0.0"
     picocolors "^1.0.0"
-    postcss "^8.4.19"
+    postcss "^8.4.21"
     postcss-media-query-parser "^0.2.3"
     postcss-resolve-nested-selector "^0.1.1"
     postcss-safe-parser "^6.0.0"
@@ -10427,11 +10987,11 @@ stylelint@^14.16.1:
     string-width "^4.2.3"
     strip-ansi "^6.0.1"
     style-search "^0.1.0"
-    supports-hyperlinks "^2.3.0"
+    supports-hyperlinks "^3.0.0"
     svg-tags "^1.0.0"
     table "^6.8.1"
     v8-compile-cache "^2.3.0"
-    write-file-atomic "^4.0.2"
+    write-file-atomic "^5.0.0"
 
 stylis@4.0.13:
   version "4.0.13"
@@ -10471,10 +11031,10 @@ supports-color@^8.0.0:
   dependencies:
     has-flag "^4.0.0"
 
-supports-hyperlinks@^2.3.0:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz#3943544347c1ff90b15effb03fc14ae45ec10624"
-  integrity sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==
+supports-hyperlinks@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-3.0.0.tgz#c711352a5c89070779b4dad54c05a2f14b15c94b"
+  integrity sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==
   dependencies:
     has-flag "^4.0.0"
     supports-color "^7.0.0"
@@ -10489,31 +11049,24 @@ svg-tags@^1.0.0:
   resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"
   integrity sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=
 
-svgo@^1.0.0:
-  version "1.3.2"
-  resolved "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz"
-  integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==
-  dependencies:
-    chalk "^2.4.1"
-    coa "^2.0.2"
-    css-select "^2.0.0"
-    css-select-base-adapter "^0.1.1"
-    css-tree "1.0.0-alpha.37"
-    csso "^4.0.2"
-    js-yaml "^3.13.1"
-    mkdirp "~0.5.1"
-    object.values "^1.1.0"
-    sax "~1.2.4"
-    stable "^0.1.8"
-    unquote "~1.1.1"
-    util.promisify "~1.0.0"
+svgo@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/svgo/-/svgo-3.0.2.tgz#5e99eeea42c68ee0dc46aa16da093838c262fe0a"
+  integrity sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==
+  dependencies:
+    "@trysound/sax" "0.2.0"
+    commander "^7.2.0"
+    css-select "^5.1.0"
+    css-tree "^2.2.1"
+    csso "^5.0.5"
+    picocolors "^1.0.0"
 
 symbol-tree@^3.2.4:
   version "3.2.4"
   resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz"
   integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
 
-table@^6.0.9, table@^6.8.1:
+table@^6.8.1:
   version "6.8.1"
   resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf"
   integrity sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==
@@ -10556,22 +11109,7 @@ tempy@^0.6.0:
     type-fest "^0.16.0"
     unique-string "^2.0.0"
 
-terser-webpack-plugin@^1.4.3:
-  version "1.4.5"
-  resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz"
-  integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==
-  dependencies:
-    cacache "^12.0.2"
-    find-cache-dir "^2.1.0"
-    is-wsl "^1.1.0"
-    schema-utils "^1.0.0"
-    serialize-javascript "^4.0.0"
-    source-map "^0.6.1"
-    terser "^4.1.2"
-    webpack-sources "^1.4.0"
-    worker-farm "^1.7.0"
-
-terser-webpack-plugin@^4.2.3:
+terser-webpack-plugin@^1.4.3, terser-webpack-plugin@^4.2.3:
   version "4.2.3"
   resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz"
   integrity sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==
@@ -10586,15 +11124,6 @@ terser-webpack-plugin@^4.2.3:
     terser "^5.3.4"
     webpack-sources "^1.4.3"
 
-terser@^4.1.2:
-  version "4.8.1"
-  resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f"
-  integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==
-  dependencies:
-    commander "^2.20.0"
-    source-map "~0.6.1"
-    source-map-support "~0.5.12"
-
 terser@^5.0.0, terser@^5.3.4:
   version "5.13.1"
   resolved "https://registry.yarnpkg.com/terser/-/terser-5.13.1.tgz#66332cdc5a01b04a224c9fad449fc1a18eaa1799"
@@ -10650,13 +11179,10 @@ throng@^4.0.0:
   dependencies:
     lodash.defaults "^4.0.1"
 
-through2@^2.0.0:
-  version "2.0.5"
-  resolved "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz"
-  integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
-  dependencies:
-    readable-stream "~2.3.6"
-    xtend "~4.0.1"
+through@^2.3.8:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+  integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
 
 thunky@^1.0.2:
   version "1.1.0"
@@ -10670,11 +11196,6 @@ timers-browserify@^2.0.4:
   dependencies:
     setimmediate "^1.0.4"
 
-timsort@^0.3.0:
-  version "0.3.0"
-  resolved "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz"
-  integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
-
 tiny-invariant@^1.0.2:
   version "1.2.0"
   resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz"
@@ -10771,6 +11292,13 @@ tr46@^3.0.0:
   dependencies:
     punycode "^2.1.1"
 
+tr46@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469"
+  integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==
+  dependencies:
+    punycode "^2.3.0"
+
 tr46@~0.0.3:
   version "0.0.3"
   resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz"
@@ -10791,6 +11319,23 @@ tsconfig-paths@^3.14.1:
     minimist "^1.2.6"
     strip-bom "^3.0.0"
 
+tslib@^1.8.1:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+  integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+
+tslib@^2.1.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf"
+  integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
+
+tsutils@^3.21.0:
+  version "3.21.0"
+  resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
+  integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
+  dependencies:
+    tslib "^1.8.1"
+
 tty-browserify@0.0.0:
   version "0.0.0"
   resolved "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz"
@@ -10850,6 +11395,11 @@ type-fest@^0.20.2:
   resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz"
   integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
 
+type-fest@^0.21.3:
+  version "0.21.3"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
+  integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
+
 type-fest@^0.6.0:
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
@@ -10878,10 +11428,10 @@ type@^2.5.0:
   resolved "https://registry.npmjs.org/type/-/type-2.5.0.tgz"
   integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==
 
-typedarray@^0.0.6:
-  version "0.0.6"
-  resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz"
-  integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
+typescript@^5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.3.tgz#fe976f0c826a88d0a382007681cbb2da44afdedf"
+  integrity sha512-xv8mOEDnigb/tN9PSMTwSEqAnUvkoXMQlicOb0IUVDBSQCgBSaAAROUZYy2IcUy5qU6XajK5jjjO7TMWqBTKZA==
 
 unbox-primitive@^1.0.2:
   version "1.0.2"
@@ -10936,16 +11486,6 @@ union-value@^1.0.0:
     is-extendable "^0.1.1"
     set-value "^2.0.1"
 
-uniq@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz"
-  integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=
-
-uniqs@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz"
-  integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI=
-
 unique-filename@^1.1.1:
   version "1.1.1"
   resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz"
@@ -10987,11 +11527,6 @@ unpipe@1.0.0, unpipe@~1.0.0:
   resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
   integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
 
-unquote@~1.1.1:
-  version "1.1.1"
-  resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz"
-  integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
-
 unset-value@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz"
@@ -11005,10 +11540,10 @@ upath@^1.1.1, upath@^1.2.0:
   resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz"
   integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
 
-update-browserslist-db@^1.0.9:
-  version "1.0.9"
-  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18"
-  integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==
+update-browserslist-db@^1.0.10:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
+  integrity sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==
   dependencies:
     escalade "^3.1.1"
     picocolors "^1.0.0"
@@ -11071,10 +11606,10 @@ use@^3.1.0:
   resolved "https://registry.npmjs.org/use/-/use-3.1.1.tgz"
   integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
 
-utf-8-validate@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-6.0.0.tgz#91a169e91ee5441a2bab5f059f4a39cdd402caf2"
-  integrity sha512-OCJuwxQsnG51swYmNloViggxNOFO/leOZpnb/vVeoastJbrzrZZU7lGsYlUcdkCl9nsBu2nkKLjpljb3Ckvb/Q==
+utf-8-validate@^6.0.3:
+  version "6.0.3"
+  resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-6.0.3.tgz#7d8c936d854e86b24d1d655f138ee27d2636d777"
+  integrity sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==
   dependencies:
     node-gyp-build "^4.3.0"
 
@@ -11083,16 +11618,6 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
   resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
   integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
 
-util.promisify@~1.0.0:
-  version "1.0.1"
-  resolved "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz"
-  integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==
-  dependencies:
-    define-properties "^1.1.3"
-    es-abstract "^1.17.2"
-    has-symbols "^1.0.1"
-    object.getownpropertydescriptors "^2.1.0"
-
 util@0.10.3:
   version "0.10.3"
   resolved "https://registry.npmjs.org/util/-/util-0.10.3.tgz"
@@ -11122,7 +11647,7 @@ uuid@^8.3.1, uuid@^8.3.2:
   resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz"
   integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
 
-v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.1, v8-compile-cache@^2.3.0:
+v8-compile-cache@^2.1.1, v8-compile-cache@^2.3.0:
   version "2.3.0"
   resolved "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz"
   integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
@@ -11154,11 +11679,6 @@ vary@~1.1.2:
   resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
   integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
 
-vendors@^1.0.0:
-  version "1.0.4"
-  resolved "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz"
-  integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==
-
 vm-browserify@^1.0.1:
   version "1.1.2"
   resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz"
@@ -11247,11 +11767,12 @@ webpack-assets-manifest@^4.0.6:
     tapable "^1.0"
     webpack-sources "^1.0"
 
-webpack-bundle-analyzer@^4.7.0:
-  version "4.7.0"
-  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.7.0.tgz#33c1c485a7fcae8627c547b5c3328b46de733c66"
-  integrity sha512-j9b8ynpJS4K+zfO5GGwsAcQX4ZHpWV+yRiHDiL+bE0XHJ8NiPYLTNVQdlFYWxtpg9lfAQNlwJg16J9AJtFSXRg==
+webpack-bundle-analyzer@^4.8.0:
+  version "4.8.0"
+  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz#951b8aaf491f665d2ae325d8b84da229157b1d04"
+  integrity sha512-ZzoSBePshOKhr+hd8u6oCkZVwpVaXgpw23ScGLFpR6SjYI7+7iIWYarjN6OEYOfRt8o7ZyZZQk0DuMizJ+LEIg==
   dependencies:
+    "@discoveryjs/json-ext" "0.5.7"
     acorn "^8.0.4"
     acorn-walk "^8.0.0"
     chalk "^4.1.0"
@@ -11345,7 +11866,7 @@ webpack-merge@^5.8.0:
     clone-deep "^4.0.1"
     wildcard "^2.0.0"
 
-webpack-sources@^1.0, webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
+webpack-sources@^1.0, webpack-sources@^1.1.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3:
   version "1.4.3"
   resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz"
   integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
@@ -11416,6 +11937,14 @@ whatwg-url@^11.0.0:
     tr46 "^3.0.0"
     webidl-conversions "^7.0.0"
 
+whatwg-url@^12.0.0, whatwg-url@^12.0.1:
+  version "12.0.1"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c"
+  integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==
+  dependencies:
+    tr46 "^4.1.1"
+    webidl-conversions "^7.0.0"
+
 whatwg-url@^5.0.0:
   version "5.0.0"
   resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz"
@@ -11444,11 +11973,33 @@ which-boxed-primitive@^1.0.2:
     is-string "^1.0.5"
     is-symbol "^1.0.3"
 
+which-collection@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906"
+  integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==
+  dependencies:
+    is-map "^2.0.1"
+    is-set "^2.0.1"
+    is-weakmap "^2.0.1"
+    is-weakset "^2.0.1"
+
 which-module@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz"
   integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
 
+which-typed-array@^1.1.9:
+  version "1.1.9"
+  resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6"
+  integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==
+  dependencies:
+    available-typed-arrays "^1.0.5"
+    call-bind "^1.0.2"
+    for-each "^0.3.3"
+    gopd "^1.0.1"
+    has-tostringtag "^1.0.0"
+    is-typed-array "^1.1.10"
+
 which@^1.2.14, which@^1.2.9, which@^1.3.1:
   version "1.3.1"
   resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz"
@@ -11654,13 +12205,6 @@ workbox-window@6.5.4, workbox-window@^6.5.4:
     "@types/trusted-types" "^2.0.2"
     workbox-core "6.5.4"
 
-worker-farm@^1.7.0:
-  version "1.7.0"
-  resolved "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz"
-  integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==
-  dependencies:
-    errno "~0.1.7"
-
 wrap-ansi@^5.1.0:
   version "5.1.0"
   resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz"
@@ -11670,6 +12214,15 @@ wrap-ansi@^5.1.0:
     string-width "^3.0.0"
     strip-ansi "^5.0.0"
 
+wrap-ansi@^6.2.0:
+  version "6.2.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
+  integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
 wrap-ansi@^7.0.0:
   version "7.0.0"
   resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
@@ -11684,7 +12237,7 @@ wrappy@1:
   resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
   integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
 
-write-file-atomic@^4.0.1, write-file-atomic@^4.0.2:
+write-file-atomic@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd"
   integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==
@@ -11692,6 +12245,14 @@ write-file-atomic@^4.0.1, write-file-atomic@^4.0.2:
     imurmurhash "^0.1.4"
     signal-exit "^3.0.7"
 
+write-file-atomic@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-5.0.0.tgz#54303f117e109bf3d540261125c8ea5a7320fab0"
+  integrity sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w==
+  dependencies:
+    imurmurhash "^0.1.4"
+    signal-exit "^3.0.7"
+
 ws@^6.2.1:
   version "6.2.2"
   resolved "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz"
@@ -11704,10 +12265,10 @@ ws@^7.3.1:
   resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
   integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
 
-ws@^8.11.0, ws@^8.12.0:
-  version "8.12.0"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-8.12.0.tgz#485074cc392689da78e1828a9ff23585e06cddd8"
-  integrity sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==
+ws@^8.11.0, ws@^8.12.1, ws@^8.13.0:
+  version "8.13.0"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
+  integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
 
 xml-name-validator@^4.0.0:
   version "4.0.0"
@@ -11719,7 +12280,7 @@ xmlchars@^2.2.0:
   resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz"
   integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
 
-xtend@^4.0.0, xtend@~4.0.1:
+xtend@^4.0.0:
   version "4.0.2"
   resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz"
   integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
@@ -11749,6 +12310,11 @@ yaml@^1.10.0, yaml@^1.7.2:
   resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
   integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
 
+yaml@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.1.tgz#3014bf0482dcd15147aa8e56109ce8632cd60ce4"
+  integrity sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==
+
 yargs-parser@^13.1.2:
   version "13.1.2"
   resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz"
@@ -11783,10 +12349,10 @@ yargs@^13.3.2:
     y18n "^4.0.0"
     yargs-parser "^13.1.2"
 
-yargs@^17.3.1, yargs@^17.6.2:
-  version "17.6.2"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541"
-  integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==
+yargs@^17.3.1, yargs@^17.7.1:
+  version "17.7.1"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.1.tgz#34a77645201d1a8fc5213ace787c220eabbd0967"
+  integrity sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==
   dependencies:
     cliui "^8.0.1"
     escalade "^3.1.1"